File size: 4,459 Bytes
7510827
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "sqliteInt.h"
#include "unity.h"
#include <stdlib.h>
#include <string.h>

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; i<nCte; i++){
    p->a[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();
}