File size: 6,987 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 |
#include "sqliteInt.h"
#include "unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* The wrapper for the static function is provided by the module under test. */
extern void test_addConstraintFunc(sqlite3_context*, int, sqlite3_value**);
static sqlite3 *gDb = NULL;
static const char *gFuncName = "test_addc";
/* Helper to register the function under a SQL name on gDb */
static void register_add_constraint_func(void){
int rc = sqlite3_create_function(gDb, gFuncName, 3, SQLITE_UTF8, 0,
test_addConstraintFunc, 0, 0);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_create_function failed");
}
/* Helper to call the function and capture result text or error.
Returns a heap-allocated copy of the result text (sqlite3_mprintf) on success
(caller must sqlite3_free), or NULL if result is SQL NULL or on error.
If out_step_rc is non-NULL, it is set to the return from sqlite3_step.
If out_errcode is non-NULL, it is set to sqlite3_errcode(gDb) after step.
*/
static char* call_add_constraint(const char *zCreate, const char *zCons, int iCol,
int *out_step_rc, int *out_errcode){
char *zSql = sqlite3_mprintf("SELECT %s(%Q,%Q,%d)", gFuncName, zCreate, zCons, iCol);
TEST_ASSERT_NOT_NULL_MESSAGE(zSql, "sqlite3_mprintf returned NULL");
sqlite3_stmt *pStmt = NULL;
int rc = sqlite3_prepare_v2(gDb, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_prepare_v2 failed");
rc = sqlite3_step(pStmt);
if( out_step_rc ) *out_step_rc = rc;
if( out_errcode ) *out_errcode = sqlite3_errcode(gDb);
char *res = NULL;
if( rc==SQLITE_ROW ){
const unsigned char *txt = sqlite3_column_text(pStmt, 0);
if( txt ){
res = (char*)sqlite3_mprintf("%s", txt);
}
}
sqlite3_finalize(pStmt);
return res;
}
void setUp(void) {
int rc = sqlite3_open(":memory:", &gDb);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");
register_add_constraint_func();
}
void tearDown(void) {
if( gDb ){
sqlite3_close(gDb);
gDb = NULL;
}
}
/* Test: insert constraint into the first (0th) column */
void test_addConstraintFunc_adds_to_first_column_simple(void){
int stepRc = 0, err = 0;
const char *inSql = "CREATE TABLE t(a, b)";
const char *cons = "CHECK(a>0)";
char *out = call_add_constraint(inSql, cons, 0, &stepRc, &err);
TEST_ASSERT_EQUAL_INT(SQLITE_ROW, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, err);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("CREATE TABLE t(a CHECK(a>0), b)", out);
sqlite3_free(out);
}
/* Test: insert constraint into the second (1st) column */
void test_addConstraintFunc_adds_to_second_column_simple(void){
int stepRc = 0, err = 0;
const char *inSql = "CREATE TABLE t(a, b)";
const char *cons = "DEFAULT 5";
char *out = call_add_constraint(inSql, cons, 1, &stepRc, &err);
TEST_ASSERT_EQUAL_INT(SQLITE_ROW, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, err);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("CREATE TABLE t(a, b DEFAULT 5)", out);
sqlite3_free(out);
}
/* Test: append a table constraint with iCol < 0 (use -1) */
void test_addConstraintFunc_appends_table_constraint_neg1(void){
int stepRc = 0, err = 0;
const char *inSql = "CREATE TABLE t(a INTEGER, b TEXT)";
const char *cons = "UNIQUE(a,b)";
char *out = call_add_constraint(inSql, cons, -1, &stepRc, &err);
TEST_ASSERT_EQUAL_INT(SQLITE_ROW, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, err);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("CREATE TABLE t(a INTEGER, b TEXT, UNIQUE(a,b))", out);
sqlite3_free(out);
}
/* Test: handles comments and nested parentheses in type, inserts before delimiter.
Note: The implementation advances past whitespace/comments before the delimiter,
so the inserted constraint appears AFTER any trailing comments and BEFORE the comma. */
void test_addConstraintFunc_handles_comments_and_parens(void){
int stepRc = 0, err = 0;
const char *inSql =
"CREATE TABLE t( /*c*/ a DECIMAL(10, 5) /*x*/, b TEXT )";
const char *cons = "NOT NULL";
char *out = call_add_constraint(inSql, cons, 0, &stepRc, &err);
TEST_ASSERT_EQUAL_INT(SQLITE_ROW, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, err);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(
"CREATE TABLE t( /*c*/ a DECIMAL(10, 5) /*x*/ NOT NULL, b TEXT )", out);
sqlite3_free(out);
}
/* Test: nested parentheses inside default expressions */
void test_addConstraintFunc_handles_nested_parens_in_defaults(void){
int stepRc = 0, err = 0;
const char *inSql =
"CREATE TABLE t(a TEXT DEFAULT (printf('x(%d)', 1)), b INT)";
const char *cons = "CHECK(length(a)>0)";
char *out = call_add_constraint(inSql, cons, 0, &stepRc, &err);
TEST_ASSERT_EQUAL_INT(SQLITE_ROW, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, err);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING(
"CREATE TABLE t(a TEXT DEFAULT (printf('x(%d)', 1)) CHECK(length(a)>0), b INT)", out);
sqlite3_free(out);
}
/* Test: NULL SQL input returns NULL result (no error set) */
void test_addConstraintFunc_null_sql_returns_null(void){
int stepRc = 0, err = 0;
char *out = call_add_constraint(NULL, "ANY", 0, &stepRc, &err);
TEST_ASSERT_EQUAL_INT(SQLITE_ROW, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, err);
TEST_ASSERT_NULL(out);
sqlite3_free(out); /* ok to free NULL */
}
/* Test: out-of-range column index triggers SQLITE_CORRUPT via illegal token handling */
void test_addConstraintFunc_out_of_range_column_reports_corrupt(void){
int stepRc = 0, err = 0;
/* Only 1 column present, ask for column index 5 */
const char *inSql = "CREATE TABLE t(a)";
char *out = call_add_constraint(inSql, "ZZZ", 5, &stepRc, &err);
TEST_ASSERT(out == NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, err);
}
/* Test: malformed SQL (unterminated string) triggers SQLITE_CORRUPT */
void test_addConstraintFunc_malformed_sql_reports_corrupt(void){
int stepRc = 0, err = 0;
/* Unterminated string literal inside column definition */
const char *inSql = "CREATE TABLE t(a DEFAULT 'unterminated, b INT)";
char *out = call_add_constraint(inSql, "NOT NULL", 0, &stepRc, &err);
TEST_ASSERT(out == NULL);
TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, stepRc);
TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, err);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_addConstraintFunc_adds_to_first_column_simple);
RUN_TEST(test_addConstraintFunc_adds_to_second_column_simple);
RUN_TEST(test_addConstraintFunc_appends_table_constraint_neg1);
RUN_TEST(test_addConstraintFunc_handles_comments_and_parens);
RUN_TEST(test_addConstraintFunc_handles_nested_parens_in_defaults);
RUN_TEST(test_addConstraintFunc_null_sql_returns_null);
RUN_TEST(test_addConstraintFunc_out_of_range_column_reports_corrupt);
RUN_TEST(test_addConstraintFunc_malformed_sql_reports_corrupt);
return UNITY_END();
} |