AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* External wrapper provided in the module under test */7extern int test_autoIncBegin(Parse *pParse, int iDb, Table *pTab);8 9static sqlite3 *gDb = NULL;10 11/* Helpers */12static Table* alloc_table(sqlite3 *db, int tabFlags, int nCol){13 Table *p = (Table*)sqlite3DbMallocZero(db, sizeof(Table));14 if( p ){15 p->tabFlags = tabFlags;16 p->nCol = nCol;17 }18 return p;19}20 21/* Create and install a sqlite_sequence table into schema for iDb */22static Table* install_seqtab(sqlite3 *db, int iDb, int hasRowid, int nCol){23 Table *pSeq = alloc_table(db, 0, nCol);24 if( pSeq ){25 if( !hasRowid ){26 pSeq->tabFlags |= TF_WithoutRowid;27 }28 db->aDb[iDb].pSchema->pSeqTab = pSeq;29 }30 return pSeq;31}32 33void setUp(void) {34 int rc = sqlite3_open(":memory:", &gDb);35 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);36 TEST_ASSERT_NOT_NULL(gDb);37 TEST_ASSERT_NOT_NULL(gDb->aDb);38 TEST_ASSERT_NOT_NULL(gDb->aDb[0].pSchema);39 /* Ensure a clean mDbFlags for each test */40 gDb->mDbFlags = 0;41}42 43void tearDown(void) {44 /* Close the database. Any memory allocated with sqlite3DbMalloc* that is45 attached to the db will be reclaimed on close. */46 if( gDb ){47 sqlite3_close(gDb);48 gDb = NULL;49 }50}51 52/* Not AUTOINCREMENT: should return 0 and make no changes */53void test_autoIncBegin_no_autoinc_returns_zero(void){54 Parse p;55 memset(&p, 0, sizeof(p));56 p.db = gDb;57 int iDb = 0;58 59 /* Do not install sqlite_sequence - should not be needed since table is not AUTOINCREMENT */60 Table *pTab = alloc_table(gDb, 0, 1); /* No TF_Autoincrement */61 TEST_ASSERT_NOT_NULL(pTab);62 63 int nMem0 = p.nMem;64 int nErr0 = p.nErr;65 66 int reg = test_autoIncBegin(&p, iDb, pTab);67 TEST_ASSERT_EQUAL_INT(0, reg);68 TEST_ASSERT_EQUAL_INT(nMem0, p.nMem);69 TEST_ASSERT_EQUAL_INT(nErr0, p.nErr);70 TEST_ASSERT_EQUAL_INT(0, p.rc);71 TEST_ASSERT_NULL(p.pAinc);72}73 74/* DBFLAG_Vacuum set: should return 0 and make no changes even if AUTOINCREMENT */75void test_autoIncBegin_vacuum_returns_zero(void){76 Parse p;77 memset(&p, 0, sizeof(p));78 p.db = gDb;79 int iDb = 0;80 81 Table *pTab = alloc_table(gDb, TF_Autoincrement, 1);82 TEST_ASSERT_NOT_NULL(pTab);83 84 gDb->mDbFlags |= DBFLAG_Vacuum;85 86 int nMem0 = p.nMem;87 int nErr0 = p.nErr;88 89 int reg = test_autoIncBegin(&p, iDb, pTab);90 TEST_ASSERT_EQUAL_INT(0, reg);91 TEST_ASSERT_EQUAL_INT(nMem0, p.nMem);92 TEST_ASSERT_EQUAL_INT(nErr0, p.nErr);93 TEST_ASSERT_EQUAL_INT(0, p.rc);94 TEST_ASSERT_NULL(p.pAinc);95 96 gDb->mDbFlags &= ~DBFLAG_Vacuum;97}98 99/* Missing sqlite_sequence table: error path */100void test_autoIncBegin_error_when_seqtab_missing(void){101 Parse p;102 memset(&p, 0, sizeof(p));103 p.db = gDb;104 int iDb = 0;105 106 /* Ensure pSeqTab is NULL */107 gDb->aDb[iDb].pSchema->pSeqTab = NULL;108 109 Table *pTab = alloc_table(gDb, TF_Autoincrement, 3);110 TEST_ASSERT_NOT_NULL(pTab);111 112 int nErr0 = p.nErr;113 114 int reg = test_autoIncBegin(&p, iDb, pTab);115 TEST_ASSERT_EQUAL_INT(0, reg);116 TEST_ASSERT_EQUAL_INT(nErr0+1, p.nErr);117 TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT_SEQUENCE, p.rc);118 TEST_ASSERT_NULL(p.pAinc);119}120 121/* Invalid sqlite_sequence: wrong column count */122void test_autoIncBegin_error_when_seqtab_wrong_ncol(void){123 Parse p;124 memset(&p, 0, sizeof(p));125 p.db = gDb;126 int iDb = 0;127 128 /* Install a seqtab with wrong nCol */129 Table *pSeq = install_seqtab(gDb, iDb, 1, 3);130 TEST_ASSERT_NOT_NULL(pSeq);131 132 Table *pTab = alloc_table(gDb, TF_Autoincrement, 5);133 TEST_ASSERT_NOT_NULL(pTab);134 135 int nErr0 = p.nErr;136 137 int reg = test_autoIncBegin(&p, iDb, pTab);138 TEST_ASSERT_EQUAL_INT(0, reg);139 TEST_ASSERT_EQUAL_INT(nErr0+1, p.nErr);140 TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT_SEQUENCE, p.rc);141 TEST_ASSERT_NULL(p.pAinc);142}143 144/* Invalid sqlite_sequence: WITHOUT ROWID table */145void test_autoIncBegin_error_when_seqtab_without_rowid(void){146 Parse p;147 memset(&p, 0, sizeof(p));148 p.db = gDb;149 int iDb = 0;150 151 /* Install a seqtab that lacks a rowid (TF_WithoutRowid set) */152 Table *pSeq = install_seqtab(gDb, iDb, 0, 2);153 TEST_ASSERT_NOT_NULL(pSeq);154 155 Table *pTab = alloc_table(gDb, TF_Autoincrement, 4);156 TEST_ASSERT_NOT_NULL(pTab);157 158 int nErr0 = p.nErr;159 160 int reg = test_autoIncBegin(&p, iDb, pTab);161 TEST_ASSERT_EQUAL_INT(0, reg);162 TEST_ASSERT_EQUAL_INT(nErr0+1, p.nErr);163 TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT_SEQUENCE, p.rc);164 TEST_ASSERT_NULL(p.pAinc);165}166 167/* Successful path: allocates registers and reuses AutoincInfo on subsequent calls */168void test_autoIncBegin_success_allocates_and_reuses_registers(void){169 Parse p;170 memset(&p, 0, sizeof(p));171 p.db = gDb;172 int iDb = 0;173 174 /* Valid sqlite_sequence: rowid table with exactly 2 columns */175 Table *pSeq = install_seqtab(gDb, iDb, 1, 2);176 TEST_ASSERT_NOT_NULL(pSeq);177 178 /* AUTOINCREMENT table */179 Table *pTab = alloc_table(gDb, TF_Autoincrement, 3);180 TEST_ASSERT_NOT_NULL(pTab);181 182 p.nMem = 7; /* Start with some existing registers */183 184 int reg1 = test_autoIncBegin(&p, iDb, pTab);185 TEST_ASSERT_NOT_EQUAL(0, reg1);186 187 /* Expected register allocation pattern:188 name register: +1 -> 8189 regCtr: ++ -> 9 (returned)190 two more: +2 -> 11 total191 */192 TEST_ASSERT_EQUAL_INT(9, reg1);193 TEST_ASSERT_EQUAL_INT(11, p.nMem);194 TEST_ASSERT_NOT_NULL(p.pAinc);195 TEST_ASSERT_EQUAL_PTR(pTab, p.pAinc->pTab);196 TEST_ASSERT_EQUAL_INT(iDb, p.pAinc->iDb);197 TEST_ASSERT_EQUAL_INT(reg1, p.pAinc->regCtr);198 199 int nMemAfterFirst = p.nMem;200 201 /* Second call with same table should reuse AutoincInfo */202 int reg2 = test_autoIncBegin(&p, iDb, pTab);203 TEST_ASSERT_EQUAL_INT(reg1, reg2);204 TEST_ASSERT_EQUAL_INT(nMemAfterFirst, p.nMem);205 TEST_ASSERT_NOT_NULL(p.pAinc);206 TEST_ASSERT_NULL(p.pAinc->pNext);207}208 209/* Multiple AUTOINCREMENT tables: distinct entries and register allocation */210void test_autoIncBegin_multiple_tables_allocate_distinct(void){211 Parse p;212 memset(&p, 0, sizeof(p));213 p.db = gDb;214 int iDb = 0;215 216 Table *pSeq = install_seqtab(gDb, iDb, 1, 2);217 TEST_ASSERT_NOT_NULL(pSeq);218 219 Table *pTab1 = alloc_table(gDb, TF_Autoincrement, 3);220 Table *pTab2 = alloc_table(gDb, TF_Autoincrement, 4);221 TEST_ASSERT_NOT_NULL(pTab1);222 TEST_ASSERT_NOT_NULL(pTab2);223 224 p.nMem = 0; /* Start clean */225 226 int reg1 = test_autoIncBegin(&p, iDb, pTab1);227 TEST_ASSERT_EQUAL_INT(2, reg1); /* name:1, regCtr:2, total nMem:4 after */228 TEST_ASSERT_EQUAL_INT(4, p.nMem);229 TEST_ASSERT_NOT_NULL(p.pAinc);230 TEST_ASSERT_EQUAL_PTR(pTab1, p.pAinc->pTab);231 TEST_ASSERT_EQUAL_INT(reg1, p.pAinc->regCtr);232 233 int reg2 = test_autoIncBegin(&p, iDb, pTab2);234 /* After first, nMem=4; second call allocates name:5, regCtr:6, total nMem:8 */235 TEST_ASSERT_EQUAL_INT(6, reg2);236 TEST_ASSERT_EQUAL_INT(8, p.nMem);237 TEST_ASSERT_NOT_NULL(p.pAinc);238 /* Either order pAinc list could be (new head insertion). Verify both exist. */239 AutoincInfo *a1 = p.pAinc;240 AutoincInfo *a2 = a1 ? a1->pNext : NULL;241 TEST_ASSERT_NOT_NULL(a1);242 TEST_ASSERT_NOT_NULL(a2);243 /* Verify we have entries for both tables and correct regCtrs */244 int seen1 = 0, seen2 = 0;245 AutoincInfo *it = p.pAinc;246 while( it ){247 if( it->pTab == pTab1 ){248 TEST_ASSERT_EQUAL_INT(reg1, it->regCtr);249 seen1 = 1;250 }else if( it->pTab == pTab2 ){251 TEST_ASSERT_EQUAL_INT(reg2, it->regCtr);252 seen2 = 1;253 }254 it = it->pNext;255 }256 TEST_ASSERT_TRUE(seen1);257 TEST_ASSERT_TRUE(seen2);258}259 260int main(void) {261 UNITY_BEGIN();262 RUN_TEST(test_autoIncBegin_no_autoinc_returns_zero);263 RUN_TEST(test_autoIncBegin_vacuum_returns_zero);264 RUN_TEST(test_autoIncBegin_error_when_seqtab_missing);265 RUN_TEST(test_autoIncBegin_error_when_seqtab_wrong_ncol);266 RUN_TEST(test_autoIncBegin_error_when_seqtab_without_rowid);267 RUN_TEST(test_autoIncBegin_success_allocates_and_reuses_registers);268 RUN_TEST(test_autoIncBegin_multiple_tables_allocate_distinct);269 return UNITY_END();270}