AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* External wrapper for the static function under test */7extern int test_findCompatibleInRhsSubrtn(Parse *pParse, Expr *pExpr, SubrtnSig *pNewSig);8 9/* Unity fixtures */10void setUp(void) {11 /* No global setup needed */12}13void tearDown(void) {14 /* No global teardown needed */15}16 17/* Helper: create a new in-expression with an attached (empty) Select */18static Expr* newInExprWithSelect(sqlite3 *db){19 Expr *pExpr = (Expr*)sqlite3DbMallocZero(db, sizeof(Expr));20 TEST_ASSERT_NOT_NULL(pExpr);21 pExpr->op = TK_IN;22 /* Mark that x union is a Select */23 pExpr->flags |= EP_xIsSelect;24 pExpr->x.pSelect = (Select*)sqlite3DbMallocZero(db, sizeof(Select));25 TEST_ASSERT_NOT_NULL(pExpr->x.pSelect);26 pExpr->x.pSelect->selFlags = 0; /* Ensure SF_All not set */27 return pExpr;28}29 30/* Helper: create a fresh Parse with Vdbe attached */31static void initParseAndVdbe(sqlite3 *db, Parse *pParse){32 memset(pParse, 0, sizeof(Parse));33 pParse->db = db;34 Vdbe *v = sqlite3VdbeCreate(db);35 TEST_ASSERT_NOT_NULL(v);36 pParse->pVdbe = v;37}38 39/* Helper: allocate a SubrtnSig for use as P4 of OP_BeginSubrtn (db memory) */40static SubrtnSig* makeDbSubrtnSig(sqlite3 *db, int selId, const char *zAff,41 int iAddr, int regReturn, int iTable, int bComplete){42 SubrtnSig *p = (SubrtnSig*)sqlite3DbMallocZero(db, sizeof(SubrtnSig));43 TEST_ASSERT_NOT_NULL(p);44 p->selId = selId;45 p->zAff = sqlite3DbStrDup(db, zAff ? zAff : "");46 TEST_ASSERT_NOT_NULL(p->zAff);47 p->iAddr = iAddr;48 p->regReturn = regReturn;49 p->iTable = iTable;50 p->bComplete = bComplete ? 1 : 0;51 return p;52}53 54/* Helper: allocate a SubrtnSig for the "new" signature (heap memory) */55static SubrtnSig* makeHeapSubrtnSig(int selId, const char *zAff){56 SubrtnSig *p = (SubrtnSig*)sqlite3_malloc(sizeof(SubrtnSig));57 TEST_ASSERT_NOT_NULL(p);58 memset(p, 0, sizeof(SubrtnSig));59 p->selId = selId;60 if( zAff ){61 size_t n = strlen(zAff);62 char *z = (char*)sqlite3_malloc(n+1);63 TEST_ASSERT_NOT_NULL(z);64 memcpy(z, zAff, n+1);65 p->zAff = z;66 }else{67 p->zAff = NULL;68 }69 return p;70}71 72/* Helper: add a NOOP at index 0 to ensure scan (which starts at 1) does not skip program entirely */73static void addLeadingNoop(Vdbe *v){74 sqlite3VdbeAddOp0(v, OP_Noop);75}76 77/* Helper: add OP_BeginSubrtn with P4_SUBRTNSIG */78static void addBeginSubrtn(Vdbe *v, SubrtnSig *pSig){79 sqlite3VdbeAddOp4(v, OP_BeginSubrtn, 0, 0, 0, (const char*)pSig, P4_SUBRTNSIG);80}81 82/* Helper: simple trailing NOOP to make sure end-pointer iteration includes our entry */83static void addTrailingNoop(Vdbe *v){84 sqlite3VdbeAddOp0(v, OP_Noop);85}86 87/* Test: Early-out when pNewSig == NULL */88static void test_findCompatibleInRhsSubrtn_null_newsig(void){89 sqlite3 *db = 0;90 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));91 Parse sParse;92 memset(&sParse, 0, sizeof(sParse)); /* Will early-return before using pVdbe */93 94 Expr *pExpr = newInExprWithSelect(db);95 int rc = test_findCompatibleInRhsSubrtn(&sParse, pExpr, NULL);96 TEST_ASSERT_EQUAL_INT(0, rc);97 98 sqlite3ExprDelete(db, pExpr);99 sqlite3_close(db);100}101 102/* Test: Early-out when mSubrtnSig bit for (selId&7) is not set */103static void test_findCompatibleInRhsSubrtn_missing_bitmask(void){104 sqlite3 *db = 0;105 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));106 107 Parse sParse;108 memset(&sParse, 0, sizeof(sParse)); /* no Vdbe needed due to early return */109 110 Expr *pExpr = newInExprWithSelect(db);111 SubrtnSig *pNew = makeHeapSubrtnSig(3, "AFF");112 sParse.mSubrtnSig = 0; /* required bit not set */113 114 int rc = test_findCompatibleInRhsSubrtn(&sParse, pExpr, pNew);115 TEST_ASSERT_EQUAL_INT(0, rc);116 TEST_ASSERT_FALSE( (pExpr->flags & EP_Subrtn)!=0 );117 118 sqlite3_free((void*)pNew->zAff);119 sqlite3_free(pNew);120 sqlite3ExprDelete(db, pExpr);121 sqlite3_close(db);122}123 124/* Test: No OP_BeginSubrtn with P4_SUBRTNSIG present -> no match */125static void test_findCompatibleInRhsSubrtn_no_matching_opcodes(void){126 sqlite3 *db = 0;127 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));128 129 Parse sParse;130 initParseAndVdbe(db, &sParse);131 Vdbe *v = sParse.pVdbe;132 addLeadingNoop(v); /* index 0 */133 addTrailingNoop(v); /* index 1 (no BeginSubrtn at all) */134 135 Expr *pExpr = newInExprWithSelect(db);136 SubrtnSig *pNew = makeHeapSubrtnSig(2, "ABC");137 sParse.mSubrtnSig = (1 << (pNew->selId & 7));138 139 int rc = test_findCompatibleInRhsSubrtn(&sParse, pExpr, pNew);140 TEST_ASSERT_EQUAL_INT(0, rc);141 TEST_ASSERT_FALSE( (pExpr->flags & EP_Subrtn)!=0 );142 143 sqlite3_free((void*)pNew->zAff);144 sqlite3_free(pNew);145 sqlite3ExprDelete(db, pExpr);146 sqlite3_close(db); /* also frees Vdbe and its p4 payloads */147}148 149/* Test: OP_BeginSubrtn present but SubrtnSig incomplete (bComplete==0) -> no match */150static void test_findCompatibleInRhsSubrtn_incomplete_signature(void){151 sqlite3 *db = 0;152 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));153 154 Parse sParse;155 initParseAndVdbe(db, &sParse);156 Vdbe *v = sParse.pVdbe;157 158 addLeadingNoop(v); /* index 0 */159 160 /* Add an incomplete signature */161 SubrtnSig *pDbSig = makeDbSubrtnSig(db, 5, "XYZ", 123, 7, 99, 0);162 addBeginSubrtn(v, pDbSig);163 addTrailingNoop(v);164 165 Expr *pExpr = newInExprWithSelect(db);166 SubrtnSig *pNew = makeHeapSubrtnSig(5, "XYZ");167 sParse.mSubrtnSig = (1 << (pNew->selId & 7));168 169 int rc = test_findCompatibleInRhsSubrtn(&sParse, pExpr, pNew);170 TEST_ASSERT_EQUAL_INT(0, rc);171 TEST_ASSERT_FALSE( (pExpr->flags & EP_Subrtn)!=0 );172 173 sqlite3_free((void*)pNew->zAff);174 sqlite3_free(pNew);175 sqlite3ExprDelete(db, pExpr);176 sqlite3_close(db);177}178 179/* Test: selId mismatch or zAff mismatch -> no match */180static void test_findCompatibleInRhsSubrtn_mismatch_cases(void){181 sqlite3 *db = 0;182 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));183 184 Parse sParse;185 initParseAndVdbe(db, &sParse);186 Vdbe *v = sParse.pVdbe;187 addLeadingNoop(v);188 189 /* Add a complete signature with selId 4 and zAff "AAA" */190 SubrtnSig *pDbSig = makeDbSubrtnSig(db, 4, "AAA", 200, 10, 300, 1);191 addBeginSubrtn(v, pDbSig);192 addTrailingNoop(v);193 194 Expr *pExpr1 = newInExprWithSelect(db);195 SubrtnSig *pNew1 = makeHeapSubrtnSig(3, "AAA"); /* selId mismatch */196 sParse.mSubrtnSig = (1 << (pNew1->selId & 7));197 int rc1 = test_findCompatibleInRhsSubrtn(&sParse, pExpr1, pNew1);198 TEST_ASSERT_EQUAL_INT(0, rc1);199 TEST_ASSERT_FALSE( (pExpr1->flags & EP_Subrtn)!=0 );200 201 Expr *pExpr2 = newInExprWithSelect(db);202 SubrtnSig *pNew2 = makeHeapSubrtnSig(4, "BBB"); /* zAff mismatch */203 sParse.mSubrtnSig = (1 << (pNew2->selId & 7));204 int rc2 = test_findCompatibleInRhsSubrtn(&sParse, pExpr2, pNew2);205 TEST_ASSERT_EQUAL_INT(0, rc2);206 TEST_ASSERT_FALSE( (pExpr2->flags & EP_Subrtn)!=0 );207 208 sqlite3_free((void*)pNew1->zAff);209 sqlite3_free(pNew1);210 sqlite3ExprDelete(db, pExpr1);211 212 sqlite3_free((void*)pNew2->zAff);213 sqlite3_free(pNew2);214 sqlite3ExprDelete(db, pExpr2);215 216 sqlite3_close(db);217}218 219/* Test: Successful match populates y.sub fields, iTable and sets EP_Subrtn */220static void test_findCompatibleInRhsSubrtn_success_match(void){221 sqlite3 *db = 0;222 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));223 224 Parse sParse;225 initParseAndVdbe(db, &sParse);226 Vdbe *v = sParse.pVdbe;227 addLeadingNoop(v);228 229 int selId = 6;230 const char *zAff = "ABC";231 int expectedAddr = 321;232 int expectedRet = 11;233 int expectedTable = 777;234 235 SubrtnSig *pDbSig = makeDbSubrtnSig(db, selId, zAff, expectedAddr, expectedRet, expectedTable, 1);236 addBeginSubrtn(v, pDbSig);237 addTrailingNoop(v);238 239 Expr *pExpr = newInExprWithSelect(db);240 TEST_ASSERT_FALSE( (pExpr->flags & EP_Subrtn)!=0 );241 TEST_ASSERT_EQUAL_INT(0, pExpr->iTable);242 243 SubrtnSig *pNew = makeHeapSubrtnSig(selId, zAff);244 sParse.mSubrtnSig = (1 << (pNew->selId & 7));245 246 int rc = test_findCompatibleInRhsSubrtn(&sParse, pExpr, pNew);247 TEST_ASSERT_EQUAL_INT(1, rc);248 TEST_ASSERT_TRUE( (pExpr->flags & EP_Subrtn)!=0 );249 TEST_ASSERT_EQUAL_INT(expectedAddr, pExpr->y.sub.iAddr);250 TEST_ASSERT_EQUAL_INT(expectedRet, pExpr->y.sub.regReturn);251 TEST_ASSERT_EQUAL_INT(expectedTable, pExpr->iTable);252 253 sqlite3_free((void*)pNew->zAff);254 sqlite3_free(pNew);255 sqlite3ExprDelete(db, pExpr);256 sqlite3_close(db);257}258 259/* Test: First matching OP_BeginSubrtn wins when multiple matches exist */260static void test_findCompatibleInRhsSubrtn_first_match_wins(void){261 sqlite3 *db = 0;262 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));263 264 Parse sParse;265 initParseAndVdbe(db, &sParse);266 Vdbe *v = sParse.pVdbe;267 268 addLeadingNoop(v);269 270 int selId = 2;271 const char *zAff = "XYZ";272 273 /* First matching signature (should be selected) */274 SubrtnSig *pDbSig1 = makeDbSubrtnSig(db, selId, zAff, 1000, 21, 400, 1);275 addBeginSubrtn(v, pDbSig1);276 277 /* Second matching signature (should be ignored) */278 SubrtnSig *pDbSig2 = makeDbSubrtnSig(db, selId, zAff, 2000, 22, 500, 1);279 addBeginSubrtn(v, pDbSig2);280 281 addTrailingNoop(v);282 283 Expr *pExpr = newInExprWithSelect(db);284 SubrtnSig *pNew = makeHeapSubrtnSig(selId, zAff);285 sParse.mSubrtnSig = (1 << (pNew->selId & 7));286 287 int rc = test_findCompatibleInRhsSubrtn(&sParse, pExpr, pNew);288 TEST_ASSERT_EQUAL_INT(1, rc);289 TEST_ASSERT_TRUE( (pExpr->flags & EP_Subrtn)!=0 );290 TEST_ASSERT_EQUAL_INT(1000, pExpr->y.sub.iAddr);291 TEST_ASSERT_EQUAL_INT(21, pExpr->y.sub.regReturn);292 TEST_ASSERT_EQUAL_INT(400, pExpr->iTable);293 294 sqlite3_free((void*)pNew->zAff);295 sqlite3_free(pNew);296 sqlite3ExprDelete(db, pExpr);297 sqlite3_close(db);298}299 300int main(void){301 UNITY_BEGIN();302 RUN_TEST(test_findCompatibleInRhsSubrtn_null_newsig);303 RUN_TEST(test_findCompatibleInRhsSubrtn_missing_bitmask);304 RUN_TEST(test_findCompatibleInRhsSubrtn_no_matching_opcodes);305 RUN_TEST(test_findCompatibleInRhsSubrtn_incomplete_signature);306 RUN_TEST(test_findCompatibleInRhsSubrtn_mismatch_cases);307 RUN_TEST(test_findCompatibleInRhsSubrtn_success_match);308 RUN_TEST(test_findCompatibleInRhsSubrtn_first_match_wins);309 return UNITY_END();310}