#include "sqliteInt.h" #include "unity.h" #include #include extern void test_renameWalkWith(Walker *pWalker, Select *pSelect); static sqlite3 *gDb = NULL; /* Unity setup/teardown */ void setUp(void) { int rc = sqlite3_open(":memory:", &gDb); TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); TEST_ASSERT_NOT_NULL(gDb); } void tearDown(void) { if (gDb) { sqlite3_close(gDb); gDb = NULL; } } /* Helper: allocate a minimal Select with given selFlags */ static Select *allocSelect(u32 selFlags){ Select *p = (Select*)sqlite3DbMallocZero(gDb, sizeof(Select)); TEST_ASSERT_NOT_NULL_MESSAGE(p, "allocSelect failed"); p->selFlags = selFlags; return p; } /* Helper: allocate a With object with nCte entries and the provided Selects */ static With *allocWith(int nCte, Select **apSel){ /* With uses a flexible array member a[1] */ int nByte = (int)(sizeof(With) + (nCte-1) * sizeof(p->a[0])); With *p = (With*)sqlite3DbMallocZero(gDb, nByte); TEST_ASSERT_NOT_NULL_MESSAGE(p, "allocWith failed"); p->nCte = nCte; p->pOuter = 0; for(int i=0; ia[i].pSelect = apSel ? apSel[i] : 0; p->a[i].pCols = 0; /* keep NULL to avoid rename mapping side-effects */ p->a[i].zName = 0; /* not needed for these tests */ p->a[i].zCteErr = 0; } return p; } /* Walker select callback that increments a counter in walker.u.p */ static int countSelectCb(Walker *pWalker, Select *pSel){ (void)pSel; int *pCnt = (int*)pWalker->u.p; if( pCnt ) (*pCnt)++; return WRC_Continue; } /* Test 1: When pSelect->pWith is NULL, nothing happens */ void test_renameWalkWith_no_with_clause(void){ Parse sParse; memset(&sParse, 0, sizeof(sParse)); sParse.db = gDb; /* Create a dummy outer WITH to ensure pParse->pWith is not touched */ With *pOuter = allocWith(1, NULL); sParse.pWith = pOuter; /* Minimal top-level SELECT with no WITH clause */ Select *pTop = allocSelect(SF_Expanded); /* flags irrelevant here */ pTop->pWith = NULL; int count = 0; Walker w; memset(&w, 0, sizeof(w)); w.pParse = &sParse; w.xSelectCallback = countSelectCb; w.u.p = &count; test_renameWalkWith(&w, pTop); TEST_ASSERT_EQUAL_INT(0, count); /* no CTEs walked */ TEST_ASSERT_EQUAL_PTR(pOuter, sParse.pWith); /* unchanged */ } /* Test 2: WITH present with SF_Expanded set: walks each CTE; no stack push/pop */ void test_renameWalkWith_expanded_with_multiple_ctes(void){ Parse sParse; memset(&sParse, 0, sizeof(sParse)); sParse.db = gDb; With *pOuter = allocWith(1, NULL); sParse.pWith = pOuter; /* Create two simple CTE selects with SF_Expanded set */ Select *pCTE1 = allocSelect(SF_Expanded); Select *pCTE2 = allocSelect(SF_Expanded); Select *apCTE[2] = { pCTE1, pCTE2 }; With *pWith = allocWith(2, apCTE); /* Top-level select that carries the WITH clause */ Select *pTop = allocSelect(0); pTop->pWith = pWith; int count = 0; Walker w; memset(&w, 0, sizeof(w)); w.pParse = &sParse; w.xSelectCallback = countSelectCb; w.u.p = &count; test_renameWalkWith(&w, pTop); TEST_ASSERT_EQUAL_INT(2, count); /* walked both CTEs */ TEST_ASSERT_EQUAL_PTR(pOuter, sParse.pWith); /* no push/pop -> unchanged */ } /* Test 3: WITH present with SF_Expanded not set: pushes/pops with-stack and walks each CTE */ void test_renameWalkWith_push_and_pop_with_stack(void){ Parse sParse; memset(&sParse, 0, sizeof(sParse)); sParse.db = gDb; With *pOuter = allocWith(1, NULL); sParse.pWith = pOuter; /* Create three simple CTE selects with SF_Expanded unset to force push/dup */ Select *pCTE1 = allocSelect(0); Select *pCTE2 = allocSelect(0); Select *pCTE3 = allocSelect(0); Select *apCTE[3] = { pCTE1, pCTE2, pCTE3 }; With *pWith = allocWith(3, apCTE); /* Top-level select that carries the WITH clause */ Select *pTop = allocSelect(0); pTop->pWith = pWith; int count = 0; Walker w; memset(&w, 0, sizeof(w)); w.pParse = &sParse; w.xSelectCallback = countSelectCb; w.u.p = &count; test_renameWalkWith(&w, pTop); TEST_ASSERT_EQUAL_INT(3, count); /* walked all CTEs */ TEST_ASSERT_EQUAL_PTR(pOuter, sParse.pWith); /* stack restored after pop */ } int main(void){ UNITY_BEGIN(); RUN_TEST(test_renameWalkWith_no_with_clause); RUN_TEST(test_renameWalkWith_expanded_with_multiple_ctes); RUN_TEST(test_renameWalkWith_push_and_pop_with_stack); return UNITY_END(); }