AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8/* Unity setup/teardown */9void setUp(void) {10 /* no-op */11}12void tearDown(void) {13 /* no-op */14}15 16/* Helper: execute SQL and assert success */17static void exec_sql_ok(sqlite3 *db, const char *sql){18 char *err = 0;19 int rc = sqlite3_exec(db, sql, 0, 0, &err);20 if( rc!=SQLITE_OK ){21 fprintf(stderr, "SQL error: %s\nWhile executing: %s\n", err?err:"(null)", sql);22 }23 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);24 if( err ) sqlite3_free(err);25}26 27/* Helper: count indexes and collect them in enumeration order (pTab->pIndex order) */28static int collectIndexes(Table *pTab, Index ***ppArr){29 int n = 0;30 Index *p;31 for(p=pTab->pIndex; p; p=p->pNext) n++;32 if( ppArr ){33 Index **arr = (Index**)sqlite3_malloc(sizeof(Index*)*n);34 TEST_ASSERT_NOT_NULL(arr);35 int i=0;36 for(p=pTab->pIndex; p; p=p->pNext){37 arr[i++] = p;38 }39 *ppArr = arr;40 }41 return n;42}43 44/* Helper: collect OP_IdxDelete ops in order */45static int collectIdxDeleteOps(Vdbe *v, VdbeOp ***ppOps){46 int n = 0;47 for(int i=0; i<v->nOp; i++){48 if( v->aOp[i].opcode==OP_IdxDelete ) n++;49 }50 if( ppOps ){51 VdbeOp **arr = (VdbeOp**)sqlite3_malloc(sizeof(VdbeOp*)*n);52 TEST_ASSERT_NOT_NULL(arr);53 int j=0;54 for(int i=0; i<v->nOp; i++){55 if( v->aOp[i].opcode==OP_IdxDelete ){56 arr[j++] = &v->aOp[i];57 }58 }59 *ppOps = arr;60 }61 return n;62}63 64/* Test 1: Rowid table; expect one OP_IdxDelete per index with correct P1/P3/P5 */65static void test_sqlite3GenerateRowIndexDelete_rowid_table_basic(void){66 sqlite3 *db = 0;67 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));68 69 /* Create a rowid table with:70 - Unique NOT NULL index on (a,b)71 - Normal index on (c)72 - Partial index on (b) WHERE c IS NOT NULL73 */74 exec_sql_ok(db, "CREATE TABLE t1(a NOT NULL, b NOT NULL, c);");75 exec_sql_ok(db, "CREATE UNIQUE INDEX t1_ab_u ON t1(a,b);");76 exec_sql_ok(db, "CREATE INDEX t1_c ON t1(c);");77 exec_sql_ok(db, "CREATE INDEX t1_b_partial ON t1(b) WHERE c IS NOT NULL;");78 79 Table *pTab = sqlite3FindTable(db, "t1", "main");80 TEST_ASSERT_NOT_NULL(pTab);81 82 Index **idxArr = 0;83 int nIdx = collectIndexes(pTab, &idxArr);84 TEST_ASSERT_TRUE(nIdx >= 3); /* We created 3 indexes */85 86 Parse sParse;87 memset(&sParse, 0, sizeof(sParse));88 sParse.db = db;89 sParse.pVdbe = sqlite3VdbeCreate(db);90 TEST_ASSERT_NOT_NULL(sParse.pVdbe);91 92 int iDataCur = 1;93 int iIdxCur = 5;94 int *aRegIdx = 0; /* delete all indexes */95 int iIdxNoSeek = -1;96 97 sqlite3GenerateRowIndexDelete(&sParse, pTab, iDataCur, iIdxCur, aRegIdx, iIdxNoSeek);98 99 Vdbe *v = sParse.pVdbe;100 VdbeOp **ops = 0;101 int nDel = collectIdxDeleteOps(v, &ops);102 103 /* All indexes should be targeted (no PK for rowid tables) */104 TEST_ASSERT_EQUAL_INT(nIdx, nDel);105 106 /* Validate each OP_IdxDelete's P1, P3, and P5 */107 for(int i=0; i<nDel; i++){108 Index *pIdx = idxArr[i];109 VdbeOp *op = ops[i];110 TEST_ASSERT_EQUAL_INT(iIdxCur + i, op->p1);111 /* P3 is nKeyCol if uniqNotNull, else nColumn */112 int expectedP3 = pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn;113 TEST_ASSERT_EQUAL_INT(expectedP3, op->p3);114 TEST_ASSERT_EQUAL_INT(1, op->p5); /* Should cause error if not found */115 }116 117 sqlite3_free(ops);118 sqlite3_free(idxArr);119 sqlite3VdbeDelete(sParse.pVdbe);120 sqlite3_close(db);121}122 123/* Test 2: aRegIdx filtering: skip one index (where aRegIdx[i]==0) */124static void test_sqlite3GenerateRowIndexDelete_respects_aRegIdx(void){125 sqlite3 *db = 0;126 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));127 128 exec_sql_ok(db, "CREATE TABLE t2(a NOT NULL, b NOT NULL, c);");129 exec_sql_ok(db, "CREATE UNIQUE INDEX t2_ab_u ON t2(a,b);");130 exec_sql_ok(db, "CREATE INDEX t2_c ON t2(c);");131 exec_sql_ok(db, "CREATE INDEX t2_b ON t2(b);");132 133 Table *pTab = sqlite3FindTable(db, "t2", "main");134 TEST_ASSERT_NOT_NULL(pTab);135 136 Index **idxArr = 0;137 int nIdx = collectIndexes(pTab, &idxArr);138 TEST_ASSERT_TRUE(nIdx >= 3);139 140 int *aRegIdx = (int*)sqlite3_malloc(sizeof(int)*nIdx);141 TEST_ASSERT_NOT_NULL(aRegIdx);142 /* Allow all except skip the middle one (iSkip) */143 int iSkip = (nIdx>=2) ? 1 : 0;144 for(int i=0;i<nIdx;i++) aRegIdx[i] = (i==iSkip) ? 0 : 1;145 146 Parse ps;147 memset(&ps, 0, sizeof(ps));148 ps.db = db;149 ps.pVdbe = sqlite3VdbeCreate(db);150 TEST_ASSERT_NOT_NULL(ps.pVdbe);151 152 int iDataCur = 2;153 int iIdxCur = 10;154 int iIdxNoSeek = -1;155 156 sqlite3GenerateRowIndexDelete(&ps, pTab, iDataCur, iIdxCur, aRegIdx, iIdxNoSeek);157 158 VdbeOp **ops = 0;159 int nDel = collectIdxDeleteOps(ps.pVdbe, &ops);160 TEST_ASSERT_EQUAL_INT(nIdx - 1, nDel);161 162 /* Build expectation: all indexes except iSkip */163 int j = 0;164 for(int i=0; i<nIdx; i++){165 if( i==iSkip ) continue;166 Index *pIdx = idxArr[i];167 VdbeOp *op = ops[j++];168 TEST_ASSERT_EQUAL_INT(iIdxCur + i, op->p1);169 int expectedP3 = pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn;170 TEST_ASSERT_EQUAL_INT(expectedP3, op->p3);171 TEST_ASSERT_EQUAL_INT(1, op->p5);172 }173 174 sqlite3_free(ops);175 sqlite3_free(aRegIdx);176 sqlite3_free(idxArr);177 sqlite3VdbeDelete(ps.pVdbe);178 sqlite3_close(db);179}180 181/* Test 3: iIdxNoSeek skip: ensure selected cursor is skipped */182static void test_sqlite3GenerateRowIndexDelete_respects_iIdxNoSeek(void){183 sqlite3 *db = 0;184 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));185 186 exec_sql_ok(db, "CREATE TABLE t3(a, b, c);");187 exec_sql_ok(db, "CREATE INDEX t3_a ON t3(a);");188 exec_sql_ok(db, "CREATE INDEX t3_b ON t3(b);");189 exec_sql_ok(db, "CREATE INDEX t3_c ON t3(c);");190 191 Table *pTab = sqlite3FindTable(db, "t3", "main");192 TEST_ASSERT_NOT_NULL(pTab);193 194 Index **idxArr = 0;195 int nIdx = collectIndexes(pTab, &idxArr);196 TEST_ASSERT_TRUE(nIdx >= 3);197 198 Parse ps;199 memset(&ps, 0, sizeof(ps));200 ps.db = db;201 ps.pVdbe = sqlite3VdbeCreate(db);202 TEST_ASSERT_NOT_NULL(ps.pVdbe);203 204 int iDataCur = 3;205 int iIdxCur = 20;206 207 /* Choose to skip the last enumerated index */208 int iSkip = nIdx - 1;209 int iIdxNoSeek = iIdxCur + iSkip;210 211 sqlite3GenerateRowIndexDelete(&ps, pTab, iDataCur, iIdxCur, 0, iIdxNoSeek);212 213 VdbeOp **ops = 0;214 int nDel = collectIdxDeleteOps(ps.pVdbe, &ops);215 TEST_ASSERT_EQUAL_INT(nIdx - 1, nDel);216 217 int j = 0;218 for(int i=0; i<nIdx; i++){219 if( i==iSkip ) continue;220 Index *pIdx = idxArr[i];221 VdbeOp *op = ops[j++];222 TEST_ASSERT_EQUAL_INT(iIdxCur + i, op->p1);223 int expectedP3 = pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn;224 TEST_ASSERT_EQUAL_INT(expectedP3, op->p3);225 TEST_ASSERT_EQUAL_INT(1, op->p5);226 }227 228 sqlite3_free(ops);229 sqlite3_free(idxArr);230 sqlite3VdbeDelete(ps.pVdbe);231 sqlite3_close(db);232}233 234/* Test 4: WITHOUT ROWID table: ensure PRIMARY KEY index is skipped */235static void test_sqlite3GenerateRowIndexDelete_wo_rowid_skips_pk(void){236 sqlite3 *db = 0;237 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));238 239 exec_sql_ok(db, "CREATE TABLE w1(a, b, c, PRIMARY KEY(a,b)) WITHOUT ROWID;");240 exec_sql_ok(db, "CREATE INDEX w1_c ON w1(c);");241 exec_sql_ok(db, "CREATE UNIQUE INDEX w1_b_u ON w1(b);");242 243 Table *pTab = sqlite3FindTable(db, "w1", "main");244 TEST_ASSERT_NOT_NULL(pTab);245 246 /* Ensure HasRowid is false */247 TEST_ASSERT_FALSE( HasRowid(pTab) );248 249 Index **idxArr = 0;250 int nIdx = collectIndexes(pTab, &idxArr);251 TEST_ASSERT_TRUE(nIdx >= 2);252 253 Index *pPk = sqlite3PrimaryKeyIndex(pTab);254 TEST_ASSERT_NOT_NULL(pPk);255 256 Parse ps;257 memset(&ps, 0, sizeof(ps));258 ps.db = db;259 ps.pVdbe = sqlite3VdbeCreate(db);260 TEST_ASSERT_NOT_NULL(ps.pVdbe);261 262 int iDataCur = 100; /* choose values different from iIdxCur to avoid assertion */263 int iIdxCur = 200;264 265 sqlite3GenerateRowIndexDelete(&ps, pTab, iDataCur, iIdxCur, 0, -1);266 267 VdbeOp **ops = 0;268 int nDel = collectIdxDeleteOps(ps.pVdbe, &ops);269 270 /* We expect one OP_IdxDelete per non-PK index */271 int nNonPk = 0;272 for(int i=0; i<nIdx; i++){273 if( idxArr[i] != pPk ) nNonPk++;274 }275 TEST_ASSERT_EQUAL_INT(nNonPk, nDel);276 277 /* Validate that only non-PK indexes are targeted and P1/P3/P5 are correct */278 int j = 0;279 for(int i=0; i<nIdx; i++){280 Index *pIdx = idxArr[i];281 if( pIdx==pPk ) continue;282 VdbeOp *op = ops[j++];283 TEST_ASSERT_EQUAL_INT(iIdxCur + i, op->p1);284 int expectedP3 = pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn;285 TEST_ASSERT_EQUAL_INT(expectedP3, op->p3);286 TEST_ASSERT_EQUAL_INT(1, op->p5);287 }288 289 sqlite3_free(ops);290 sqlite3_free(idxArr);291 sqlite3VdbeDelete(ps.pVdbe);292 sqlite3_close(db);293}294 295int main(void){296 UNITY_BEGIN();297 RUN_TEST(test_sqlite3GenerateRowIndexDelete_rowid_table_basic);298 RUN_TEST(test_sqlite3GenerateRowIndexDelete_respects_aRegIdx);299 RUN_TEST(test_sqlite3GenerateRowIndexDelete_respects_iIdxNoSeek);300 RUN_TEST(test_sqlite3GenerateRowIndexDelete_wo_rowid_skips_pk);301 return UNITY_END();302}