#include "sqliteInt.h" #include "unity.h" #include #include /* The wrapper for the static function is provided by the build harness. */ extern int test_isRealTable(Parse *pParse, Table *pTab, int iOp); void setUp(void) { /* No global setup needed */ } void tearDown(void) { /* No global teardown needed */ } static sqlite3* open_mem_db(void){ sqlite3 *db = 0; int rc = sqlite3_open(":memory:", &db); TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed"); TEST_ASSERT_NOT_NULL_MESSAGE(db, "db is NULL after sqlite3_open"); return db; } static void clear_parse_err(sqlite3 *db, Parse *pParse){ if( pParse->zErrMsg ){ sqlite3DbFree(db, pParse->zErrMsg); pParse->zErrMsg = 0; } } /* Helper to initialize a Parse object */ static void init_parse(Parse *pParse, sqlite3 *db){ memset(pParse, 0, sizeof(*pParse)); pParse->db = db; } /* Make a "real" table (neither view nor virtual) */ static void make_real_table(Table *pTab, const char *zName){ memset(pTab, 0, sizeof(*pTab)); pTab->zName = (char*)zName; } /* Make a "view" table */ static void make_view_table(Table *pTab, const char *zName){ memset(pTab, 0, sizeof(*pTab)); pTab->zName = (char*)zName; /* Make IsView(pTab) evaluate true across SQLite versions */ static Select dummySelect; /* address used, not dereferenced */ memset(&dummySelect, 0, sizeof(dummySelect)); pTab->pSelect = &dummySelect; #ifdef TF_View pTab->tabFlags |= TF_View; #endif } /* Make a "virtual" table */ static void make_virtual_table(Table *pTab, const char *zName){ memset(pTab, 0, sizeof(*pTab)); pTab->zName = (char*)zName; #ifdef TF_Virtual pTab->tabFlags |= TF_Virtual; #else /* TF_Virtual should exist in modern SQLite, but ensure some non-zero to hint IsVirtual if macro differs */ pTab->tabFlags = 0xFFFFFFFF; #endif } void test_isRealTable_returns_zero_for_real_table(void){ sqlite3 *db = open_mem_db(); Parse parse; init_parse(&parse, db); Table tab; make_real_table(&tab, "t_real"); int rc = test_isRealTable(&parse, &tab, 0); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_NULL(parse.zErrMsg); sqlite3_close(db); } void test_isRealTable_view_rename_columns_sets_error(void){ sqlite3 *db = open_mem_db(); Parse parse; init_parse(&parse, db); Table tab; make_view_table(&tab, "v1"); int rc = test_isRealTable(&parse, &tab, 0); /* "rename columns of" */ TEST_ASSERT_EQUAL_INT(1, rc); TEST_ASSERT_NOT_NULL(parse.zErrMsg); /* Expect: cannot rename columns of view "v1" */ const char *msg = parse.zErrMsg; TEST_ASSERT_NOT_NULL(strstr(msg, "cannot ")); TEST_ASSERT_NOT_NULL(strstr(msg, "rename columns of")); TEST_ASSERT_NOT_NULL(strstr(msg, "view")); TEST_ASSERT_NOT_NULL(strstr(msg, "\"v1\"")); clear_parse_err(db, &parse); sqlite3_close(db); } void test_isRealTable_view_drop_column_sets_error(void){ sqlite3 *db = open_mem_db(); Parse parse; init_parse(&parse, db); Table tab; make_view_table(&tab, "v_drop"); int rc = test_isRealTable(&parse, &tab, 1); /* "drop column from" */ TEST_ASSERT_EQUAL_INT(1, rc); TEST_ASSERT_NOT_NULL(parse.zErrMsg); const char *msg = parse.zErrMsg; TEST_ASSERT_NOT_NULL(strstr(msg, "cannot ")); TEST_ASSERT_NOT_NULL(strstr(msg, "drop column from")); TEST_ASSERT_NOT_NULL(strstr(msg, "view")); TEST_ASSERT_NOT_NULL(strstr(msg, "\"v_drop\"")); clear_parse_err(db, &parse); sqlite3_close(db); } void test_isRealTable_virtual_edit_constraints_sets_error(void){ sqlite3 *db = open_mem_db(); Parse parse; init_parse(&parse, db); Table tab; make_virtual_table(&tab, "vt1"); int rc = test_isRealTable(&parse, &tab, 2); /* "edit constraints of" */ TEST_ASSERT_EQUAL_INT(1, rc); TEST_ASSERT_NOT_NULL(parse.zErrMsg); const char *msg = parse.zErrMsg; TEST_ASSERT_NOT_NULL(strstr(msg, "cannot ")); TEST_ASSERT_NOT_NULL(strstr(msg, "edit constraints of")); TEST_ASSERT_NOT_NULL(strstr(msg, "virtual table")); TEST_ASSERT_NOT_NULL(strstr(msg, "\"vt1\"")); clear_parse_err(db, &parse); sqlite3_close(db); } void test_isRealTable_both_view_and_virtual_prefers_virtual_in_message(void){ sqlite3 *db = open_mem_db(); Parse parse; init_parse(&parse, db); Table tab; make_view_table(&tab, "bothy"); /* Also mark as virtual so the second branch overwrites zType */ #ifdef TF_Virtual tab.tabFlags |= TF_Virtual; #endif int rc = test_isRealTable(&parse, &tab, 0); TEST_ASSERT_EQUAL_INT(1, rc); TEST_ASSERT_NOT_NULL(parse.zErrMsg); const char *msg = parse.zErrMsg; /* Ensure "virtual table" appears (wins over "view") */ TEST_ASSERT_NOT_NULL(strstr(msg, "virtual table")); TEST_ASSERT_NOT_NULL(strstr(msg, "\"bothy\"")); clear_parse_err(db, &parse); sqlite3_close(db); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_isRealTable_returns_zero_for_real_table); RUN_TEST(test_isRealTable_view_rename_columns_sets_error); RUN_TEST(test_isRealTable_view_drop_column_sets_error); RUN_TEST(test_isRealTable_virtual_edit_constraints_sets_error); RUN_TEST(test_isRealTable_both_view_and_virtual_prefers_virtual_in_message); return UNITY_END(); }