AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5 6/* Global db handle for tests */7static sqlite3 *gDb = NULL;8 9/* Unity hooks */10void setUp(void) {11 int rc = sqlite3_open(":memory:", &gDb);12 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);13}14 15void tearDown(void) {16 if (gDb) {17 sqlite3_close(gDb);18 gDb = NULL;19 }20}21 22/* Helper: create a minimal Parse with Vdbe and enough registers */23static void initParse(Parse *pParse, Vdbe **ppVdbe){24 memset(pParse, 0, sizeof(*pParse));25 pParse->db = gDb;26 Vdbe *v = sqlite3VdbeCreate(gDb);27 TEST_ASSERT_NOT_NULL(v);28 pParse->pVdbe = v;29 pParse->nMem = 200; /* big enough for target/temp regs */30 if (ppVdbe) *ppVdbe = v;31}32 33/* Helper: build a TK_VARIABLE Expr. Uses string literals for tokens. */34static void makeVarExpr(Expr *p, int varIndex, int setSubqueryFlag){35 memset(p, 0, sizeof(*p));36 p->op = TK_VARIABLE;37 p->iColumn = varIndex; /* used as P1 by OP_Variable */38 p->u.zToken = (varIndex==1)?"?1":(varIndex==2?"?2":"?9");39 if (setSubqueryFlag) p->flags |= EP_Subquery;40}41 42/* Helper: build top-level AND/OR Expr with given children */43static void makeAndOrExpr(Expr *p, int op, Expr *pLeft, Expr *pRight){44 memset(p, 0, sizeof(*p));45 p->op = op; /* TK_AND or TK_OR */46 p->pLeft = pLeft;47 p->pRight = pRight;48}49 50/* Helper: find first opcode index matching 'opcode', or -1 if not found */51static int findFirstOpcode(Vdbe *v, int opcode){52 int nOp = sqlite3VdbeCurrentAddr(v);53 for(int i=0;i<nOp;i++){54 Op *pOp = sqlite3VdbeGetOp(v, i);55 if( pOp && pOp->opcode==opcode ) return i;56 }57 return -1;58}59 60/* Test 1: AND with right-first evaluation (left has EP_Subquery). Expect OP_IfNot and trailing short-circuit OP_Or. */61void test_exprCodeTargetAndOr_AND_right_first_shortcircuit_structure(void){62 Parse parse; Vdbe *v; initParse(&parse, &v);63 64 Expr left, right, top;65 makeVarExpr(&left, 1, /*EP_Subquery*/1);66 makeVarExpr(&right, 2, /*EP_Subquery*/0);67 makeAndOrExpr(&top, TK_AND, &left, &right);68 69 int tmpReg = 0;70 int target = 5;71 int retReg = test_exprCodeTargetAndOr(&parse, &top, target, &tmpReg);72 73 /* Returned register equals target */74 TEST_ASSERT_EQUAL_INT(target, retReg);75 /* A temp register should be used for the non-target operand */76 TEST_ASSERT_TRUE_MESSAGE(tmpReg>0, "Expected non-zero temp register");77 78 int nOp = sqlite3VdbeCurrentAddr(v);79 TEST_ASSERT_TRUE(nOp >= 5);80 81 int idxIfNot = findFirstOpcode(v, OP_IfNot);82 TEST_ASSERT_TRUE_MESSAGE(idxIfNot >= 0, "Expected OP_IfNot for TK_AND short-circuit");83 84 /* The last opcode should be the short-circuit OP_Or(regSS,regSS,target) */85 Op *pLast = sqlite3VdbeGetOp(v, nOp-1);86 TEST_ASSERT_NOT_NULL(pLast);87 TEST_ASSERT_EQUAL_INT(OP_Or, pLast->opcode);88 TEST_ASSERT_EQUAL_INT(target, pLast->p3);89 90 /* There should be an OP_And that combines both operands */91 int idxAnd = findFirstOpcode(v, OP_And);92 TEST_ASSERT_TRUE_MESSAGE(idxAnd >= 0, "Expected OP_And combining operands");93 94 /* There should be a OP_Goto before the final OP_Or (skip to end on no-short-circuit path) */95 int idxGoto = findFirstOpcode(v, OP_Goto);96 TEST_ASSERT_TRUE_MESSAGE(idxGoto >= 0, "Expected OP_Goto before jump target patching");97 98 sqlite3VdbeDelete(v);99}100 101/* Test 2: OR with left-first evaluation and right has EP_Subquery. Expect OP_If (not IfNot) and trailing OP_Or. */102void test_exprCodeTargetAndOr_OR_left_first_right_is_subquery(void){103 Parse parse; Vdbe *v; initParse(&parse, &v);104 105 Expr left, right, top;106 makeVarExpr(&left, 1, /*EP_Subquery*/0);107 makeVarExpr(&right, 2, /*EP_Subquery*/1); /* right is subquery -> may skip */108 makeAndOrExpr(&top, TK_OR, &left, &right);109 110 int tmpReg = 0;111 int target = 7;112 int retReg = test_exprCodeTargetAndOr(&parse, &top, target, &tmpReg);113 114 TEST_ASSERT_EQUAL_INT(target, retReg);115 TEST_ASSERT_TRUE_MESSAGE(tmpReg>0, "Expected non-zero temp register");116 117 int nOp = sqlite3VdbeCurrentAddr(v);118 TEST_ASSERT_TRUE(nOp >= 5);119 120 int idxIf = findFirstOpcode(v, OP_If);121 TEST_ASSERT_TRUE_MESSAGE(idxIf >= 0, "Expected OP_If for TK_OR short-circuit");122 TEST_ASSERT_EQUAL_INT(-1, findFirstOpcode(v, OP_IfNot)); /* Ensure no OP_IfNot in this path */123 124 Op *pLast = sqlite3VdbeGetOp(v, nOp-1);125 TEST_ASSERT_NOT_NULL(pLast);126 TEST_ASSERT_EQUAL_INT(OP_Or, pLast->opcode);127 TEST_ASSERT_EQUAL_INT(target, pLast->p3);128 129 /* There should be at least one OP_Or combining the two operands before the trailing OP_Or */130 /* The trailing OP_Or is the last op; ensure we also have another OP_Or before it or OP_Variable/OP_Goto exist */131 int idxOr = findFirstOpcode(v, OP_Or);132 TEST_ASSERT_TRUE_MESSAGE(idxOr >= 0, "Expected OP_Or combining operands");133 134 int idxGoto = findFirstOpcode(v, OP_Goto);135 TEST_ASSERT_TRUE_MESSAGE(idxGoto >= 0, "Expected OP_Goto before final short-circuit OP_Or");136 137 sqlite3VdbeDelete(v);138}139 140/* Test 3: AND without any EP_Subquery flags (no skip branch). Expect no trailing OP_Goto/extra OP_Or; last opcode is OP_And. */141void test_exprCodeTargetAndOr_AND_no_skip_branch_last_is_And(void){142 Parse parse; Vdbe *v; initParse(&parse, &v);143 144 Expr left, right, top;145 makeVarExpr(&left, 1, /*EP_Subquery*/0);146 makeVarExpr(&right, 2, /*EP_Subquery*/0);147 makeAndOrExpr(&top, TK_AND, &left, &right);148 149 int tmpReg = 0;150 int target = 11;151 int retReg = test_exprCodeTargetAndOr(&parse, &top, target, &tmpReg);152 153 TEST_ASSERT_EQUAL_INT(target, retReg);154 TEST_ASSERT_TRUE_MESSAGE(tmpReg>0, "Expected non-zero temp register");155 156 /* No skip branch expected */157 TEST_ASSERT_EQUAL_INT(-1, findFirstOpcode(v, OP_If));158 TEST_ASSERT_EQUAL_INT(-1, findFirstOpcode(v, OP_IfNot));159 160 int nOp = sqlite3VdbeCurrentAddr(v);161 TEST_ASSERT_TRUE(nOp >= 3);162 163 Op *pLast = sqlite3VdbeGetOp(v, nOp-1);164 TEST_ASSERT_NOT_NULL(pLast);165 TEST_ASSERT_EQUAL_INT(OP_And, pLast->opcode);166 167 /* Ensure no OP_Goto exists in this no-skip path */168 TEST_ASSERT_EQUAL_INT(-1, findFirstOpcode(v, OP_Goto));169 /* Ensure no trailing OP_Or exists */170 /* There could be no OP_Or at all in this path */171 sqlite3VdbeDelete(v);172}173 174/* Test 4: OR with right-first evaluation (left has EP_Subquery). Expect OP_If and trailing OP_Or. */175void test_exprCodeTargetAndOr_OR_right_first_shortcircuit_structure(void){176 Parse parse; Vdbe *v; initParse(&parse, &v);177 178 Expr left, right, top;179 makeVarExpr(&left, 1, /*EP_Subquery*/1); /* left is marked subquery -> compute right first */180 makeVarExpr(&right, 2, /*EP_Subquery*/0);181 makeAndOrExpr(&top, TK_OR, &left, &right);182 183 int tmpReg = 0;184 int target = 13;185 int retReg = test_exprCodeTargetAndOr(&parse, &top, target, &tmpReg);186 187 TEST_ASSERT_EQUAL_INT(target, retReg);188 TEST_ASSERT_TRUE_MESSAGE(tmpReg>0, "Expected non-zero temp register");189 190 int idxIf = findFirstOpcode(v, OP_If);191 TEST_ASSERT_TRUE_MESSAGE(idxIf >= 0, "Expected OP_If for TK_OR skip branch");192 193 int nOp = sqlite3VdbeCurrentAddr(v);194 Op *pLast = sqlite3VdbeGetOp(v, nOp-1);195 TEST_ASSERT_NOT_NULL(pLast);196 TEST_ASSERT_EQUAL_INT(OP_Or, pLast->opcode);197 TEST_ASSERT_EQUAL_INT(target, pLast->p3);198 199 sqlite3VdbeDelete(v);200}201 202int main(void) {203 UNITY_BEGIN();204 RUN_TEST(test_exprCodeTargetAndOr_AND_right_first_shortcircuit_structure);205 RUN_TEST(test_exprCodeTargetAndOr_OR_left_first_right_is_subquery);206 RUN_TEST(test_exprCodeTargetAndOr_AND_no_skip_branch_last_is_And);207 RUN_TEST(test_exprCodeTargetAndOr_OR_right_first_shortcircuit_structure);208 return UNITY_END();209}