AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Wrapper provided in the source for calling the static function under test */7void test_sqlite3CodeVerifySchemaAtToplevel(Parse *pToplevel, int iDb);8 9static void enterDbMutex(sqlite3 *db){10 if( db && db->mutex ){11 sqlite3_mutex_enter(db->mutex);12 }else{13 /* In some builds, db->mutex may be NULL; sqlite3_mutex_enter(NULL) is a no-op anyway */14 sqlite3_mutex_enter(db ? db->mutex : 0);15 }16}17static void leaveDbMutex(sqlite3 *db){18 if( db && db->mutex ){19 sqlite3_mutex_leave(db->mutex);20 }else{21 sqlite3_mutex_leave(db ? db->mutex : 0);22 }23}24 25void setUp(void) {26 /* No global setup needed */27}28void tearDown(void) {29 /* No global teardown needed */30}31 32/* Helper: create a fresh in-memory db connection */33static sqlite3* open_memory_db(void){34 sqlite3 *db = 0;35 int rc = sqlite3_open(":memory:", &db);36 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory database");37 TEST_ASSERT_NOT_NULL(db);38 return db;39}40 41/* Ensure main database B-Tree (aDb[0].pBt) is opened by creating a table */42static void ensure_main_btree_open(sqlite3 *db){43 char *errmsg = 0;44 int rc = sqlite3_exec(db, "CREATE TABLE t(x)", 0, 0, &errmsg);45 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, errmsg ? errmsg : "sqlite3_exec failed");46 if( errmsg ) sqlite3_free(errmsg);47 TEST_ASSERT_NOT_NULL_MESSAGE(db->aDb[0].pBt, "Main DB pBt should be non-NULL after creating a table");48}49 50/* Test: For iDb==1 (TEMP), when cookie bit is unset and explain==0,51 the bit is set and TEMP database is opened (pBt becomes non-NULL). */52void test_sqlite3CodeVerifySchemaAtToplevel_sets_mask_and_opens_temp_db(void){53 sqlite3 *db = open_memory_db();54 Parse p;55 memset(&p, 0, sizeof(p));56 p.db = db;57 p.explain = 0;58 DbMaskZero(p.cookieMask);59 60 TEST_ASSERT_NULL(db->aDb[1].pBt);61 62 enterDbMutex(db);63 test_sqlite3CodeVerifySchemaAtToplevel(&p, 1);64 leaveDbMutex(db);65 66 TEST_ASSERT_TRUE(DbMaskTest(p.cookieMask, 1));67 TEST_ASSERT_NOT_NULL_MESSAGE(db->aDb[1].pBt, "TEMP DB pBt should be opened");68 69 sqlite3_close(db);70}71 72/* Test: For iDb==1 (TEMP), when cookie bit is already set,73 the function does not attempt to open TEMP (pBt remains NULL). */74void test_sqlite3CodeVerifySchemaAtToplevel_no_open_when_bit_already_set_for_temp(void){75 sqlite3 *db = open_memory_db();76 Parse p;77 memset(&p, 0, sizeof(p));78 p.db = db;79 p.explain = 0;80 DbMaskZero(p.cookieMask);81 DbMaskSet(p.cookieMask, 1);82 83 TEST_ASSERT_NULL(db->aDb[1].pBt);84 85 enterDbMutex(db);86 test_sqlite3CodeVerifySchemaAtToplevel(&p, 1);87 leaveDbMutex(db);88 89 TEST_ASSERT_TRUE(DbMaskTest(p.cookieMask, 1));90 TEST_ASSERT_NULL_MESSAGE(db->aDb[1].pBt, "TEMP DB pBt should remain NULL when bit already set");91 92 sqlite3_close(db);93}94 95/* Test: For iDb==0 (main DB), when cookie bit is unset,96 the bit is set and TEMP DB is not opened/touched. */97void test_sqlite3CodeVerifySchemaAtToplevel_sets_mask_for_main_db_only(void){98 sqlite3 *db = open_memory_db();99 100 /* Ensure main database B-Tree exists to satisfy assert */101 ensure_main_btree_open(db);102 103 Parse p;104 memset(&p, 0, sizeof(p));105 p.db = db;106 p.explain = 0;107 DbMaskZero(p.cookieMask);108 109 void *initialTempBt = db->aDb[1].pBt; /* likely NULL */110 111 enterDbMutex(db);112 test_sqlite3CodeVerifySchemaAtToplevel(&p, 0);113 leaveDbMutex(db);114 115 TEST_ASSERT_TRUE(DbMaskTest(p.cookieMask, 0));116 TEST_ASSERT_EQUAL_PTR_MESSAGE(initialTempBt, db->aDb[1].pBt, "TEMP DB pBt should be unchanged for iDb=0");117 118 sqlite3_close(db);119}120 121/* Test: For iDb==1 (TEMP), when cookie bit is unset but Parse.explain is true,122 the bit is set but TEMP DB is not opened (pBt stays NULL). */123void test_sqlite3CodeVerifySchemaAtToplevel_with_explain_does_not_open_temp(void){124 sqlite3 *db = open_memory_db();125 126 Parse p;127 memset(&p, 0, sizeof(p));128 p.db = db;129 p.explain = 1; /* explain mode prevents temp DB open inside sqlite3OpenTempDatabase */130 DbMaskZero(p.cookieMask);131 132 TEST_ASSERT_NULL(db->aDb[1].pBt);133 134 enterDbMutex(db);135 test_sqlite3CodeVerifySchemaAtToplevel(&p, 1);136 leaveDbMutex(db);137 138 TEST_ASSERT_TRUE(DbMaskTest(p.cookieMask, 1));139 TEST_ASSERT_NULL_MESSAGE(db->aDb[1].pBt, "TEMP DB pBt should remain NULL when in explain mode");140 141 sqlite3_close(db);142}143 144/* Test: Idempotency for TEMP DB: first call opens TEMP and sets bit,145 second call should not change pBt or cookieMask. */146void test_sqlite3CodeVerifySchemaAtToplevel_idempotent_for_temp_db(void){147 sqlite3 *db = open_memory_db();148 149 Parse p;150 memset(&p, 0, sizeof(p));151 p.db = db;152 p.explain = 0;153 DbMaskZero(p.cookieMask);154 155 /* First call: set bit and open temp DB */156 enterDbMutex(db);157 test_sqlite3CodeVerifySchemaAtToplevel(&p, 1);158 leaveDbMutex(db);159 160 TEST_ASSERT_TRUE(DbMaskTest(p.cookieMask, 1));161 TEST_ASSERT_NOT_NULL(db->aDb[1].pBt);162 void *tempBtFirst = db->aDb[1].pBt;163 164 /* Second call: no-op regarding opening, pBt should remain the same */165 enterDbMutex(db);166 test_sqlite3CodeVerifySchemaAtToplevel(&p, 1);167 leaveDbMutex(db);168 169 TEST_ASSERT_TRUE(DbMaskTest(p.cookieMask, 1));170 TEST_ASSERT_EQUAL_PTR_MESSAGE(tempBtFirst, db->aDb[1].pBt, "TEMP DB pBt should not change on second call");171 172 sqlite3_close(db);173}174 175int main(void) {176 UNITY_BEGIN();177 178 RUN_TEST(test_sqlite3CodeVerifySchemaAtToplevel_sets_mask_and_opens_temp_db);179 RUN_TEST(test_sqlite3CodeVerifySchemaAtToplevel_no_open_when_bit_already_set_for_temp);180 RUN_TEST(test_sqlite3CodeVerifySchemaAtToplevel_sets_mask_for_main_db_only);181 RUN_TEST(test_sqlite3CodeVerifySchemaAtToplevel_with_explain_does_not_open_temp);182 RUN_TEST(test_sqlite3CodeVerifySchemaAtToplevel_idempotent_for_temp_db);183 184 return UNITY_END();185}