Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_insert_sqlite3ComputeGeneratedColumns.c228 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Globals used by tests */7static sqlite3 *gDb = NULL;8 9/* Unity setup/teardown */10void setUp(void) {11  /* Ensure no leftover db */12  if( gDb ){13    sqlite3_close(gDb);14    gDb = NULL;15  }16  TEST_ASSERT_EQUAL(SQLITE_OK, sqlite3_open(":memory:", &gDb));17}18 19void tearDown(void) {20  if( gDb ){21    sqlite3_close(gDb);22    gDb = NULL;23  }24}25 26/* Helpers */27 28/* Find the first VdbeOp with a given opcode. Returns NULL if not found. */29static VdbeOp *findFirstOpByOpcode(Vdbe *v, int opcode){30  if( v==0 || v->nOp<=0 ) return NULL;31  for(int i=0; i<v->nOp; i++){32    if( v->aOp[i].opcode==opcode ) return &v->aOp[i];33  }34  return NULL;35}36 37/* Find the last VdbeOp with a given opcode. Returns NULL if not found. */38static VdbeOp *findLastOpByOpcode(Vdbe *v, int opcode){39  if( v==0 || v->nOp<=0 ) return NULL;40  for(int i=v->nOp-1; i>=0; i--){41    if( v->aOp[i].opcode==opcode ) return &v->aOp[i];42  }43  return NULL;44}45 46/* Build the expected affinity string after sqlite3ComputeGeneratedColumns modifies47** the OP_Affinity for stored generated columns.48** The input is the table's current zColAff (baseline). We copy it into outBuf and49** then, scanning columns left-to-right, replace characters at positions corresponding50** to non-virtual stored generated columns with '@'. We only apply replacements while51** jj < baseLen, matching the target function's loop that stops at the end of zP4.52*/53static void buildExpectedAdjustedAffinityForStored(Table *pTab, char *outBuf, int outBufSize, int *pOutLen){54  TEST_ASSERT_NOT_NULL(pTab);55  TEST_ASSERT_NOT_NULL(pTab->zColAff);56  int baseLen = sqlite3Strlen30NN(pTab->zColAff);57  TEST_ASSERT_TRUE_MESSAGE(baseLen < outBufSize, "Output buffer too small for affinity string");58  memcpy(outBuf, pTab->zColAff, baseLen);59  outBuf[baseLen] = 0;60  int jj = 0;61  for(int ii=0; ii<pTab->nCol && jj<baseLen; ii++){62    Column *pCol = &pTab->aCol[ii];63    if( (pCol->colFlags & COLFLAG_VIRTUAL)!=0 ){64      continue;65    }66    if( (pCol->colFlags & COLFLAG_STORED)!=0 ){67      outBuf[jj] = SQLITE_AFF_NONE; /* '@' */68    }69    jj++;70  }71  if( pOutLen ) *pOutLen = baseLen;72}73 74/* Verify that no generated column retains COLFLAG_NOTAVAIL after the call */75static void assertGeneratedColsAvailable(Table *pTab){76  for(int i=0; i<pTab->nCol; i++){77    Column *pCol = &pTab->aCol[i];78    if( (pCol->colFlags & COLFLAG_GENERATED)!=0 ){79      TEST_ASSERT_BITS_LOW(COLFLAG_NOTAVAIL, pCol->colFlags);80    }81  }82}83 84/* Test 1: Non-STRICT table with both stored and virtual generated columns.85** Expect OP_Affinity P4 string to have '@' for stored generated columns.86** Also ensure pParse->iSelfTab reset and COLFLAG_NOTAVAIL cleared.87*/88void test_sqlite3ComputeGeneratedColumns_affinity_adjusts_for_stored(void){89  const char *sql =90    "CREATE TABLE t1(\n"91    "  a TEXT,\n"92    "  b INT GENERATED ALWAYS AS (length(a)) STORED,\n"93    "  c TEXT GENERATED ALWAYS AS (a || 'x') VIRTUAL\n"94    ");";95  char *zErr = 0;96  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_exec(gDb, sql, 0, 0, &zErr));97  if( zErr ){ sqlite3_free(zErr); zErr = 0; }98 99  Table *pTab = sqlite3FindTable(gDb, "t1", "main");100  TEST_ASSERT_NOT_NULL(pTab);101  TEST_ASSERT_BITS_HIGH(TF_HasGenerated, pTab->tabFlags);102  TEST_ASSERT_BITS_LOW(TF_Strict, pTab->tabFlags); /* Non-STRICT */103 104  Parse sParse;105  memset(&sParse, 0, sizeof(sParse));106  sParse.db = gDb;107  sParse.pVdbe = sqlite3VdbeCreate(gDb);108  TEST_ASSERT_NOT_NULL(sParse.pVdbe);109 110  /* iRegStore = 1: pretend to load starting at register 1 */111  sqlite3ComputeGeneratedColumns(&sParse, 1, pTab);112 113  /* There should be an OP_Affinity whose P4 string is adjusted */114  VdbeOp *pAff = findFirstOpByOpcode(sParse.pVdbe, OP_Affinity);115  TEST_ASSERT_NOT_NULL_MESSAGE(pAff, "OP_Affinity not found");116  TEST_ASSERT_NOT_NULL(pTab->zColAff);117 118  char expected[64];119  int expectedLen = 0;120  buildExpectedAdjustedAffinityForStored(pTab, expected, (int)sizeof(expected), &expectedLen);121 122  TEST_ASSERT_GREATER_THAN_INT(0, expectedLen);123  TEST_ASSERT_EQUAL_INT(expectedLen, pAff->p2); /* p2 holds number of affinity chars */124  TEST_ASSERT_NOT_NULL(pAff->p4.z);125 126  /* Compare exactly p2 bytes */127  TEST_ASSERT_EQUAL_INT(0, memcmp(expected, pAff->p4.z, pAff->p2));128 129  /* Ensure iSelfTab restored */130  TEST_ASSERT_EQUAL_INT(0, sParse.iSelfTab);131 132  /* Ensure generated columns are no longer marked NOTAVAIL */133  assertGeneratedColsAvailable(pTab);134 135  sqlite3VdbeDelete(sParse.pVdbe);136}137 138/* Test 2: STRICT table with a stored generated column.139** Expect OP_TypeCheck with p3 set to 1 (skip checking generated columns).140*/141void test_sqlite3ComputeGeneratedColumns_strict_sets_typecheck_p3_for_stored(void){142  const char *sql =143    "CREATE TABLE t2(\n"144    "  a TEXT,\n"145    "  b INT GENERATED ALWAYS AS (length(a)) STORED\n"146    ") STRICT;";147  char *zErr = 0;148  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_exec(gDb, sql, 0, 0, &zErr));149  if( zErr ){ sqlite3_free(zErr); zErr = 0; }150 151  Table *pTab = sqlite3FindTable(gDb, "t2", "main");152  TEST_ASSERT_NOT_NULL(pTab);153  TEST_ASSERT_BITS_HIGH(TF_HasGenerated, pTab->tabFlags);154  TEST_ASSERT_BITS_HIGH(TF_Strict, pTab->tabFlags);155 156  Parse sParse;157  memset(&sParse, 0, sizeof(sParse));158  sParse.db = gDb;159  sParse.pVdbe = sqlite3VdbeCreate(gDb);160  TEST_ASSERT_NOT_NULL(sParse.pVdbe);161 162  sqlite3ComputeGeneratedColumns(&sParse, 3, pTab); /* arbitrary iRegStore */163 164  /* In STRICT mode, sqlite3TableAffinity emits OP_TypeCheck and we expect p3==1 */165  VdbeOp *pTC = findFirstOpByOpcode(sParse.pVdbe, OP_TypeCheck);166  TEST_ASSERT_NOT_NULL_MESSAGE(pTC, "OP_TypeCheck not found");167  TEST_ASSERT_EQUAL_INT(1, pTC->p3); /* Generated columns should not be type-checked */168 169  TEST_ASSERT_EQUAL_INT(0, sParse.iSelfTab);170  assertGeneratedColsAvailable(pTab);171 172  sqlite3VdbeDelete(sParse.pVdbe);173}174 175/* Test 3: STRICT table with only virtual generated columns.176** Expect OP_TypeCheck present but p3 remains 0 (nothing to skip).177*/178void test_sqlite3ComputeGeneratedColumns_strict_virtual_only_leaves_typecheck_p3_zero(void){179  const char *sql =180    "CREATE TABLE t3(\n"181    "  a TEXT,\n"182    "  c TEXT GENERATED ALWAYS AS (a || 'x') VIRTUAL\n"183    ") STRICT;";184  char *zErr = 0;185  TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_exec(gDb, sql, 0, 0, &zErr));186  if( zErr ){ sqlite3_free(zErr); zErr = 0; }187 188  Table *pTab = sqlite3FindTable(gDb, "t3", "main");189  TEST_ASSERT_NOT_NULL(pTab);190  TEST_ASSERT_BITS_HIGH(TF_HasGenerated, pTab->tabFlags);191  TEST_ASSERT_BITS_HIGH(TF_Strict, pTab->tabFlags);192 193  /* Sanity: Ensure there are no stored generated columns */194  int hasStored = 0;195  for(int i=0; i<pTab->nCol; i++){196    if( (pTab->aCol[i].colFlags & COLFLAG_STORED)!=0 ){197      hasStored = 1;198      break;199    }200  }201  TEST_ASSERT_EQUAL_INT(0, hasStored);202 203  Parse sParse;204  memset(&sParse, 0, sizeof(sParse));205  sParse.db = gDb;206  sParse.pVdbe = sqlite3VdbeCreate(gDb);207  TEST_ASSERT_NOT_NULL(sParse.pVdbe);208 209  sqlite3ComputeGeneratedColumns(&sParse, 5, pTab);210 211  VdbeOp *pTC = findFirstOpByOpcode(sParse.pVdbe, OP_TypeCheck);212  TEST_ASSERT_NOT_NULL_MESSAGE(pTC, "OP_TypeCheck not found");213  TEST_ASSERT_EQUAL_INT(0, pTC->p3); /* Nothing to skip: no stored generated columns */214 215  TEST_ASSERT_EQUAL_INT(0, sParse.iSelfTab);216  assertGeneratedColsAvailable(pTab);217 218  sqlite3VdbeDelete(sParse.pVdbe);219}220 221/* Main */222int main(void){223  UNITY_BEGIN();224  RUN_TEST(test_sqlite3ComputeGeneratedColumns_affinity_adjusts_for_stored);225  RUN_TEST(test_sqlite3ComputeGeneratedColumns_strict_sets_typecheck_p3_for_stored);226  RUN_TEST(test_sqlite3ComputeGeneratedColumns_strict_virtual_only_leaves_typecheck_p3_zero);227  return UNITY_END();228}