Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_btree_modifyPagePointer.c283 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3 4#include <string.h>5#include <stdlib.h>6#include <stdio.h>7 8/* Wrapper for the static function under test (provided by source module) */9extern int test_modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType);10 11static sqlite3 *gDb = NULL;12 13/* Helpers */14static void open_inmemory_db(void){15  int rc = sqlite3_open(":memory:", &gDb);16  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);17}18 19static void close_db(void){20  if( gDb ){21    sqlite3_close(gDb);22    gDb = NULL;23  }24}25 26/* Create a raw table btree using the Btree API and return root pgno */27static Pgno create_raw_table(Btree *pBt){28  int rc;29  Pgno root = 0;30  rc = sqlite3BtreeBeginTrans(pBt, 1, 0);31  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);32  rc = sqlite3BtreeCreateTable(pBt, &root, BTREE_INTKEY|BTREE_LEAFDATA);33  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);34  TEST_ASSERT_TRUE(root>1);35  return root;36}37 38/* Insert a large payload row to force overflow pages */39static void insert_big_row(Btree *pBt, Pgno root, i64 rowid, int nData){40  int rc;41  BtCursor cur;42  memset(&cur, 0, sizeof(cur));43  rc = sqlite3BtreeCursor(pBt, root, BTREE_WRCSR, 0, &cur);44  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);45 46  void *buf = sqlite3_malloc(nData);47  TEST_ASSERT_NOT_NULL(buf);48  memset(buf, 0xAB, nData);49 50  BtreePayload X;51  memset(&X, 0, sizeof(X));52  X.pKey = NULL;53  X.nKey = rowid;54  X.pData = buf;55  X.nData = nData;56  X.nZero = 0;57 58  rc = sqlite3BtreeInsert(&cur, &X, 0, 0);59  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);60 61  sqlite3_free(buf);62  sqlite3BtreeCloseCursor(&cur);63}64 65/* Position cursor at rowid (table btree), return cursor (open) */66static void open_cursor_at_row(Btree *pBt, Pgno root, i64 rowid, BtCursor *pCurOut){67  int rc;68  int res = 0;69  rc = sqlite3BtreeCursor(pBt, root, BTREE_WRCSR, 0, pCurOut);70  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);71  rc = sqlite3BtreeTableMoveto(pCurOut, rowid, 0, &res);72  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);73  TEST_ASSERT_EQUAL_INT_MESSAGE(0, res, "Rowid not found");74  TEST_ASSERT_EQUAL_INT(CURSOR_VALID, pCurOut->eState);75}76 77/* Compute pCell pointer and CellInfo for current cursor position */78static void get_cell_and_info(BtCursor *pCur, u8 **ppCell, CellInfo *pInfo){79  MemPage *pLeaf = pCur->pPage;80  TEST_ASSERT_NOT_NULL(pLeaf);81  TEST_ASSERT_TRUE(pCur->ix < pLeaf->nCell);82  u8 *pCell = pLeaf->aData + ((pLeaf->maskPage & get2byteAligned(&pLeaf->aCellIdx[2*pCur->ix])));83  pLeaf->xParseCell(pLeaf, pCell, pInfo);84  *ppCell = pCell;85}86 87/* Force root to be internal by reducing page-size and inserting many rows */88static Pgno create_deep_tree(Btree *pBt, int pageSize, int nRows){89  int rc;90  /* Set page size before it is fixed (new db) */91  rc = sqlite3BtreeSetPageSize(pBt, pageSize, -1, 0);92  TEST_ASSERT_TRUE(rc==SQLITE_OK || rc==SQLITE_READONLY);93 94  Pgno root = create_raw_table(pBt);95 96  /* Open cursor and insert many small rows */97  BtCursor cur;98  memset(&cur, 0, sizeof(cur));99  rc = sqlite3BtreeCursor(pBt, root, BTREE_WRCSR, 0, &cur);100  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101 102  BtreePayload X;103  memset(&X, 0, sizeof(X));104  /* Insert empty payload (0 bytes) to maximize number of rows per page */105  X.pKey = NULL;106  X.pData = NULL;107  X.nData = 0;108  X.nZero = 0;109 110  for(int i=1;i<=nRows;i++){111    X.nKey = i;112    rc = sqlite3BtreeInsert(&cur, &X, BTREE_APPEND, 0);113    TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);114  }115  sqlite3BtreeCloseCursor(&cur);116  return root;117}118 119void setUp(void){120  /* Nothing by default; each test opens/closes its own db */121}122void tearDown(void){123  close_db();124}125 126/* Test: modifyPagePointer with PTRMAP_OVERFLOW1 successfully updates the overflow pointer in a leaf cell */127void test_modifyPagePointer_overflow1_success_and_mismatch(void){128  open_inmemory_db();129  /* Access main database btree */130  Btree *pBtH = gDb->aDb[0].pBt;131  TEST_ASSERT_NOT_NULL(pBtH);132  BtShared *pBt = pBtH->pBt;133 134  /* Create raw table and big row to ensure overflow */135  Pgno root = create_raw_table(pBtH);136  insert_big_row(pBtH, root, 1, 8000);137 138  /* Open cursor at row 1 */139  BtCursor cur;140  memset(&cur, 0, sizeof(cur));141  open_cursor_at_row(pBtH, root, 1, &cur);142 143  /* Prepare pointers */144  u8 *pCell = NULL;145  CellInfo info;146  memset(&info, 0, sizeof(info));147  get_cell_and_info(&cur, &pCell, &info);148 149  TEST_ASSERT_TRUE_MESSAGE(info.nPayload > info.nLocal, "Payload unexpectedly does not use overflow");150 151  /* Compute first overflow page number from cell */152  Pgno ovfl1 = get4byte(&pCell[info.nSize - 4]);153 154  /* Enter btree mutex and mark leaf page writeable */155  sqlite3BtreeEnter(pBtH);156  int rc = sqlite3PagerWrite(cur.pPage->pDbPage);157  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);158 159  /* New target value */160  Pgno newOvfl = ovfl1 + 1;161 162  /* Call function under test */163  rc = test_modifyPagePointer(cur.pPage, ovfl1, newOvfl, PTRMAP_OVERFLOW1);164  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);165  /* Verify cell last 4 bytes updated */166  TEST_ASSERT_EQUAL_UINT(newOvfl, get4byte(&pCell[info.nSize - 4]));167 168  /* Mismatch case: use a bogus iFrom, expect SQLITE_CORRUPT */169  rc = test_modifyPagePointer(cur.pPage, (Pgno)(newOvfl+123), newOvfl+321, PTRMAP_OVERFLOW1);170  TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, rc);171 172  sqlite3BtreeLeave(pBtH);173  sqlite3BtreeCloseCursor(&cur);174}175 176/* Test: modifyPagePointer with PTRMAP_OVERFLOW2 on an actual overflow page */177void test_modifyPagePointer_overflow2_success_and_mismatch(void){178  open_inmemory_db();179  Btree *pBtH = gDb->aDb[0].pBt;180  TEST_ASSERT_NOT_NULL(pBtH);181  BtShared *pBt = pBtH->pBt;182 183  Pgno root = create_raw_table(pBtH);184  insert_big_row(pBtH, root, 1, 12000);185 186  BtCursor cur;187  memset(&cur, 0, sizeof(cur));188  open_cursor_at_row(pBtH, root, 1, &cur);189 190  /* Get overflow page # from cell */191  u8 *pCell = NULL;192  CellInfo info;193  memset(&info, 0, sizeof(info));194  get_cell_and_info(&cur, &pCell, &info);195  TEST_ASSERT_TRUE(info.nPayload > info.nLocal);196  Pgno ovfl1 = get4byte(&pCell[info.nSize - 4]);197 198  /* Lookup overflow DbPage and wrap as MemPage with required fields */199  DbPage *pDbPage = NULL;200  int rc = sqlite3PagerGet(pBt->pPager, ovfl1, &pDbPage, 0);201  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);202  MemPage *pOvfl = (MemPage*)sqlite3PagerGetExtra(pDbPage);203  pOvfl->pBt = pBt;204  pOvfl->pDbPage = pDbPage;205  pOvfl->aData = (u8*)sqlite3PagerGetData(pDbPage);206 207  /* Read current next pointer and prepare new one */208  Pgno next = get4byte(pOvfl->aData);209  Pgno newNext = next + 2;210 211  sqlite3BtreeEnter(pBtH);212  /* Mark overflow page writeable */213  rc = sqlite3PagerWrite(pDbPage);214  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);215 216  rc = test_modifyPagePointer(pOvfl, next, newNext, PTRMAP_OVERFLOW2);217  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);218  TEST_ASSERT_EQUAL_UINT(newNext, get4byte(pOvfl->aData));219 220  /* Mismatch case */221  rc = test_modifyPagePointer(pOvfl, (Pgno)(newNext+123), (Pgno)(newNext+456), PTRMAP_OVERFLOW2);222  TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, rc);223 224  sqlite3BtreeLeave(pBtH);225  sqlite3PagerUnref(pDbPage);226  sqlite3BtreeCloseCursor(&cur);227}228 229/* Test: modifyPagePointer with PTRMAP_BTREE updates right-child pointer of an internal root page */230void test_modifyPagePointer_btree_success_and_mismatch(void){231  open_inmemory_db();232  Btree *pBtH = gDb->aDb[0].pBt;233  TEST_ASSERT_NOT_NULL(pBtH);234  BtShared *pBt = pBtH->pBt;235 236  /* Create a deeper tree by using small page size and many rows */237  Pgno root = create_deep_tree(pBtH, 512, 2000);238 239  /* Open a cursor and move to root. After moveToRoot(), apPage[0] is root. */240  BtCursor cur;241  memset(&cur, 0, sizeof(cur));242  int rc = sqlite3BtreeCursor(pBtH, root, BTREE_WRCSR, 0, &cur);243  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);244  rc = sqlite3BtreeFirst(&cur, &rc); /* moveToRoot is done inside; ensure valid */245  /* We just need apPage[0] root; ignore rc from First if table empty. */246 247  MemPage *pRoot = cur.apPage[0];248  TEST_ASSERT_NOT_NULL(pRoot);249  /* Ensure it is an internal page; if not, skip this test gracefully */250  if( pRoot->leaf ){251    /* Not enough rows to force an internal root, ignore this test. */252    sqlite3BtreeCloseCursor(&cur);253    TEST_IGNORE_MESSAGE("Root is a leaf; could not build internal root for PTRMAP_BTREE test");254    return;255  }256 257  /* Right child pointer at header+8 */258  Pgno rightChild = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);259  Pgno newRightChild = rightChild + 10;260 261  sqlite3BtreeEnter(pBtH);262  rc = sqlite3PagerWrite(pRoot->pDbPage);263  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);264 265  rc = test_modifyPagePointer(pRoot, rightChild, newRightChild, PTRMAP_BTREE);266  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);267  TEST_ASSERT_EQUAL_UINT(newRightChild, get4byte(&pRoot->aData[pRoot->hdrOffset+8]));268 269  /* Mismatch: use a value not present */270  rc = test_modifyPagePointer(pRoot, (Pgno)(newRightChild+9999), (Pgno)(newRightChild+1), PTRMAP_BTREE);271  TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, rc);272 273  sqlite3BtreeLeave(pBtH);274  sqlite3BtreeCloseCursor(&cur);275}276 277int main(void){278  UNITY_BEGIN();279  RUN_TEST(test_modifyPagePointer_overflow1_success_and_mismatch);280  RUN_TEST(test_modifyPagePointer_overflow2_success_and_mismatch);281  RUN_TEST(test_modifyPagePointer_btree_success_and_mismatch);282  return UNITY_END();283}