| #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(); |
| } |