sqlite / tests /tests_alter_sqlite3AlterAddConstraint.c
AryaWu's picture
Upload folder using huggingface_hub
7510827 verified
#include "sqliteInt.h"
#include "unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Helpers */
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 ){
/* Ensure any error message is freed even if rc==SQLITE_OK (unlikely) */
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;
}
/* Unity fixtures */
void setUp(void) {
/* empty */
}
void tearDown(void) {
/* empty */
}
/* Test: Successful ALTER adds named constraint and enforces it for new rows */
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)");
/* Add a named CHECK constraint */
assert_exec_ok(db, "ALTER TABLE t1 ADD CONSTRAINT ck_pos CHECK(x>0)");
/* Inserting a violating row should now fail */
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");
/* Valid insert still succeeds */
assert_exec_ok(db, "INSERT INTO t1 VALUES(2)");
/* Adding another constraint with the same name should fail */
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);
}
/* Test: ALTER fails if existing rows violate the new CHECK expression */
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)");
/* This should fail because existing row violates x>0 */
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");
/* Schema should not be modified */
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);
}
/* Test: Trailing '--' is trimmed but trailing '/* ... *\/' comment is preserved */
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)");
/* Trailing C-style comment should be preserved; trailing -- comment removed */
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);
/* Ensure C-style comment present, '--' tail absent */
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);
}
/* Direct call smoke test: invoke sqlite3AlterAddConstraint with constructed Parse/SrcList/Tokens */
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)");
/* Prepare Parse with a Vdbe */
Parse p;
memset(&p, 0, sizeof(p));
p.db = db;
/* Ensure there is a VDBE to receive generated bytecode */
sqlite3GetVdbe(&p);
TEST_ASSERT_NOT_NULL(p.pVdbe);
/* Build a SrcList referring to table t4 */
Token tTbl = { "t4", 2 };
SrcList *pSrc = sqlite3SrcListAppend(db, 0, &tTbl, 0);
TEST_ASSERT_NOT_NULL(pSrc);
TEST_ASSERT_EQUAL_INT(1, pSrc->nSrc);
/* Construct constraint text and tokens */
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;
/* Set sLastToken to mark the end of the constraint text buffer */
p.sLastToken.z = zCons + strlen(zCons);
p.sLastToken.n = 0;
/* Invoke the target function directly */
sqlite3AlterAddConstraint(&p, pSrc, &firstTok, &nameTok, zExpr, nExpr);
/* Basic sanity: no parse errors recorded and VDBE remains allocated */
TEST_ASSERT_NOT_NULL(p.pVdbe);
TEST_ASSERT_EQUAL_INT(0, p.nErr);
/* Cleanup */
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();
}