File size: 6,578 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 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
#include "sqliteInt.h"
#include "unity.h"
#include <string.h>
#include <stdlib.h>
/* Globals for tests */
static sqlite3 *gDb = NULL;
/* Unity setup/teardown */
void setUp(void) {
int rc = sqlite3_open(":memory:", &gDb);
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);
TEST_ASSERT_NOT_NULL(gDb);
}
void tearDown(void) {
if (gDb) {
sqlite3_close(gDb);
gDb = NULL;
}
}
/* Helper: create a SrcList with a single table name */
static SrcList* makeSrcList(const char *zName){
Token t;
memset(&t, 0, sizeof(t));
t.z = zName;
t.n = (int)strlen(zName);
return sqlite3SrcListAppend(gDb, 0, &t, 0);
}
/* Helper: initialize a Parse object for our db */
static void initParse(Parse *p){
memset(p, 0, sizeof(*p));
p->db = gDb;
}
/* Test: successful copy for an ordinary table */
void test_sqlite3AlterBeginAddColumn_success_creates_partial_copy(void) {
char *err = 0;
int rc;
/* Create a normal table with multiple columns and defaults */
rc = sqlite3_exec(gDb,
"CREATE TABLE t(a INTEGER DEFAULT 1, b TEXT, c REAL DEFAULT 3.14);",
0, 0, &err);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, err ? err : "create failed");
if (err) sqlite3_free(err), err = 0;
/* Find original table metadata */
Table *pOrig = sqlite3FindTable(gDb, "t", "main");
TEST_ASSERT_NOT_NULL(pOrig);
TEST_ASSERT_TRUE(pOrig->nCol > 0);
/* Prepare SrcList and Parse */
SrcList *pSrc = makeSrcList("t");
TEST_ASSERT_NOT_NULL(pSrc);
Parse parse;
initParse(&parse);
TEST_ASSERT_NULL(parse.pNewTable);
/* Call the target function while holding btree mutexes */
sqlite3BtreeEnterAll(gDb);
sqlite3AlterBeginAddColumn(&parse, pSrc);
sqlite3BtreeLeaveAll(gDb);
/* Verify success: no error and pNewTable populated */
TEST_ASSERT_EQUAL_INT(0, parse.nErr);
TEST_ASSERT_NOT_NULL(parse.pNewTable);
Table *pNew = parse.pNewTable;
/* Name must be prefixed as "sqlite_altertab_t" */
TEST_ASSERT_NOT_NULL(pNew->zName);
TEST_ASSERT_EQUAL_STRING("sqlite_altertab_t", pNew->zName);
/* Column count should match */
TEST_ASSERT_EQUAL_INT(pOrig->nCol, pNew->nCol);
/* Column names duplicated and hashes computed */
for(int i=0; i<pNew->nCol; i++){
Column *cOrig = &pOrig->aCol[i];
Column *cNew = &pNew->aCol[i];
TEST_ASSERT_NOT_NULL(cNew->zCnName);
TEST_ASSERT_NOT_NULL(cOrig->zCnName);
/* Ensure duplicated pointer (not aliasing original) and same content */
TEST_ASSERT(cNew->zCnName != cOrig->zCnName);
TEST_ASSERT_EQUAL_STRING(cOrig->zCnName, cNew->zCnName);
/* Ensure hash is set to case-insensitive hash of the name */
TEST_ASSERT_EQUAL_INT(sqlite3StrIHash(cNew->zCnName), cNew->hName);
}
/* Schema and addColOffset should match original */
TEST_ASSERT(pNew->pSchema == pOrig->pSchema);
TEST_ASSERT_TRUE(pNew->u.tab.addColOffset > 0);
TEST_ASSERT_EQUAL_INT(pOrig->u.tab.addColOffset, pNew->u.tab.addColOffset);
/* Default expression list should be duplicated if present on original */
if( pOrig->u.tab.pDfltList ){
TEST_ASSERT_NOT_NULL(pNew->u.tab.pDfltList);
TEST_ASSERT_NOT_NULL(pOrig->u.tab.pDfltList);
TEST_ASSERT(pNew->u.tab.pDfltList != pOrig->u.tab.pDfltList);
TEST_ASSERT_EQUAL_INT(pOrig->u.tab.pDfltList->nExpr,
pNew->u.tab.pDfltList->nExpr);
}
/* New table refcount should be 1 */
TEST_ASSERT_EQUAL_INT(1, pNew->nTabRef);
/* Cleanup: delete the allocated table copy */
sqlite3DeleteTable(gDb, pNew);
parse.pNewTable = 0;
}
/* Test: attempting to alter a view should raise an error */
void test_sqlite3AlterBeginAddColumn_on_view_sets_error(void) {
char *err = 0;
int rc;
rc = sqlite3_exec(gDb, "CREATE VIEW v AS SELECT 1 AS x;", 0, 0, &err);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, err ? err : "create view failed");
if (err) sqlite3_free(err), err = 0;
SrcList *pSrc = makeSrcList("v");
TEST_ASSERT_NOT_NULL(pSrc);
Parse parse;
initParse(&parse);
sqlite3BtreeEnterAll(gDb);
sqlite3AlterBeginAddColumn(&parse, pSrc);
sqlite3BtreeLeaveAll(gDb);
TEST_ASSERT_TRUE(parse.nErr > 0);
TEST_ASSERT_NULL(parse.pNewTable);
TEST_ASSERT_NOT_NULL(parse.zErrMsg);
TEST_ASSERT_NOT_NULL(strstr(parse.zErrMsg, "Cannot add a column to a view"));
sqlite3DbFree(gDb, parse.zErrMsg);
parse.zErrMsg = 0;
}
/* Test: attempting to alter a system table (sqlite_sequence) should raise an error */
void test_sqlite3AlterBeginAddColumn_on_system_table_sets_error(void) {
char *err = 0;
int rc;
/* Create an AUTOINCREMENT table to ensure sqlite_sequence exists */
rc = sqlite3_exec(gDb,
"CREATE TABLE at(id INTEGER PRIMARY KEY AUTOINCREMENT, b TEXT);",
0, 0, &err);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, err ? err : "create autoinc failed");
if (err) sqlite3_free(err), err = 0;
/* Ensure sqlite_sequence exists by inserting a row (usually created at table create, but this is safe) */
rc = sqlite3_exec(gDb, "INSERT INTO at(b) VALUES('x');", 0, 0, &err);
TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, err ? err : "insert failed");
if (err) sqlite3_free(err), err = 0;
/* Now attempt to alter sqlite_sequence */
SrcList *pSrc = makeSrcList("sqlite_sequence");
TEST_ASSERT_NOT_NULL(pSrc);
Parse parse;
initParse(&parse);
sqlite3BtreeEnterAll(gDb);
sqlite3AlterBeginAddColumn(&parse, pSrc);
sqlite3BtreeLeaveAll(gDb);
TEST_ASSERT_TRUE(parse.nErr > 0);
TEST_ASSERT_NULL(parse.pNewTable);
TEST_ASSERT_NOT_NULL(parse.zErrMsg);
TEST_ASSERT_NOT_NULL(strstr(parse.zErrMsg, "table sqlite_sequence may not be altered"));
sqlite3DbFree(gDb, parse.zErrMsg);
parse.zErrMsg = 0;
}
/* Test: table not found should set an error and no pNewTable */
void test_sqlite3AlterBeginAddColumn_table_not_found_sets_error(void) {
SrcList *pSrc = makeSrcList("no_such_table_123");
TEST_ASSERT_NOT_NULL(pSrc);
Parse parse;
initParse(&parse);
sqlite3BtreeEnterAll(gDb);
sqlite3AlterBeginAddColumn(&parse, pSrc);
sqlite3BtreeLeaveAll(gDb);
TEST_ASSERT_TRUE(parse.nErr > 0);
TEST_ASSERT_NULL(parse.pNewTable);
TEST_ASSERT_NOT_NULL(parse.zErrMsg);
TEST_ASSERT_NOT_NULL(strstr(parse.zErrMsg, "no such table"));
sqlite3DbFree(gDb, parse.zErrMsg);
parse.zErrMsg = 0;
}
/* Main runner */
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_sqlite3AlterBeginAddColumn_success_creates_partial_copy);
RUN_TEST(test_sqlite3AlterBeginAddColumn_on_view_sets_error);
RUN_TEST(test_sqlite3AlterBeginAddColumn_on_system_table_sets_error);
RUN_TEST(test_sqlite3AlterBeginAddColumn_table_not_found_sets_error);
return UNITY_END();
} |