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

/* Wrapper provided by the SQLite build for static function renameTableTest */
extern void test_renameTableTest(sqlite3_context*, int, sqlite3_value**);

static sqlite3 *gDb = NULL;

/* Bridge function to invoke the wrapper from SQL */
static void unit_rename_table_test(sqlite3_context *ctx, int argc, sqlite3_value **argv){
  /* Directly forward to the provided test_ wrapper */
  test_renameTableTest(ctx, argc, argv);
}

static void exec_ok(const char *sql){
  char *errmsg = NULL;
  int rc = sqlite3_exec(gDb, sql, 0, 0, &errmsg);
  if( rc!=SQLITE_OK ){
    const char *msg = errmsg ? errmsg : "exec failed";
    TEST_FAIL_MESSAGE(msg);
  }
  sqlite3_free(errmsg);
}

/* Helper to call the unit function; returns the sqlite3_step rc.
** If SQLITE_ROW, *pColType is set to the type of column 0. */
static int call_unit_fn(
  const char *zDb,
  const char *zInput,
  const char *zType,
  const char *zName,
  int isTemp,
  const char *zWhen,
  int bNoDQS,
  sqlite3_stmt **ppStmt,
  int *pColType
){
  char *sql = sqlite3_mprintf(
    "SELECT unit_rename_table_test(%Q,%Q,%Q,%Q,%d,%Q,%d)",
    zDb, zInput, zType, zName, isTemp, zWhen, bNoDQS
  );
  TEST_ASSERT_NOT_NULL(sql);

  sqlite3_stmt *stmt = NULL;
  int rc = sqlite3_prepare_v2(gDb, sql, -1, &stmt, 0);
  sqlite3_free(sql);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);

  rc = sqlite3_step(stmt);
  if( rc==SQLITE_ROW ){
    if( pColType ) *pColType = sqlite3_column_type(stmt, 0);
  }
  if( ppStmt ) *ppStmt = stmt; else sqlite3_finalize(stmt);
  return rc;
}

void setUp(void) {
  int rc = sqlite3_open(":memory:", &gDb);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  /* Register the 7-arg unit test SQL function */
  rc = sqlite3_create_function(gDb, "unit_rename_table_test", 7, SQLITE_UTF8,
                               NULL, unit_rename_table_test, NULL, NULL);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
}

void tearDown(void) {
  if( gDb ){
    sqlite3_close(gDb);
    gDb = NULL;
  }
}

/* 1) Valid CREATE TABLE -> returns NULL */
void test_renameTableTest_create_table_returns_null(void){
  int colType = -1;
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("main", "CREATE TABLE t1(a)", "table", "t1", 0, "when", 0, &stmt, &colType);
  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_NULL, colType);
  sqlite3_finalize(stmt);
}

/* 2) Invalid SQL with writable_schema OFF -> raises error with contextual message */
void test_renameTableTest_invalid_sql_raises_error_without_writable_schema(void){
  /* Missing closing parenthesis */
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("main", "CREATE TABLE t1(a", "table", "t1", 0, "when", 0, &stmt, NULL);
  TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);
  sqlite3_finalize(stmt);
  const char *errmsg = sqlite3_errmsg(gDb);
  TEST_ASSERT_NOT_NULL(errmsg);
  TEST_ASSERT_NOT_NULL(strstr(errmsg, "error in table t1 when:"));
}

/* 3) Invalid SQL with writable_schema ON -> returns NULL instead of error */
void test_renameTableTest_invalid_sql_returns_null_with_writable_schema(void){
  exec_ok("PRAGMA writable_schema=ON");
  int colType = -1;
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("main", "CREATE TABLE t1(a", "table", "t1", 0, "when", 0, &stmt, &colType);
  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_NULL, colType);
  sqlite3_finalize(stmt);
  exec_ok("PRAGMA writable_schema=OFF");
}

/* 4) Trigger on main.t1, zDb='main' -> returns 1 */
void test_renameTableTest_trigger_on_main_returns_1_in_main_db(void){
  exec_ok("CREATE TABLE t1(x)");
  const char *trigSql = "CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END";
  int colType = -1;
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("main", trigSql, "trigger", "tr1", 0, "when", 0, &stmt, &colType);
  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_INTEGER, colType);
  TEST_ASSERT_EQUAL_INT(1, sqlite3_column_int(stmt, 0));
  sqlite3_finalize(stmt);
}

/* 5) Trigger on main.t1, zDb='temp' -> returns NULL (no match) */
void test_renameTableTest_trigger_on_main_returns_null_in_temp_db(void){
  exec_ok("CREATE TABLE t1(x)");
  const char *trigSql = "CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END";
  int colType = -1;
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("temp", trigSql, "trigger", "tr1", 1, "when", 0, &stmt, &colType);
  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_NULL, colType);
  sqlite3_finalize(stmt);
}

/* 6) Trigger on temp.t2, zDb='temp' -> returns 1 */
void test_renameTableTest_trigger_on_temp_returns_1_in_temp_db(void){
  exec_ok("CREATE TEMP TABLE t2(y)");
  const char *trigSql = "CREATE TRIGGER tr2 AFTER INSERT ON t2 BEGIN SELECT 1; END";
  int colType = -1;
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("temp", trigSql, "trigger", "tr2", 1, "when", 0, &stmt, &colType);
  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_INTEGER, colType);
  TEST_ASSERT_EQUAL_INT(1, sqlite3_column_int(stmt, 0));
  sqlite3_finalize(stmt);
}

/* 7) View with double-quoted string: with DQS enabled (bNoDQS=0) -> returns NULL */
void test_renameTableTest_view_dqs_enabled_returns_null(void){
  const char *viewSql = "CREATE VIEW v1 AS SELECT \"string\"";
  int colType = -1;
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("main", viewSql, "view", "v1", 0, "when", 0, &stmt, &colType);
  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_NULL, colType);
  sqlite3_finalize(stmt);
}

/* 8) Same view with DQS disabled (bNoDQS=1) -> unresolved identifier error */
void test_renameTableTest_view_dqs_disabled_raises_error(void){
  const char *viewSql = "CREATE VIEW v1 AS SELECT \"string\"";
  sqlite3_stmt *stmt = NULL;
  int rc = call_unit_fn("main", viewSql, "view", "v1", 0, "when", 1, &stmt, NULL);
  TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);
  sqlite3_finalize(stmt);
  const char *errmsg = sqlite3_errmsg(gDb);
  TEST_ASSERT_NOT_NULL(errmsg);
  TEST_ASSERT_NOT_NULL(strstr(errmsg, "error in view v1 when:"));
}

int main(void){
  UNITY_BEGIN();
  RUN_TEST(test_renameTableTest_create_table_returns_null);
  RUN_TEST(test_renameTableTest_invalid_sql_raises_error_without_writable_schema);
  RUN_TEST(test_renameTableTest_invalid_sql_returns_null_with_writable_schema);
  RUN_TEST(test_renameTableTest_trigger_on_main_returns_1_in_main_db);
  RUN_TEST(test_renameTableTest_trigger_on_main_returns_null_in_temp_db);
  RUN_TEST(test_renameTableTest_trigger_on_temp_returns_1_in_temp_db);
  RUN_TEST(test_renameTableTest_view_dqs_enabled_returns_null);
  RUN_TEST(test_renameTableTest_view_dqs_disabled_raises_error);
  return UNITY_END();
}