#include "sqliteInt.h" #include "unity.h" #include #include #include static sqlite3 *gDb = NULL; /* The wrapper for the static function under test is assumed to be provided. */ extern void test_dropConstraintFunc(sqlite3_context*, int, sqlite3_value**); /* Register the test function as an SQL scalar function "dropc". */ static void register_dropc(sqlite3 *db){ int rc = sqlite3_create_function(db, "dropc", 2, SQLITE_UTF8, NULL, test_dropConstraintFunc, NULL, NULL); TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_create_function(dropc) failed"); } /* Helper: call dropc(SQL, TEXT) and capture result or error. On success, returns 0 and *pzOut is sqlite3_malloc()'d string (caller must sqlite3_free()). On error, returns 1 and *pzErr is sqlite3_malloc()'d string (caller must sqlite3_free()). */ static int call_dropc_text(sqlite3 *db, const char *zSql, const char *zName, char **pzOut, char **pzErr){ int rc; sqlite3_stmt *pStmt = NULL; *pzOut = NULL; *pzErr = NULL; rc = sqlite3_prepare_v2(db, "SELECT dropc(?1, ?2)", -1, &pStmt, NULL); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); return 1; } rc = sqlite3_bind_text(pStmt, 1, zSql, -1, SQLITE_TRANSIENT); if( rc==SQLITE_OK ){ rc = sqlite3_bind_text(pStmt, 2, zName, -1, SQLITE_TRANSIENT); } if( rc!=SQLITE_OK ){ sqlite3_finalize(pStmt); *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); return 1; } rc = sqlite3_step(pStmt); if( rc==SQLITE_ROW ){ const unsigned char *z = sqlite3_column_text(pStmt, 0); if( z ){ *pzOut = sqlite3_mprintf("%s", (const char*)z); }else{ *pzOut = NULL; /* explicit NULL result */ } sqlite3_finalize(pStmt); return 0; }else{ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); sqlite3_finalize(pStmt); return 1; } } /* Helper: call dropc(SQL, INTEGER) and capture result or error. */ static int call_dropc_int(sqlite3 *db, const char *zSql, int iCol, char **pzOut, char **pzErr){ int rc; sqlite3_stmt *pStmt = NULL; *pzOut = NULL; *pzErr = NULL; rc = sqlite3_prepare_v2(db, "SELECT dropc(?1, ?2)", -1, &pStmt, NULL); if( rc!=SQLITE_OK ){ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); return 1; } rc = sqlite3_bind_text(pStmt, 1, zSql, -1, SQLITE_TRANSIENT); if( rc==SQLITE_OK ){ rc = sqlite3_bind_int(pStmt, 2, iCol); } if( rc!=SQLITE_OK ){ sqlite3_finalize(pStmt); *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); return 1; } rc = sqlite3_step(pStmt); if( rc==SQLITE_ROW ){ const unsigned char *z = sqlite3_column_text(pStmt, 0); if( z ){ *pzOut = sqlite3_mprintf("%s", (const char*)z); }else{ *pzOut = NULL; } sqlite3_finalize(pStmt); return 0; }else{ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); sqlite3_finalize(pStmt); return 1; } } void setUp(void) { int rc = sqlite3_open(":memory:", &gDb); TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed"); register_dropc(gDb); } void tearDown(void) { if( gDb ){ sqlite3_close(gDb); gDb = NULL; } } /* Drop NOT NULL on first column */ void test_dropConstraintFunc_drop_not_null_first_column(void){ const char *inSql = "CREATE TABLE t1(a NOT NULL,b)"; const char *expect = "CREATE TABLE t1(a ,b)"; char *out = NULL, *err = NULL; int rc = call_dropc_int(gDb, inSql, 0, &out, &err); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, err ? err : "Unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING(expect, out); sqlite3_free(out); } /* Drop NOT NULL on column that is not NOT NULL -> should return original SQL unchanged */ void test_dropConstraintFunc_drop_not_null_noop_when_absent(void){ const char *inSql = "CREATE TABLE t1(a,b)"; char *out = NULL, *err = NULL; int rc = call_dropc_int(gDb, inSql, 1, &out, &err); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, err ? err : "Unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING(inSql, out); sqlite3_free(out); } /* Drop named CHECK table constraint and verify comma handling */ void test_dropConstraintFunc_drop_named_check_table_constraint(void){ const char *inSql = "CREATE TABLE t1(a,CONSTRAINT ck CHECK(a))"; const char *expect = "CREATE TABLE t1(a)"; char *out = NULL, *err = NULL; int rc = call_dropc_text(gDb, inSql, "ck", &out, &err); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, err ? err : "Unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING(expect, out); sqlite3_free(out); } /* Attempt to drop disallowed constraint (PRIMARY KEY) -> error */ void test_dropConstraintFunc_error_on_primary_key(void){ const char *inSql = "CREATE TABLE t1(a,CONSTRAINT pk PRIMARY KEY(a))"; char *out = NULL, *err = NULL; int rc = call_dropc_text(gDb, inSql, "pk", &out, &err); TEST_ASSERT_EQUAL_INT(1, rc); TEST_ASSERT_NULL(out); TEST_ASSERT_NOT_NULL(err); TEST_ASSERT_EQUAL_STRING("constraint may not be dropped: pk", err); sqlite3_free(err); } /* Named constraint not found -> error */ void test_dropConstraintFunc_error_on_no_such_constraint(void){ const char *inSql = "CREATE TABLE t1(a,CONSTRAINT ck CHECK(a))"; char *out = NULL, *err = NULL; int rc = call_dropc_text(gDb, inSql, "nosuch", &out, &err); TEST_ASSERT_EQUAL_INT(1, rc); TEST_ASSERT_NULL(out); TEST_ASSERT_NOT_NULL(err); TEST_ASSERT_EQUAL_STRING("no such constraint: nosuch", err); sqlite3_free(err); } /* Drop named NOT NULL column constraint */ void test_dropConstraintFunc_drop_named_not_null_column_constraint(void){ const char *inSql = "CREATE TABLE t1(a CONSTRAINT nn NOT NULL,b)"; const char *expect = "CREATE TABLE t1(a ,b)"; char *out = NULL, *err = NULL; int rc = call_dropc_text(gDb, inSql, "nn", &out, &err); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, err ? err : "Unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING(expect, out); sqlite3_free(out); } /* Case-insensitive and quoted constraint name matching */ void test_dropConstraintFunc_case_insensitive_and_quoted_name(void){ const char *inSql = "CREATE TABLE t1(a,CONSTRAINT \"MiX\" CHECK(a>0))"; const char *expect = "CREATE TABLE t1(a)"; char *out = NULL, *err = NULL; int rc = call_dropc_text(gDb, inSql, "mix", &out, &err); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, err ? err : "Unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING(expect, out); sqlite3_free(out); } /* Drop only the first of two consecutive CONSTRAINT names */ void test_dropConstraintFunc_drop_first_of_two_constraint_names(void){ const char *inSql = "CREATE TABLE t1(x CONSTRAINT one CONSTRAINT two NOT NULL)"; const char *expect = "CREATE TABLE t1(x CONSTRAINT two NOT NULL)"; char *out = NULL, *err = NULL; int rc = call_dropc_text(gDb, inSql, "one", &out, &err); TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, err ? err : "Unexpected error"); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_STRING(expect, out); sqlite3_free(out); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_dropConstraintFunc_drop_not_null_first_column); RUN_TEST(test_dropConstraintFunc_drop_not_null_noop_when_absent); RUN_TEST(test_dropConstraintFunc_drop_named_check_table_constraint); RUN_TEST(test_dropConstraintFunc_error_on_primary_key); RUN_TEST(test_dropConstraintFunc_error_on_no_such_constraint); RUN_TEST(test_dropConstraintFunc_drop_named_not_null_column_constraint); RUN_TEST(test_dropConstraintFunc_case_insensitive_and_quoted_name); RUN_TEST(test_dropConstraintFunc_drop_first_of_two_constraint_names); return UNITY_END(); }