AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4 5/* Unity setup/teardown */6void setUp(void) { }7void tearDown(void) { }8 9/* Helper: open a test database */10static sqlite3* openTestDb(void){11 sqlite3 *db = 0;12 int rc = sqlite3_open(":memory:", &db);13 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);14 TEST_ASSERT_NOT_NULL(db);15 return db;16}17 18/* Helper: create and bind a VDBE to a Parse */19static Vdbe* makeVdbe(sqlite3 *db, Parse *pParse){20 memset(pParse, 0, sizeof(*pParse));21 pParse->db = db;22 Vdbe *v = sqlite3VdbeCreate(db);23 TEST_ASSERT_NOT_NULL(v);24 pParse->pVdbe = v;25 return v;26}27 28/* Helper: create a minimal Table with nCol columns */29static Table* makeTable(sqlite3 *db, int nCol){30 Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));31 TEST_ASSERT_NOT_NULL(pTab);32 pTab->nCol = nCol;33 pTab->tabFlags = 0;34 pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);35 TEST_ASSERT_NOT_NULL(pTab->aCol);36 /* Column details are not needed for OP_Column generation here */37 return pTab;38}39 40/* Helper: create an Index with given column list on pTab */41static Index* makeIndex(sqlite3 *db, Table *pTab, int nColumn, int nKeyCol,42 int uniqNotNull, const int *aiCols){43 Index *pIdx = (Index*)sqlite3DbMallocZero(db, sizeof(Index));44 TEST_ASSERT_NOT_NULL(pIdx);45 pIdx->pTable = pTab;46 pIdx->nColumn = (i16)nColumn;47 pIdx->nKeyCol = (i16)nKeyCol;48 pIdx->uniqNotNull = (u8)(uniqNotNull ? 1 : 0);49 pIdx->aiColumn = (i16*)sqlite3DbMallocZero(db, sizeof(i16)*nColumn);50 TEST_ASSERT_NOT_NULL(pIdx->aiColumn);51 for(int i=0;i<nColumn;i++){52 pIdx->aiColumn[i] = (i16)aiCols[i];53 }54 /* pPartIdxWhere left NULL unless explicitly set in test */55 return pIdx;56}57 58/* Helper: free Index (optionally also free the attached Table) */59static void freeIndexAndMaybeTable(sqlite3 *db, Index *pIdx, int freeTable){60 if( pIdx ){61 Table *pTab = pIdx->pTable;62 if( pIdx->aiColumn ) sqlite3DbFree(db, pIdx->aiColumn);63 sqlite3DbFree(db, pIdx);64 if( freeTable && pTab ){65 if( pTab->aCol ) sqlite3DbFree(db, pTab->aCol);66 sqlite3DbFree(db, pTab);67 }68 }69}70 71/* Helper: count number of occurrences of a specific opcode */72static int countOpcode(Vdbe *v, int opcode){73 int n = sqlite3VdbeCurrentAddr(v);74 int cnt = 0;75 for(int i=0; i<n; i++){76 VdbeOp *op = sqlite3VdbeGetOp(v, i);77 if( op && op->opcode==opcode ) cnt++;78 }79 return cnt;80}81 82/* Test 1: Basic generation with OP_MakeRecord and full column load */83void test_sqlite3GenerateIndexKey_basic_make_record(void){84 sqlite3 *db = openTestDb();85 Parse parse;86 Vdbe *v = makeVdbe(db, &parse);87 88 Table *pTab = makeTable(db, 3);89 int cols[] = {0,1,2};90 Index *pIdx = makeIndex(db, pTab, 3, 3, 0, cols);91 92 int regOut = 7;93 int regBase = sqlite3GenerateIndexKey(&parse, pIdx, 2, regOut, 0, NULL, NULL, 0);94 95 TEST_ASSERT_TRUE(regBase > 0);96 TEST_ASSERT_EQUAL_INT(3, countOpcode(v, OP_Column));97 98 int nOp = sqlite3VdbeCurrentAddr(v);99 TEST_ASSERT_TRUE(nOp > 0);100 VdbeOp *last = sqlite3VdbeGetOp(v, nOp-1);101 TEST_ASSERT_NOT_NULL(last);102 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, last->opcode);103 TEST_ASSERT_EQUAL_INT(regBase, last->p1);104 TEST_ASSERT_EQUAL_INT(3, last->p2);105 TEST_ASSERT_EQUAL_INT(regOut, last->p3);106 107 sqlite3VdbeDelete(v);108 freeIndexAndMaybeTable(db, pIdx, 1);109 sqlite3_close(db);110}111 112/* Test 2: prefixOnly with uniqNotNull==true limits to nKeyCol columns */113void test_sqlite3GenerateIndexKey_prefixOnly_uniqNotNull(void){114 sqlite3 *db = openTestDb();115 Parse parse;116 Vdbe *v = makeVdbe(db, &parse);117 118 Table *pTab = makeTable(db, 3);119 int cols[] = {0,1,2};120 Index *pIdx = makeIndex(db, pTab, 3, 2, 1, cols); /* uniqNotNull=1, nKeyCol=2 */121 122 int regOut = 3;123 int regBase = sqlite3GenerateIndexKey(&parse, pIdx, 1, regOut, 1, NULL, NULL, 0);124 125 TEST_ASSERT_TRUE(regBase > 0);126 TEST_ASSERT_EQUAL_INT(2, countOpcode(v, OP_Column));127 128 int nOp = sqlite3VdbeCurrentAddr(v);129 VdbeOp *last = sqlite3VdbeGetOp(v, nOp-1);130 TEST_ASSERT_NOT_NULL(last);131 TEST_ASSERT_EQUAL_INT(OP_MakeRecord, last->opcode);132 TEST_ASSERT_EQUAL_INT(2, last->p2);133 TEST_ASSERT_EQUAL_INT(regOut, last->p3);134 135 sqlite3VdbeDelete(v);136 freeIndexAndMaybeTable(db, pIdx, 1);137 sqlite3_close(db);138}139 140/* Test 3: No OP_MakeRecord when regOut==0 */141void test_sqlite3GenerateIndexKey_no_make_record_when_regOut_zero(void){142 sqlite3 *db = openTestDb();143 Parse parse;144 Vdbe *v = makeVdbe(db, &parse);145 146 Table *pTab = makeTable(db, 2);147 int cols[] = {0,1};148 Index *pIdx = makeIndex(db, pTab, 2, 2, 0, cols);149 150 (void)sqlite3GenerateIndexKey(&parse, pIdx, 0, 0, 0, NULL, NULL, 0);151 152 TEST_ASSERT_EQUAL_INT(0, countOpcode(v, OP_MakeRecord));153 TEST_ASSERT_EQUAL_INT(2, countOpcode(v, OP_Column));154 155 sqlite3VdbeDelete(v);156 freeIndexAndMaybeTable(db, pIdx, 1);157 sqlite3_close(db);158}159 160/* Test 4: pPrior optimization skips identical columns when regPrior==regBase */161void test_sqlite3GenerateIndexKey_skip_with_pPrior_same_columns(void){162 sqlite3 *db = openTestDb();163 Parse parse;164 Vdbe *v = makeVdbe(db, &parse);165 166 Table *pTab = makeTable(db, 3);167 int cols[] = {0,1,2};168 Index *pPrior = makeIndex(db, pTab, 3, 3, 0, cols);169 Index *pIdx = makeIndex(db, pTab, 3, 3, 0, cols); /* identical columns */170 171 /* Ensure regPrior matches the expected first temp register base (likely 1) */172 int regPrior = 1;173 (void)sqlite3GenerateIndexKey(&parse, pIdx, 0, 0, 0, NULL, pPrior, regPrior);174 175 /* All columns should be skipped because pPrior matches and regPrior==regBase */176 TEST_ASSERT_EQUAL_INT(0, countOpcode(v, OP_Column));177 TEST_ASSERT_EQUAL_INT(0, countOpcode(v, OP_MakeRecord));178 179 sqlite3VdbeDelete(v);180 /* Free both indices; table only once */181 freeIndexAndMaybeTable(db, pPrior, 0);182 freeIndexAndMaybeTable(db, pIdx, 1);183 sqlite3_close(db);184}185 186/* Test 5: pPrior ignored when regPrior != regBase (all columns loaded) */187void test_sqlite3GenerateIndexKey_pPrior_ignored_when_reg_base_differs(void){188 sqlite3 *db = openTestDb();189 Parse parse;190 Vdbe *v = makeVdbe(db, &parse);191 192 Table *pTab = makeTable(db, 3);193 int cols[] = {0,1,2};194 Index *pPrior = makeIndex(db, pTab, 3, 3, 0, cols);195 Index *pIdx = makeIndex(db, pTab, 3, 3, 0, cols);196 197 int regPrior = 999; /* Mismatch forces pPrior to be ignored */198 (void)sqlite3GenerateIndexKey(&parse, pIdx, 0, 0, 0, NULL, pPrior, regPrior);199 200 TEST_ASSERT_EQUAL_INT(3, countOpcode(v, OP_Column));201 TEST_ASSERT_EQUAL_INT(0, countOpcode(v, OP_MakeRecord));202 203 sqlite3VdbeDelete(v);204 freeIndexAndMaybeTable(db, pPrior, 0);205 freeIndexAndMaybeTable(db, pIdx, 1);206 sqlite3_close(db);207}208 209/* Test 6: piPartIdxLabel set to 0 when pPartIdxWhere==NULL */210void test_sqlite3GenerateIndexKey_piPartIdxLabel_zero_when_no_partial_index(void){211 sqlite3 *db = openTestDb();212 Parse parse;213 Vdbe *v = makeVdbe(db, &parse);214 215 Table *pTab = makeTable(db, 1);216 int cols[] = {0};217 Index *pIdx = makeIndex(db, pTab, 1, 1, 0, cols);218 /* Ensure no partial-index WHERE is set */219 TEST_ASSERT_NULL(pIdx->pPartIdxWhere);220 221 int label = -1;222 (void)sqlite3GenerateIndexKey(&parse, pIdx, 0, 0, 0, &label, NULL, 0);223 224 TEST_ASSERT_EQUAL_INT(0, label);225 226 sqlite3VdbeDelete(v);227 freeIndexAndMaybeTable(db, pIdx, 1);228 sqlite3_close(db);229}230 231int main(void){232 UNITY_BEGIN();233 RUN_TEST(test_sqlite3GenerateIndexKey_basic_make_record);234 RUN_TEST(test_sqlite3GenerateIndexKey_prefixOnly_uniqNotNull);235 RUN_TEST(test_sqlite3GenerateIndexKey_no_make_record_when_regOut_zero);236 RUN_TEST(test_sqlite3GenerateIndexKey_skip_with_pPrior_same_columns);237 RUN_TEST(test_sqlite3GenerateIndexKey_pPrior_ignored_when_reg_base_differs);238 RUN_TEST(test_sqlite3GenerateIndexKey_piPartIdxLabel_zero_when_no_partial_index);239 return UNITY_END();240}