AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6/* Global database handle for allocations using sqlite3DbMalloc... */7static sqlite3 *gDb = NULL;8 9/* Unity setup/teardown */10void setUp(void) {11 int rc = sqlite3_open(":memory:", &gDb);12 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);13}14 15void tearDown(void) {16 if (gDb) {17 sqlite3_close(gDb);18 gDb = NULL;19 }20}21 22/* Helpers to build Table and FKey structures */23 24static Table* makeTable(sqlite3 *db, const char *zName, int nCol){25 Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));26 TEST_ASSERT_NOT_NULL(pTab);27 pTab->zName = sqlite3DbStrDup(db, zName ? zName : "t");28 pTab->nCol = (i16)nCol;29 pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);30 TEST_ASSERT_NOT_NULL(pTab->aCol);31 /* Ordinary table by default: tabFlags = 0 should suffice */32 pTab->tabFlags = 0;33 /* Link to main schema if available */34 if( db && db->nDb>0 ){35 pTab->pSchema = db->aDb[0].pSchema;36 }37 return pTab;38}39 40/* Allocate an FKey with nCol entries. fromCols[] holds iFrom values. */41static FKey* makeFKey(sqlite3 *db, Table *pFrom, int nCol, const int *fromCols){42 /* FKey has a flexible array member aCol[]. Allocate enough space. */43 size_t sz = sizeof(FKey) + (size_t)(nCol>0 ? (nCol-1) : 0) * sizeof(pFrom->u.tab.pFKey->aCol[0]);44 FKey *pFk = (FKey*)sqlite3DbMallocZero(db, sz);45 TEST_ASSERT_NOT_NULL(pFk);46 pFk->pFrom = pFrom;47 pFk->nCol = (i16)nCol;48 /* zTo is not used by sqlite3FkOldmask, but set something benign. */49 pFk->zTo = sqlite3DbStrDup(db, "parent");50 for(int i=0; i<nCol; i++){51 pFk->aCol[i].iFrom = fromCols[i];52 pFk->aCol[i].zCol = 0; /* not needed for this path */53 }54 return pFk;55}56 57/* Append an FKey to the pTab->u.tab.pFKey chain (outgoing FKs) */58static void appendOutgoingFKey(Table *pTab, FKey *pFk){59 if( pTab->u.tab.pFKey==0 ){60 pTab->u.tab.pFKey = pFk;61 }else{62 FKey *p = pTab->u.tab.pFKey;63 while( p->pNextFrom ) p = p->pNextFrom;64 p->pNextFrom = pFk;65 }66}67 68/* Free a Table and its outgoing FKey chain (allocated via sqlite3DbMalloc...). */69static void freeTableAndFKeys(sqlite3 *db, Table *pTab){70 if( !pTab ) return;71 /* Free outgoing FKeys */72 FKey *p = pTab->u.tab.pFKey;73 while( p ){74 FKey *pNext = p->pNextFrom;75 if( p->zTo ) sqlite3DbFree(db, p->zTo);76 sqlite3DbFree(db, p);77 p = pNext;78 }79 if( pTab->aCol ) sqlite3DbFree(db, pTab->aCol);80 if( pTab->zName ) sqlite3DbFree(db, pTab->zName);81 sqlite3DbFree(db, pTab);82}83 84/* Compute expected mask for given set of column indexes */85static u32 maskForCols(const int *cols, int n){86 u32 m = 0;87 for(int i=0; i<n; i++){88 m |= COLUMN_MASK(cols[i]);89 }90 return m;91}92 93/* Test 1: With foreign keys disabled, mask should be 0 even if FKs exist */94void test_sqlite3FkOldmask_returns_zero_when_foreign_keys_disabled(void){95 /* Arrange */96 Parse parse;97 memset(&parse, 0, sizeof(parse));98 parse.db = gDb;99 100 /* Explicitly ensure FK processing is disabled */101 gDb->flags &= ~SQLITE_ForeignKeys;102 103 Table *pTab = makeTable(gDb, "child", 8);104 105 int cols1[] = {1};106 int cols2[] = {3, 4};107 FKey *fk1 = makeFKey(gDb, pTab, 1, cols1);108 FKey *fk2 = makeFKey(gDb, pTab, 2, cols2);109 appendOutgoingFKey(pTab, fk1);110 appendOutgoingFKey(pTab, fk2);111 112 /* Act */113 u32 mask = sqlite3FkOldmask(&parse, pTab);114 115 /* Assert */116 TEST_ASSERT_EQUAL_HEX32(0, mask);117 118 /* Cleanup */119 freeTableAndFKeys(gDb, pTab);120}121 122/* Test 2: With foreign keys enabled, mask is union of child-side FK column bits */123void test_sqlite3FkOldmask_aggregates_mask_from_child_foreign_keys(void){124 /* Arrange */125 Parse parse;126 memset(&parse, 0, sizeof(parse));127 parse.db = gDb;128 129 gDb->flags |= SQLITE_ForeignKeys;130 131 Table *pTab = makeTable(gDb, "child", 16);132 133 int cols1[] = {0};134 int cols2[] = {2, 1, 5};135 int cols3[] = {5, 2}; /* duplicates to ensure union behavior */136 FKey *fk1 = makeFKey(gDb, pTab, 1, cols1);137 FKey *fk2 = makeFKey(gDb, pTab, 3, cols2);138 FKey *fk3 = makeFKey(gDb, pTab, 2, cols3);139 appendOutgoingFKey(pTab, fk1);140 appendOutgoingFKey(pTab, fk2);141 appendOutgoingFKey(pTab, fk3);142 143 /* Expected mask: bits for columns 0,1,2,5 */144 int expectedCols[] = {0,1,2,5};145 u32 expected = maskForCols(expectedCols, (int)(sizeof(expectedCols)/sizeof(expectedCols[0])));146 147 /* Act */148 u32 mask = sqlite3FkOldmask(&parse, pTab);149 150 /* Assert */151 TEST_ASSERT_EQUAL_HEX32(expected, mask);152 153 /* Cleanup */154 freeTableAndFKeys(gDb, pTab);155}156 157/* Test 3: With foreign keys enabled but no FKs at all, mask is 0 */158void test_sqlite3FkOldmask_returns_zero_when_no_foreign_keys(void){159 /* Arrange */160 Parse parse;161 memset(&parse, 0, sizeof(parse));162 parse.db = gDb;163 164 gDb->flags |= SQLITE_ForeignKeys;165 166 Table *pTab = makeTable(gDb, "no_fk", 10);167 /* No outgoing FKeys attached; incoming (parent) refs are not set in this test. */168 169 /* Act */170 u32 mask = sqlite3FkOldmask(&parse, pTab);171 172 /* Assert */173 TEST_ASSERT_EQUAL_HEX32(0, mask);174 175 /* Cleanup */176 freeTableAndFKeys(gDb, pTab);177}178 179/* Unity main */180int main(void){181 UNITY_BEGIN();182 RUN_TEST(test_sqlite3FkOldmask_returns_zero_when_foreign_keys_disabled);183 RUN_TEST(test_sqlite3FkOldmask_aggregates_mask_from_child_foreign_keys);184 RUN_TEST(test_sqlite3FkOldmask_returns_zero_when_no_foreign_keys);185 return UNITY_END();186}