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

/* Some builds may not expose the prototype in headers, so declare here. */
extern void sqlite3AlterFunctions(sqlite3*);

static char* call_rename_quotefix(sqlite3 *db, const char *zDb, const char *zInput, int *pRc){
  char *zSQL = sqlite3_mprintf("SELECT sqlite_rename_quotefix(%Q,%Q)", zDb, zInput);
  sqlite3_stmt *pStmt = 0;
  int rc = sqlite3_prepare_v2(db, zSQL, -1, &pStmt, 0);
  sqlite3_free(zSQL);
  if( rc!=SQLITE_OK ){
    if(pRc) *pRc = rc;
    return NULL;
  }
  rc = sqlite3_step(pStmt);
  char *zRes = NULL;
  if( rc==SQLITE_ROW ){
    const unsigned char *z = sqlite3_column_text(pStmt, 0);
    if( z ){
      zRes = sqlite3_mprintf("%s", z);
    }
    rc = SQLITE_OK;
  }
  int rc2 = sqlite3_finalize(pStmt);
  if( rc==SQLITE_OK && rc2!=SQLITE_OK ) rc = rc2;
  if( pRc ) *pRc = rc;
  return zRes;
}

static void execSQL(sqlite3 *db, const char *zSql){
  char *zErr = 0;
  int rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
  TEST_ASSERT_EQUAL_MESSAGE(SQLITE_OK, rc, zErr ? zErr : "execSQL failed");
  if( zErr ) sqlite3_free(zErr);
}

void setUp(void) {
  /* no-op */
}

void tearDown(void) {
  /* no-op */
}

void test_renameQuotefixFunc_view_basic(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);
  execSQL(db, "CREATE TABLE t1(a, b, c);");

  const char *inSql = "CREATE VIEW v1 AS SELECT \"a\", \"string\" FROM t1";
  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(out);
  TEST_ASSERT_EQUAL_STRING("CREATE VIEW v1 AS SELECT \"a\", 'string' FROM t1", out);

  sqlite3_free(out);
  sqlite3_close(db);
}

void test_renameQuotefixFunc_table_check_expr(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);

  const char *inSql = "CREATE TABLE t2(a CHECK(\"ok\" IN (\"ok\",\"bad\")))";
  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(out);
  TEST_ASSERT_EQUAL_STRING("CREATE TABLE t2(a CHECK('ok' IN ('ok','bad')))", out);

  sqlite3_free(out);
  sqlite3_close(db);
}

void test_renameQuotefixFunc_index_partial_where(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);
  execSQL(db, "CREATE TABLE t1(a, b);");

  const char *inSql = "CREATE INDEX i1 ON t1(a) WHERE b=\"str\"";
  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(out);
  TEST_ASSERT_EQUAL_STRING("CREATE INDEX i1 ON t1(a) WHERE b='str'", out);

  sqlite3_free(out);
  sqlite3_close(db);
}

void test_renameQuotefixFunc_trigger_when_and_step(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);
  execSQL(db, "CREATE TABLE t1(a, b);");

  const char *inSql =
    "CREATE TRIGGER tr1 AFTER INSERT ON t1 "
    "WHEN \"string\"='x' BEGIN UPDATE t1 SET a=1 WHERE b=\"y\"; END";

  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(out);
  TEST_ASSERT_EQUAL_STRING(
    "CREATE TRIGGER tr1 AFTER INSERT ON t1 "
    "WHEN 'string'='x' BEGIN UPDATE t1 SET a=1 WHERE b='y'; END",
    out
  );

  sqlite3_free(out);
  sqlite3_close(db);
}

void test_renameQuotefixFunc_adjacent_single_quote_alias_spacing(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);
  execSQL(db, "CREATE TABLE t1(a);");

  /* Ensure that a space is inserted to avoid adjacent literal concatenation */
  const char *inSql = "CREATE VIEW v2 AS SELECT \"string\"'alias' FROM t1";
  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(out);
  TEST_ASSERT_EQUAL_STRING("CREATE VIEW v2 AS SELECT 'string' 'alias' FROM t1", out);

  sqlite3_free(out);
  sqlite3_close(db);
}

void test_renameQuotefixFunc_writable_schema_returns_input_on_error(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);

  execSQL(db, "PRAGMA writable_schema=ON;");
  const char *inSql = "CREATE VIEW v_bad AS SELECT \"x\" FROM nosuchtable";
  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  /* With writable_schema=ON and an error during processing, the original input is returned */
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
  TEST_ASSERT_NOT_NULL(out);
  TEST_ASSERT_EQUAL_STRING(inSql, out);

  sqlite3_free(out);
  sqlite3_close(db);
}

void test_renameQuotefixFunc_error_without_writable_schema(void){
  sqlite3 *db = 0; int rc;
  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &db));
  sqlite3AlterFunctions(db);

  const char *inSql = "CREATE VIEW v_bad AS SELECT \"x\" FROM nosuchtable";
  char *out = call_rename_quotefix(db, "main", inSql, &rc);
  /* Expect an error (no row) because name resolution fails and writable_schema is OFF */
  TEST_ASSERT_NULL(out);
  TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);

  sqlite3_close(db);
}

int main(void){
  UNITY_BEGIN();
  RUN_TEST(test_renameQuotefixFunc_view_basic);
  RUN_TEST(test_renameQuotefixFunc_table_check_expr);
  RUN_TEST(test_renameQuotefixFunc_index_partial_where);
  RUN_TEST(test_renameQuotefixFunc_trigger_when_and_step);
  RUN_TEST(test_renameQuotefixFunc_adjacent_single_quote_alias_spacing);
  RUN_TEST(test_renameQuotefixFunc_writable_schema_returns_input_on_error);
  RUN_TEST(test_renameQuotefixFunc_error_without_writable_schema);
  return UNITY_END();
}