AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Wrapper for the static target function (provided by the module) */7extern void test_analyzeOneTable(8 Parse *pParse,9 Table *pTab,10 Index *pOnlyIdx,11 int iStatCur,12 int iMem,13 int iTab14);15 16static void make_db(sqlite3 **pDb){17 int rc = sqlite3_open(":memory:", pDb);18 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);19}20 21static void exec_sql(sqlite3 *db, const char *zSql){22 char *zErr = 0;23 int rc = sqlite3_exec(db, zSql, 0, 0, &zErr);24 if( rc!=SQLITE_OK ){25 const char *msg = zErr ? zErr : "exec_sql error";26 TEST_FAIL_MESSAGE(msg);27 }28 if( zErr ) sqlite3_free(zErr);29}30 31static void init_parse(sqlite3 *db, Parse *p){32 memset(p, 0, sizeof(*p));33 p->db = db;34}35 36static int count_op(Vdbe *v, int op){37 int cnt = 0;38 if( v==0 ) return 0;39 for(int i=0; i<v->nOp; i++){40 if( v->aOp[i].opcode==op ) cnt++;41 }42 return cnt;43}44 45static int count_op_p1(Vdbe *v, int op, int p1){46 int cnt = 0;47 if( v==0 ) return 0;48 for(int i=0; i<v->nOp; i++){49 if( v->aOp[i].opcode==op && v->aOp[i].p1==p1 ) cnt++;50 }51 return cnt;52}53 54void setUp(void) {55 /* No global setup */56}57 58void tearDown(void) {59 /* No global teardown */60}61 62/* Test 1: System table is ignored (should generate no opcodes) */63void test_analyzeOneTable_system_table_noops(void){64 sqlite3 *db = 0;65 make_db(&db);66 67 /* Obtain Table* for sqlite_master */68 Table *pTab = sqlite3FindTable(db, "sqlite_master", db->aDb[0].zDbSName);69 TEST_ASSERT_NOT_NULL(pTab);70 71 Parse p;72 init_parse(db, &p);73 74 /* Use arbitrary cursor/register bases */75 int iStatCur = 1;76 int iMem = 1;77 int iTab = 2;78 79 test_analyzeOneTable(&p, pTab, 0, iStatCur, iMem, iTab);80 81 /* VDBE should exist but have no opcodes emitted */82 TEST_ASSERT_NOT_NULL(p.pVdbe);83 TEST_ASSERT_EQUAL_INT(0, p.pVdbe->nOp);84 85 sqlite3_close(db);86}87 88/* Test 2: Simple user table without indexes generates code including an89** OP_Insert into sqlite_stat1 (cursor iStatCur). */90void test_analyzeOneTable_simple_table_generates_stat1_insert(void){91 sqlite3 *db = 0;92 make_db(&db);93 94 exec_sql(db, "CREATE TABLE t1(a INTEGER, b TEXT);");95 96 Table *pTab = sqlite3FindTable(db, "t1", db->aDb[0].zDbSName);97 TEST_ASSERT_NOT_NULL(pTab);98 99 Parse p;100 init_parse(db, &p);101 102 int iStatCur = 3;103 int iMem = 1;104 int iTab = 2;105 106 test_analyzeOneTable(&p, pTab, 0, iStatCur, iMem, iTab);107 108 TEST_ASSERT_NOT_NULL(p.pVdbe);109 TEST_ASSERT_TRUE(p.pVdbe->nOp > 0);110 111 /* Expect at least one OP_OpenRead (on the table) */112 TEST_ASSERT_TRUE(count_op(p.pVdbe, OP_OpenRead) >= 1);113 114 /* Expect at least one OP_Insert to iStatCur for sqlite_stat1 */115 int nIns = count_op_p1(p.pVdbe, OP_Insert, iStatCur);116 TEST_ASSERT_TRUE_MESSAGE(nIns >= 1, "Expected OP_Insert into stat1 cursor");117 118 sqlite3_close(db);119}120 121/* Test 3: pOnlyIdx limits analysis to that index. With two indexes,122** analyzing only one should produce fewer OP_OpenRead than analyzing all. */123void test_analyzeOneTable_only_specific_index_restricts(void){124 sqlite3 *db = 0;125 make_db(&db);126 127 exec_sql(db, "CREATE TABLE t2(a, b);\n"128 "CREATE INDEX t2_i1 ON t2(a);\n"129 "CREATE INDEX t2_i2 ON t2(b);");130 131 Table *pTab = sqlite3FindTable(db, "t2", db->aDb[0].zDbSName);132 TEST_ASSERT_NOT_NULL(pTab);133 134 Index *pIdx1 = sqlite3FindIndex(db, "t2_i1", db->aDb[0].zDbSName);135 TEST_ASSERT_NOT_NULL(pIdx1);136 137 /* Analyze only index i1 */138 Parse pOne;139 init_parse(db, &pOne);140 int iStatCur = 4;141 int iMem = 1;142 int iTab = 2;143 test_analyzeOneTable(&pOne, pTab, pIdx1, iStatCur, iMem, iTab);144 TEST_ASSERT_NOT_NULL(pOne.pVdbe);145 int nOpenReadOne = count_op(pOne.pVdbe, OP_OpenRead);146 147 /* Analyze all indexes */148 Parse pAll;149 init_parse(db, &pAll);150 test_analyzeOneTable(&pAll, pTab, 0, iStatCur, iMem, iTab);151 TEST_ASSERT_NOT_NULL(pAll.pVdbe);152 int nOpenReadAll = count_op(pAll.pVdbe, OP_OpenRead);153 154 /* Expect at least one more OP_OpenRead when both indexes are analyzed */155 TEST_ASSERT_TRUE(nOpenReadAll >= nOpenReadOne + 1);156 157 sqlite3_close(db);158}159 160/* Test 4: Final table-count entry (NULL idx in stat1) is included only if161** there are no non-partial indexes. Compare a partial-index-only table vs162** a table with a normal (non-partial) index. */163void test_analyzeOneTable_tablecount_depends_on_partial_indexes(void){164 /* Case A: Only partial index -> expect two OP_Insert to iStatCur:165 one for the partial index entry and one for the table-count entry. */166 {167 sqlite3 *db = 0;168 make_db(&db);169 exec_sql(db, "CREATE TABLE t3(x, y);\n"170 "CREATE INDEX t3_px ON t3(x) WHERE x IS NOT NULL;");171 172 Table *pTab = sqlite3FindTable(db, "t3", db->aDb[0].zDbSName);173 TEST_ASSERT_NOT_NULL(pTab);174 175 Parse p;176 init_parse(db, &p);177 int iStatCur = 5;178 int iMem = 1;179 int iTab = 2;180 test_analyzeOneTable(&p, pTab, 0, iStatCur, iMem, iTab);181 182 int nIns = count_op_p1(p.pVdbe, OP_Insert, iStatCur);183 TEST_ASSERT_TRUE_MESSAGE(nIns >= 2, "Expected table-count+index entries for partial-index-only table");184 185 sqlite3_close(db);186 }187 188 /* Case B: Has a non-partial index -> expect only per-index OP_Insert, no189 extra table-count OP_Insert at the end. */190 {191 sqlite3 *db = 0;192 make_db(&db);193 exec_sql(db, "CREATE TABLE t4(x, y);\n"194 "CREATE INDEX t4_nx ON t4(x);");195 196 Table *pTab = sqlite3FindTable(db, "t4", db->aDb[0].zDbSName);197 TEST_ASSERT_NOT_NULL(pTab);198 199 Parse p;200 init_parse(db, &p);201 int iStatCur = 6;202 int iMem = 1;203 int iTab = 2;204 test_analyzeOneTable(&p, pTab, 0, iStatCur, iMem, iTab);205 206 int nIns = count_op_p1(p.pVdbe, OP_Insert, iStatCur);207 /* Exactly one OP_Insert expected for the single non-partial index */208 TEST_ASSERT_EQUAL_INT(1, nIns);209 210 sqlite3_close(db);211 }212}213 214int main(void){215 UNITY_BEGIN();216 RUN_TEST(test_analyzeOneTable_system_table_noops);217 RUN_TEST(test_analyzeOneTable_simple_table_generates_stat1_insert);218 RUN_TEST(test_analyzeOneTable_only_specific_index_restricts);219 RUN_TEST(test_analyzeOneTable_tablecount_depends_on_partial_indexes);220 return UNITY_END();221}