Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_alter_sqlite3AlterFinishAddColumn.c222 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6/* Global db handle for tests */7static sqlite3 *gDb = NULL;8 9/* Helpers to create minimal Parse/pNewTable environment needed by sqlite3AlterFinishAddColumn */10 11static void init_db_and_base_table(const char *zCreate){12  int rc;13  rc = sqlite3_open(":memory:", &gDb);14  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);15  rc = sqlite3_exec(gDb, zCreate, 0, 0, 0);16  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);17}18 19/* Allocate a minimal Table used as pParse->pNewTable for ALTER ADD COLUMN. */20static Table *allocNewAlterTable(sqlite3 *db, const char *zTabName){21  Table *pNew = (Table*)sqlite3DbMallocZero(db, sizeof(Table));22  TEST_ASSERT_NOT_NULL(pNew);23  /* Ordinary table with schema = main */24  pNew->pSchema = db->aDb[0].pSchema;25  /* pNew->zName must be "sqlite_altertab_<zTabName>" as code expects to skip first 16 chars */26  pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", zTabName);27  TEST_ASSERT_NOT_NULL(pNew->zName);28  /* Single column which is the new column */29  pNew->nCol = 1;30  pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column));31  TEST_ASSERT_NOT_NULL(pNew->aCol);32  /* Make sure this is treated as ordinary table */33  pNew->tabFlags = 0; /* IsOrdinaryTable() should pass */34  /* Some reasonable addColOffset value used by printf() in nested parse */35  pNew->u.tab.addColOffset = 1;36  return pNew;37}38 39/* Construct a TK_SPAN Expr whose pLeft has the given op (e.g., TK_NULL, TK_INTEGER, TK_FUNCTION) */40static Expr *makeSpanExprWithLeft(sqlite3 *db, int leftOp){41  Expr *pLeft = (Expr*)sqlite3DbMallocZero(db, sizeof(Expr));42  TEST_ASSERT_NOT_NULL(pLeft);43  pLeft->op = (u8)leftOp;44  Expr *pSpan = (Expr*)sqlite3DbMallocZero(db, sizeof(Expr));45  TEST_ASSERT_NOT_NULL(pSpan);46  pSpan->op = TK_SPAN;47  pSpan->pLeft = pLeft;48  return pSpan;49}50 51/* Initialize a Parse object with the given pNewTable and coldef Token. */52static void initParse(Parse *pParse, sqlite3 *db, Table *pNew, const char *zColDef, Token *pTokOut){53  memset(pParse, 0, sizeof(*pParse));54  pParse->db = db;55  db->pParse = pParse; /* Required by sqlite3AlterFinishAddColumn asserts */56  pParse->pNewTable = pNew;57  /* Token for column definition text */58  pTokOut->z = (const unsigned char*)zColDef;59  pTokOut->n = (int)strlen(zColDef);60  pTokOut->dyn = 0;61}62 63/* Cleanup minimal allocations done for pNewTable */64static void freeNewAlterTable(sqlite3 *db, Table *pNew){65  if( pNew ){66    if( pNew->aCol ){67      /* free any default expressions we might have attached */68      if( pNew->aCol[0].pDflt ){69        sqlite3ExprDelete(db, pNew->aCol[0].pDflt);70        pNew->aCol[0].pDflt = 0;71      }72      sqlite3DbFree(db, pNew->aCol);73    }74    if( pNew->zName ) sqlite3DbFree(db, pNew->zName);75    sqlite3DbFree(db, pNew);76  }77}78 79void setUp(void) {80  /* fresh in-memory db with base table */81  init_db_and_base_table("CREATE TABLE t1(a)");82}83 84void tearDown(void) {85  if( gDb ){86    sqlite3_close(gDb);87    gDb = NULL;88  }89}90 91/* Test: PRIMARY KEY on new column is rejected immediately with error message */92void test_sqlite3AlterFinishAddColumn_rejects_primary_key(void){93  Table *pNew = allocNewAlterTable(gDb, "t1");94  Column *pCol = &pNew->aCol[0];95  pCol->colFlags |= COLFLAG_PRIMKEY;96 97  Parse sParse;98  Token sTok;99  initParse(&sParse, gDb, pNew, "x INTEGER PRIMARY KEY", &sTok);100 101  sqlite3AlterFinishAddColumn(&sParse, &sTok);102 103  TEST_ASSERT_GREATER_THAN(0, sParse.nErr);104  TEST_ASSERT_NOT_NULL(sParse.zErrMsg);105  TEST_ASSERT_EQUAL_INT(0, strstr(sParse.zErrMsg, "Cannot add a PRIMARY KEY column")==NULL);106 107  freeNewAlterTable(gDb, pNew);108}109 110/* Test: UNIQUE constraint on new column is rejected immediately with error message */111void test_sqlite3AlterFinishAddColumn_rejects_unique(void){112  Table *pNew = allocNewAlterTable(gDb, "t1");113  Column *pCol = &pNew->aCol[0];114  pCol->colFlags &= ~COLFLAG_PRIMKEY; /* ensure not primary key */115  /* Any non-NULL pIndex indicates UNIQUE column in this code-path */116  pNew->pIndex = (Index*)pNew; /* dummy non-NULL */117 118  Parse sParse;119  Token sTok;120  initParse(&sParse, gDb, pNew, "x INTEGER UNIQUE", &sTok);121 122  sqlite3AlterFinishAddColumn(&sParse, &sTok);123 124  TEST_ASSERT_GREATER_THAN(0, sParse.nErr);125  TEST_ASSERT_NOT_NULL(sParse.zErrMsg);126  TEST_ASSERT_EQUAL_INT(0, strstr(sParse.zErrMsg, "Cannot add a UNIQUE column")==NULL);127 128  /* reset to avoid dangling pIndex into freed memory */129  pNew->pIndex = 0;130  freeNewAlterTable(gDb, pNew);131}132 133/* Test: NOT NULL with literal NULL default schedules runtime error (no immediate error, VDBE generated) */134void test_sqlite3AlterFinishAddColumn_notnull_with_null_default_generates_runtime_check(void){135  Table *pNew = allocNewAlterTable(gDb, "t1");136  Column *pCol = &pNew->aCol[0];137  pCol->notNull = 1; /* any non-zero triggers the check */138  /* Default is literal NULL: represented as TK_SPAN with pLeft->op == TK_NULL */139  pCol->pDflt = makeSpanExprWithLeft(gDb, TK_NULL);140 141  Parse sParse;142  Token sTok;143  initParse(&sParse, gDb, pNew, "x INTEGER NOT NULL DEFAULT NULL", &sTok);144 145  sqlite3AlterFinishAddColumn(&sParse, &sTok);146 147  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);148  TEST_ASSERT_NOT_NULL(sParse.pVdbe); /* VDBE should be created due to nested parse and follow-up code */149 150  freeNewAlterTable(gDb, pNew);151}152 153/* Test: STORED generated column is rejected via runtime check path (no immediate error, VDBE generated) */154void test_sqlite3AlterFinishAddColumn_stored_generated_column_rejected_runtime(void){155  Table *pNew = allocNewAlterTable(gDb, "t1");156  Column *pCol = &pNew->aCol[0];157  pCol->colFlags |= (COLFLAG_GENERATED | COLFLAG_STORED);158 159  Parse sParse;160  Token sTok;161  initParse(&sParse, gDb, pNew, "x GENERATED ALWAYS AS (a+1) STORED", &sTok);162 163  sqlite3AlterFinishAddColumn(&sParse, &sTok);164 165  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);166  TEST_ASSERT_NOT_NULL(sParse.pVdbe);167 168  freeNewAlterTable(gDb, pNew);169}170 171/* Test: Constant default value allowed (no immediate error, VDBE generated) */172void test_sqlite3AlterFinishAddColumn_constant_default_allowed(void){173  Table *pNew = allocNewAlterTable(gDb, "t1");174  Column *pCol = &pNew->aCol[0];175  /* Not generated column */176  pCol->colFlags &= ~COLFLAG_GENERATED;177  /* A simple constant default: represent as TK_SPAN with left TK_INTEGER */178  pCol->pDflt = makeSpanExprWithLeft(gDb, TK_INTEGER);179 180  Parse sParse;181  Token sTok;182  initParse(&sParse, gDb, pNew, "x INTEGER DEFAULT 5", &sTok);183 184  sqlite3AlterFinishAddColumn(&sParse, &sTok);185 186  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);187  TEST_ASSERT_NOT_NULL(sParse.pVdbe);188 189  freeNewAlterTable(gDb, pNew);190}191 192/* Test: Non-constant default leads to runtime check (no immediate error, VDBE generated) */193void test_sqlite3AlterFinishAddColumn_nonconstant_default_generates_runtime_check(void){194  Table *pNew = allocNewAlterTable(gDb, "t1");195  Column *pCol = &pNew->aCol[0];196  /* Not generated */197  pCol->colFlags &= ~COLFLAG_GENERATED;198  /* Non-constant default: represent as TK_SPAN with left TK_FUNCTION (e.g., CURRENT_TIME) */199  pCol->pDflt = makeSpanExprWithLeft(gDb, TK_FUNCTION);200 201  Parse sParse;202  Token sTok;203  initParse(&sParse, gDb, pNew, "x TEXT DEFAULT (CURRENT_TIME)", &sTok);204 205  sqlite3AlterFinishAddColumn(&sParse, &sTok);206 207  TEST_ASSERT_EQUAL_INT(0, sParse.nErr);208  TEST_ASSERT_NOT_NULL(sParse.pVdbe);209 210  freeNewAlterTable(gDb, pNew);211}212 213int main(void){214  UNITY_BEGIN();215  RUN_TEST(test_sqlite3AlterFinishAddColumn_rejects_primary_key);216  RUN_TEST(test_sqlite3AlterFinishAddColumn_rejects_unique);217  RUN_TEST(test_sqlite3AlterFinishAddColumn_notnull_with_null_default_generates_runtime_check);218  RUN_TEST(test_sqlite3AlterFinishAddColumn_stored_generated_column_rejected_runtime);219  RUN_TEST(test_sqlite3AlterFinishAddColumn_constant_default_allowed);220  RUN_TEST(test_sqlite3AlterFinishAddColumn_nonconstant_default_generates_runtime_check);221  return UNITY_END();222}