File size: 4,073 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
#include "sqliteInt.h"
#include "unity.h"
#include <string.h>

/* Global database handle for allocations */
static sqlite3 *gDb = NULL;

static Expr* make_id_expr(Parse *pParse, const char *zId){
  Token t;
  t.z = (char*)zId;
  t.n = (int)strlen(zId);
  return sqlite3ExprAlloc(pParse->db, TK_ID, &t, 0);
}

static Expr* make_int_expr(Parse *pParse, const char *zNum){
  Token t;
  t.z = (char*)zNum;
  t.n = (int)strlen(zNum);
  return sqlite3ExprAlloc(pParse->db, TK_INTEGER, &t, 0);
}

void setUp(void) {
  if (!gDb) {
    int rc = sqlite3_open(":memory:", &gDb);
    TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory DB");
  }
}

void tearDown(void) {
  if (gDb) {
    int rc = sqlite3_close(gDb);
    TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to close in-memory DB");
    gDb = NULL;
  }
}

/* Helper to initialize a Parse object */
static void initParse(Parse *pParse){
  memset(pParse, 0, sizeof(Parse));
  pParse->db = gDb;
  /* Set a non-UNMAP mode to verify restoration */
  pParse->eParseMode = PARSE_MODE_RENAME;
}

/* Test: NULL expression must be safe and must restore eParseMode */
void test_sqlite3RenameExprUnmap_restores_mode_when_expr_null(void){
  Parse sParse;
  initParse(&sParse);
  u8 orig = sParse.eParseMode;

  sqlite3RenameExprUnmap(&sParse, NULL);

  TEST_ASSERT_EQUAL_UINT8(orig, sParse.eParseMode);
  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);
}

/* Test: Simple literal expression, mode restored after call */
void test_sqlite3RenameExprUnmap_restores_mode_when_expr_simple(void){
  Parse sParse;
  initParse(&sParse);

  Expr *pInt = make_int_expr(&sParse, "123");
  TEST_ASSERT_NOT_NULL(pInt);

  u8 orig = sParse.eParseMode;
  sqlite3RenameExprUnmap(&sParse, pInt);

  TEST_ASSERT_EQUAL_UINT8(orig, sParse.eParseMode);
  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);

  sqlite3ExprDelete(sParse.db, pInt);
}

/* Test: Expression tree should not be modified by Unmap (structure intact) */
void test_sqlite3RenameExprUnmap_does_not_modify_tree(void){
  Parse sParse;
  initParse(&sParse);

  Expr *pA = make_id_expr(&sParse, "a");
  Expr *pB = make_id_expr(&sParse, "b");
  TEST_ASSERT_NOT_NULL(pA);
  TEST_ASSERT_NOT_NULL(pB);

  Expr *pPlus = sqlite3PExpr(&sParse, TK_PLUS, pA, pB);
  TEST_ASSERT_NOT_NULL(pPlus);

  Expr *pLeftBefore = pPlus->pLeft;
  Expr *pRightBefore = pPlus->pRight;

  sqlite3RenameExprUnmap(&sParse, pPlus);

  /* Validate the expression shape is unchanged */
  TEST_ASSERT_EQUAL_PTR(pLeftBefore, pPlus->pLeft);
  TEST_ASSERT_EQUAL_PTR(pRightBefore, pPlus->pRight);
  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);

  sqlite3ExprDelete(sParse.db, pPlus); /* deletes pA and pB as well */
}

/* Test: Nested expression trees traverse without errors and restore mode */
void test_sqlite3RenameExprUnmap_handles_nested_tree_no_side_effects(void){
  Parse sParse;
  initParse(&sParse);

  /* Build (a + b) * 3 */
  Expr *pA = make_id_expr(&sParse, "a");
  Expr *pB = make_id_expr(&sParse, "b");
  Expr *pPlus = sqlite3PExpr(&sParse, TK_PLUS, pA, pB);
  TEST_ASSERT_NOT_NULL(pA);
  TEST_ASSERT_NOT_NULL(pB);
  TEST_ASSERT_NOT_NULL(pPlus);

  Expr *pThree = make_int_expr(&sParse, "3");
  TEST_ASSERT_NOT_NULL(pThree);

  /* Multiplication operator is TK_STAR in SQLite */
  Expr *pMul = sqlite3PExpr(&sParse, TK_STAR, pPlus, pThree);
  TEST_ASSERT_NOT_NULL(pMul);

  u8 orig = sParse.eParseMode;
  sqlite3RenameExprUnmap(&sParse, pMul);

  /* No parse errors introduced and mode restored */
  TEST_ASSERT_EQUAL_UINT8(orig, sParse.eParseMode);
  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);

  /* Tree is intact */
  TEST_ASSERT_EQUAL_PTR(pPlus, pMul->pLeft);
  TEST_ASSERT_EQUAL_PTR(pThree, pMul->pRight);

  sqlite3ExprDelete(sParse.db, pMul); /* deletes entire tree */
}

int main(void) {
  UNITY_BEGIN();
  RUN_TEST(test_sqlite3RenameExprUnmap_restores_mode_when_expr_null);
  RUN_TEST(test_sqlite3RenameExprUnmap_restores_mode_when_expr_simple);
  RUN_TEST(test_sqlite3RenameExprUnmap_does_not_modify_tree);
  RUN_TEST(test_sqlite3RenameExprUnmap_handles_nested_tree_no_side_effects);
  return UNITY_END();
}