Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_fkey_sqlite3FkClearTriggerCache.c227 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4 5/* Unity setup/teardown */6void setUp(void) {7  /* No global setup needed */8}9void tearDown(void) {10  /* No global teardown needed */11}12 13/* Helper: Execute SQL and assert success */14static void exec_sql_ok(sqlite3 *db, const char *zSql){15  char *zErr = 0;16  int rc = sqlite3_exec(db, zSql, 0, 0, &zErr);17  if( rc!=SQLITE_OK ){18    /* Provide better error message if available */19    const char *msg = zErr ? zErr : "sqlite3_exec failed";20    TEST_FAIL_MESSAGE(msg);21  }22  sqlite3_free(zErr);23}24 25/* Helper: Find database index by schema name (e.g., "main", "temp", "aux") */26static int get_db_index(sqlite3 *db, const char *zDb){27  int i;28  for(i=0; i<db->nDb; i++){29    if( db->aDb[i].zDbSName && 0==strcmp(db->aDb[i].zDbSName, zDb) ){30      return i;31    }32  }33  return -1;34}35 36/* Helper: Find a Table* by name and schema */37static Table* find_table(sqlite3 *db, const char *zDb, const char *zTab){38  Table *pTab = sqlite3FindTable(db, zTab, zDb);39  return pTab;40}41 42/* Helper: For all FKs of a table, populate apTrigger[0] and [1] with allocated Trigger objects */43static int populate_fk_triggers(sqlite3 *db, const char *zDb, const char *zTab){44  Table *pTab = find_table(db, zDb, zTab);45  TEST_ASSERT_NOT_NULL_MESSAGE(pTab, "Table not found in schema");46  int nSet = 0;47  for(FKey *pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){48    /* Allocate Trigger and minimal TriggerStep so fkTriggerDelete() can safely delete sub-components */49    Trigger *t0 = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));50    Trigger *t1 = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));51    TEST_ASSERT_NOT_NULL(t0);52    TEST_ASSERT_NOT_NULL(t1);53    TriggerStep *s0 = (TriggerStep*)sqlite3DbMallocZero(db, sizeof(TriggerStep));54    TriggerStep *s1 = (TriggerStep*)sqlite3DbMallocZero(db, sizeof(TriggerStep));55    TEST_ASSERT_NOT_NULL(s0);56    TEST_ASSERT_NOT_NULL(s1);57 58    t0->step_list = s0;59    t1->step_list = s1;60 61    pFKey->apTrigger[0] = t0;62    pFKey->apTrigger[1] = t1;63    nSet++;64  }65  return nSet;66}67 68/* Helper: Assert that for all FKs of a table, apTrigger[*] are NULL or non-NULL according to expectNull */69static void assert_fk_triggers_state(sqlite3 *db, const char *zDb, const char *zTab, int expectNull){70  Table *pTab = find_table(db, zDb, zTab);71  TEST_ASSERT_NOT_NULL_MESSAGE(pTab, "Table not found in schema");72  int nFk = 0;73  for(FKey *pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){74    nFk++;75    if( expectNull ){76      TEST_ASSERT_NULL(pFKey->apTrigger[0]);77      TEST_ASSERT_NULL(pFKey->apTrigger[1]);78    }else{79      TEST_ASSERT_NOT_NULL(pFKey->apTrigger[0]);80      TEST_ASSERT_NOT_NULL(pFKey->apTrigger[1]);81    }82  }83  /* Ensure table actually has FKs when checking non-NULL state */84  if( !expectNull ){85    TEST_ASSERT_MESSAGE(nFk>0, "Expected at least one foreign key on table");86  }87}88 89/* Test 1: Clears triggers for a single FK on main schema */90static void test_sqlite3FkClearTriggerCache_clears_triggers_for_main(void){91  sqlite3 *db = 0;92  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));93 94  exec_sql_ok(db, "PRAGMA foreign_keys=ON;");95  exec_sql_ok(db, "CREATE TABLE p(id INTEGER PRIMARY KEY);");96  exec_sql_ok(db, "CREATE TABLE c(id INT, pid INT, FOREIGN KEY(pid) REFERENCES p(id) ON DELETE CASCADE ON UPDATE CASCADE);");97 98  int nSet = populate_fk_triggers(db, "main", "c");99  TEST_ASSERT_MESSAGE(nSet>0, "No foreign keys found on table c to populate triggers");100  assert_fk_triggers_state(db, "main", "c", 0);101 102  sqlite3FkClearTriggerCache(db, 0); /* 0 is always 'main' */103  assert_fk_triggers_state(db, "main", "c", 1);104 105  sqlite3_close(db);106}107 108/* Test 2: Handles multiple tables and multiple FKs in main schema */109static void test_sqlite3FkClearTriggerCache_multiple_tables_and_fks(void){110  sqlite3 *db = 0;111  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));112 113  exec_sql_ok(db, "PRAGMA foreign_keys=ON;");114  exec_sql_ok(db, "CREATE TABLE p1(id INTEGER PRIMARY KEY);");115  exec_sql_ok(db, "CREATE TABLE p2(id INTEGER PRIMARY KEY);");116  exec_sql_ok(db, "CREATE TABLE c1(x INT, r1 INT, FOREIGN KEY(r1) REFERENCES p1(id));");117  /* c2 has two FKs */118  exec_sql_ok(db, "CREATE TABLE c2(a INT, r1 INT, r2 INT, "119                   "FOREIGN KEY(r1) REFERENCES p1(id) ON DELETE CASCADE, "120                   "FOREIGN KEY(r2) REFERENCES p2(id) ON UPDATE SET NULL);");121 122  int nSet1 = populate_fk_triggers(db, "main", "c1");123  int nSet2 = populate_fk_triggers(db, "main", "c2");124  TEST_ASSERT_MESSAGE(nSet1>0, "Expected FK on c1");125  TEST_ASSERT_MESSAGE(nSet2>=2, "Expected at least 2 FKs on c2");126 127  assert_fk_triggers_state(db, "main", "c1", 0);128  assert_fk_triggers_state(db, "main", "c2", 0);129 130  sqlite3FkClearTriggerCache(db, 0);131 132  assert_fk_triggers_state(db, "main", "c1", 1);133  assert_fk_triggers_state(db, "main", "c2", 1);134 135  sqlite3_close(db);136}137 138/* Test 3: Only clears triggers in specified database (scope by iDb) */139static void test_sqlite3FkClearTriggerCache_scoped_to_specified_db(void){140  sqlite3 *db = 0;141  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));142 143  exec_sql_ok(db, "PRAGMA foreign_keys=ON;");144  /* Main schema tables */145  exec_sql_ok(db, "CREATE TABLE p_main(id INTEGER PRIMARY KEY);");146  exec_sql_ok(db, "CREATE TABLE c_main(r INT, FOREIGN KEY(r) REFERENCES p_main(id));");147  /* Attach an auxiliary in-memory database and create FK there as well */148  exec_sql_ok(db, "ATTACH ':memory:' AS aux;");149  exec_sql_ok(db, "CREATE TABLE aux.p_aux(id INTEGER PRIMARY KEY);");150  exec_sql_ok(db, "CREATE TABLE aux.c_aux(r INT, FOREIGN KEY(r) REFERENCES p_aux(id) ON DELETE CASCADE);");151 152  int nSetMain = populate_fk_triggers(db, "main", "c_main");153  int nSetAux  = populate_fk_triggers(db, "aux",  "c_aux");154  TEST_ASSERT_MESSAGE(nSetMain>0, "Expected FK on c_main");155  TEST_ASSERT_MESSAGE(nSetAux>0,  "Expected FK on c_aux");156 157  assert_fk_triggers_state(db, "main", "c_main", 0);158  assert_fk_triggers_state(db, "aux",  "c_aux",  0);159 160  /* Clear only main (iDb=0) */161  sqlite3FkClearTriggerCache(db, 0);162  assert_fk_triggers_state(db, "main", "c_main", 1);163  /* Aux should remain intact */164  assert_fk_triggers_state(db, "aux",  "c_aux",  0);165 166  /* Now clear aux specifically */167  int iAux = get_db_index(db, "aux");168  TEST_ASSERT_MESSAGE(iAux>=0, "Could not locate 'aux' database index");169  sqlite3FkClearTriggerCache(db, iAux);170  assert_fk_triggers_state(db, "aux",  "c_aux",  1);171 172  sqlite3_close(db);173}174 175/* Test 4: No-op safety when there are no FKs or triggers; also with a non-ordinary object present */176static void test_sqlite3FkClearTriggerCache_noop_when_no_fk(void){177  sqlite3 *db = 0;178  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));179 180  exec_sql_ok(db, "CREATE TABLE t0(a INTEGER);");181  exec_sql_ok(db, "CREATE VIEW v0 AS SELECT a FROM t0;");182 183  Table *pT0 = find_table(db, "main", "t0");184  TEST_ASSERT_NOT_NULL(pT0);185  TEST_ASSERT_NULL(pT0->u.tab.pFKey); /* No foreign keys on t0 */186 187  /* Should not crash or change anything */188  sqlite3FkClearTriggerCache(db, 0);189 190  /* Still no foreign keys */191  TEST_ASSERT_NULL(pT0->u.tab.pFKey);192 193  sqlite3_close(db);194}195 196/* Test 5: Idempotence - safe to call multiple times */197static void test_sqlite3FkClearTriggerCache_idempotent(void){198  sqlite3 *db = 0;199  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));200 201  exec_sql_ok(db, "PRAGMA foreign_keys=ON;");202  exec_sql_ok(db, "CREATE TABLE p(id INTEGER PRIMARY KEY);");203  exec_sql_ok(db, "CREATE TABLE c(r INT, FOREIGN KEY(r) REFERENCES p(id));");204 205  int nSet = populate_fk_triggers(db, "main", "c");206  TEST_ASSERT_MESSAGE(nSet>0, "Expected FK on c");207  assert_fk_triggers_state(db, "main", "c", 0);208 209  sqlite3FkClearTriggerCache(db, 0);210  assert_fk_triggers_state(db, "main", "c", 1);211 212  /* Call again - should remain NULL and not crash */213  sqlite3FkClearTriggerCache(db, 0);214  assert_fk_triggers_state(db, "main", "c", 1);215 216  sqlite3_close(db);217}218 219int main(void){220  UNITY_BEGIN();221  RUN_TEST(test_sqlite3FkClearTriggerCache_clears_triggers_for_main);222  RUN_TEST(test_sqlite3FkClearTriggerCache_multiple_tables_and_fks);223  RUN_TEST(test_sqlite3FkClearTriggerCache_scoped_to_specified_db);224  RUN_TEST(test_sqlite3FkClearTriggerCache_noop_when_no_fk);225  RUN_TEST(test_sqlite3FkClearTriggerCache_idempotent);226  return UNITY_END();227}