Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_btree_btreeOverwriteCell.c296 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* Wrapper for the static function under test (provided by source as noted) */8extern int test_btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX);9 10static sqlite3 *gDb = NULL;11 12/* Helpers */13 14static int exec_sql(const char *zSql){15  char *zErr = NULL;16  int rc = sqlite3_exec(gDb, zSql, 0, 0, &zErr);17  if( zErr ){18    /* Useful to see errors if running interactively */19    /* fprintf(stderr, "SQL error: %s\n", zErr); */20    sqlite3_free(zErr);21  }22  return rc;23}24 25static Pgno get_rootpage_for(const char *zName){26  Pgno root = 0;27  sqlite3_stmt *pStmt = NULL;28  int rc = sqlite3_prepare_v2(gDb,29    "SELECT rootpage FROM sqlite_schema WHERE name=?1", -1, &pStmt, 0);30  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);31  rc = sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC);32  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);33  if( sqlite3_step(pStmt)==SQLITE_ROW ){34    root = (Pgno)sqlite3_column_int(pStmt, 0);35  }36  sqlite3_finalize(pStmt);37  return root;38}39 40static void open_write_cursor(Btree *pBt, Pgno root, BtCursor *pCur){41  int rc;42  sqlite3BtreeEnter(pBt);43  rc = sqlite3BtreeBeginTrans(pBt, 1, 0);44  sqlite3BtreeLeave(pBt);45  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);46 47  sqlite3BtreeEnter(pBt);48  rc = sqlite3BtreeCursor(pBt, root, BTREE_WRCSR, 0, pCur);49  sqlite3BtreeLeave(pBt);50  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);51}52 53static void close_cursor(BtCursor *pCur){54  int rc = sqlite3BtreeCloseCursor(pCur);55  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);56}57 58static void move_to_row(BtCursor *pCur, i64 rowid){59  int rc, res = 0;60  sqlite3BtreeEnter(pCur->pBtree);61  rc = sqlite3BtreeTableMoveto(pCur, rowid, 0, &res);62  sqlite3BtreeLeave(pCur->pBtree);63  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);64  TEST_ASSERT_EQUAL_INT(0, res); /* exact match */65}66 67static u32 payload_size(BtCursor *pCur){68  u32 n;69  sqlite3BtreeEnter(pCur->pBtree);70  n = sqlite3BtreePayloadSize(pCur);71  sqlite3BtreeLeave(pCur->pBtree);72  return n;73}74 75static void read_payload(BtCursor *pCur, u32 off, u32 amt, void *pBuf){76  int rc;77  sqlite3BtreeEnter(pCur->pBtree);78  rc = sqlite3BtreePayload(pCur, off, amt, pBuf);79  sqlite3BtreeLeave(pCur->pBtree);80  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);81}82 83static void build_payload(BtreePayload *px, const void *pData, int nData){84  memset(px, 0, sizeof(*px));85  px->pKey = NULL;86  px->nKey = 0;87  px->pData = pData;88  px->nData = nData;89  px->nZero = 0;90  px->nMem = 0;91  px->aMem = NULL;92}93 94/* Unity hooks */95 96void setUp(void) {97  int rc = sqlite3_initialize();98  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);99  rc = sqlite3_open(":memory:", &gDb);100  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101  /* Ensure a predictable pagesize (default usually 4096, but set explicitly) */102  TEST_ASSERT_EQUAL_INT(SQLITE_OK, exec_sql("PRAGMA page_size=4096"));103  TEST_ASSERT_EQUAL_INT(SQLITE_OK, exec_sql("PRAGMA journal_mode=OFF"));104}105 106void tearDown(void) {107  if( gDb ){108    sqlite3_close(gDb);109    gDb = NULL;110  }111}112 113/* Tests */114 115void test_btreeOverwriteCell_local_updates_data(void){116  /* Create table and insert small blob that fits locally (no overflow). */117  TEST_ASSERT_EQUAL_INT(SQLITE_OK, exec_sql("CREATE TABLE t(x BLOB)"));118  TEST_ASSERT_EQUAL_INT(SQLITE_OK,119    exec_sql("INSERT INTO t(rowid,x) VALUES(1, zeroblob(100))"));120 121  Btree *pBt = gDb->aDb[0].pBt;122  Pgno root = get_rootpage_for("t");123  TEST_ASSERT_TRUE(root>0);124 125  BtCursor cur;126  open_write_cursor(pBt, root, &cur);127  move_to_row(&cur, 1);128 129  /* Populate cur.info by calling payload-size API. */130  u32 nOrig = payload_size(&cur);131  TEST_ASSERT_EQUAL_UINT32(100u, nOrig);132 133  /* Overwrite with new 100-byte pattern */134  unsigned char newData[100];135  for(int i=0;i<100;i++) newData[i] = (unsigned char)(i ^ 0x5A);136 137  BtreePayload px;138  build_payload(&px, newData, sizeof(newData));139 140  int rc = test_btreeOverwriteCell(&cur, &px);141  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);142 143  /* Verify the payload was updated */144  unsigned char got[100];145  read_payload(&cur, 0, sizeof(got), got);146  TEST_ASSERT_EQUAL_UINT8_ARRAY(newData, got, sizeof(got));147 148  close_cursor(&cur);149 150  /* Commit */151  sqlite3BtreeEnter(pBt);152  rc = sqlite3BtreeCommit(pBt);153  sqlite3BtreeLeave(pBt);154  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);155}156 157void test_btreeOverwriteCell_overflow_updates_data(void){158  /* Create a table and insert a large blob that forces overflow. */159  TEST_ASSERT_EQUAL_INT(SQLITE_OK, exec_sql("CREATE TABLE t2(x BLOB)"));160  /* 5000 bytes likely exceeds maxLocal for 4KiB pages, forcing overflow */161  TEST_ASSERT_EQUAL_INT(SQLITE_OK,162    exec_sql("INSERT INTO t2(rowid,x) VALUES(2, zeroblob(5000))"));163 164  Btree *pBt = gDb->aDb[0].pBt;165  Pgno root = get_rootpage_for("t2");166  TEST_ASSERT_TRUE(root>0);167 168  BtCursor cur;169  open_write_cursor(pBt, root, &cur);170  move_to_row(&cur, 2);171 172  u32 nOrig = payload_size(&cur);173  TEST_ASSERT_TRUE(nOrig >= 5000u);174 175  /* Build a new data buffer of equal size with a distinct pattern. */176  unsigned char *newBig = (unsigned char*)sqlite3_malloc(nOrig);177  TEST_ASSERT_NOT_NULL(newBig);178  for(u32 i=0;i<nOrig;i++){179    newBig[i] = (unsigned char)((i*131) & 0xFF);180  }181 182  BtreePayload px;183  build_payload(&px, newBig, (int)nOrig);184 185  int rc = test_btreeOverwriteCell(&cur, &px);186  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);187 188  /* Verify the entire payload matches */189  unsigned char *got = (unsigned char*)sqlite3_malloc(nOrig);190  TEST_ASSERT_NOT_NULL(got);191  read_payload(&cur, 0, nOrig, got);192  TEST_ASSERT_EQUAL_UINT8_ARRAY(newBig, got, nOrig);193 194  sqlite3_free(got);195  sqlite3_free(newBig);196 197  close_cursor(&cur);198 199  sqlite3BtreeEnter(pBt);200  rc = sqlite3BtreeCommit(pBt);201  sqlite3BtreeLeave(pBt);202  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);203}204 205void test_btreeOverwriteCell_invalid_bounds_returns_corrupt(void){206  /* Set up a small-row table and cursor, then corrupt pCur->info to trigger207     the bounds checks in btreeOverwriteCell(). */208  TEST_ASSERT_EQUAL_INT(SQLITE_OK, exec_sql("CREATE TABLE t3(x BLOB)"));209  TEST_ASSERT_EQUAL_INT(SQLITE_OK,210    exec_sql("INSERT INTO t3(rowid,x) VALUES(3, zeroblob(32))"));211 212  Btree *pBt = gDb->aDb[0].pBt;213  Pgno root = get_rootpage_for("t3");214  TEST_ASSERT_TRUE(root>0);215 216  BtCursor cur;217  open_write_cursor(pBt, root, &cur);218  move_to_row(&cur, 3);219 220  /* Initialize info */221  (void)payload_size(&cur);222 223  /* Corrupt the nLocal so that pPayload + nLocal > aDataEnd */224  sqlite3BtreeEnter(pBt);225  u8 *end = cur.pPage->aDataEnd;226  uptr payloadEnd = (uptr)cur.info.pPayload + (uptr)cur.info.nLocal;227  if( payloadEnd <= (uptr)end ){228    /* Inflate nLocal to exceed aDataEnd by at least 1 */229    cur.info.nLocal += (int)((uptr)end - payloadEnd) + 1;230  }231  sqlite3BtreeLeave(pBt);232 233  unsigned char buf[32];234  memset(buf, 0xAB, sizeof(buf));235  BtreePayload px;236  build_payload(&px, buf, sizeof(buf));237 238  int rc = test_btreeOverwriteCell(&cur, &px);239  /* Expect a corruption error */240  TEST_ASSERT_EQUAL_INT(SQLITE_CORRUPT, (rc & 0xff));241 242  close_cursor(&cur);243 244  sqlite3BtreeEnter(pBt);245  rc = sqlite3BtreeCommit(pBt);246  sqlite3BtreeLeave(pBt);247  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);248}249 250void test_btreeOverwriteCell_same_data_noop_ok(void){251  /* Insert a local-only blob and overwrite with identical content. */252  TEST_ASSERT_EQUAL_INT(SQLITE_OK, exec_sql("CREATE TABLE t4(x BLOB)"));253  TEST_ASSERT_EQUAL_INT(SQLITE_OK,254    exec_sql("INSERT INTO t4(rowid,x) VALUES(4, randomblob(64))"));255 256  Btree *pBt = gDb->aDb[0].pBt;257  Pgno root = get_rootpage_for("t4");258  TEST_ASSERT_TRUE(root>0);259 260  BtCursor cur;261  open_write_cursor(pBt, root, &cur);262  move_to_row(&cur, 4);263 264  u32 n = payload_size(&cur);265  TEST_ASSERT_EQUAL_UINT32(64u, n);266 267  unsigned char orig[64];268  read_payload(&cur, 0, sizeof(orig), orig);269 270  BtreePayload px;271  build_payload(&px, orig, sizeof(orig));272 273  int rc = test_btreeOverwriteCell(&cur, &px);274  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);275 276  /* Verify unchanged content */277  unsigned char got[64];278  read_payload(&cur, 0, sizeof(got), got);279  TEST_ASSERT_EQUAL_UINT8_ARRAY(orig, got, sizeof(orig));280 281  close_cursor(&cur);282 283  sqlite3BtreeEnter(pBt);284  rc = sqlite3BtreeCommit(pBt);285  sqlite3BtreeLeave(pBt);286  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);287}288 289int main(void){290  UNITY_BEGIN();291  RUN_TEST(test_btreeOverwriteCell_local_updates_data);292  RUN_TEST(test_btreeOverwriteCell_overflow_updates_data);293  RUN_TEST(test_btreeOverwriteCell_invalid_bounds_returns_corrupt);294  RUN_TEST(test_btreeOverwriteCell_same_data_noop_ok);295  return UNITY_END();296}