AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Wrapper for the static function under test (provided in the module) */7extern void test_sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage);8 9/* Helper: execute SQL and assert success */10static void exec_ok(sqlite3 *db, const char *sql){11 char *err = 0;12 int rc = sqlite3_exec(db, sql, 0, 0, &err);13 if( rc!=SQLITE_OK ){14 const char *msg = err ? err : "(null)";15 TEST_FAIL_MESSAGE(msg);16 }17 if( err ) sqlite3_free(err);18}19 20/* Helper: find Index* by name from "main" schema */21static Index* findIndex(sqlite3 *db, const char *zIdxName){22 int iDb = sqlite3FindDbName(db, "main");23 TEST_ASSERT_TRUE_MESSAGE(iDb>=0, "main database not found");24 Schema *pSchema = db->aDb[iDb].pSchema;25 TEST_ASSERT_NOT_NULL(pSchema);26 return (Index*)sqlite3HashFind(&pSchema->idxHash, zIdxName);27}28 29/* Helper: find the first opcode index */30static int findOp(Vdbe *v, int opcode){31 for(int i=0; i<v->nOp; i++){32 if( v->aOp[i].opcode==opcode ) return i;33 }34 return -1;35}36 37/* Helper: count opcodes */38static int countOp(Vdbe *v, int opcode){39 int n=0;40 for(int i=0; i<v->nOp; i++){41 if( v->aOp[i].opcode==opcode ) n++;42 }43 return n;44}45 46/* Helper: find OP with specific p1 and p2 */47static int findOpP1P2(Vdbe *v, int opcode, int p1, int p2){48 for(int i=0; i<v->nOp; i++){49 if( v->aOp[i].opcode==opcode && v->aOp[i].p1==p1 && v->aOp[i].p2==p2 ){50 return i;51 }52 }53 return -1;54}55 56void setUp(void) {57 /* No global setup needed */58}59void tearDown(void) {60 /* No global teardown needed */61}62 63/* Test 1: Non-unique index, memRootPage < 0 */64void test_sqlite3RefillIndex_nonunique_memRootNeg_generates_expected_ops(void){65 sqlite3 *db = 0;66 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));67 68 exec_ok(db, "CREATE TABLE t(a INT, b INT);");69 exec_ok(db, "INSERT INTO t VALUES(1,10),(2,20),(3,30);");70 exec_ok(db, "CREATE INDEX idx_b ON t(b);");71 72 Index *pIdx = findIndex(db, "idx_b");73 TEST_ASSERT_NOT_NULL(pIdx);74 TEST_ASSERT_FALSE(IsUniqueIndex(pIdx)); /* Ensure non-unique */75 76 Parse p;77 memset(&p, 0, sizeof(p));78 p.db = db;79 80 test_sqlite3RefillIndex(&p, pIdx, -1);81 82 Vdbe *v = p.pVdbe;83 TEST_ASSERT_NOT_NULL(v);84 85 /* Core opcodes expected */86 TEST_ASSERT_TRUE(findOp(v, OP_SorterOpen) >= 0);87 TEST_ASSERT_TRUE(findOp(v, OP_OpenRead) >= 0);88 TEST_ASSERT_TRUE(findOp(v, OP_Rewind) >= 0);89 TEST_ASSERT_TRUE(findOp(v, OP_SorterInsert) >= 0);90 TEST_ASSERT_TRUE(findOp(v, OP_OpenWrite) >= 0);91 TEST_ASSERT_TRUE(findOp(v, OP_SorterSort) >= 0);92 TEST_ASSERT_TRUE(findOp(v, OP_SorterData) >= 0);93 TEST_ASSERT_TRUE(findOp(v, OP_IdxInsert) >= 0);94 TEST_ASSERT_TRUE(findOp(v, OP_SorterNext) >= 0);95 96 /* Since memRootPage < 0, there should be an OP_Clear for the index root */97 int iDb = sqlite3SchemaToIndex(db, pIdx->pSchema);98 TEST_ASSERT_TRUE(findOpP1P2(v, OP_Clear, (int)pIdx->tnum, iDb) >= 0);99 100 /* OpenWrite flags: BULKCSR set; P2ISREG not set */101 int iOpenWrite = findOp(v, OP_OpenWrite);102 TEST_ASSERT_TRUE(iOpenWrite >= 0);103 Op *opOpenWrite = &v->aOp[iOpenWrite];104 TEST_ASSERT_TRUE((opOpenWrite->p5 & OPFLAG_BULKCSR) != 0);105 TEST_ASSERT_TRUE((opOpenWrite->p5 & OPFLAG_P2ISREG) == 0);106 107 /* Non-unique path: no SorterCompare, but SeekEnd optimization present */108 TEST_ASSERT_EQUAL_INT(0, countOp(v, OP_SorterCompare));109 TEST_ASSERT_TRUE(findOp(v, OP_SeekEnd) >= 0);110 111 /* Parse flags set appropriately */112 TEST_ASSERT_EQUAL_INT(1, p.isMultiWrite);113 TEST_ASSERT_EQUAL_INT(1, p.mayAbort);114 115 sqlite3VdbeDelete(v);116 sqlite3_close(db);117}118 119/* Test 2: Unique index, memRootPage < 0 */120void test_sqlite3RefillIndex_unique_generates_compare_and_no_mayAbort(void){121 sqlite3 *db = 0;122 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));123 124 exec_ok(db, "CREATE TABLE u(a INT, b INT);");125 exec_ok(db, "INSERT INTO u VALUES(1,10),(2,20),(3,20);");126 exec_ok(db, "CREATE UNIQUE INDEX idx_ub ON u(b);");127 128 Index *pIdx = findIndex(db, "idx_ub");129 TEST_ASSERT_NOT_NULL(pIdx);130 TEST_ASSERT_TRUE(IsUniqueIndex(pIdx));131 132 Parse p;133 memset(&p, 0, sizeof(p));134 p.db = db;135 136 test_sqlite3RefillIndex(&p, pIdx, -1);137 138 Vdbe *v = p.pVdbe;139 TEST_ASSERT_NOT_NULL(v);140 141 /* Unique path should include SorterCompare */142 TEST_ASSERT_TRUE(countOp(v, OP_SorterCompare) >= 1);143 144 /* Clear existing index since memRootPage < 0 */145 int iDb = sqlite3SchemaToIndex(db, pIdx->pSchema);146 TEST_ASSERT_TRUE(findOpP1P2(v, OP_Clear, (int)pIdx->tnum, iDb) >= 0);147 148 /* OpenWrite flags: BULKCSR set; P2ISREG not set */149 int iOpenWrite = findOp(v, OP_OpenWrite);150 TEST_ASSERT_TRUE(iOpenWrite >= 0);151 Op *opOpenWrite = &v->aOp[iOpenWrite];152 TEST_ASSERT_TRUE((opOpenWrite->p5 & OPFLAG_BULKCSR) != 0);153 TEST_ASSERT_TRUE((opOpenWrite->p5 & OPFLAG_P2ISREG) == 0);154 155 /* isMultiWrite should be set; mayAbort is not set by the unique path */156 TEST_ASSERT_EQUAL_INT(1, p.isMultiWrite);157 TEST_ASSERT_EQUAL_INT(0, p.mayAbort);158 159 sqlite3VdbeDelete(v);160 sqlite3_close(db);161}162 163/* Test 3: Non-unique index, memRootPage >= 0 (P2 is register), no Clear */164void test_sqlite3RefillIndex_memRootReg_sets_P2ISREG_and_no_clear(void){165 sqlite3 *db = 0;166 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));167 168 exec_ok(db, "CREATE TABLE t2(a INT, b INT);");169 exec_ok(db, "INSERT INTO t2 VALUES(1,100),(2,200);");170 exec_ok(db, "CREATE INDEX idx_b2 ON t2(b);");171 172 Index *pIdx = findIndex(db, "idx_b2");173 TEST_ASSERT_NOT_NULL(pIdx);174 TEST_ASSERT_FALSE(IsUniqueIndex(pIdx));175 176 Parse p;177 memset(&p, 0, sizeof(p));178 p.db = db;179 180 int regRoot = 42; /* arbitrary register number */181 test_sqlite3RefillIndex(&p, pIdx, regRoot);182 183 Vdbe *v = p.pVdbe;184 TEST_ASSERT_NOT_NULL(v);185 186 /* There must not be any OP_Clear since memRootPage >= 0 */187 TEST_ASSERT_EQUAL_INT(0, countOp(v, OP_Clear));188 189 /* OpenWrite must use P2 as register and match regRoot */190 int iOpenWrite = findOp(v, OP_OpenWrite);191 TEST_ASSERT_TRUE(iOpenWrite >= 0);192 Op *opOpenWrite = &v->aOp[iOpenWrite];193 TEST_ASSERT_EQUAL_INT(regRoot, opOpenWrite->p2);194 TEST_ASSERT_TRUE((opOpenWrite->p5 & OPFLAG_P2ISREG) != 0);195 TEST_ASSERT_TRUE((opOpenWrite->p5 & OPFLAG_BULKCSR) != 0);196 197 /* Non-unique path implies mayAbort set; and isMultiWrite set */198 TEST_ASSERT_EQUAL_INT(1, p.isMultiWrite);199 TEST_ASSERT_EQUAL_INT(1, p.mayAbort);200 201 sqlite3VdbeDelete(v);202 sqlite3_close(db);203}204 205int main(void) {206 UNITY_BEGIN();207 RUN_TEST(test_sqlite3RefillIndex_nonunique_memRootNeg_generates_expected_ops);208 RUN_TEST(test_sqlite3RefillIndex_unique_generates_compare_and_no_mayAbort);209 RUN_TEST(test_sqlite3RefillIndex_memRootReg_sets_P2ISREG_and_no_clear);210 return UNITY_END();211}