|
|
#include "sqliteInt.h" |
|
|
#include "unity.h" |
|
|
#include <string.h> |
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
|
static sqlite3 *gDb = NULL; |
|
|
|
|
|
|
|
|
|
|
|
static void init_db_and_base_table(const char *zCreate){ |
|
|
int rc; |
|
|
rc = sqlite3_open(":memory:", &gDb); |
|
|
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); |
|
|
rc = sqlite3_exec(gDb, zCreate, 0, 0, 0); |
|
|
TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc); |
|
|
} |
|
|
|
|
|
|
|
|
static Table *allocNewAlterTable(sqlite3 *db, const char *zTabName){ |
|
|
Table *pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table)); |
|
|
TEST_ASSERT_NOT_NULL(pNew); |
|
|
|
|
|
pNew->pSchema = db->aDb[0].pSchema; |
|
|
|
|
|
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", zTabName); |
|
|
TEST_ASSERT_NOT_NULL(pNew->zName); |
|
|
|
|
|
pNew->nCol = 1; |
|
|
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)); |
|
|
TEST_ASSERT_NOT_NULL(pNew->aCol); |
|
|
|
|
|
pNew->tabFlags = 0; |
|
|
|
|
|
pNew->u.tab.addColOffset = 1; |
|
|
return pNew; |
|
|
} |
|
|
|
|
|
|
|
|
static Expr *makeSpanExprWithLeft(sqlite3 *db, int leftOp){ |
|
|
Expr *pLeft = (Expr*)sqlite3DbMallocZero(db, sizeof(Expr)); |
|
|
TEST_ASSERT_NOT_NULL(pLeft); |
|
|
pLeft->op = (u8)leftOp; |
|
|
Expr *pSpan = (Expr*)sqlite3DbMallocZero(db, sizeof(Expr)); |
|
|
TEST_ASSERT_NOT_NULL(pSpan); |
|
|
pSpan->op = TK_SPAN; |
|
|
pSpan->pLeft = pLeft; |
|
|
return pSpan; |
|
|
} |
|
|
|
|
|
|
|
|
static void initParse(Parse *pParse, sqlite3 *db, Table *pNew, const char *zColDef, Token *pTokOut){ |
|
|
memset(pParse, 0, sizeof(*pParse)); |
|
|
pParse->db = db; |
|
|
db->pParse = pParse; |
|
|
pParse->pNewTable = pNew; |
|
|
|
|
|
pTokOut->z = (const unsigned char*)zColDef; |
|
|
pTokOut->n = (int)strlen(zColDef); |
|
|
pTokOut->dyn = 0; |
|
|
} |
|
|
|
|
|
|
|
|
static void freeNewAlterTable(sqlite3 *db, Table *pNew){ |
|
|
if( pNew ){ |
|
|
if( pNew->aCol ){ |
|
|
|
|
|
if( pNew->aCol[0].pDflt ){ |
|
|
sqlite3ExprDelete(db, pNew->aCol[0].pDflt); |
|
|
pNew->aCol[0].pDflt = 0; |
|
|
} |
|
|
sqlite3DbFree(db, pNew->aCol); |
|
|
} |
|
|
if( pNew->zName ) sqlite3DbFree(db, pNew->zName); |
|
|
sqlite3DbFree(db, pNew); |
|
|
} |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
init_db_and_base_table("CREATE TABLE t1(a)"); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
if( gDb ){ |
|
|
sqlite3_close(gDb); |
|
|
gDb = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterFinishAddColumn_rejects_primary_key(void){ |
|
|
Table *pNew = allocNewAlterTable(gDb, "t1"); |
|
|
Column *pCol = &pNew->aCol[0]; |
|
|
pCol->colFlags |= COLFLAG_PRIMKEY; |
|
|
|
|
|
Parse sParse; |
|
|
Token sTok; |
|
|
initParse(&sParse, gDb, pNew, "x INTEGER PRIMARY KEY", &sTok); |
|
|
|
|
|
sqlite3AlterFinishAddColumn(&sParse, &sTok); |
|
|
|
|
|
TEST_ASSERT_GREATER_THAN(0, sParse.nErr); |
|
|
TEST_ASSERT_NOT_NULL(sParse.zErrMsg); |
|
|
TEST_ASSERT_EQUAL_INT(0, strstr(sParse.zErrMsg, "Cannot add a PRIMARY KEY column")==NULL); |
|
|
|
|
|
freeNewAlterTable(gDb, pNew); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterFinishAddColumn_rejects_unique(void){ |
|
|
Table *pNew = allocNewAlterTable(gDb, "t1"); |
|
|
Column *pCol = &pNew->aCol[0]; |
|
|
pCol->colFlags &= ~COLFLAG_PRIMKEY; |
|
|
|
|
|
pNew->pIndex = (Index*)pNew; |
|
|
|
|
|
Parse sParse; |
|
|
Token sTok; |
|
|
initParse(&sParse, gDb, pNew, "x INTEGER UNIQUE", &sTok); |
|
|
|
|
|
sqlite3AlterFinishAddColumn(&sParse, &sTok); |
|
|
|
|
|
TEST_ASSERT_GREATER_THAN(0, sParse.nErr); |
|
|
TEST_ASSERT_NOT_NULL(sParse.zErrMsg); |
|
|
TEST_ASSERT_EQUAL_INT(0, strstr(sParse.zErrMsg, "Cannot add a UNIQUE column")==NULL); |
|
|
|
|
|
|
|
|
pNew->pIndex = 0; |
|
|
freeNewAlterTable(gDb, pNew); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterFinishAddColumn_notnull_with_null_default_generates_runtime_check(void){ |
|
|
Table *pNew = allocNewAlterTable(gDb, "t1"); |
|
|
Column *pCol = &pNew->aCol[0]; |
|
|
pCol->notNull = 1; |
|
|
|
|
|
pCol->pDflt = makeSpanExprWithLeft(gDb, TK_NULL); |
|
|
|
|
|
Parse sParse; |
|
|
Token sTok; |
|
|
initParse(&sParse, gDb, pNew, "x INTEGER NOT NULL DEFAULT NULL", &sTok); |
|
|
|
|
|
sqlite3AlterFinishAddColumn(&sParse, &sTok); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, sParse.nErr); |
|
|
TEST_ASSERT_NOT_NULL(sParse.pVdbe); |
|
|
|
|
|
freeNewAlterTable(gDb, pNew); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterFinishAddColumn_stored_generated_column_rejected_runtime(void){ |
|
|
Table *pNew = allocNewAlterTable(gDb, "t1"); |
|
|
Column *pCol = &pNew->aCol[0]; |
|
|
pCol->colFlags |= (COLFLAG_GENERATED | COLFLAG_STORED); |
|
|
|
|
|
Parse sParse; |
|
|
Token sTok; |
|
|
initParse(&sParse, gDb, pNew, "x GENERATED ALWAYS AS (a+1) STORED", &sTok); |
|
|
|
|
|
sqlite3AlterFinishAddColumn(&sParse, &sTok); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, sParse.nErr); |
|
|
TEST_ASSERT_NOT_NULL(sParse.pVdbe); |
|
|
|
|
|
freeNewAlterTable(gDb, pNew); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterFinishAddColumn_constant_default_allowed(void){ |
|
|
Table *pNew = allocNewAlterTable(gDb, "t1"); |
|
|
Column *pCol = &pNew->aCol[0]; |
|
|
|
|
|
pCol->colFlags &= ~COLFLAG_GENERATED; |
|
|
|
|
|
pCol->pDflt = makeSpanExprWithLeft(gDb, TK_INTEGER); |
|
|
|
|
|
Parse sParse; |
|
|
Token sTok; |
|
|
initParse(&sParse, gDb, pNew, "x INTEGER DEFAULT 5", &sTok); |
|
|
|
|
|
sqlite3AlterFinishAddColumn(&sParse, &sTok); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, sParse.nErr); |
|
|
TEST_ASSERT_NOT_NULL(sParse.pVdbe); |
|
|
|
|
|
freeNewAlterTable(gDb, pNew); |
|
|
} |
|
|
|
|
|
|
|
|
void test_sqlite3AlterFinishAddColumn_nonconstant_default_generates_runtime_check(void){ |
|
|
Table *pNew = allocNewAlterTable(gDb, "t1"); |
|
|
Column *pCol = &pNew->aCol[0]; |
|
|
|
|
|
pCol->colFlags &= ~COLFLAG_GENERATED; |
|
|
|
|
|
pCol->pDflt = makeSpanExprWithLeft(gDb, TK_FUNCTION); |
|
|
|
|
|
Parse sParse; |
|
|
Token sTok; |
|
|
initParse(&sParse, gDb, pNew, "x TEXT DEFAULT (CURRENT_TIME)", &sTok); |
|
|
|
|
|
sqlite3AlterFinishAddColumn(&sParse, &sTok); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, sParse.nErr); |
|
|
TEST_ASSERT_NOT_NULL(sParse.pVdbe); |
|
|
|
|
|
freeNewAlterTable(gDb, pNew); |
|
|
} |
|
|
|
|
|
int main(void){ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_sqlite3AlterFinishAddColumn_rejects_primary_key); |
|
|
RUN_TEST(test_sqlite3AlterFinishAddColumn_rejects_unique); |
|
|
RUN_TEST(test_sqlite3AlterFinishAddColumn_notnull_with_null_default_generates_runtime_check); |
|
|
RUN_TEST(test_sqlite3AlterFinishAddColumn_stored_generated_column_rejected_runtime); |
|
|
RUN_TEST(test_sqlite3AlterFinishAddColumn_constant_default_allowed); |
|
|
RUN_TEST(test_sqlite3AlterFinishAddColumn_nonconstant_default_generates_runtime_check); |
|
|
return UNITY_END(); |
|
|
} |