|
|
#include "sqliteInt.h" |
|
|
#include "unity.h" |
|
|
|
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
|
static int exec_sql(sqlite3 *db, const char *sql, char **pzErr){ |
|
|
return sqlite3_exec(db, sql, 0, 0, pzErr); |
|
|
} |
|
|
|
|
|
static void assert_exec_ok(sqlite3 *db, const char *sql){ |
|
|
char *zErr = NULL; |
|
|
int rc = exec_sql(db, sql, &zErr); |
|
|
if( zErr ){ |
|
|
|
|
|
sqlite3_free(zErr); |
|
|
zErr = NULL; |
|
|
} |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, sql); |
|
|
} |
|
|
|
|
|
static int exec_expect_error_contains(sqlite3 *db, const char *sql, const char *needle, int *pRcOut){ |
|
|
char *zErr = NULL; |
|
|
int rc = exec_sql(db, sql, &zErr); |
|
|
if( pRcOut ) *pRcOut = rc; |
|
|
int ok = (rc!=SQLITE_OK) && (zErr!=NULL) && (strstr(zErr, needle)!=0); |
|
|
if( zErr ) sqlite3_free(zErr); |
|
|
return ok; |
|
|
} |
|
|
|
|
|
static char *get_table_sql(sqlite3 *db, const char *zName){ |
|
|
sqlite3_stmt *pStmt = NULL; |
|
|
const char *zSql = "SELECT sql FROM sqlite_schema WHERE name=?1"; |
|
|
if( sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0)!=SQLITE_OK ){ |
|
|
return NULL; |
|
|
} |
|
|
sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); |
|
|
char *zOut = NULL; |
|
|
if( sqlite3_step(pStmt)==SQLITE_ROW ){ |
|
|
const unsigned char *z = sqlite3_column_text(pStmt, 0); |
|
|
if( z ){ |
|
|
zOut = sqlite3_mprintf("%s", z); |
|
|
} |
|
|
} |
|
|
sqlite3_finalize(pStmt); |
|
|
return zOut; |
|
|
} |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterAddConstraint_add_named_and_enforce(void){ |
|
|
sqlite3 *db = NULL; |
|
|
TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); |
|
|
|
|
|
assert_exec_ok(db, "CREATE TABLE t1(x)"); |
|
|
assert_exec_ok(db, "INSERT INTO t1 VALUES(1)"); |
|
|
|
|
|
|
|
|
assert_exec_ok(db, "ALTER TABLE t1 ADD CONSTRAINT ck_pos CHECK(x>0)"); |
|
|
|
|
|
|
|
|
int rc = SQLITE_OK; |
|
|
TEST_ASSERT_TRUE_MESSAGE( |
|
|
exec_expect_error_contains(db, "INSERT INTO t1 VALUES(0)", "CHECK constraint failed", &rc), |
|
|
"Expected CHECK constraint failure on INSERT after ALTER" |
|
|
); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_CONSTRAINT, rc, "Expected SQLITE_CONSTRAINT"); |
|
|
|
|
|
|
|
|
assert_exec_ok(db, "INSERT INTO t1 VALUES(2)"); |
|
|
|
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE( |
|
|
exec_expect_error_contains(db, |
|
|
"ALTER TABLE t1 ADD CONSTRAINT ck_pos CHECK(x<10)", |
|
|
"already exists", |
|
|
NULL |
|
|
), |
|
|
"Expected duplicate constraint name error" |
|
|
); |
|
|
|
|
|
sqlite3_close(db); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterAddConstraint_violation_on_alter(void){ |
|
|
sqlite3 *db = NULL; |
|
|
TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); |
|
|
|
|
|
assert_exec_ok(db, "CREATE TABLE t2(x)"); |
|
|
assert_exec_ok(db, "INSERT INTO t2 VALUES(-1)"); |
|
|
|
|
|
|
|
|
int rc = SQLITE_OK; |
|
|
TEST_ASSERT_TRUE_MESSAGE( |
|
|
exec_expect_error_contains(db, |
|
|
"ALTER TABLE t2 ADD CHECK(x>0)", |
|
|
"constraint failed", |
|
|
&rc |
|
|
), |
|
|
"Expected 'constraint failed' during ALTER due to existing data" |
|
|
); |
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_CONSTRAINT, rc, "Expected SQLITE_CONSTRAINT"); |
|
|
|
|
|
|
|
|
char *zSchema = get_table_sql(db, "t2"); |
|
|
TEST_ASSERT_NOT_NULL(zSchema); |
|
|
TEST_ASSERT_TRUE(strstr(zSchema, "CHECK")==NULL); |
|
|
sqlite3_free(zSchema); |
|
|
|
|
|
sqlite3_close(db); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterAddConstraint_trim_line_comment_preserve_c_comment(void){ |
|
|
sqlite3 *db = NULL; |
|
|
TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); |
|
|
|
|
|
assert_exec_ok(db, "CREATE TABLE t3(x)"); |
|
|
assert_exec_ok(db, "INSERT INTO t3 VALUES(1)"); |
|
|
|
|
|
|
|
|
assert_exec_ok(db, "ALTER TABLE t3 ADD CHECK (x>0) /*C-keep*/ -- line-drop"); |
|
|
|
|
|
char *zSchema = get_table_sql(db, "t3"); |
|
|
TEST_ASSERT_NOT_NULL(zSchema); |
|
|
|
|
|
|
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(zSchema, "/*C-keep*/"), "Expected C-style comment preserved in schema SQL"); |
|
|
TEST_ASSERT_NULL_MESSAGE(strstr(zSchema, "-- line-drop"), "Expected '--' trailing comment removed from schema SQL"); |
|
|
|
|
|
sqlite3_free(zSchema); |
|
|
sqlite3_close(db); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterAddConstraint_direct_call_smoke(void){ |
|
|
sqlite3 *db = NULL; |
|
|
TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db)); |
|
|
assert_exec_ok(db, "CREATE TABLE t4(a)"); |
|
|
|
|
|
|
|
|
Parse p; |
|
|
memset(&p, 0, sizeof(p)); |
|
|
p.db = db; |
|
|
|
|
|
sqlite3GetVdbe(&p); |
|
|
TEST_ASSERT_NOT_NULL(p.pVdbe); |
|
|
|
|
|
|
|
|
Token tTbl = { "t4", 2 }; |
|
|
SrcList *pSrc = sqlite3SrcListAppend(db, 0, &tTbl, 0); |
|
|
TEST_ASSERT_NOT_NULL(pSrc); |
|
|
TEST_ASSERT_EQUAL_INT(1, pSrc->nSrc); |
|
|
|
|
|
|
|
|
const char *zCons = "CONSTRAINT ckx CHECK(a>0) /*preserve*/ -- trim"; |
|
|
Token firstTok; |
|
|
firstTok.z = zCons; |
|
|
firstTok.n = (int)strlen(zCons); |
|
|
Token nameTok = { "ckx", 3 }; |
|
|
const char *zExpr = "a>0"; |
|
|
int nExpr = 3; |
|
|
|
|
|
|
|
|
p.sLastToken.z = zCons + strlen(zCons); |
|
|
p.sLastToken.n = 0; |
|
|
|
|
|
|
|
|
sqlite3AlterAddConstraint(&p, pSrc, &firstTok, &nameTok, zExpr, nExpr); |
|
|
|
|
|
|
|
|
TEST_ASSERT_NOT_NULL(p.pVdbe); |
|
|
TEST_ASSERT_EQUAL_INT(0, p.nErr); |
|
|
|
|
|
|
|
|
sqlite3SrcListDelete(db, pSrc); |
|
|
sqlite3VdbeDelete(p.pVdbe); |
|
|
sqlite3_close(db); |
|
|
} |
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_sqlite3AlterAddConstraint_add_named_and_enforce); |
|
|
RUN_TEST(test_sqlite3AlterAddConstraint_violation_on_alter); |
|
|
RUN_TEST(test_sqlite3AlterAddConstraint_trim_line_comment_preserve_c_comment); |
|
|
RUN_TEST(test_sqlite3AlterAddConstraint_direct_call_smoke); |
|
|
return UNITY_END(); |
|
|
} |