AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* The test harness provides a global wrapper for the static function. */7extern int test_insertCellFast(MemPage *pPage, int i, u8 *pCell, int sz);8 9/* Helpers: create a new in-memory DB and return it */10static sqlite3* open_memory_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 18/* Helpers: begin a write transaction and create a new table using Btree API.19** Returns root page number via *pRoot. */20static void begin_and_create(Btree *pBt, Pgno *pRoot){21 int rc;22 sqlite3BtreeEnter(pBt);23 rc = sqlite3BtreeBeginTrans(pBt, 1, 0);24 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);25 rc = sqlite3BtreeCreateTable(pBt, pRoot, BTREE_INTKEY|BTREE_LEAFDATA);26 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);27 sqlite3BtreeLeave(pBt);28}29 30/* Helpers: open a write cursor on a given root page and ensure its MemPage is loaded. */31static BtCursor* open_write_cursor_loaded(Btree *pBt, Pgno root){32 int rc, isEmpty;33 BtCursor *pCur = (BtCursor*)sqlite3Malloc(sqlite3BtreeCursorSize());34 TEST_ASSERT_NOT_NULL(pCur);35 sqlite3BtreeEnter(pBt);36 sqlite3BtreeCursorZero(pCur);37 rc = sqlite3BtreeCursor(pBt, root, BTREE_WRCSR, 0, pCur);38 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);39 /* Force page to be loaded */40 rc = sqlite3BtreeFirst(pCur, &isEmpty);41 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);42 TEST_ASSERT_NOT_NULL(pCur->pPage);43 sqlite3BtreeLeave(pBt);44 return pCur;45}46 47/* cell builder: build a minimal table-leaf cell with 0 payload and 1-byte rowid varint.48** Returns size 4 bytes as per minimum cell size rule. */49static void build_min_table_leaf_cell(u8 rowidByte, u8 *out /* size>=4 */, int *pSz){50 /* Format: [varint payload-size=0x00][varint rowid=rowidByte] plus padding to 4 bytes */51 out[0] = 0x00; /* nPayload = 0 */52 out[1] = rowidByte; /* small varint rowid */53 out[2] = 0x00; /* padding */54 out[3] = 0x00; /* padding */55 *pSz = 4;56}57 58void setUp(void) {59 /* empty */60}61 62void tearDown(void) {63 /* empty */64}65 66/* Test 1: Basic insertion into an empty leaf page */67void test_insertCellFast_basic_insert_into_empty_leaf(void){68 sqlite3 *db = open_memory_db();69 /* Main database btree handle */70 Btree *pBt = db->aDb[0].pBt;71 TEST_ASSERT_NOT_NULL(pBt);72 73 Pgno root = 0;74 begin_and_create(pBt, &root);75 76 /* Open cursor and load page */77 BtCursor *pCur = open_write_cursor_loaded(pBt, root);78 79 /* Pre-conditions: empty leaf page */80 sqlite3BtreeEnter(pBt);81 MemPage *pPage = pCur->pPage;82 TEST_ASSERT_NOT_NULL(pPage);83 TEST_ASSERT_EQUAL_UINT16(0, pPage->nCell);84 TEST_ASSERT_TRUE(pPage->leaf!=0);85 TEST_ASSERT_TRUE(pPage->intKeyLeaf!=0);86 87 /* Build a minimal valid cell */88 u8 cell[8];89 int sz = 0;90 build_min_table_leaf_cell(0x2A, cell, &sz);91 TEST_ASSERT_EQUAL_INT(4, sz);92 93 /* Call the target wrapper */94 int rc = test_insertCellFast(pPage, 0, cell, sz);95 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);96 97 /* Verify results: one cell inserted */98 TEST_ASSERT_EQUAL_UINT16(1, pPage->nCell);99 TEST_ASSERT_EQUAL_UINT16(0, pPage->nOverflow);100 101 /* Verify the cell-pointer points to data that starts with (0x00, 0x2A) */102 int hdr = pPage->hdrOffset;103 u16 off = get2byte(&pPage->aCellIdx[0]);104 TEST_ASSERT_TRUE(off >= pPage->cellOffset); /* plausible offset */105 TEST_ASSERT_TRUE(off+2 <= pPage->pBt->usableSize);106 TEST_ASSERT_EQUAL_UINT8(0x00, pPage->aData[off+0]);107 TEST_ASSERT_EQUAL_UINT8(0x2A, pPage->aData[off+1]);108 109 sqlite3BtreeLeave(pBt);110 111 /* Cleanup */112 sqlite3BtreeEnter(pBt);113 sqlite3BtreeCloseCursor(pCur);114 sqlite3BtreeCommit(pBt);115 sqlite3BtreeLeave(pBt);116 sqlite3_free(pCur);117 sqlite3_close(db);118}119 120/* Test 2: Force overflow-array path (no space) without touching page body */121void test_insertCellFast_overflow_slot_when_no_free_space(void){122 sqlite3 *db = open_memory_db();123 Btree *pBt = db->aDb[0].pBt;124 TEST_ASSERT_NOT_NULL(pBt);125 126 Pgno root = 0;127 begin_and_create(pBt, &root);128 BtCursor *pCur = open_write_cursor_loaded(pBt, root);129 130 sqlite3BtreeEnter(pBt);131 MemPage *pPage = pCur->pPage;132 TEST_ASSERT_NOT_NULL(pPage);133 TEST_ASSERT_EQUAL_UINT16(0, pPage->nCell);134 TEST_ASSERT_EQUAL_UINT16(0, pPage->nOverflow);135 136 /* Artificially force low free space to trigger overflow-slot path */137 u16 savedFree = pPage->nFree;138 pPage->nFree = 0;139 140 u8 cell[4];141 int sz = 0;142 build_min_table_leaf_cell(0x01, cell, &sz);143 144 int rc = test_insertCellFast(pPage, 0, cell, sz);145 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);146 147 /* Verify overflow queued */148 TEST_ASSERT_EQUAL_UINT16(1, pPage->nOverflow);149 TEST_ASSERT_EQUAL_PTR(cell, pPage->apOvfl[0]);150 TEST_ASSERT_EQUAL_UINT16(0, pPage->aiOvfl[0]);151 TEST_ASSERT_EQUAL_UINT16(0, pPage->nCell);152 153 /* Restore free space to avoid leaving the page inconsistent for subsequent ops */154 pPage->nFree = savedFree;155 156 sqlite3BtreeLeave(pBt);157 158 /* Cleanup */159 sqlite3BtreeEnter(pBt);160 sqlite3BtreeCloseCursor(pCur);161 sqlite3BtreeCommit(pBt);162 sqlite3BtreeLeave(pBt);163 sqlite3_free(pCur);164 sqlite3_close(db);165}166 167/* Test 3: Insert two cells with the second inserted at index 0 (pointer index shift) */168void test_insertCellFast_insert_before_shifts_index(void){169 sqlite3 *db = open_memory_db();170 Btree *pBt = db->aDb[0].pBt;171 TEST_ASSERT_NOT_NULL(pBt);172 173 Pgno root = 0;174 begin_and_create(pBt, &root);175 BtCursor *pCur = open_write_cursor_loaded(pBt, root);176 177 sqlite3BtreeEnter(pBt);178 MemPage *pPage = pCur->pPage;179 TEST_ASSERT_NOT_NULL(pPage);180 181 /* First insert */182 u8 cellA[4], cellB[4];183 int szA=0, szB=0;184 build_min_table_leaf_cell(0x0A, cellA, &szA); /* rowid 10 */185 int rc = test_insertCellFast(pPage, 0, cellA, szA);186 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);187 TEST_ASSERT_EQUAL_UINT16(1, pPage->nCell);188 189 /* Second insert at index 0 (before the first) */190 build_min_table_leaf_cell(0x05, cellB, &szB); /* rowid 5 */191 rc = test_insertCellFast(pPage, 0, cellB, szB);192 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);193 TEST_ASSERT_EQUAL_UINT16(2, pPage->nCell);194 195 /* Check that the first cell-pointer now points to cellB bytes */196 u16 off0 = get2byte(&pPage->aCellIdx[0]);197 TEST_ASSERT_TRUE(off0+2 <= pPage->pBt->usableSize);198 TEST_ASSERT_EQUAL_UINT8(0x00, pPage->aData[off0+0]);199 TEST_ASSERT_EQUAL_UINT8(0x05, pPage->aData[off0+1]);200 201 /* And second pointer should point to cellA bytes */202 u16 off1 = get2byte(&pPage->aCellIdx[2]);203 TEST_ASSERT_TRUE(off1+2 <= pPage->pBt->usableSize);204 TEST_ASSERT_EQUAL_UINT8(0x00, pPage->aData[off1+0]);205 TEST_ASSERT_EQUAL_UINT8(0x0A, pPage->aData[off1+1]);206 207 sqlite3BtreeLeave(pBt);208 209 /* Cleanup */210 sqlite3BtreeEnter(pBt);211 sqlite3BtreeCloseCursor(pCur);212 sqlite3BtreeCommit(pBt);213 sqlite3BtreeLeave(pBt);214 sqlite3_free(pCur);215 sqlite3_close(db);216}217 218int main(void) {219 UNITY_BEGIN();220 RUN_TEST(test_insertCellFast_basic_insert_into_empty_leaf);221 RUN_TEST(test_insertCellFast_overflow_slot_when_no_free_space);222 RUN_TEST(test_insertCellFast_insert_before_shifts_index);223 return UNITY_END();224}