AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Unity setup/teardown */7void setUp(void) {8 /* No global setup required */9}10 11void tearDown(void) {12 /* No global teardown required */13}14 15/* Helper: open an in-memory database */16static sqlite3* helper_open_db(void){17 sqlite3 *db = 0;18 int rc = sqlite3_open(":memory:", &db);19 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);20 TEST_ASSERT_NOT_NULL(db);21 return db;22}23 24/* Helper: ensure schema->pSeqTab exists */25static void helper_ensure_seq_table(sqlite3 *db){26 sqlite3_mutex_enter(db->mutex);27 Schema *pSchema = db->aDb[0].pSchema;28 if( pSchema->pSeqTab==0 ){29 Table *pSeq = (Table*)sqlite3DbMallocZero(db, sizeof(Table));30 TEST_ASSERT_NOT_NULL(pSeq);31 pSeq->zName = sqlite3DbStrDup(db, "sqlite_sequence");32 TEST_ASSERT_NOT_NULL(pSeq->zName);33 pSeq->tnum = 1;34 pSeq->nNVCol = 2;35 pSeq->pSchema = pSchema;36 pSeq->tabFlags = 0; /* HasRowid by default */37 pSchema->pSeqTab = pSeq;38 }39 sqlite3_mutex_leave(db->mutex);40}41 42/* Helper: initialize a Parse with a VDBE */43static void helper_init_parse(sqlite3 *db, Parse *pParse){44 memset(pParse, 0, sizeof(Parse));45 pParse->db = db;46 pParse->pAinc = 0;47 pParse->pTriggerTab = 0;48 pParse->pToplevel = 0; /* Top-level parse */49 /* Create a new VDBE for this parse */50 Vdbe *v = sqlite3VdbeCreate(pParse);51 TEST_ASSERT_NOT_NULL(v);52 TEST_ASSERT_EQUAL_PTR(v, pParse->pVdbe);53}54 55/* Helper: create an AutoincInfo node with a given table name and regCtr */56static AutoincInfo* helper_make_ainc(sqlite3 *db, const char *zTabName, int iDb, int regCtr){57 AutoincInfo *pA = (AutoincInfo*)sqlite3DbMallocZero(db, sizeof(AutoincInfo));58 TEST_ASSERT_NOT_NULL(pA);59 Table *pT = (Table*)sqlite3DbMallocZero(db, sizeof(Table));60 TEST_ASSERT_NOT_NULL(pT);61 pT->zName = sqlite3DbStrDup(db, zTabName);62 TEST_ASSERT_NOT_NULL(pT->zName);63 pT->pSchema = db->aDb[iDb].pSchema;64 pT->nNVCol = 1;65 pT->tabFlags = 0; /* HasRowid */66 pA->pTab = pT;67 pA->iDb = iDb;68 pA->regCtr = regCtr;69 pA->pNext = 0;70 return pA;71}72 73/* Helper: free a linked list of AutoincInfo nodes created by helper_make_ainc */74static void helper_free_ainc_chain(sqlite3 *db, AutoincInfo *pA){75 while( pA ){76 AutoincInfo *pNext = pA->pNext;77 if( pA->pTab ){78 if( pA->pTab->zName ) sqlite3DbFree(db, pA->pTab->zName);79 sqlite3DbFree(db, pA->pTab);80 }81 sqlite3DbFree(db, pA);82 pA = pNext;83 }84}85 86/* Test 1: No autoincrement info -> no ops generated, nTab unchanged */87void test_sqlite3AutoincrementBegin_no_autoinc_list(void){88 sqlite3 *db = helper_open_db();89 helper_ensure_seq_table(db);90 91 Parse parse;92 helper_init_parse(db, &parse);93 parse.nTab = 0;94 95 sqlite3_mutex_enter(db->mutex);96 sqlite3AutoincrementBegin(&parse);97 sqlite3_mutex_leave(db->mutex);98 99 Vdbe *v = parse.pVdbe;100 TEST_ASSERT_NOT_NULL(v);101 TEST_ASSERT_EQUAL_INT(0, v->nOp);102 TEST_ASSERT_EQUAL_INT(0, parse.nTab);103 104 /* Cleanup */105 sqlite3VdbeFinalize(v);106 int rc = sqlite3_close(db);107 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);108}109 110/* Test 2: Single AutoincInfo -> verify program shape and operand adjustments; nTab set to 1 */111void test_sqlite3AutoincrementBegin_single_entry_generates_expected_ops(void){112 sqlite3 *db = helper_open_db();113 helper_ensure_seq_table(db);114 115 Parse parse;116 helper_init_parse(db, &parse);117 parse.nTab = 0;118 119 /* Build one AutoincInfo node */120 int iDb = 0;121 int regCtr = 5;122 AutoincInfo *pA = helper_make_ainc(db, "t1", iDb, regCtr);123 parse.pAinc = pA;124 125 sqlite3_mutex_enter(db->mutex);126 sqlite3AutoincrementBegin(&parse);127 sqlite3_mutex_leave(db->mutex);128 129 Vdbe *v = parse.pVdbe;130 TEST_ASSERT_NOT_NULL(v);131 132 /* Expect: 1 (OpenRead) + 1 (String load) + 12 (autoInc list) = 14 ops */133 TEST_ASSERT_EQUAL_INT(14, v->nOp);134 VdbeOp *op = v->aOp;135 TEST_ASSERT_NOT_NULL(op);136 137 /* Check first op: OP_OpenRead on cursor 0, rootpage = seq->tnum, db = iDb */138 TEST_ASSERT_EQUAL_INT(OP_OpenRead, op[0].opcode);139 TEST_ASSERT_EQUAL_INT(0, op[0].p1);140 TEST_ASSERT_EQUAL_INT(db->aDb[iDb].pSchema->pSeqTab->tnum, op[0].p2);141 TEST_ASSERT_EQUAL_INT(iDb, op[0].p3);142 143 /* Skip op[1] (String8/String) variability; start of autoInc block at op[2] */144 TEST_ASSERT_EQUAL_INT(OP_Null, op[2].opcode);145 TEST_ASSERT_EQUAL_INT(OP_Rewind, op[3].opcode);146 TEST_ASSERT_EQUAL_INT(OP_Column, op[4].opcode);147 TEST_ASSERT_EQUAL_INT(OP_Ne, op[5].opcode);148 TEST_ASSERT_EQUAL_INT(OP_Rowid, op[6].opcode);149 TEST_ASSERT_EQUAL_INT(OP_Column, op[7].opcode);150 TEST_ASSERT_EQUAL_INT(OP_AddImm, op[8].opcode);151 TEST_ASSERT_EQUAL_INT(OP_Copy, op[9].opcode);152 TEST_ASSERT_EQUAL_INT(OP_Goto, op[10].opcode);153 TEST_ASSERT_EQUAL_INT(OP_Next, op[11].opcode);154 TEST_ASSERT_EQUAL_INT(OP_Integer, op[12].opcode);155 TEST_ASSERT_EQUAL_INT(OP_Close, op[13].opcode);156 157 /* Verify operand adjustments based on regCtr as per implementation */158 /* aOp[0] in template -> program op[2] */159 TEST_ASSERT_EQUAL_INT(regCtr, op[2].p2);160 TEST_ASSERT_EQUAL_INT(regCtr+2, op[2].p3);161 /* aOp[2] in template -> program op[4] */162 TEST_ASSERT_EQUAL_INT(regCtr, op[4].p3);163 /* aOp[3] in template -> program op[5] */164 TEST_ASSERT_EQUAL_INT(regCtr-1, op[5].p1);165 TEST_ASSERT_EQUAL_INT(regCtr, op[5].p3);166 TEST_ASSERT_EQUAL_INT(SQLITE_JUMPIFNULL, op[5].p5);167 /* aOp[4] -> op[6] */168 TEST_ASSERT_EQUAL_INT(regCtr+1, op[6].p2);169 /* aOp[5] -> op[7] */170 TEST_ASSERT_EQUAL_INT(regCtr, op[7].p3);171 /* aOp[6] -> op[8] */172 TEST_ASSERT_EQUAL_INT(regCtr, op[8].p1);173 /* aOp[7] -> op[9] */174 TEST_ASSERT_EQUAL_INT(regCtr, op[9].p1);175 TEST_ASSERT_EQUAL_INT(regCtr+2, op[9].p2);176 /* aOp[10] -> op[12] */177 TEST_ASSERT_EQUAL_INT(regCtr, op[12].p2);178 179 /* nTab should be set to 1 because it started at 0 */180 TEST_ASSERT_EQUAL_INT(1, parse.nTab);181 182 /* Cleanup */183 sqlite3VdbeFinalize(v);184 helper_free_ainc_chain(db, pA);185 int rc = sqlite3_close(db);186 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);187}188 189/* Test 3: Two AutoincInfo nodes -> two blocks appended; verify second block operands */190void test_sqlite3AutoincrementBegin_multiple_entries_two_blocks(void){191 sqlite3 *db = helper_open_db();192 helper_ensure_seq_table(db);193 194 Parse parse;195 helper_init_parse(db, &parse);196 parse.nTab = 0;197 198 int iDb = 0;199 AutoincInfo *pA1 = helper_make_ainc(db, "t1", iDb, 7);200 AutoincInfo *pA2 = helper_make_ainc(db, "t2", iDb, 20);201 pA1->pNext = pA2;202 parse.pAinc = pA1;203 204 sqlite3_mutex_enter(db->mutex);205 sqlite3AutoincrementBegin(&parse);206 sqlite3_mutex_leave(db->mutex);207 208 Vdbe *v = parse.pVdbe;209 TEST_ASSERT_NOT_NULL(v);210 /* Expect 2 * (1 + 1 + 12) = 28 ops */211 TEST_ASSERT_EQUAL_INT(28, v->nOp);212 VdbeOp *op = v->aOp;213 TEST_ASSERT_NOT_NULL(op);214 215 /* First block sanity (start at 0, autoInc at 2) */216 TEST_ASSERT_EQUAL_INT(OP_OpenRead, op[0].opcode);217 TEST_ASSERT_EQUAL_INT(OP_Null, op[2].opcode);218 219 /* Second block starts at index 14 */220 int base = 14;221 TEST_ASSERT_EQUAL_INT(OP_OpenRead, op[base + 0].opcode);222 /* base+1 is String load */223 TEST_ASSERT_EQUAL_INT(OP_Null, op[base + 2].opcode);224 TEST_ASSERT_EQUAL_INT(OP_Rewind, op[base + 3].opcode);225 TEST_ASSERT_EQUAL_INT(OP_Column, op[base + 4].opcode);226 TEST_ASSERT_EQUAL_INT(OP_Ne, op[base + 5].opcode);227 TEST_ASSERT_EQUAL_INT(OP_Rowid, op[base + 6].opcode);228 TEST_ASSERT_EQUAL_INT(OP_Column, op[base + 7].opcode);229 TEST_ASSERT_EQUAL_INT(OP_AddImm, op[base + 8].opcode);230 TEST_ASSERT_EQUAL_INT(OP_Copy, op[base + 9].opcode);231 TEST_ASSERT_EQUAL_INT(OP_Goto, op[base + 10].opcode);232 TEST_ASSERT_EQUAL_INT(OP_Next, op[base + 11].opcode);233 TEST_ASSERT_EQUAL_INT(OP_Integer, op[base + 12].opcode);234 TEST_ASSERT_EQUAL_INT(OP_Close, op[base + 13].opcode);235 236 int regCtr2 = 20;237 /* Verify operand adjustments for second block */238 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 2].p2);239 TEST_ASSERT_EQUAL_INT(regCtr2+2, op[base + 2].p3);240 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 4].p3);241 TEST_ASSERT_EQUAL_INT(regCtr2-1, op[base + 5].p1);242 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 5].p3);243 TEST_ASSERT_EQUAL_INT(SQLITE_JUMPIFNULL, op[base + 5].p5);244 TEST_ASSERT_EQUAL_INT(regCtr2+1, op[base + 6].p2);245 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 7].p3);246 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 8].p1);247 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 9].p1);248 TEST_ASSERT_EQUAL_INT(regCtr2+2, op[base + 9].p2);249 TEST_ASSERT_EQUAL_INT(regCtr2, op[base + 12].p2);250 251 /* nTab should be set to 1 since it started at 0 */252 TEST_ASSERT_EQUAL_INT(1, parse.nTab);253 254 /* Cleanup */255 sqlite3VdbeFinalize(v);256 helper_free_ainc_chain(db, pA1); /* also frees pA2 */257 int rc = sqlite3_close(db);258 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);259}260 261/* Test 4: nTab non-zero initially -> remains unchanged after call */262void test_sqlite3AutoincrementBegin_nTab_preserved_when_nonzero(void){263 sqlite3 *db = helper_open_db();264 helper_ensure_seq_table(db);265 266 Parse parse;267 helper_init_parse(db, &parse);268 parse.nTab = 7;269 270 AutoincInfo *pA = helper_make_ainc(db, "t1", 0, 3);271 parse.pAinc = pA;272 273 sqlite3_mutex_enter(db->mutex);274 sqlite3AutoincrementBegin(&parse);275 sqlite3_mutex_leave(db->mutex);276 277 TEST_ASSERT_EQUAL_INT(7, parse.nTab);278 279 /* Cleanup */280 sqlite3VdbeFinalize(parse.pVdbe);281 helper_free_ainc_chain(db, pA);282 int rc = sqlite3_close(db);283 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);284}285 286int main(void){287 UNITY_BEGIN();288 RUN_TEST(test_sqlite3AutoincrementBegin_no_autoinc_list);289 RUN_TEST(test_sqlite3AutoincrementBegin_single_entry_generates_expected_ops);290 RUN_TEST(test_sqlite3AutoincrementBegin_multiple_entries_two_blocks);291 RUN_TEST(test_sqlite3AutoincrementBegin_nTab_preserved_when_nonzero);292 return UNITY_END();293}