File size: 5,154 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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include "sqliteInt.h"
#include "unity.h"
#include <string.h>
#include <stdio.h>

/* The wrapper for the static function is provided by the build harness. */
extern int test_isRealTable(Parse *pParse, Table *pTab, int iOp);

void setUp(void) {
  /* No global setup needed */
}

void tearDown(void) {
  /* No global teardown needed */
}

static sqlite3* open_mem_db(void){
  sqlite3 *db = 0;
  int rc = sqlite3_open(":memory:", &db);
  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");
  TEST_ASSERT_NOT_NULL_MESSAGE(db, "db is NULL after sqlite3_open");
  return db;
}

static void clear_parse_err(sqlite3 *db, Parse *pParse){
  if( pParse->zErrMsg ){
    sqlite3DbFree(db, pParse->zErrMsg);
    pParse->zErrMsg = 0;
  }
}

/* Helper to initialize a Parse object */
static void init_parse(Parse *pParse, sqlite3 *db){
  memset(pParse, 0, sizeof(*pParse));
  pParse->db = db;
}

/* Make a "real" table (neither view nor virtual) */
static void make_real_table(Table *pTab, const char *zName){
  memset(pTab, 0, sizeof(*pTab));
  pTab->zName = (char*)zName;
}

/* Make a "view" table */
static void make_view_table(Table *pTab, const char *zName){
  memset(pTab, 0, sizeof(*pTab));
  pTab->zName = (char*)zName;
  /* Make IsView(pTab) evaluate true across SQLite versions */
  static Select dummySelect; /* address used, not dereferenced */
  memset(&dummySelect, 0, sizeof(dummySelect));
  pTab->pSelect = &dummySelect;
#ifdef TF_View
  pTab->tabFlags |= TF_View;
#endif
}

/* Make a "virtual" table */
static void make_virtual_table(Table *pTab, const char *zName){
  memset(pTab, 0, sizeof(*pTab));
  pTab->zName = (char*)zName;
#ifdef TF_Virtual
  pTab->tabFlags |= TF_Virtual;
#else
  /* TF_Virtual should exist in modern SQLite, but ensure some non-zero to hint IsVirtual if macro differs */
  pTab->tabFlags = 0xFFFFFFFF;
#endif
}

void test_isRealTable_returns_zero_for_real_table(void){
  sqlite3 *db = open_mem_db();
  Parse parse;
  init_parse(&parse, db);

  Table tab;
  make_real_table(&tab, "t_real");

  int rc = test_isRealTable(&parse, &tab, 0);
  TEST_ASSERT_EQUAL_INT(0, rc);
  TEST_ASSERT_NULL(parse.zErrMsg);

  sqlite3_close(db);
}

void test_isRealTable_view_rename_columns_sets_error(void){
  sqlite3 *db = open_mem_db();
  Parse parse;
  init_parse(&parse, db);

  Table tab;
  make_view_table(&tab, "v1");

  int rc = test_isRealTable(&parse, &tab, 0); /* "rename columns of" */
  TEST_ASSERT_EQUAL_INT(1, rc);
  TEST_ASSERT_NOT_NULL(parse.zErrMsg);

  /* Expect: cannot rename columns of view "v1" */
  const char *msg = parse.zErrMsg;
  TEST_ASSERT_NOT_NULL(strstr(msg, "cannot "));
  TEST_ASSERT_NOT_NULL(strstr(msg, "rename columns of"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "view"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "\"v1\""));

  clear_parse_err(db, &parse);
  sqlite3_close(db);
}

void test_isRealTable_view_drop_column_sets_error(void){
  sqlite3 *db = open_mem_db();
  Parse parse;
  init_parse(&parse, db);

  Table tab;
  make_view_table(&tab, "v_drop");

  int rc = test_isRealTable(&parse, &tab, 1); /* "drop column from" */
  TEST_ASSERT_EQUAL_INT(1, rc);
  TEST_ASSERT_NOT_NULL(parse.zErrMsg);

  const char *msg = parse.zErrMsg;
  TEST_ASSERT_NOT_NULL(strstr(msg, "cannot "));
  TEST_ASSERT_NOT_NULL(strstr(msg, "drop column from"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "view"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "\"v_drop\""));

  clear_parse_err(db, &parse);
  sqlite3_close(db);
}

void test_isRealTable_virtual_edit_constraints_sets_error(void){
  sqlite3 *db = open_mem_db();
  Parse parse;
  init_parse(&parse, db);

  Table tab;
  make_virtual_table(&tab, "vt1");

  int rc = test_isRealTable(&parse, &tab, 2); /* "edit constraints of" */
  TEST_ASSERT_EQUAL_INT(1, rc);
  TEST_ASSERT_NOT_NULL(parse.zErrMsg);

  const char *msg = parse.zErrMsg;
  TEST_ASSERT_NOT_NULL(strstr(msg, "cannot "));
  TEST_ASSERT_NOT_NULL(strstr(msg, "edit constraints of"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "virtual table"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "\"vt1\""));

  clear_parse_err(db, &parse);
  sqlite3_close(db);
}

void test_isRealTable_both_view_and_virtual_prefers_virtual_in_message(void){
  sqlite3 *db = open_mem_db();
  Parse parse;
  init_parse(&parse, db);

  Table tab;
  make_view_table(&tab, "bothy");
  /* Also mark as virtual so the second branch overwrites zType */
#ifdef TF_Virtual
  tab.tabFlags |= TF_Virtual;
#endif

  int rc = test_isRealTable(&parse, &tab, 0);
  TEST_ASSERT_EQUAL_INT(1, rc);
  TEST_ASSERT_NOT_NULL(parse.zErrMsg);

  const char *msg = parse.zErrMsg;
  /* Ensure "virtual table" appears (wins over "view") */
  TEST_ASSERT_NOT_NULL(strstr(msg, "virtual table"));
  TEST_ASSERT_NOT_NULL(strstr(msg, "\"bothy\""));

  clear_parse_err(db, &parse);
  sqlite3_close(db);
}

int main(void) {
  UNITY_BEGIN();
  RUN_TEST(test_isRealTable_returns_zero_for_real_table);
  RUN_TEST(test_isRealTable_view_rename_columns_sets_error);
  RUN_TEST(test_isRealTable_view_drop_column_sets_error);
  RUN_TEST(test_isRealTable_virtual_edit_constraints_sets_error);
  RUN_TEST(test_isRealTable_both_view_and_virtual_prefers_virtual_in_message);
  return UNITY_END();
}