Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_fkey_sqlite3FkLocateIndex.c372 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6#include <stddef.h>7 8/* Global database handle for allocations via sqlite3DbMalloc* */9static sqlite3 *gDb = NULL;10 11/* Helper: create a Table with given columns and IPK index */12static Table* makeTable(sqlite3 *db, const char *zName, int nCol, const char **azColName, int iPKey){13  Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));14  TEST_ASSERT_NOT_NULL_MESSAGE(pTab, "OOM creating Table");15  pTab->zName = (char*)zName;16  pTab->nCol = (i16)nCol;17  pTab->iPKey = (i16)iPKey;18  if( nCol>0 ){19    Column *aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);20    TEST_ASSERT_NOT_NULL_MESSAGE(aCol, "OOM creating columns");21    for(int i=0;i<nCol;i++){22      aCol[i].zCnName = (char*)azColName[i];23      /* Leave zColl NULL to imply default "BINARY" unless tests set otherwise */24      aCol[i].zColl = 0;25    }26    pTab->aCol = aCol;27  }28  return pTab;29}30 31/* Helper: create and link an Index on pTab */32static Index* makeIndex(33  sqlite3 *db, const char *zName, Table *pTab,34  int nKeyCol, const int *aiCol, const char **azColl,35  int idxType, int onError, int isPartial36){37  Index *pIdx = (Index*)sqlite3DbMallocZero(db, sizeof(Index));38  TEST_ASSERT_NOT_NULL_MESSAGE(pIdx, "OOM creating Index");39  pIdx->zName = (char*)zName;40  pIdx->pTable = pTab;41  pIdx->nKeyCol = (i16)nKeyCol;42  pIdx->nColumn = (i16)nKeyCol;43  pIdx->aiColumn = (i16*)sqlite3DbMallocZero(db, sizeof(i16)*nKeyCol);44  TEST_ASSERT_NOT_NULL_MESSAGE(pIdx->aiColumn, "OOM creating aiColumn");45  pIdx->azColl = (char**)sqlite3DbMallocZero(db, sizeof(char*)*nKeyCol);46  TEST_ASSERT_NOT_NULL_MESSAGE(pIdx->azColl, "OOM creating azColl");47  for(int i=0;i<nKeyCol;i++){48    pIdx->aiColumn[i] = (i16)aiCol[i];49    pIdx->azColl[i] = (char*)(azColl ? azColl[i] : "BINARY");50  }51  pIdx->idxType = (u8)idxType;52  pIdx->onError = (u8)onError;53  pIdx->pPartIdxWhere = isPartial ? (Expr*)0x1 : 0;54  /* Link at head of list */55  pIdx->pNext = pTab->pIndex;56  pTab->pIndex = pIdx;57  return pIdx;58}59 60/* Helper: create an FKey with nCol entries */61static FKey* makeFKey(62  sqlite3 *db, Table *pFrom, const char *zTo,63  int nCol, const char **azParentCol, const int *aiFrom64){65  size_t sz = offsetof(FKey, aCol) + nCol * sizeof(((FKey*)0)->aCol[0]);66  FKey *pFKey = (FKey*)sqlite3DbMallocZero(db, (int)sz);67  TEST_ASSERT_NOT_NULL_MESSAGE(pFKey, "OOM creating FKey");68  pFKey->pFrom = pFrom;69  pFKey->zTo = (char*)zTo;70  pFKey->nCol = nCol;71  for(int i=0;i<nCol;i++){72    pFKey->aCol[i].zCol = (char*)azParentCol[i];73    pFKey->aCol[i].iFrom = aiFrom[i];74  }75  return pFKey;76}77 78/* Helper: free an Index list */79static void freeIndexList(sqlite3 *db, Index *pIdx){80  while(pIdx){81    Index *pNext = pIdx->pNext;82    if( pIdx->aiColumn ) sqlite3DbFree(db, pIdx->aiColumn);83    if( pIdx->azColl ) sqlite3DbFree(db, pIdx->azColl);84    sqlite3DbFree(db, pIdx);85    pIdx = pNext;86  }87}88 89/* Helper: free a Table (columns and indexes) */90static void freeTable(sqlite3 *db, Table *pTab){91  if( !pTab ) return;92  if( pTab->pIndex ) freeIndexList(db, pTab->pIndex);93  if( pTab->aCol ) sqlite3DbFree(db, pTab->aCol);94  sqlite3DbFree(db, pTab);95}96 97/* Helper: free an FKey */98static void freeFKey(sqlite3 *db, FKey *pFKey){99  if( pFKey ) sqlite3DbFree(db, pFKey);100}101 102void setUp(void) {103  int rc = sqlite3_open(":memory:", &gDb);104  TEST_ASSERT_EQUAL_INT(0, rc);105  TEST_ASSERT_NOT_NULL(gDb);106}107 108void tearDown(void) {109  if( gDb ){110    sqlite3_close(gDb);111    gDb = NULL;112  }113}114 115/* Test 1: Single-column FK implicitly mapping to INTEGER PRIMARY KEY */116void test_sqlite3FkLocateIndex_singlecol_implicit_ipk(void){117  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;118 119  const char *colsP[] = { "id" };120  Table *pParent = makeTable(gDb, "parent", 1, colsP, 0); /* iPKey=0 */121  const char *colsC[] = { "cid" };122  Table *pChild = makeTable(gDb, "child", 1, colsC, -1);123 124  const char *azParentCol[] = { NULL }; /* implicit to PK */125  int aiFrom[] = { 0 };126  FKey *pFKey = makeFKey(gDb, pChild, "parent", 1, azParentCol, aiFrom);127 128  Index *pIdx = NULL;129  int *aiCol = NULL;130  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);131  TEST_ASSERT_EQUAL_INT(0, rc);132  TEST_ASSERT_NULL(pIdx);133  TEST_ASSERT_NULL(aiCol);134 135  freeFKey(gDb, pFKey);136  freeTable(gDb, pChild);137  freeTable(gDb, pParent);138}139 140/* Test 2: Single-column FK explicitly naming the INTEGER PRIMARY KEY */141void test_sqlite3FkLocateIndex_singlecol_explicit_ipk(void){142  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;143 144  const char *colsP[] = { "id" };145  Table *pParent = makeTable(gDb, "parent", 1, colsP, 0);146  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);147 148  const char *azParentCol[] = { "id" }; /* explicit PK column */149  int aiFrom[] = { 2 };150  FKey *pFKey = makeFKey(gDb, pChild, "parent", 1, azParentCol, aiFrom);151 152  Index *pIdx = NULL;153  int *aiCol = NULL;154  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);155  TEST_ASSERT_EQUAL_INT(0, rc);156  TEST_ASSERT_NULL(pIdx);157  TEST_ASSERT_NULL(aiCol);158 159  freeFKey(gDb, pFKey);160  freeTable(gDb, pChild);161  freeTable(gDb, pParent);162}163 164/* Test 3: Composite FK implicitly mapping to PRIMARY KEY index */165void test_sqlite3FkLocateIndex_composite_implicit_pk(void){166  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;167 168  const char *colsP[] = { "a", "b" };169  Table *pParent = makeTable(gDb, "parent", 2, colsP, -1); /* no IPK for composite */170  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);171 172  int aiIndexCols[] = { 0, 1 };173  const char *azColl[] = { "BINARY", "BINARY" };174  Index *pPK = makeIndex(gDb, "pk_parent", pParent, 2, aiIndexCols, azColl,175                         SQLITE_IDXTYPE_PRIMARYKEY, OE_Abort, 0);176 177  const char *azParentCol[] = { NULL, NULL }; /* implicit to PK */178  int aiFrom[] = { 4, 7 };179  FKey *pFKey = makeFKey(gDb, pChild, "parent", 2, azParentCol, aiFrom);180 181  Index *pIdx = NULL;182  int *aiCol = NULL;183  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);184  TEST_ASSERT_EQUAL_INT(0, rc);185  TEST_ASSERT_EQUAL_PTR(pPK, pIdx);186  TEST_ASSERT_NOT_NULL(aiCol);187  TEST_ASSERT_EQUAL_INT(4, aiCol[0]);188  TEST_ASSERT_EQUAL_INT(7, aiCol[1]);189 190  sqlite3DbFree(gDb, aiCol);191  freeFKey(gDb, pFKey);192  freeTable(gDb, pChild);193  freeTable(gDb, pParent);194}195 196/* Test 4: Explicit mapping to UNIQUE non-PK index with matching collations */197void test_sqlite3FkLocateIndex_explicit_unique_index_collation_ok(void){198  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;199 200  const char *colsP[] = { "a", "b", "c" };201  Table *pParent = makeTable(gDb, "parent", 3, colsP, -1);202  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);203 204  int aiIndexCols[] = { 1, 0 };  /* index on (b, a) */205  const char *azColl[] = { "BINARY", "BINARY" };206  Index *pU = makeIndex(gDb, "u_b_a", pParent, 2, aiIndexCols, azColl,207                        SQLITE_IDXTYPE_APPDEF, OE_Abort, 0);208 209  const char *azParentCol[] = { "b", "a" };210  int aiFrom[] = { 5, 6 };211  FKey *pFKey = makeFKey(gDb, pChild, "parent", 2, azParentCol, aiFrom);212 213  Index *pIdx = NULL;214  int *aiCol = NULL;215  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);216  TEST_ASSERT_EQUAL_INT(0, rc);217  TEST_ASSERT_EQUAL_PTR(pU, pIdx);218  TEST_ASSERT_NOT_NULL(aiCol);219  TEST_ASSERT_EQUAL_INT(5, aiCol[0]);220  TEST_ASSERT_EQUAL_INT(6, aiCol[1]);221 222  sqlite3DbFree(gDb, aiCol);223  freeFKey(gDb, pFKey);224  freeTable(gDb, pChild);225  freeTable(gDb, pParent);226}227 228/* Test 5: Explicit mapping fails due to index using non-default collation */229void test_sqlite3FkLocateIndex_explicit_wrong_collation_fail_and_error(void){230  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;231 232  const char *colsP[] = { "a", "b" };233  Table *pParent = makeTable(gDb, "parent", 2, colsP, -1);234  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);235 236  int aiIndexCols[] = { 0, 1 };237  const char *azCollBad[] = { "BINARY", "NOCASE" }; /* second column wrong */238  makeIndex(gDb, "u_a_b", pParent, 2, aiIndexCols, azCollBad,239            SQLITE_IDXTYPE_APPDEF, OE_Abort, 0);240 241  const char *azParentCol[] = { "a", "b" };242  int aiFrom[] = { 1, 2 };243  FKey *pFKey = makeFKey(gDb, pChild, "parent", 2, azParentCol, aiFrom);244 245  Index *pIdx = NULL;246  int *aiCol = NULL;247  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);248 249  TEST_ASSERT_EQUAL_INT(1, rc);250  TEST_ASSERT_NULL(pIdx);251  /* aiCol is freed inside on failure; do not dereference or free. Reset to NULL to avoid accidental use. */252  aiCol = NULL;253 254  TEST_ASSERT_NOT_NULL(parse.zErrMsg);255  TEST_ASSERT_NOT_EQUAL(-1, strstr(parse.zErrMsg, "foreign key mismatch") ? 0 : -1);256  sqlite3DbFree(gDb, parse.zErrMsg); parse.zErrMsg = NULL;257 258  freeFKey(gDb, pFKey);259  freeTable(gDb, pChild);260  freeTable(gDb, pParent);261}262 263/* Test 6: Implicit composite mapping requires PRIMARY KEY, not just UNIQUE index */264void test_sqlite3FkLocateIndex_implicit_needs_pk_not_unique_idx_fail(void){265  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;266 267  const char *colsP[] = { "x", "y" };268  Table *pParent = makeTable(gDb, "parent", 2, colsP, -1);269  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);270 271  int aiIndexCols[] = { 0, 1 };272  const char *azColl[] = { "BINARY", "BINARY" };273  /* Create UNIQUE index, but not PRIMARY KEY */274  makeIndex(gDb, "u_x_y", pParent, 2, aiIndexCols, azColl,275            SQLITE_IDXTYPE_APPDEF, OE_Abort, 0);276 277  const char *azParentCol[] = { NULL, NULL }; /* implicit to PK */278  int aiFrom[] = { 10, 11 };279  FKey *pFKey = makeFKey(gDb, pChild, "parent", 2, azParentCol, aiFrom);280 281  Index *pIdx = NULL;282  int *aiCol = NULL;283  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);284 285  TEST_ASSERT_EQUAL_INT(1, rc);286  TEST_ASSERT_NULL(pIdx);287  aiCol = NULL; /* freed internally on failure */288  TEST_ASSERT_NOT_NULL(parse.zErrMsg);289  sqlite3DbFree(gDb, parse.zErrMsg); parse.zErrMsg = NULL;290 291  freeFKey(gDb, pFKey);292  freeTable(gDb, pChild);293  freeTable(gDb, pParent);294}295 296/* Test 7: Explicit mapping fails for expression index (aiColumn < 0) */297void test_sqlite3FkLocateIndex_explicit_expression_index_fail(void){298  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;299 300  const char *colsP[] = { "a" };301  Table *pParent = makeTable(gDb, "parent", 1, colsP, -1);302  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);303 304  int aiIndexCols[] = { -2 };     /* expression index */305  const char *azColl[] = { "BINARY" };306  makeIndex(gDb, "u_expr", pParent, 1, aiIndexCols, azColl,307            SQLITE_IDXTYPE_APPDEF, OE_Abort, 0);308 309  const char *azParentCol[] = { "a" };310  int aiFrom[] = { 0 };311  FKey *pFKey = makeFKey(gDb, pChild, "parent", 1, azParentCol, aiFrom);312 313  Index *pIdx = NULL;314  int *aiCol = NULL;315  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);316 317  TEST_ASSERT_EQUAL_INT(1, rc);318  TEST_ASSERT_NULL(pIdx);319  aiCol = NULL; /* freed internally on failure */320  TEST_ASSERT_NOT_NULL(parse.zErrMsg);321  sqlite3DbFree(gDb, parse.zErrMsg); parse.zErrMsg = NULL;322 323  freeFKey(gDb, pFKey);324  freeTable(gDb, pChild);325  freeTable(gDb, pParent);326}327 328/* Test 8: Explicit mapping fails when named parent columns do not exist in any usable index */329void test_sqlite3FkLocateIndex_explicit_nonexistent_cols_fail(void){330  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = gDb;331 332  const char *colsP[] = { "p", "q" };333  Table *pParent = makeTable(gDb, "parent", 2, colsP, -1);334  Table *pChild = makeTable(gDb, "child", 0, NULL, -1);335 336  /* Create a usable UNIQUE index, but on different columns than referenced */337  int aiIndexCols[] = { 0, 1 };338  const char *azColl[] = { "BINARY", "BINARY" };339  makeIndex(gDb, "u_p_q", pParent, 2, aiIndexCols, azColl,340            SQLITE_IDXTYPE_APPDEF, OE_Abort, 0);341 342  const char *azParentCol[] = { "x", "y" }; /* do not exist */343  int aiFrom[] = { 2, 3 };344  FKey *pFKey = makeFKey(gDb, pChild, "parent", 2, azParentCol, aiFrom);345 346  Index *pIdx = NULL;347  int *aiCol = NULL;348  int rc = sqlite3FkLocateIndex(&parse, pParent, pFKey, &pIdx, &aiCol);349 350  TEST_ASSERT_EQUAL_INT(1, rc);351  TEST_ASSERT_NULL(pIdx);352  aiCol = NULL; /* freed internally on failure */353  TEST_ASSERT_NOT_NULL(parse.zErrMsg);354  sqlite3DbFree(gDb, parse.zErrMsg); parse.zErrMsg = NULL;355 356  freeFKey(gDb, pFKey);357  freeTable(gDb, pChild);358  freeTable(gDb, pParent);359}360 361int main(void) {362  UNITY_BEGIN();363  RUN_TEST(test_sqlite3FkLocateIndex_singlecol_implicit_ipk);364  RUN_TEST(test_sqlite3FkLocateIndex_singlecol_explicit_ipk);365  RUN_TEST(test_sqlite3FkLocateIndex_composite_implicit_pk);366  RUN_TEST(test_sqlite3FkLocateIndex_explicit_unique_index_collation_ok);367  RUN_TEST(test_sqlite3FkLocateIndex_explicit_wrong_collation_fail_and_error);368  RUN_TEST(test_sqlite3FkLocateIndex_implicit_needs_pk_not_unique_idx_fail);369  RUN_TEST(test_sqlite3FkLocateIndex_explicit_expression_index_fail);370  RUN_TEST(test_sqlite3FkLocateIndex_explicit_nonexistent_cols_fail);371  return UNITY_END();372}