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

/* Unity setUp/tearDown */
void setUp(void) {
  /* No global setup required */
}
void tearDown(void) {
  /* No global teardown required */
}

/* Helper: create a SrcList with one item for table zTab in database zDb (or NULL for main) */
static SrcList *makeSrcListOne(sqlite3 *db, const char *zDb, const char *zTab){
  /* SrcList has a flexible array a[1], so sizeof(SrcList) is sufficient for one element */
  SrcList *p = (SrcList*)sqlite3DbMallocZero(db, sizeof(SrcList));
  TEST_ASSERT_NOT_NULL_MESSAGE(p, "sqlite3DbMallocZero failed creating SrcList");
  p->nAlloc = 1;
  p->nSrc = 1;
  p->a[0].zName = sqlite3DbStrDup(db, zTab);
  TEST_ASSERT_NOT_NULL_MESSAGE(p->a[0].zName, "sqlite3DbStrDup failed for table name");
  if( zDb ){
    p->a[0].zDatabase = sqlite3DbStrDup(db, zDb);
    TEST_ASSERT_NOT_NULL_MESSAGE(p->a[0].zDatabase, "sqlite3DbStrDup failed for database name");
  }
  return p;
}

/* Helper: make a Token from a C string literal */
static Token makeToken(const char *z){
  Token t;
  t.z = z;
  t.n = (int)strlen(z);
  return t;
}

/* Helper: initialize a Parse object for a given db */
static void initParse(Parse *pParse, sqlite3 *db){
  memset(pParse, 0, sizeof(*pParse));
  pParse->db = db;
}

/* Helper: exec SQL and assert SQLITE_OK */
static void execSqlOk(sqlite3 *db, const char *zSql){
  int rc = sqlite3_exec(db, zSql, 0, 0, 0);
  if( rc!=SQLITE_OK ){
    const char *err = sqlite3_errmsg(db);
    char msg[512];
    sqlite3_snprintf(sizeof(msg), msg, "SQL exec failed (%d): %s; SQL: %s", rc, err ? err : "(null)", zSql);
    TEST_FAIL_MESSAGE(msg);
  }
}

/* Test: Nonexistent table should cause early return: no VDBE and an error recorded */
void test_sqlite3AlterDropConstraint_nonexistent_table(void){
  sqlite3 *db = 0;
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));
  Parse p;
  initParse(&p, db);

  SrcList *pSrc = makeSrcListOne(db, NULL, "nosuchtable");
  Token cons = makeToken("c1");

  sqlite3AlterDropConstraint(&p, pSrc, &cons, 0);

  TEST_ASSERT_NULL_MESSAGE(p.pVdbe, "VDBE should not be created for nonexistent table");
  TEST_ASSERT_TRUE_MESSAGE(p.nErr>0, "An error should be recorded for nonexistent table");

  sqlite3SrcListDelete(db, pSrc);
  if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);
  sqlite3_close(db);
}

/* Test: Existing table, drop NOT NULL from existing column -> code generated (VDBE created) */
void test_sqlite3AlterDropConstraint_drop_not_null_on_existing_col_generates_code(void){
  sqlite3 *db = 0;
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));
  execSqlOk(db, "CREATE TABLE t1(a INTEGER NOT NULL, b TEXT);");

  Parse p;
  initParse(&p, db);

  SrcList *pSrc = makeSrcListOne(db, NULL, "t1");
  Token col = makeToken("a");

  sqlite3AlterDropConstraint(&p, pSrc, 0, &col);

  TEST_ASSERT_NOT_NULL_MESSAGE(p.pVdbe, "VDBE should be created for valid DROP NOT NULL operation");
  /* We do not execute the VDBE here; we only ensure code generation occurred. */

  sqlite3SrcListDelete(db, pSrc);
  if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);
  sqlite3_close(db);
}

/* Test: Existing table, drop NOT NULL from nonexistent column -> error, no VDBE */
void test_sqlite3AlterDropConstraint_nonexistent_column_reports_error(void){
  sqlite3 *db = 0;
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));
  execSqlOk(db, "CREATE TABLE t2(a INTEGER NOT NULL, b TEXT);");

  Parse p;
  initParse(&p, db);

  SrcList *pSrc = makeSrcListOne(db, NULL, "t2");
  Token col = makeToken("no_such_col");

  sqlite3AlterDropConstraint(&p, pSrc, 0, &col);

  TEST_ASSERT_NULL_MESSAGE(p.pVdbe, "VDBE should not be created when column does not exist");
  TEST_ASSERT_TRUE_MESSAGE(p.nErr>0, "An error should be recorded for nonexistent column");

  sqlite3SrcListDelete(db, pSrc);
  if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);
  sqlite3_close(db);
}

/* Test: Existing table with named constraint, drop named constraint -> code generated */
void test_sqlite3AlterDropConstraint_drop_named_constraint_generates_code(void){
  sqlite3 *db = 0;
  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));
  /* Create a table with a named CHECK constraint */
  execSqlOk(db, "CREATE TABLE t3(x INT, y INT, CONSTRAINT c1 CHECK(x>0));");

  Parse p;
  initParse(&p, db);

  SrcList *pSrc = makeSrcListOne(db, NULL, "t3");
  Token cons = makeToken("c1");

  sqlite3AlterDropConstraint(&p, pSrc, &cons, 0);

  TEST_ASSERT_NOT_NULL_MESSAGE(p.pVdbe, "VDBE should be created for valid DROP CONSTRAINT operation");

  sqlite3SrcListDelete(db, pSrc);
  if( p.pVdbe ) sqlite3VdbeDelete(p.pVdbe);
  sqlite3_close(db);
}

int main(void){
  UNITY_BEGIN();
  RUN_TEST(test_sqlite3AlterDropConstraint_nonexistent_table);
  RUN_TEST(test_sqlite3AlterDropConstraint_drop_not_null_on_existing_col_generates_code);
  RUN_TEST(test_sqlite3AlterDropConstraint_nonexistent_column_reports_error);
  RUN_TEST(test_sqlite3AlterDropConstraint_drop_named_constraint_generates_code);
  return UNITY_END();
}