#include "sqliteInt.h" #include "unity.h" #include #include /* Unity setUp/tearDown */ void setUp(void) { /* No global setup required */ } void tearDown(void) { /* No global teardown required */ } /* Helper: create a SrcList with one item for table zTab in database zDb (or NULL for main) */ static SrcList *makeSrcListOne(sqlite3 *db, const char *zDb, const char *zTab){ /* SrcList has a flexible array a[1], so sizeof(SrcList) is sufficient for one element */ SrcList *p = (SrcList*)sqlite3DbMallocZero(db, sizeof(SrcList)); TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3DbMallocZero failed creating SrcList"); p->nAlloc = 1; p->nSrc = 1; p->a[0].zName = sqlite3DbStrDup(db, zTab); TEST_ASSERT_NOT_NULL_MESSAGE(p->a[0].zName, "sqlite3DbStrDup failed for table name"); if( zDb ){ p->a[0].zDatabase = sqlite3DbStrDup(db, zDb); TEST_ASSERT_NOT_NULL_MESSAGE(p->a[0].zDatabase, "sqlite3DbStrDup failed for database name"); } return p; } /* Helper: make a Token from a C string literal */ static Token makeToken(const char *z){ Token t; t.z = z; t.n = (int)strlen(z); return t; } /* Helper: initialize a Parse object for a given db */ static void initParse(Parse *pParse, sqlite3 *db){ memset(pParse, 0, sizeof(*pParse)); pParse->db = db; } /* Helper: exec SQL and assert SQLITE_OK */ static void execSqlOk(sqlite3 *db, const char *zSql){ int rc = sqlite3_exec(db, zSql, 0, 0, 0); if( rc!=SQLITE_OK ){ const char *err = sqlite3_errmsg(db); char msg[512]; sqlite3_snprintf(sizeof(msg), msg, "SQL exec failed (%d): %s; SQL: %s", rc, err ? err : "(null)", zSql); TEST_FAIL_MESSAGE(msg); } } /* Test: Nonexistent table should cause early return: no VDBE and an error recorded */ void test_sqlite3AlterDropConstraint_nonexistent_table(void){ sqlite3 *db = 0; TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); Parse p; initParse(&p, db); SrcList *pSrc = makeSrcListOne(db, NULL, "nosuchtable"); Token cons = makeToken("c1"); sqlite3AlterDropConstraint(&p, pSrc, &cons, 0); TEST_ASSERT_NULL_MESSAGE(p.pVdbe, "VDBE should not be created for nonexistent table"); TEST_ASSERT_TRUE_MESSAGE(p.nErr>0, "An error should be recorded for nonexistent table"); sqlite3SrcListDelete(db, pSrc); if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe); sqlite3_close(db); } /* Test: Existing table, drop NOT NULL from existing column -> code generated (VDBE created) */ void test_sqlite3AlterDropConstraint_drop_not_null_on_existing_col_generates_code(void){ sqlite3 *db = 0; TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); execSqlOk(db, "CREATE TABLE t1(a INTEGER NOT NULL, b TEXT);"); Parse p; initParse(&p, db); SrcList *pSrc = makeSrcListOne(db, NULL, "t1"); Token col = makeToken("a"); sqlite3AlterDropConstraint(&p, pSrc, 0, &col); TEST_ASSERT_NOT_NULL_MESSAGE(p.pVdbe, "VDBE should be created for valid DROP NOT NULL operation"); /* We do not execute the VDBE here; we only ensure code generation occurred. */ sqlite3SrcListDelete(db, pSrc); if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe); sqlite3_close(db); } /* Test: Existing table, drop NOT NULL from nonexistent column -> error, no VDBE */ void test_sqlite3AlterDropConstraint_nonexistent_column_reports_error(void){ sqlite3 *db = 0; TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); execSqlOk(db, "CREATE TABLE t2(a INTEGER NOT NULL, b TEXT);"); Parse p; initParse(&p, db); SrcList *pSrc = makeSrcListOne(db, NULL, "t2"); Token col = makeToken("no_such_col"); sqlite3AlterDropConstraint(&p, pSrc, 0, &col); TEST_ASSERT_NULL_MESSAGE(p.pVdbe, "VDBE should not be created when column does not exist"); TEST_ASSERT_TRUE_MESSAGE(p.nErr>0, "An error should be recorded for nonexistent column"); sqlite3SrcListDelete(db, pSrc); if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe); sqlite3_close(db); } /* Test: Existing table with named constraint, drop named constraint -> code generated */ void test_sqlite3AlterDropConstraint_drop_named_constraint_generates_code(void){ sqlite3 *db = 0; TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); /* Create a table with a named CHECK constraint */ execSqlOk(db, "CREATE TABLE t3(x INT, y INT, CONSTRAINT c1 CHECK(x>0));"); Parse p; initParse(&p, db); SrcList *pSrc = makeSrcListOne(db, NULL, "t3"); Token cons = makeToken("c1"); sqlite3AlterDropConstraint(&p, pSrc, &cons, 0); TEST_ASSERT_NOT_NULL_MESSAGE(p.pVdbe, "VDBE should be created for valid DROP CONSTRAINT operation"); sqlite3SrcListDelete(db, pSrc); if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe); sqlite3_close(db); } int main(void){ UNITY_BEGIN(); RUN_TEST(test_sqlite3AlterDropConstraint_nonexistent_table); RUN_TEST(test_sqlite3AlterDropConstraint_drop_not_null_on_existing_col_generates_code); RUN_TEST(test_sqlite3AlterDropConstraint_nonexistent_column_reports_error); RUN_TEST(test_sqlite3AlterDropConstraint_drop_named_constraint_generates_code); return UNITY_END(); }