AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Global database handle used in tests */7static sqlite3 *gDb = NULL;8 9/* Helper: run an SQL statement, assert success */10static void run_sql(const char *zSql){11 char *zErr = NULL;12 int rc = sqlite3_exec(gDb, zSql, 0, 0, &zErr);13 if( rc!=SQLITE_OK ){14 const char *msg = zErr ? zErr : "unknown SQL error";15 TEST_FAIL_MESSAGE(msg);16 }17 if( zErr ) sqlite3_free(zErr);18}19 20/* Helper: fetch Index* by name from "main" schema */21static Index* get_index(const char *zIdxName){22 Index *pIdx = sqlite3FindIndex(gDb, zIdxName, "main");23 return pIdx;24}25 26/* Helper: create a TK_COLUMN Expr node */27static Expr* makeColumnExpr(int iCur, int iCol){28 Expr *p = (Expr*)sqlite3DbMallocZero(gDb, sizeof(Expr));29 TEST_ASSERT_NOT_NULL(p);30 p->op = TK_COLUMN;31 p->iTable = iCur;32 p->iColumn = iCol;33 return p;34}35 36/* Helper: create a binary Expr node with given op and children */37static Expr* makeBinaryExpr(int op, Expr *pLeft, Expr *pRight){38 Expr *p = (Expr*)sqlite3DbMallocZero(gDb, sizeof(Expr));39 TEST_ASSERT_NOT_NULL(p);40 p->op = op;41 p->pLeft = pLeft;42 p->pRight = pRight;43 return p;44}45 46/* Helper: create a constant integer Expr node */47static Expr* makeConstIntExpr(void){48 Expr *p = (Expr*)sqlite3DbMallocZero(gDb, sizeof(Expr));49 TEST_ASSERT_NOT_NULL(p);50 p->op = TK_INTEGER;51 return p;52}53 54void setUp(void) {55 int rc = sqlite3_open(":memory:", &gDb);56 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);57 /* Create a simple table and two indexes for coverage tests */58 run_sql("CREATE TABLE t1(a, b, c);");59 run_sql("CREATE INDEX i_ab ON t1(a, b);");60 run_sql("CREATE INDEX i_c ON t1(c);");61}62 63void tearDown(void) {64 if( gDb ){65 sqlite3_close(gDb);66 gDb = NULL;67 }68}69 70/* Test: single column referenced is covered by index i_ab */71void test_sqlite3ExprCoveredByIndex_single_column_covered(void){72 Index *pIdx = get_index("i_ab");73 TEST_ASSERT_NOT_NULL(pIdx);74 75 int iCur = 1;76 Expr *pColA = makeColumnExpr(iCur, 0); /* column 'a' -> index 0 */77 78 int covered = sqlite3ExprCoveredByIndex(pColA, iCur, pIdx);79 TEST_ASSERT_TRUE(covered);80 81 sqlite3ExprDelete(gDb, pColA);82}83 84/* Test: single column referenced is NOT covered by index i_ab */85void test_sqlite3ExprCoveredByIndex_single_column_not_covered(void){86 Index *pIdx = get_index("i_ab");87 TEST_ASSERT_NOT_NULL(pIdx);88 89 int iCur = 2;90 Expr *pColC = makeColumnExpr(iCur, 2); /* column 'c' -> index 2, not in i_ab */91 92 int covered = sqlite3ExprCoveredByIndex(pColC, iCur, pIdx);93 TEST_ASSERT_FALSE(covered);94 95 sqlite3ExprDelete(gDb, pColC);96}97 98/* Test: expression referencing a different cursor is considered covered */99void test_sqlite3ExprCoveredByIndex_other_cursor_ignored(void){100 Index *pIdx = get_index("i_ab");101 TEST_ASSERT_NOT_NULL(pIdx);102 103 int iCur = 3;104 int otherCur = 99;105 Expr *pColOther = makeColumnExpr(otherCur, 0); /* same column index, different cursor */106 107 int covered = sqlite3ExprCoveredByIndex(pColOther, iCur, pIdx);108 TEST_ASSERT_TRUE(covered);109 110 sqlite3ExprDelete(gDb, pColOther);111}112 113/* Test: composite expression (a + b) fully covered by index i_ab */114void test_sqlite3ExprCoveredByIndex_composite_all_covered(void){115 Index *pIdx = get_index("i_ab");116 TEST_ASSERT_NOT_NULL(pIdx);117 118 int iCur = 4;119 Expr *pA = makeColumnExpr(iCur, 0);120 Expr *pB = makeColumnExpr(iCur, 1);121 Expr *pPlus = makeBinaryExpr(TK_PLUS, pA, pB);122 123 int covered = sqlite3ExprCoveredByIndex(pPlus, iCur, pIdx);124 TEST_ASSERT_TRUE(covered);125 126 sqlite3ExprDelete(gDb, pPlus); /* deletes subtree as well */127}128 129/* Test: composite expression (a + c) NOT fully covered by index i_ab */130void test_sqlite3ExprCoveredByIndex_composite_partial_not_covered(void){131 Index *pIdx = get_index("i_ab");132 TEST_ASSERT_NOT_NULL(pIdx);133 134 int iCur = 5;135 Expr *pA = makeColumnExpr(iCur, 0);136 Expr *pC = makeColumnExpr(iCur, 2);137 Expr *pPlus = makeBinaryExpr(TK_PLUS, pA, pC);138 139 int covered = sqlite3ExprCoveredByIndex(pPlus, iCur, pIdx);140 TEST_ASSERT_FALSE(covered);141 142 sqlite3ExprDelete(gDb, pPlus);143}144 145/* Test: constant expression is always covered (no columns referenced) */146void test_sqlite3ExprCoveredByIndex_constant_expr(void){147 Index *pIdx = get_index("i_ab");148 TEST_ASSERT_NOT_NULL(pIdx);149 150 int iCur = 6;151 Expr *pConst = makeConstIntExpr();152 153 int covered = sqlite3ExprCoveredByIndex(pConst, iCur, pIdx);154 TEST_ASSERT_TRUE(covered);155 156 sqlite3ExprDelete(gDb, pConst);157}158 159/* Test: mixed cursors: part from target cursor covered, part from other cursor */160void test_sqlite3ExprCoveredByIndex_mixed_cursors_other_side_irrelevant(void){161 Index *pIdx = get_index("i_ab");162 TEST_ASSERT_NOT_NULL(pIdx);163 164 int iCur = 7;165 int otherCur = 88;166 167 Expr *pA = makeColumnExpr(iCur, 0); /* covered by i_ab */168 Expr *pOtherC = makeColumnExpr(otherCur, 2); /* not in i_ab but different cursor */169 Expr *pAnd = makeBinaryExpr(TK_AND, pA, pOtherC);170 171 int covered = sqlite3ExprCoveredByIndex(pAnd, iCur, pIdx);172 TEST_ASSERT_TRUE(covered);173 174 sqlite3ExprDelete(gDb, pAnd);175}176 177/* Test: same-structure but index i_c should cover only column c */178void test_sqlite3ExprCoveredByIndex_with_different_index(void){179 Index *pIdx_c = get_index("i_c");180 TEST_ASSERT_NOT_NULL(pIdx_c);181 182 int iCur = 10;183 Expr *pC = makeColumnExpr(iCur, 2);184 Expr *pB = makeColumnExpr(iCur, 1);185 186 /* c covered by i_c */187 TEST_ASSERT_TRUE(sqlite3ExprCoveredByIndex(pC, iCur, pIdx_c));188 189 /* b not covered by i_c */190 TEST_ASSERT_FALSE(sqlite3ExprCoveredByIndex(pB, iCur, pIdx_c));191 192 sqlite3ExprDelete(gDb, pC);193 sqlite3ExprDelete(gDb, pB);194}195 196int main(void){197 UNITY_BEGIN();198 RUN_TEST(test_sqlite3ExprCoveredByIndex_single_column_covered);199 RUN_TEST(test_sqlite3ExprCoveredByIndex_single_column_not_covered);200 RUN_TEST(test_sqlite3ExprCoveredByIndex_other_cursor_ignored);201 RUN_TEST(test_sqlite3ExprCoveredByIndex_composite_all_covered);202 RUN_TEST(test_sqlite3ExprCoveredByIndex_composite_partial_not_covered);203 RUN_TEST(test_sqlite3ExprCoveredByIndex_constant_expr);204 RUN_TEST(test_sqlite3ExprCoveredByIndex_mixed_cursors_other_side_irrelevant);205 RUN_TEST(test_sqlite3ExprCoveredByIndex_with_different_index);206 return UNITY_END();207}