File size: 5,661 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 |
#include "sqliteInt.h"
#include "unity.h"
#include <stdlib.h>
#include <string.h>
/* Wrapper for the static function provided in the module under test */
extern int test_skipCreateTable(sqlite3_context *ctx, const u8 *zSql, int *piOff);
void setUp(void) {
/* Setup code here, or leave empty */
}
void tearDown(void) {
/* Cleanup code here, or leave empty */
}
/* Helper: call the wrapper with a NULL sqlite3_context, suitable for non-error paths */
static int call_skip_with_null_ctx(const char *zSql, int *piOff){
return test_skipCreateTable(NULL, (const unsigned char*)zSql, piOff);
}
/* Test 1: Basic CREATE TABLE with straightforward '(' */
void test_skipCreateTable_basic_simple(void) {
const char *zSql = "CREATE TABLE t(a INTEGER, b TEXT)";
int off = -1;
int rc = call_skip_with_null_ctx(zSql, &off);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_TRUE(off > 0);
TEST_ASSERT_EQUAL_CHAR('(', zSql[off-1]);
/* For this simple case with no prior '(', the first '(' is the column list */
const char *pFirst = strchr(zSql, '(');
TEST_ASSERT_NOT_NULL(pFirst);
TEST_ASSERT_EQUAL_INT((int)(pFirst - zSql + 1), off);
}
/* Test 2: With whitespace, comments, schema and quoted identifier */
void test_skipCreateTable_with_schema_and_quotes(void) {
const char *zSql = " CREATE TEMP TABLE IF NOT EXISTS main.\"t name\" ( col1 INTEGER )";
int off = -1;
int rc = call_skip_with_null_ctx(zSql, &off);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_TRUE(off > 0);
TEST_ASSERT_EQUAL_CHAR('(', zSql[off-1]);
const char *pFirst = strchr(zSql, '(');
TEST_ASSERT_NOT_NULL(pFirst);
TEST_ASSERT_EQUAL_INT((int)(pFirst - zSql + 1), off);
}
/* Test 3: Ensure '(' inside a string literal is ignored by tokenizer; offset is at the true column-list '(' */
void test_skipCreateTable_ignores_paren_inside_string_literal(void) {
/* The first '(' appears inside the string literal and should be ignored.
The actual column-list '(' is the second '(' in the SQL text. */
const char *zSql = "CREATE TABLE t 'ignore (this) completely' (x INT)";
int off = -1;
int rc = call_skip_with_null_ctx(zSql, &off);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_TRUE(off > 0);
TEST_ASSERT_EQUAL_CHAR('(', zSql[off-1]);
/* The correct '(' is the last one in this crafted SQL */
const char *pLast = strrchr(zSql, '(');
TEST_ASSERT_NOT_NULL(pLast);
TEST_ASSERT_EQUAL_INT((int)(pLast - zSql + 1), off);
}
/* Test 4: Parentheses inside a bracket-quoted identifier are ignored; still find the column-list '(' */
void test_skipCreateTable_ignores_paren_inside_quoted_identifier(void) {
const char *zSql = "CREATE TABLE [a(b)] (c TEXT)";
int off = -1;
int rc = call_skip_with_null_ctx(zSql, &off);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_TRUE(off > 0);
TEST_ASSERT_EQUAL_CHAR('(', zSql[off-1]);
/* The correct '(' is the second '(' in the SQL; for this crafted SQL it's the last '(' */
const char *pLast = strrchr(zSql, '(');
TEST_ASSERT_NOT_NULL(pLast);
TEST_ASSERT_EQUAL_INT((int)(pLast - zSql + 1), off);
}
/* Test 5: NULL input -> SQLITE_ERROR and piOff must remain unchanged */
void test_skipCreateTable_null_input_returns_error(void) {
int off = 1234;
int rc = test_skipCreateTable(NULL, NULL, &off);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);
TEST_ASSERT_EQUAL_INT(1234, off);
}
/* Globals used to capture results from UDF-based test */
static int g_rc_from_udf = 0;
static int g_off_from_udf = 0;
/* UDF used to obtain a valid sqlite3_context for exercising the error path */
static void udf_call_skip(sqlite3_context *ctx, int argc, sqlite3_value **argv){
(void)argc;
const unsigned char *zSql = sqlite3_value_text(argv[0]);
int off = -1;
int rc = test_skipCreateTable(ctx, zSql, &off);
g_rc_from_udf = rc;
g_off_from_udf = off;
/* Return rc as function result too (engine may still treat prior error as authoritative) */
sqlite3_result_int(ctx, rc);
}
/* Test 6: No '(' present – via UDF to provide valid sqlite3_context; expect SQLITE_ERROR */
void test_skipCreateTable_no_open_paren_illegal_token_via_udf(void) {
sqlite3 *db = NULL;
sqlite3_stmt *stmt = NULL;
int rc = sqlite3_open(":memory:", &db);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
rc = sqlite3_create_function_v2(db, "xskip", 1, SQLITE_UTF8, NULL,
udf_call_skip, NULL, NULL, NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
/* No '(' in this SQL text; skipCreateTable should eventually hit an error path */
const char *sql = "SELECT xskip('CREATE TABLE t')";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
int steprc = sqlite3_step(stmt);
/* Engine may return SQLITE_ERROR due to sqlite3_result_error_code(ctx,...)
We are tolerant to either SQLITE_ERROR or SQLITE_ROW depending on internal behavior,
but we strictly verify the function's rc captured from inside the UDF. */
(void)steprc;
rc = sqlite3_finalize(stmt);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
sqlite3_close(db);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, g_rc_from_udf);
/* piOff should not have been set on error */
TEST_ASSERT_EQUAL_INT(-1, g_off_from_udf);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_skipCreateTable_basic_simple);
RUN_TEST(test_skipCreateTable_with_schema_and_quotes);
RUN_TEST(test_skipCreateTable_ignores_paren_inside_string_literal);
RUN_TEST(test_skipCreateTable_ignores_paren_inside_quoted_identifier);
RUN_TEST(test_skipCreateTable_null_input_returns_error);
RUN_TEST(test_skipCreateTable_no_open_paren_illegal_token_via_udf);
return UNITY_END();
} |