AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Global DB handle used for allocations in tests */7static sqlite3 *gDb = NULL;8 9/* Unity setUp/tearDown */10void setUp(void) {11 int rc = sqlite3_open(":memory:", &gDb);12 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory DB");13}14 15void tearDown(void) {16 if (gDb) {17 sqlite3_close(gDb);18 gDb = NULL;19 }20}21 22/* Helpers */23static Token makeToken(const char *z){24 Token t;25 t.z = z;26 t.n = (int)strlen(z);27 return t;28}29 30static Expr* makeIdExpr(sqlite3 *db, const char *zName){31 Token t = makeToken(zName);32 return sqlite3ExprAlloc(db, TK_ID, &t, 0);33}34 35static Expr* makeIntExpr(sqlite3 *db, const char *zVal){36 Token t = makeToken(zVal);37 return sqlite3ExprAlloc(db, TK_INTEGER, &t, 0);38}39 40static Table* makeTestTable(sqlite3 *db, const char *zColName, int affinity, u32 colFlagsInit, int iDfltInit){41 Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));42 TEST_ASSERT_NOT_NULL_MESSAGE(pTab, "Failed to allocate Table");43 pTab->nCol = 1;44 pTab->nNVCol = 1; /* initially, 1 non-virtual column */45 pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column));46 TEST_ASSERT_NOT_NULL_MESSAGE(pTab->aCol, "Failed to allocate Column array");47 Column *pCol = &pTab->aCol[0];48 pCol->zCnName = sqlite3DbStrDup(db, zColName);49 TEST_ASSERT_NOT_NULL_MESSAGE(pCol->zCnName, "Failed to allocate column name");50 pCol->affinity = (char)affinity;51 pCol->colFlags = colFlagsInit;52 pCol->iDflt = iDfltInit;53 /* pTab->tabFlags defaults to 0 for ordinary table */54 return pTab;55}56 57static void freeTestTable(sqlite3 *db, Table *pTab){58 if (!pTab) return;59 if (pTab->u.tab.pDfltList){60 sqlite3ExprListDelete(db, pTab->u.tab.pDfltList);61 }62 if (pTab->aCol){63 if (pTab->aCol[0].zCnName){64 sqlite3DbFree(db, pTab->aCol[0].zCnName);65 }66 sqlite3DbFree(db, pTab->aCol);67 }68 sqlite3DbFree(db, pTab);69}70 71static int strContains(const char *hay, const char *needle){72 if (!hay || !needle) return 0;73 return strstr(hay, needle)!=0;74}75 76/* Tests */77 78/* Virtual generated column via explicit "ViRtUaL" token; ID expr is wrapped with TK_UPLUS and affinity applied */79void test_sqlite3AddGenerated_virtual_id_expr_affinity_and_flags(void){80#ifndef SQLITE_OMIT_GENERATED_COLUMNS81 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;82 Table *tab = makeTestTable(gDb, "c1", (int)'T', 0, 0);83 p.pNewTable = tab;84 85 Token tType = makeToken("ViRtUaL"); /* case-insensitive */86 Expr *e = makeIdExpr(gDb, "x");87 sqlite3AddGenerated(&p, e, &tType);88 89 TEST_ASSERT_EQUAL_INT(0, p.nErr);90 TEST_ASSERT_NULL(p.zErrMsg);91 92 Column *c = &tab->aCol[0];93 94 TEST_ASSERT_NOT_NULL(tab->u.tab.pDfltList);95 TEST_ASSERT_EQUAL_INT(1, tab->u.tab.pDfltList->nExpr);96 TEST_ASSERT_EQUAL_INT(1, c->iDflt);97 98 Expr *stored = tab->u.tab.pDfltList->a[0].pExpr;99 TEST_ASSERT_NOT_NULL(stored);100 TEST_ASSERT_EQUAL_INT(TK_UPLUS, stored->op);101 TEST_ASSERT_NOT_NULL(stored->pLeft);102 TEST_ASSERT_EQUAL_INT(TK_ID, stored->pLeft->op);103 TEST_ASSERT_EQUAL_INT((int)c->affinity, (int)stored->affExpr);104 105 TEST_ASSERT_TRUE( (c->colFlags & COLFLAG_VIRTUAL)!=0 );106 TEST_ASSERT_TRUE( (tab->tabFlags & COLFLAG_VIRTUAL)!=0 );107 TEST_ASSERT_EQUAL_INT(0, tab->nNVCol); /* decremented from 1 -> 0 */108 109 freeTestTable(gDb, tab);110 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);111#else112 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");113#endif114}115 116/* Stored generated column; nNVCol unchanged; ID expr wrapped; flags set to stored */117void test_sqlite3AddGenerated_stored_basic(void){118#ifndef SQLITE_OMIT_GENERATED_COLUMNS119 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;120 Table *tab = makeTestTable(gDb, "c1", (int)'S', 0, 0);121 p.pNewTable = tab;122 123 Token tType = makeToken("StoReD"); /* case-insensitive */124 Expr *e = makeIdExpr(gDb, "x");125 sqlite3AddGenerated(&p, e, &tType);126 127 TEST_ASSERT_EQUAL_INT(0, p.nErr);128 TEST_ASSERT_NULL(p.zErrMsg);129 130 Column *c = &tab->aCol[0];131 132 TEST_ASSERT_NOT_NULL(tab->u.tab.pDfltList);133 TEST_ASSERT_EQUAL_INT(1, tab->u.tab.pDfltList->nExpr);134 TEST_ASSERT_EQUAL_INT(1, c->iDflt);135 136 Expr *stored = tab->u.tab.pDfltList->a[0].pExpr;137 TEST_ASSERT_NOT_NULL(stored);138 TEST_ASSERT_EQUAL_INT(TK_UPLUS, stored->op);139 TEST_ASSERT_EQUAL_INT((int)c->affinity, (int)stored->affExpr);140 141 TEST_ASSERT_TRUE( (c->colFlags & COLFLAG_STORED)!=0 );142 TEST_ASSERT_TRUE( (tab->tabFlags & COLFLAG_STORED)!=0 );143 TEST_ASSERT_EQUAL_INT(1, tab->nNVCol); /* unchanged */144 145 freeTestTable(gDb, tab);146 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);147#else148 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");149#endif150}151 152/* Default type (pType=NULL) means virtual; nNVCol decremented */153void test_sqlite3AddGenerated_default_type_is_virtual(void){154#ifndef SQLITE_OMIT_GENERATED_COLUMNS155 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;156 Table *tab = makeTestTable(gDb, "c1", (int)'A', 0, 0);157 p.pNewTable = tab;158 159 Expr *e = makeIdExpr(gDb, "y");160 sqlite3AddGenerated(&p, e, NULL);161 162 Column *c = &tab->aCol[0];163 TEST_ASSERT_EQUAL_INT(0, p.nErr);164 TEST_ASSERT_TRUE( (c->colFlags & COLFLAG_VIRTUAL)!=0 );165 TEST_ASSERT_TRUE( (tab->tabFlags & COLFLAG_VIRTUAL)!=0 );166 TEST_ASSERT_EQUAL_INT(0, tab->nNVCol);167 168 TEST_ASSERT_NOT_NULL(tab->u.tab.pDfltList);169 TEST_ASSERT_EQUAL_INT(1, c->iDflt);170 171 freeTestTable(gDb, tab);172 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);173#else174 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");175#endif176}177 178/* Invalid pType token leads to "error in generated column" and no changes */179void test_sqlite3AddGenerated_invalid_type_token_errors(void){180#ifndef SQLITE_OMIT_GENERATED_COLUMNS181 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;182 Table *tab = makeTestTable(gDb, "c1", (int)'X', 0, 0);183 p.pNewTable = tab;184 185 Token bad = makeToken("unknown_type");186 Expr *e = makeIntExpr(gDb, "123");187 sqlite3AddGenerated(&p, e, &bad);188 189 TEST_ASSERT_TRUE(p.nErr > 0);190 TEST_ASSERT_NOT_NULL(p.zErrMsg);191 TEST_ASSERT_TRUE(strContains(p.zErrMsg, "error in generated column"));192 193 Column *c = &tab->aCol[0];194 TEST_ASSERT_EQUAL_INT(0, c->iDflt);195 TEST_ASSERT_NULL(tab->u.tab.pDfltList);196 TEST_ASSERT_EQUAL_INT(1, tab->nNVCol);197 TEST_ASSERT_TRUE( (c->colFlags & (COLFLAG_VIRTUAL|COLFLAG_STORED)) == 0 );198 TEST_ASSERT_TRUE( (tab->tabFlags & (COLFLAG_VIRTUAL|COLFLAG_STORED)) == 0 );199 200 freeTestTable(gDb, tab);201 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);202#else203 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");204#endif205}206 207/* Generated column marked as PRIMARY KEY -> specific error but expr still attached */208void test_sqlite3AddGenerated_primary_key_generated_errors_but_attaches_expr(void){209#ifndef SQLITE_OMIT_GENERATED_COLUMNS210 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;211 /* Start with PRIMKEY flag set */212 Table *tab = makeTestTable(gDb, "c1", (int)'P', COLFLAG_PRIMKEY, 0);213 p.pNewTable = tab;214 215 Token tType = makeToken("stored");216 Expr *e = makeIntExpr(gDb, "1");217 sqlite3AddGenerated(&p, e, &tType);218 219 TEST_ASSERT_TRUE(p.nErr > 0);220 TEST_ASSERT_NOT_NULL(p.zErrMsg);221 TEST_ASSERT_TRUE(strContains(p.zErrMsg, "cannot be part of the PRIMARY KEY"));222 223 Column *c = &tab->aCol[0];224 TEST_ASSERT_NOT_NULL(tab->u.tab.pDfltList);225 TEST_ASSERT_EQUAL_INT(1, tab->u.tab.pDfltList->nExpr);226 TEST_ASSERT_EQUAL_INT(1, c->iDflt);227 228 TEST_ASSERT_TRUE( (c->colFlags & COLFLAG_STORED)!=0 );229 TEST_ASSERT_TRUE( (tab->tabFlags & COLFLAG_STORED)!=0 );230 TEST_ASSERT_EQUAL_INT(1, tab->nNVCol); /* stored: unchanged */231 232 freeTestTable(gDb, tab);233 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);234#else235 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");236#endif237}238 239/* IN_DECLARE_VTAB -> specific error, no expr attached, no flag changes */240void test_sqlite3AddGenerated_in_declare_vtab_errors_and_no_changes(void){241#ifndef SQLITE_OMIT_GENERATED_COLUMNS242 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb; p.declareVtab = 1;243 Table *tab = makeTestTable(gDb, "c1", (int)'V', 0, 0);244 p.pNewTable = tab;245 246 Token tType = makeToken("virtual");247 Expr *e = makeIdExpr(gDb, "colref");248 sqlite3AddGenerated(&p, e, &tType);249 250 TEST_ASSERT_TRUE(p.nErr > 0);251 TEST_ASSERT_NOT_NULL(p.zErrMsg);252 TEST_ASSERT_TRUE(strContains(p.zErrMsg, "virtual tables cannot use computed columns"));253 254 Column *c = &tab->aCol[0];255 TEST_ASSERT_NULL(tab->u.tab.pDfltList);256 TEST_ASSERT_EQUAL_INT(0, c->iDflt);257 TEST_ASSERT_TRUE( (c->colFlags & (COLFLAG_VIRTUAL|COLFLAG_STORED)) == 0 );258 TEST_ASSERT_TRUE( (tab->tabFlags & (COLFLAG_VIRTUAL|COLFLAG_STORED)) == 0 );259 TEST_ASSERT_EQUAL_INT(1, tab->nNVCol);260 261 freeTestTable(gDb, tab);262 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);263#else264 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");265#endif266}267 268/* pNewTable == NULL -> no error, expr deleted, nothing else to assert */269void test_sqlite3AddGenerated_null_newtable_noop(void){270#ifndef SQLITE_OMIT_GENERATED_COLUMNS271 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;272 p.pNewTable = NULL;273 274 Token tType = makeToken("virtual");275 Expr *e = makeIntExpr(gDb, "7");276 sqlite3AddGenerated(&p, e, &tType);277 278 TEST_ASSERT_EQUAL_INT(0, p.nErr);279 TEST_ASSERT_NULL(p.zErrMsg);280 /* e is freed by sqlite3AddGenerated; nothing else to clean */281 282 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);283#else284 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");285#endif286}287 288/* If column already has a default (iDflt>0), signal error and do not attach expr */289void test_sqlite3AddGenerated_existing_default_conflict_errors(void){290#ifndef SQLITE_OMIT_GENERATED_COLUMNS291 Parse p; memset(&p, 0, sizeof(p)); p.db = gDb;292 /* Pretend column already has a default index; no list attached yet */293 Table *tab = makeTestTable(gDb, "c1", (int)'Q', 0, 2);294 p.pNewTable = tab;295 296 Token tType = makeToken("stored");297 Expr *e = makeIntExpr(gDb, "99");298 sqlite3AddGenerated(&p, e, &tType);299 300 TEST_ASSERT_TRUE(p.nErr > 0);301 TEST_ASSERT_NOT_NULL(p.zErrMsg);302 TEST_ASSERT_TRUE(strContains(p.zErrMsg, "error in generated column"));303 304 Column *c = &tab->aCol[0];305 TEST_ASSERT_NULL(tab->u.tab.pDfltList);306 TEST_ASSERT_EQUAL_INT(2, c->iDflt); /* unchanged */307 TEST_ASSERT_EQUAL_INT(1, tab->nNVCol); /* unchanged */308 TEST_ASSERT_TRUE( (c->colFlags & (COLFLAG_VIRTUAL|COLFLAG_STORED)) == 0 );309 310 freeTestTable(gDb, tab);311 if (p.zErrMsg) sqlite3DbFree(gDb, p.zErrMsg);312#else313 TEST_IGNORE_MESSAGE("Built with SQLITE_OMIT_GENERATED_COLUMNS");314#endif315}316 317int main(void) {318 UNITY_BEGIN();319 RUN_TEST(test_sqlite3AddGenerated_virtual_id_expr_affinity_and_flags);320 RUN_TEST(test_sqlite3AddGenerated_stored_basic);321 RUN_TEST(test_sqlite3AddGenerated_default_type_is_virtual);322 RUN_TEST(test_sqlite3AddGenerated_invalid_type_token_errors);323 RUN_TEST(test_sqlite3AddGenerated_primary_key_generated_errors_but_attaches_expr);324 RUN_TEST(test_sqlite3AddGenerated_in_declare_vtab_errors_and_no_changes);325 RUN_TEST(test_sqlite3AddGenerated_null_newtable_noop);326 RUN_TEST(test_sqlite3AddGenerated_existing_default_conflict_errors);327 return UNITY_END();328}