Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_build_convertToWithoutRowidTable.c260 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Wrapper for the static function provided by the build */7extern void test_convertToWithoutRowidTable(Parse *pParse, Table *pTab);8 9/* Helpers */10static sqlite3* open_db(void){11  sqlite3 *db = 0;12  int rc = sqlite3_open(":memory:", &db);13  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);14  TEST_ASSERT_NOT_NULL(db);15  return db;16}17 18static Table* alloc_table(sqlite3 *db, const char *zName, int nCol){19  Table *pTab = (Table*)sqlite3DbMallocZero(db, sizeof(Table));20  TEST_ASSERT_NOT_NULL(pTab);21  pTab->zName = sqlite3DbStrDup(db, zName);22  pTab->pSchema = db->aDb[0].pSchema;23  pTab->nCol = nCol;24  pTab->nNVCol = nCol; /* will adjust if virtuals present */25  pTab->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nCol);26  TEST_ASSERT_NOT_NULL(pTab->aCol);27  for(int i=0; i<nCol; i++){28    char namebuf[16];29    sqlite3_snprintf(sizeof(namebuf), namebuf, "c%d", i);30    pTab->aCol[i].zCnName = sqlite3DbStrDup(db, namebuf);31    pTab->aCol[i].notNull = OE_None;32    pTab->aCol[i].szEst = 1; /* small default */33  }34  return pTab;35}36 37static Index* alloc_index(sqlite3 *db, Table *pTab, int nCol){38  Index *pIdx = (Index*)sqlite3DbMallocZero(db, sizeof(Index));39  TEST_ASSERT_NOT_NULL(pIdx);40  pIdx->pTable = pTab;41  pIdx->nColumn = (u16)nCol;42  pIdx->azColl = (const char**)sqlite3DbMallocZero(db, sizeof(char*)*nCol);43  pIdx->aiColumn = (i16*)sqlite3DbMallocZero(db, sizeof(i16)*nCol);44  pIdx->aSortOrder = (u8*)sqlite3DbMallocZero(db, nCol);45  pIdx->aiRowLogEst = (LogEst*)sqlite3DbMallocZero(db, sizeof(LogEst)*nCol);46  TEST_ASSERT_NOT_NULL(pIdx->azColl);47  TEST_ASSERT_NOT_NULL(pIdx->aiColumn);48  TEST_ASSERT_NOT_NULL(pIdx->aSortOrder);49  TEST_ASSERT_NOT_NULL(pIdx->aiRowLogEst);50  return pIdx;51}52 53static void set_pk(Index *pPk, int nKeyCol, const int *cols, const u8 *sort){54  pPk->idxType = SQLITE_IDXTYPE_PRIMARYKEY;55  pPk->nKeyCol = nKeyCol;56  for(int i=0;i<nKeyCol;i++){57    pPk->aiColumn[i] = (i16)cols[i];58    pPk->azColl[i] = sqlite3StrBINARY;59    pPk->aSortOrder[i] = sort ? sort[i] : 0;60  }61}62 63void setUp(void) {64  /* empty */65}66 67void tearDown(void) {68  /* empty */69}70 71/* Test 1: NOT NULL enforcement, PK flags, covering PK, and tnum assignment */72void test_convertToWithoutRowidTable_basic_notnull_and_covering(void){73  sqlite3 *db = open_db();74  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = db;75 76  Table *pTab = alloc_table(db, "t", 3);77  pTab->tabFlags |= TF_WithoutRowid; /* already determined by parser */78  pTab->tnum = 7; /* table root-page */79 80  /* Mark columns 0 and 2 as part of PRIMARY KEY */81  pTab->aCol[0].colFlags |= COLFLAG_PRIMKEY;82  pTab->aCol[2].colFlags |= COLFLAG_PRIMKEY;83 84  /* Create PRIMARY KEY index with key columns (0,2) */85  Index *pPk = alloc_index(db, pTab, 2);86  int pkcols[2] = {0,2};87  set_pk(pPk, 2, pkcols, NULL);88  pTab->pIndex = pPk;89 90  /* Pre-conditions */91  TEST_ASSERT_EQUAL_INT(OE_None, pTab->aCol[0].notNull);92  TEST_ASSERT_EQUAL_INT(OE_None, pTab->aCol[2].notNull);93  TEST_ASSERT_TRUE((pTab->tabFlags & TF_HasNotNull)==0);94  TEST_ASSERT_NOT_NULL(sqlite3StrBINARY);95 96  test_convertToWithoutRowidTable(&parse, pTab);97 98  /* NOT NULL enforced for PK columns and TF_HasNotNull set */99  TEST_ASSERT_EQUAL_INT(OE_Abort, pTab->aCol[0].notNull);100  TEST_ASSERT_EQUAL_INT(OE_Abort, pTab->aCol[2].notNull);101  TEST_ASSERT_TRUE((pTab->tabFlags & TF_HasNotNull)!=0);102 103  /* PK flags */104  TEST_ASSERT_EQUAL_INT(1, pTab->pIndex->isCovering);105  TEST_ASSERT_EQUAL_INT(1, pTab->pIndex->uniqNotNull);106 107  /* PK rootpage becomes table rootpage */108  TEST_ASSERT_EQUAL_INT(pTab->tnum, pTab->pIndex->tnum);109 110  /* PK should be covering: includes all non-virtual columns */111  TEST_ASSERT_EQUAL_INT(3, pTab->pIndex->nColumn);112  TEST_ASSERT_EQUAL_INT(1, pTab->pIndex->aiColumn[2]);113  TEST_ASSERT_EQUAL_PTR(sqlite3StrBINARY, pTab->pIndex->azColl[2]);114 115  /* Verify colNotIdxed indicates column 1 is covered (bit cleared) */116  Bitmask mask1 = MASKBIT(1);117  TEST_ASSERT_EQUAL_UINT64(0, (pTab->pIndex->colNotIdxed & mask1));118}119 120/* Test 2: Deduplicate repeated columns in PRIMARY KEY */121void test_convertToWithoutRowidTable_deduplicates_pk_columns(void){122  sqlite3 *db = open_db();123  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = db;124 125  Table *pTab = alloc_table(db, "tdup", 3);126  pTab->tabFlags |= TF_WithoutRowid;127  pTab->tnum = 5;128  pTab->aCol[0].colFlags |= COLFLAG_PRIMKEY;129  pTab->aCol[1].colFlags |= COLFLAG_PRIMKEY;130  pTab->aCol[2].colFlags |= COLFLAG_PRIMKEY;131 132  /* PK index with duplicates: (0,1,0,1,2) */133  Index *pPk = alloc_index(db, pTab, 5);134  int cols[5] = {0,1,0,1,2};135  set_pk(pPk, 5, cols, NULL);136  pTab->pIndex = pPk;137 138  test_convertToWithoutRowidTable(&parse, pTab);139 140  /* PK columns deduplicated to (0,1,2) */141  TEST_ASSERT_EQUAL_INT(3, pTab->pIndex->nKeyCol);142  TEST_ASSERT_EQUAL_INT(0, pTab->pIndex->aiColumn[0]);143  TEST_ASSERT_EQUAL_INT(1, pTab->pIndex->aiColumn[1]);144  TEST_ASSERT_EQUAL_INT(2, pTab->pIndex->aiColumn[2]);145 146  /* Still covering all non-virtual columns after append */147  TEST_ASSERT_TRUE(pTab->pIndex->nColumn >= pTab->nNVCol);148}149 150/* Test 3: Replace rowid tail of other indexes with PRIMARY KEY columns */151void test_convertToWithoutRowidTable_updates_other_index_tails(void){152  sqlite3 *db = open_db();153  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = db;154 155  Table *pTab = alloc_table(db, "tix", 3);156  pTab->tabFlags |= TF_WithoutRowid;157  pTab->tnum = 9;158 159  /* PK on columns (0,2), with second column DESC to trigger bAscKeyBug */160  pTab->aCol[0].colFlags |= COLFLAG_PRIMKEY;161  pTab->aCol[2].colFlags |= COLFLAG_PRIMKEY;162  Index *pPk = alloc_index(db, pTab, 2);163  int pkcols[2] = {0,2};164  u8 sort[2] = {0,1};165  set_pk(pPk, 2, pkcols, sort);166 167  /* Another index on column 1, with implicit rowid tail */168  Index *pIdx = alloc_index(db, pTab, 2);169  pIdx->nKeyCol = 1;170  pIdx->aiColumn[0] = 1;171  pIdx->azColl[0] = sqlite3StrBINARY;172  pIdx->aSortOrder[0] = 0;173  pIdx->aiColumn[1] = XN_ROWID;174  pIdx->azColl[1] = sqlite3StrBINARY;175 176  /* Chain indexes: head at PK so IsPrimaryKeyIndex is encountered */177  pTab->pIndex = pPk;178  pPk->pNext = pIdx;179 180  test_convertToWithoutRowidTable(&parse, pTab);181 182  /* The other index tail should be replaced by PK cols (0,2) */183  TEST_ASSERT_EQUAL_INT(3, pIdx->nColumn);184  TEST_ASSERT_EQUAL_INT(1, pIdx->aiColumn[0]);185  TEST_ASSERT_EQUAL_INT(0, pIdx->aiColumn[1]);186  TEST_ASSERT_EQUAL_INT(2, pIdx->aiColumn[2]);187  TEST_ASSERT_EQUAL_PTR(pPk->azColl[0], pIdx->azColl[1]);188  TEST_ASSERT_EQUAL_PTR(pPk->azColl[1], pIdx->azColl[2]);189  /* bAscKeyBug set because PK column 2 had DESC */190  TEST_ASSERT_EQUAL_INT(1, pIdx->bAscKeyBug);191}192 193/* Test 4: VDBE opcode patches - CreateBtree P3 and Noop to Goto */194void test_convertToWithoutRowidTable_vdbe_patching(void){195  sqlite3 *db = open_db();196  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = db;197 198  Table *pTab = alloc_table(db, "tvdb", 2);199  pTab->tabFlags |= TF_WithoutRowid;200  pTab->aCol[0].colFlags |= COLFLAG_PRIMKEY;201  Index *pPk = alloc_index(db, pTab, 1);202  int pkc[1] = {0};203  set_pk(pPk, 1, pkc, NULL);204  pTab->pIndex = pPk;205 206  Vdbe *v = sqlite3GetVdbe(&parse);207  TEST_ASSERT_NOT_NULL(v);208  int addrNoop = sqlite3VdbeAddOp0(v, OP_Noop);209  int addrCr = sqlite3VdbeAddOp3(v, OP_CreateBtree, 0, 1, BTREE_INTKEY);210  /* Wire up parse state so convertToWithoutRowidTable can patch these */211  parse.u1.cr.addrCrTab = addrCr;212  pPk->tnum = (Pgno)addrNoop; /* convertToWithoutRowidTable will change opcode here */213 214  test_convertToWithoutRowidTable(&parse, pTab);215 216  const VdbeOp *opCr = sqlite3VdbeGetOp(v, addrCr);217  TEST_ASSERT_NOT_NULL(opCr);218  TEST_ASSERT_EQUAL_INT(OP_CreateBtree, opCr->opcode);219  TEST_ASSERT_EQUAL_INT(BTREE_BLOBKEY, opCr->p3);220 221  const VdbeOp *opNoop = sqlite3VdbeGetOp(v, addrNoop);222  TEST_ASSERT_NOT_NULL(opNoop);223  TEST_ASSERT_EQUAL_INT(OP_Goto, opNoop->opcode);224}225 226/* Test 5: imposterTable skips NOT NULL and uniqNotNull adjustments */227void test_convertToWithoutRowidTable_imposter_skips_notnull_and_uniq(void){228  sqlite3 *db = open_db();229  db->init.imposterTable = 1; /* signal imposter */230  Parse parse; memset(&parse, 0, sizeof(parse)); parse.db = db;231 232  Table *pTab = alloc_table(db, "timp", 2);233  pTab->tabFlags |= TF_WithoutRowid;234  pTab->aCol[0].colFlags |= COLFLAG_PRIMKEY;235  pTab->aCol[1].colFlags |= COLFLAG_PRIMKEY;236 237  Index *pPk = alloc_index(db, pTab, 2);238  int pkc[2] = {0,1};239  set_pk(pPk, 2, pkc, NULL);240  pPk->uniqNotNull = 0; /* pre-set */241  pTab->pIndex = pPk;242 243  test_convertToWithoutRowidTable(&parse, pTab);244 245  /* NOT NULL not forced for imposter table */246  TEST_ASSERT_EQUAL_INT(OE_None, pTab->aCol[0].notNull);247  TEST_ASSERT_EQUAL_INT(OE_None, pTab->aCol[1].notNull);248  /* uniqNotNull not forced to 1 */249  TEST_ASSERT_EQUAL_INT(0, pPk->uniqNotNull);250}251 252int main(void){253  UNITY_BEGIN();254  RUN_TEST(test_convertToWithoutRowidTable_basic_notnull_and_covering);255  RUN_TEST(test_convertToWithoutRowidTable_deduplicates_pk_columns);256  RUN_TEST(test_convertToWithoutRowidTable_updates_other_index_tails);257  RUN_TEST(test_convertToWithoutRowidTable_vdbe_patching);258  RUN_TEST(test_convertToWithoutRowidTable_imposter_skips_notnull_and_uniq);259  return UNITY_END();260}