AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdbool.h>6 7/* Unity fixtures */8void setUp(void) {9 /* No global setup */10}11void tearDown(void) {12 /* No global teardown */13}14 15/* Helper: exec SQL, assert success */16static void exec_sql_ok(sqlite3 *db, const char *sql){17 char *errmsg = 0;18 int rc = sqlite3_exec(db, sql, 0, 0, &errmsg);19 if( rc!=SQLITE_OK ){20 const char *em = errmsg ? errmsg : "unknown";21 TEST_FAIL_MESSAGE(em);22 }23 if( errmsg ) sqlite3_free(errmsg);24}25 26/* Helper: initialize a Parse+Vdbe */27static void init_parse(sqlite3 *db, Parse *pParse, Vdbe **ppVdbe){28 memset(pParse, 0, sizeof(Parse));29 pParse->db = db;30 Vdbe *v = sqlite3VdbeCreate(db);31 TEST_ASSERT_NOT_NULL(v);32 pParse->pVdbe = v;33 if( ppVdbe ) *ppVdbe = v;34}35 36/* Helper: cleanup Vdbe */37static void cleanup_vdbe(Vdbe *v){38 if( v ) sqlite3VdbeDelete(v);39}40 41/* Helper: find Table* by name in "main" */42static Table* find_table(sqlite3 *db, const char *zName){43 Table *pTab = sqlite3FindTable(db, zName, "main");44 TEST_ASSERT_NOT_NULL_MESSAGE(pTab, "Table not found in schema");45 return pTab;46}47 48/* Helper: collect Index tnums into array; returns count */49static int collect_index_tnums(Table *pTab, int *aTnum, int nAlloc){50 int n = 0;51 for(Index *pIdx = pTab->pIndex; pIdx && n<nAlloc; pIdx = pIdx->pNext){52 aTnum[n++] = pIdx->tnum;53 }54 return n;55}56 57/* Helper: check if value in array */58static bool int_in_array(int v, const int *a, int n){59 for(int i=0;i<n;i++) if( a[i]==v ) return true;60 return false;61}62 63/* Helper: find OP_OpenWrite with given cursor (p1) in ops[start..end) */64static const VdbeOp* find_openwrite_by_cursor(const Vdbe *v, int start, int cursor){65 for(int i=start; i<v->nOp; i++){66 const VdbeOp *op = &v->aOp[i];67 if( op->opcode==OP_OpenWrite && op->p1==cursor ){68 return op;69 }70 }71 return NULL;72}73 74/* Helper: find OP_OpenWrite with given tnum in P2 (index/table rootpage) */75static const VdbeOp* find_openwrite_by_tnum(const Vdbe *v, int start, int tnum){76 for(int i=start; i<v->nOp; i++){77 const VdbeOp *op = &v->aOp[i];78 if( op->opcode==OP_OpenWrite && op->p2==tnum ){79 return op;80 }81 }82 return NULL;83}84 85/* Test 1: Rowid table, full open (table + all indexes), base iBase=0 */86static void test_sqlite3OpenTableAndIndices_rowid_full_open(void){87 sqlite3 *db = NULL;88 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));89 exec_sql_ok(db,90 "CREATE TABLE t1(a, b, c);\n"91 "CREATE INDEX t1_a ON t1(a);\n"92 "CREATE INDEX t1_b ON t1(b);\n"93 );94 Table *pTab = find_table(db, "t1");95 96 Parse p;97 Vdbe *v = NULL;98 init_parse(db, &p, &v);99 100 int dataCur = -1, idxCur = -1;101 int nOp0 = v->nOp;102 int p5 = 7;103 int rc = sqlite3OpenTableAndIndices(&p, pTab, OP_OpenWrite, (u8)p5,104 0, NULL, &dataCur, &idxCur);105 TEST_ASSERT_EQUAL_INT(0, nOp0); /* Fresh Vdbe has no ops */106 TEST_ASSERT_EQUAL_INT(2, rc); /* two indexes */107 TEST_ASSERT_EQUAL_INT(0, dataCur);108 TEST_ASSERT_EQUAL_INT(1, idxCur);109 110 /* Verify table open at cursor 0 */111 const VdbeOp *opTbl = find_openwrite_by_cursor(v, nOp0, 0);112 TEST_ASSERT_NOT_NULL(opTbl);113 TEST_ASSERT_EQUAL_INT(OP_OpenWrite, opTbl->opcode);114 TEST_ASSERT_EQUAL_INT(pTab->tnum, opTbl->p2);115 116 /* Verify index opens at cursors 1 and 2 with provided p5 */117 int idxTnums[8]; /* plenty */118 int nIdx = collect_index_tnums(pTab, idxTnums, 8);119 TEST_ASSERT_EQUAL_INT(2, nIdx);120 121 const VdbeOp *opIdx1 = find_openwrite_by_cursor(v, nOp0, 1);122 const VdbeOp *opIdx2 = find_openwrite_by_cursor(v, nOp0, 2);123 TEST_ASSERT_NOT_NULL(opIdx1);124 TEST_ASSERT_NOT_NULL(opIdx2);125 TEST_ASSERT_TRUE(int_in_array(opIdx1->p2, idxTnums, nIdx));126 TEST_ASSERT_TRUE(int_in_array(opIdx2->p2, idxTnums, nIdx));127 TEST_ASSERT(opIdx1->p2 != opIdx2->p2);128 129 /* p5 applies only to index opens on rowid tables */130 TEST_ASSERT_EQUAL_UINT16(p5, opIdx1->p5);131 TEST_ASSERT_EQUAL_UINT16(p5, opIdx2->p5);132 133 cleanup_vdbe(v);134 sqlite3_close(db);135}136 137/* Test 2: Rowid table with selective aToOpen: open only the second index */138static void test_sqlite3OpenTableAndIndices_rowid_selective_aToOpen(void){139 sqlite3 *db = NULL;140 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));141 exec_sql_ok(db,142 "CREATE TABLE t2(x, y, z);\n"143 "CREATE INDEX t2_x ON t2(x);\n"144 "CREATE INDEX t2_y ON t2(y);\n"145 );146 Table *pTab = find_table(db, "t2");147 148 Parse p;149 Vdbe *v = NULL;150 init_parse(db, &p, &v);151 152 int dataCur=-1, idxCur=-1;153 int iBase = 5;154 unsigned char aToOpen[3] = {0, 0, 1}; /* skip table, skip first index, open second index only */155 int rc = sqlite3OpenTableAndIndices(&p, pTab, OP_OpenWrite, (u8)9,156 iBase, aToOpen, &dataCur, &idxCur);157 TEST_ASSERT_EQUAL_INT(2, rc);158 TEST_ASSERT_EQUAL_INT(iBase, dataCur);159 TEST_ASSERT_EQUAL_INT(iBase+1, idxCur);160 161 /* Expect only one OP_OpenWrite: for cursor 7 (second index), and none for 5 or 6 */162 const VdbeOp *op5 = find_openwrite_by_cursor(v, 0, 5);163 const VdbeOp *op6 = find_openwrite_by_cursor(v, 0, 6);164 const VdbeOp *op7 = find_openwrite_by_cursor(v, 0, 7);165 166 TEST_ASSERT_NULL(op5);167 TEST_ASSERT_NULL(op6);168 TEST_ASSERT_NOT_NULL(op7);169 170 /* The opened thing must be one of the table's indexes (not the table, since table was skipped) */171 int idxTnums[8];172 int nIdx = collect_index_tnums(pTab, idxTnums, 8);173 TEST_ASSERT_TRUE(int_in_array(op7->p2, idxTnums, nIdx));174 175 cleanup_vdbe(v);176 sqlite3_close(db);177}178 179/* Test 3: Rowid table with iBase < 0 uses pParse->nTab and updates nTab */180static void test_sqlite3OpenTableAndIndices_rowid_negative_iBase_and_nTab(void){181 sqlite3 *db = NULL;182 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));183 exec_sql_ok(db,184 "CREATE TABLE t3(p, q);\n"185 "CREATE INDEX t3_p ON t3(p);\n"186 "CREATE INDEX t3_q ON t3(q);\n"187 );188 Table *pTab = find_table(db, "t3");189 190 Parse p;191 Vdbe *v = NULL;192 init_parse(db, &p, &v);193 p.nTab = 8; /* Preexisting temp cursor space */194 195 int dataCur=-1, idxCur=-1;196 int rc = sqlite3OpenTableAndIndices(&p, pTab, OP_OpenWrite, (u8)5,197 -1, NULL, &dataCur, &idxCur);198 TEST_ASSERT_EQUAL_INT(2, rc);199 TEST_ASSERT_EQUAL_INT(8, dataCur);200 TEST_ASSERT_EQUAL_INT(9, idxCur);201 /* Should allocate data + 2 indexes => next free is 11 */202 TEST_ASSERT_EQUAL_INT(11, p.nTab);203 204 /* Verify there are opens for cursors 8, 9, 10 */205 TEST_ASSERT_NOT_NULL(find_openwrite_by_cursor(v, 0, 8));206 TEST_ASSERT_NOT_NULL(find_openwrite_by_cursor(v, 0, 9));207 TEST_ASSERT_NOT_NULL(find_openwrite_by_cursor(v, 0, 10));208 209 cleanup_vdbe(v);210 sqlite3_close(db);211}212 213/* Test 4: WITHOUT ROWID table: data cursor is PK index, and p5 forced to 0 for PK open */214static void test_sqlite3OpenTableAndIndices_without_rowid_pk_p5_zero_and_dataCur(void){215 sqlite3 *db = NULL;216 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));217 exec_sql_ok(db,218 "CREATE TABLE w(a INTEGER, b INTEGER, PRIMARY KEY(a)) WITHOUT ROWID;\n"219 "CREATE INDEX w_b ON w(b);\n"220 );221 Table *pTab = find_table(db, "w");222 Index *pPk = sqlite3PrimaryKeyIndex(pTab);223 TEST_ASSERT_NOT_NULL_MESSAGE(pPk, "Primary key index not found");224 int pkTnum = pPk->tnum;225 226 Parse p;227 Vdbe *v = NULL;228 init_parse(db, &p, &v);229 230 int dataCur=-1, idxCur=-1;231 int iBase = 2;232 int p5in = 99;233 int rc = sqlite3OpenTableAndIndices(&p, pTab, OP_OpenWrite, (u8)p5in,234 iBase, NULL, &dataCur, &idxCur);235 TEST_ASSERT_EQUAL_INT(2, rc);236 /* First index cursor number always equals iBase */237 TEST_ASSERT_EQUAL_INT(iBase, idxCur);238 239 /* Find OpenWrite for the PK index tnum */240 const VdbeOp *opPk = find_openwrite_by_tnum(v, 0, pkTnum);241 TEST_ASSERT_NOT_NULL_MESSAGE(opPk, "No OP_OpenWrite for PK index");242 /* Data cursor must be the cursor used for opening the PK index */243 TEST_ASSERT_EQUAL_INT(opPk->p1, dataCur);244 /* p5 forced to 0 for WITHOUT ROWID PK open */245 TEST_ASSERT_EQUAL_UINT16(0, opPk->p5);246 247 cleanup_vdbe(v);248 sqlite3_close(db);249}250 251int main(void){252 UNITY_BEGIN();253 RUN_TEST(test_sqlite3OpenTableAndIndices_rowid_full_open);254 RUN_TEST(test_sqlite3OpenTableAndIndices_rowid_selective_aToOpen);255 RUN_TEST(test_sqlite3OpenTableAndIndices_rowid_negative_iBase_and_nTab);256 RUN_TEST(test_sqlite3OpenTableAndIndices_without_rowid_pk_p5_zero_and_dataCur);257 return UNITY_END();258}