#include "sqliteInt.h" #include "unity.h" #include #include /* Global DB handle for each test */ static sqlite3 *gDb = NULL; /* Helper: execute SQL and assert success */ static void execSQL(const char *zSql){ char *zErr = 0; int rc = sqlite3_exec(gDb, zSql, 0, 0, &zErr); if( rc!=SQLITE_OK ){ /* Use Unity assertion here (stdout not redirected) */ TEST_FAIL_MESSAGE(zErr ? zErr : "sqlite3_exec error"); } sqlite3_free(zErr); } /* Helper: make a Token from a C-string */ static Token makeToken(const char *z){ Token t; t.z = z; t.n = (int)strlen(z); t.dyn = 0; t.enc = 0; t.term = 0; return t; } /* Helper: build a SrcList with one entry, optionally with a database/schema name. ** The SrcList is allocated using SQLite allocators and will be owned and freed ** by sqlite3AlterRenameTable(). */ static SrcList* mkSrcList(sqlite3 *db, const char *zDb, const char *zTab){ Token tTab = makeToken(zTab); if( zDb ){ Token tDb = makeToken(zDb); return sqlite3SrcListAppend(db, 0, &tTab, &tDb); }else{ return sqlite3SrcListAppend(db, 0, &tTab, 0); } } void setUp(void) { int rc = sqlite3_initialize(); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); rc = sqlite3_open(":memory:", &gDb); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); } void tearDown(void) { if( gDb ){ sqlite3_close(gDb); gDb = NULL; } } /* Success case: rename a regular table to a new unique name. ** Expect: no parse error, VDBE generated (pVdbe != NULL). */ void test_sqlite3AlterRenameTable_success_generates_vdbe(void){ execSQL("CREATE TABLE t1(a)"); Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb; SrcList *pSrc = mkSrcList(gDb, "main", "t1"); Token newName = makeToken("t2"); sqlite3BtreeEnterAll(gDb); sqlite3AlterRenameTable(&parse, pSrc, &newName); sqlite3BtreeLeaveAll(gDb); TEST_ASSERT_EQUAL_INT(0, parse.nErr); TEST_ASSERT_NULL(parse.zErrMsg); TEST_ASSERT_NOT_NULL(parse.pVdbe); if( parse.pVdbe ) sqlite3VdbeDelete(parse.pVdbe); } /* Error: rename to a name that already exists as another table */ void test_sqlite3AlterRenameTable_error_duplicate_name_table(void){ execSQL("CREATE TABLE t1(a)"); execSQL("CREATE TABLE t2(b)"); Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb; SrcList *pSrc = mkSrcList(gDb, "main", "t1"); Token newName = makeToken("t2"); sqlite3BtreeEnterAll(gDb); sqlite3AlterRenameTable(&parse, pSrc, &newName); sqlite3BtreeLeaveAll(gDb); TEST_ASSERT_GREATER_THAN_INT(0, parse.nErr); TEST_ASSERT_NOT_NULL(parse.zErrMsg); TEST_ASSERT_NULL(parse.pVdbe); if( parse.zErrMsg ) sqlite3DbFree(gDb, parse.zErrMsg); } /* Error: rename to a name that already exists as an index */ void test_sqlite3AlterRenameTable_error_duplicate_name_index(void){ execSQL("CREATE TABLE t1(a)"); execSQL("CREATE INDEX t2 ON t1(a)"); Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb; SrcList *pSrc = mkSrcList(gDb, "main", "t1"); Token newName = makeToken("t2"); sqlite3BtreeEnterAll(gDb); sqlite3AlterRenameTable(&parse, pSrc, &newName); sqlite3BtreeLeaveAll(gDb); TEST_ASSERT_GREATER_THAN_INT(0, parse.nErr); TEST_ASSERT_NOT_NULL(parse.zErrMsg); TEST_ASSERT_NULL(parse.pVdbe); if( parse.zErrMsg ) sqlite3DbFree(gDb, parse.zErrMsg); } /* Error: attempt to alter a view */ void test_sqlite3AlterRenameTable_error_view_not_alterable(void){ execSQL("CREATE TABLE t1(a)"); execSQL("CREATE VIEW v1 AS SELECT a FROM t1"); Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb; SrcList *pSrc = mkSrcList(gDb, "main", "v1"); Token newName = makeToken("v2"); sqlite3BtreeEnterAll(gDb); sqlite3AlterRenameTable(&parse, pSrc, &newName); sqlite3BtreeLeaveAll(gDb); TEST_ASSERT_GREATER_THAN_INT(0, parse.nErr); TEST_ASSERT_NOT_NULL(parse.zErrMsg); TEST_ASSERT_NULL(parse.pVdbe); if( parse.zErrMsg ) sqlite3DbFree(gDb, parse.zErrMsg); } /* Error: attempt to alter a system table (sqlite_sequence) */ void test_sqlite3AlterRenameTable_error_system_table_not_alterable(void){ /* Ensure sqlite_sequence exists by creating AUTOINCREMENT table and inserting */ execSQL("CREATE TABLE tauto(id INTEGER PRIMARY KEY AUTOINCREMENT, x)"); execSQL("INSERT INTO tauto(x) VALUES (1)"); /* Now attempt to rename sqlite_sequence itself */ Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb; SrcList *pSrc = mkSrcList(gDb, "main", "sqlite_sequence"); Token newName = makeToken("seq2"); sqlite3BtreeEnterAll(gDb); sqlite3AlterRenameTable(&parse, pSrc, &newName); sqlite3BtreeLeaveAll(gDb); TEST_ASSERT_GREATER_THAN_INT(0, parse.nErr); TEST_ASSERT_NOT_NULL(parse.zErrMsg); TEST_ASSERT_NULL(parse.pVdbe); if( parse.zErrMsg ) sqlite3DbFree(gDb, parse.zErrMsg); } /* Error: rename to a reserved/internal name (starts with sqlite_) */ void test_sqlite3AlterRenameTable_error_reserved_new_name(void){ execSQL("CREATE TABLE t1(a)"); Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb; SrcList *pSrc = mkSrcList(gDb, "main", "t1"); Token newName = makeToken("sqlite_stat1"); sqlite3BtreeEnterAll(gDb); sqlite3AlterRenameTable(&parse, pSrc, &newName); sqlite3BtreeLeaveAll(gDb); TEST_ASSERT_GREATER_THAN_INT(0, parse.nErr); TEST_ASSERT_NOT_NULL(parse.zErrMsg); TEST_ASSERT_NULL(parse.pVdbe); if( parse.zErrMsg ) sqlite3DbFree(gDb, parse.zErrMsg); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_sqlite3AlterRenameTable_success_generates_vdbe); RUN_TEST(test_sqlite3AlterRenameTable_error_duplicate_name_table); RUN_TEST(test_sqlite3AlterRenameTable_error_duplicate_name_index); RUN_TEST(test_sqlite3AlterRenameTable_error_view_not_alterable); RUN_TEST(test_sqlite3AlterRenameTable_error_system_table_not_alterable); RUN_TEST(test_sqlite3AlterRenameTable_error_reserved_new_name); return UNITY_END(); }