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