AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Wrapper provided in module for calling the static function */7extern void test_deleteTable(sqlite3 *db, Table *pTable);8 9static Table* makeOrdinaryTableWithColsAndIdx(sqlite3 *db, Schema *pSchema,10 int nCol, int nIdx){11 Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));12 TEST_ASSERT_NOT_NULL(pTab);13 14 pTab->pSchema = pSchema;15 pTab->tabFlags |= TF_Ephemeral; /* Avoid debug lookaside checks */16 pTab->zName = sqlite3DbStrDup(db, "t1");17 pTab->zColAff = sqlite3DbStrDup(db, "aa");18 19 /* Columns */20 pTab->nCol = nCol;21 pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);22 TEST_ASSERT_NOT_NULL(pTab->aCol);23 for(int i=0; i<nCol; i++){24 char zBuf[16];25 sqlite3_snprintf(sizeof(zBuf), zBuf, "c%d", i+1);26 pTab->aCol[i].zCnName = sqlite3DbStrDup(db, zBuf);27 TEST_ASSERT_NOT_NULL(pTab->aCol[i].zCnName);28 pTab->aCol[i].hName = sqlite3StrIHash(pTab->aCol[i].zCnName);29 }30 31 /* Indices chain */32 Index *pHead = NULL;33 for(int i=0; i<nIdx; i++){34 Index *pIdx = (Index*)sqlite3DbMallocZero(db, sizeof(Index));35 TEST_ASSERT_NOT_NULL(pIdx);36 /* Minimal, but sufficient for free-path */37 pIdx->pSchema = pSchema;38 pIdx->zName = (char*)"idx"; /* literal; not freed by sqlite3FreeIndex */39 pIdx->zColAff = sqlite3DbStrDup(db, "aa");40 TEST_ASSERT_NOT_NULL(pIdx->zColAff);41 if( (i%2)==0 ){42 /* Exercise azColl free path when isResized is true */43 pIdx->isResized = 1;44 pIdx->azColl = (const char**)sqlite3DbMallocZero(db, sizeof(char*)*2);45 TEST_ASSERT_NOT_NULL((void*)pIdx->azColl);46 }47 /* push-front into chain */48 pIdx->pNext = pHead;49 pHead = pIdx;50 }51 pTab->pIndex = pHead;52 53 return pTab;54}55 56static Table* makeViewTableWithSelect(sqlite3 *db, Schema *pSchema, int nCol){57 Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));58 TEST_ASSERT_NOT_NULL(pTab);59 60 pTab->pSchema = pSchema;61 pTab->tabFlags |= (TF_View | TF_Ephemeral); /* Mark as view; skip lookaside check */62 pTab->zName = sqlite3DbStrDup(db, "v1");63 pTab->zColAff = sqlite3DbStrDup(db, "a");64 65 /* Columns */66 pTab->nCol = nCol;67 pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);68 TEST_ASSERT_NOT_NULL(pTab->aCol);69 for(int i=0; i<nCol; i++){70 char zBuf[16];71 sqlite3_snprintf(sizeof(zBuf), zBuf, "x%d", i+1);72 pTab->aCol[i].zCnName = sqlite3DbStrDup(db, zBuf);73 TEST_ASSERT_NOT_NULL(pTab->aCol[i].zCnName);74 pTab->aCol[i].hName = sqlite3StrIHash(pTab->aCol[i].zCnName);75 }76 77 /* Minimal Select object to be deleted via sqlite3SelectDelete */78 pTab->u.view.pSelect = (Select*)sqlite3DbMallocZero(db, sizeof(Select));79 TEST_ASSERT_NOT_NULL(pTab->u.view.pSelect);80 81 return pTab;82}83 84void setUp(void) {85 /* nothing */86}87void tearDown(void) {88 /* nothing */89}90 91static sqlite3* open_db(void){92 sqlite3 *db = 0;93 int rc = sqlite3_open(":memory:", &db);94 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);95 TEST_ASSERT_NOT_NULL(db);96 return db;97}98 99static size_t strsz(const char *z){ return z ? (strlen(z)+1) : 0; }100 101/* Test 1: In byte-counting mode, memory is accounted and table fields are preserved */102void test_deleteTable_measurement_mode_preserves_and_counts(void){103 sqlite3 *db = open_db();104 Schema *pSchema = db->aDb[0].pSchema;105 TEST_ASSERT_NOT_NULL(pSchema);106 107 /* Build ordinary table with 2 columns and 2 indices */108 Table *pTab = makeOrdinaryTableWithColsAndIdx(db, pSchema, 2, 2);109 110 /* Compute a conservative minimum expected counted bytes */111 size_t expectedMin = 0;112 expectedMin += sizeof(Table);113 expectedMin += sizeof(Column)*2;114 expectedMin += strsz("t1"); /* zName */115 expectedMin += strsz("aa"); /* zColAff */116 expectedMin += strsz("c1") + strsz("c2");117 /* Two indices: each frees its zColAff and the Index struct itself.118 One of them (first created with even i) also frees azColl. */119 expectedMin += sizeof(Index) + strsz("aa") + sizeof(char*)*2; /* i=0: has azColl */120 expectedMin += sizeof(Index) + strsz("aa"); /* i=1: no azColl */121 122 i64 nFreed = 0;123 db->pnBytesFreed = &nFreed;124 125 /* Call the wrapper to invoke the static function */126 test_deleteTable(db, pTab);127 128 /* In counting mode, fields should remain intact */129 TEST_ASSERT_NOT_NULL(pTab->aCol);130 TEST_ASSERT_EQUAL_INT(2, pTab->nCol);131 TEST_ASSERT_NOT_NULL(pTab->pIndex);132 TEST_ASSERT_TRUE(nFreed >= (i64)expectedMin);133 134 /* Now actually free structures (must hold schema mutex for hash unlink path) */135 db->pnBytesFreed = 0;136 sqlite3_mutex_enter(db->mutex);137 test_deleteTable(db, pTab);138 sqlite3_mutex_leave(db->mutex);139 140 sqlite3_close(db);141}142 143/* Test 2: In real-free mode, indices are unlinked from the schema hash */144void test_deleteTable_unlinks_indexes_from_schema_hash(void){145 sqlite3 *db = open_db();146 Schema *pSchema = db->aDb[0].pSchema;147 TEST_ASSERT_NOT_NULL(pSchema);148 149 /* Build table with 1 index */150 Table *pTab = makeOrdinaryTableWithColsAndIdx(db, pSchema, 1, 1);151 TEST_ASSERT_NOT_NULL(pTab->pIndex);152 /* Give the index a concrete name and register it in the schema idxHash */153 pTab->pIndex->zName = (char*)"idxA";154 /* Insert into schema hash (using internal sqlite3HashInsert) */155 sqlite3HashInsert(&pSchema->idxHash, pTab->pIndex->zName, pTab->pIndex);156 /* Sanity: should be findable */157 TEST_ASSERT_NOT_NULL(sqlite3HashFind(&pSchema->idxHash, "idxA"));158 159 /* Real free mode (pnBytesFreed == 0); hold schema mutex for assert */160 sqlite3_mutex_enter(db->mutex);161 test_deleteTable(db, pTab);162 sqlite3_mutex_leave(db->mutex);163 164 /* After deleteTable, the index should be unlinked from schema hash */165 void *pFound = sqlite3HashFind(&pSchema->idxHash, "idxA");166 TEST_ASSERT_NULL(pFound);167 168 sqlite3_close(db);169}170 171/* Test 3: View-table path in counting mode includes Select object and preserves fields */172void test_deleteTable_view_path_counts_and_preserves(void){173 sqlite3 *db = open_db();174 Schema *pSchema = db->aDb[0].pSchema;175 TEST_ASSERT_NOT_NULL(pSchema);176 177 Table *pTab = makeViewTableWithSelect(db, pSchema, 1);178 179 size_t expectedMin = 0;180 expectedMin += sizeof(Table);181 expectedMin += sizeof(Column)*1;182 expectedMin += strsz("v1"); /* zName */183 expectedMin += strsz("a"); /* zColAff */184 expectedMin += strsz("x1"); /* column name */185 expectedMin += sizeof(Select); /* view Select struct */186 187 i64 nFreed = 0;188 db->pnBytesFreed = &nFreed;189 190 test_deleteTable(db, pTab);191 192 /* In counting mode, verify preservation and accounting */193 TEST_ASSERT_NOT_NULL(pTab->aCol);194 TEST_ASSERT_EQUAL_INT(1, pTab->nCol);195 TEST_ASSERT_TRUE(nFreed >= (i64)expectedMin);196 197 /* Now actually free with mutex held */198 db->pnBytesFreed = 0;199 sqlite3_mutex_enter(db->mutex);200 test_deleteTable(db, pTab);201 sqlite3_mutex_leave(db->mutex);202 203 sqlite3_close(db);204}205 206int main(void){207 UNITY_BEGIN();208 RUN_TEST(test_deleteTable_measurement_mode_preserves_and_counts);209 RUN_TEST(test_deleteTable_unlinks_indexes_from_schema_hash);210 RUN_TEST(test_deleteTable_view_path_counts_and_preserves);211 return UNITY_END();212}