#include "sqliteInt.h" #include "unity.h" #include #include /* Global test database connection */ static sqlite3 *gDb = NULL; /* Wrapper provided by the module for testing the static function */ extern void test_renameTestSchema( Parse *pParse, const char *zDb, int bTemp, const char *zWhen, int bNoDQS ); /* Provide sqlite_rename_test function so nested parses compile and run */ static void xRenameTestFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv){ /* This function is used by nested parse SQL: return NULL to satisfy "=NULL" */ SQLITE_USE(argc); SQLITE_USE(argv); sqlite3_result_null(ctx); } /* Helper: initialize a fresh Parse object bound to gDb */ static void initParse(Parse *p){ memset(p, 0, sizeof(*p)); p->db = gDb; } /* Helper: cleanup VDBE and error message in Parse to avoid leaks */ static void cleanupParse(Parse *p){ if( p->pVdbe ){ sqlite3VdbeDelete(p->pVdbe); p->pVdbe = 0; } if( p->zErrMsg ){ sqlite3DbFree(p->db, p->zErrMsg); p->zErrMsg = 0; } } void setUp(void) { int rc = sqlite3_open(":memory:", &gDb); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); /* Register sqlite_rename_test(name in SQL) with 6 args */ rc = sqlite3_create_function(gDb, "sqlite_rename_test", 6, SQLITE_UTF8, 0, xRenameTestFunc, 0, 0); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); } void tearDown(void) { if( gDb ){ int rc = sqlite3_close(gDb); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); gDb = NULL; } } /* Basic sanity: sets colNamesSet, creates a VDBE, and emits some code */ void test_renameTestSchema_sets_colNamesSet_and_creates_vdbe(void){ Parse p; initParse(&p); test_renameTestSchema(&p, "main", 0, "during rename", 0); /* Verify colNamesSet is set */ TEST_ASSERT_EQUAL_INT(1, p.colNamesSet); /* Verify a VDBE was created and has some ops */ TEST_ASSERT_NOT_NULL(p.pVdbe); int nOp = sqlite3VdbeCurrentAddr(p.pVdbe); TEST_ASSERT_TRUE(nOp > 0); cleanupParse(&p); } /* bTemp affects the number of nested parses: 0 => two queries, 1 => one query */ void test_renameTestSchema_bTemp_affects_instruction_count(void){ Parse p1, p2; initParse(&p1); initParse(&p2); /* One nested parse (temp db only) */ test_renameTestSchema(&p1, "temp", 1, "phase A", 0); TEST_ASSERT_EQUAL_INT(1, p1.colNamesSet); TEST_ASSERT_NOT_NULL(p1.pVdbe); int nOp1 = sqlite3VdbeCurrentAddr(p1.pVdbe); TEST_ASSERT_TRUE(nOp1 > 0); /* Two nested parses (main + temp) */ test_renameTestSchema(&p2, "main", 0, "phase B", 0); TEST_ASSERT_EQUAL_INT(1, p2.colNamesSet); TEST_ASSERT_NOT_NULL(p2.pVdbe); int nOp2 = sqlite3VdbeCurrentAddr(p2.pVdbe); TEST_ASSERT_TRUE(nOp2 > 0); /* Expect more ops when bTemp==0 due to the extra nested SELECT */ TEST_ASSERT_TRUE(nOp2 > nOp1); cleanupParse(&p1); cleanupParse(&p2); } /* Using "temp" db name with bTemp==1 should still function correctly */ void test_renameTestSchema_temp_db_only(void){ Parse p; initParse(&p); test_renameTestSchema(&p, "temp", 1, "temp-only", 1 /* bNoDQS */); TEST_ASSERT_EQUAL_INT(1, p.colNamesSet); TEST_ASSERT_NOT_NULL(p.pVdbe); int nOp = sqlite3VdbeCurrentAddr(p.pVdbe); TEST_ASSERT_TRUE(nOp > 0); cleanupParse(&p); } /* Ensure NULL zWhen and varying bNoDQS do not crash and still generate code */ void test_renameTestSchema_null_when_and_noDQS_variations(void){ Parse pA, pB; initParse(&pA); initParse(&pB); /* zWhen NULL, bNoDQS = 0 */ test_renameTestSchema(&pA, "main", 0, NULL, 0); TEST_ASSERT_EQUAL_INT(1, pA.colNamesSet); TEST_ASSERT_NOT_NULL(pA.pVdbe); int nOpA = sqlite3VdbeCurrentAddr(pA.pVdbe); TEST_ASSERT_TRUE(nOpA > 0); /* zWhen NULL, bNoDQS = 1 */ test_renameTestSchema(&pB, "main", 0, NULL, 1); TEST_ASSERT_EQUAL_INT(1, pB.colNamesSet); TEST_ASSERT_NOT_NULL(pB.pVdbe); int nOpB = sqlite3VdbeCurrentAddr(pB.pVdbe); TEST_ASSERT_TRUE(nOpB > 0); cleanupParse(&pA); cleanupParse(&pB); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_renameTestSchema_sets_colNamesSet_and_creates_vdbe); RUN_TEST(test_renameTestSchema_bTemp_affects_instruction_count); RUN_TEST(test_renameTestSchema_temp_db_only); RUN_TEST(test_renameTestSchema_null_when_and_noDQS_variations); return UNITY_END(); }