AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5 6/* Helper: create an in-memory DB, begin write tx, create intkey table,7 open write cursor positioned to given rowid with given payload. */8static void helper_make_table_and_cursor(9 sqlite3 **pDb,10 Btree **ppBt,11 BtCursor *pCur,12 Pgno *pRoot,13 i64 rowid,14 const unsigned char *pData,15 int nData16){17 int rc;18 sqlite3 *db = 0;19 Btree *pBt = 0;20 Pgno root = 0;21 22 rc = sqlite3_open(":memory:", &db);23 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);24 TEST_ASSERT_NOT_NULL(db);25 26 /* Access the Btree of the main database. */27 pBt = db->aDb[0].pBt;28 TEST_ASSERT_NOT_NULL(pBt);29 30 sqlite3_mutex_enter(db->mutex);31 32 /* Start write transaction */33 rc = sqlite3BtreeBeginTrans(pBt, 1, 0);34 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);35 36 /* Create a new INTKEY table */37 rc = sqlite3BtreeCreateTable(pBt, &root, BTREE_INTKEY | BTREE_LEAFDATA);38 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);39 TEST_ASSERT_TRUE(root>=2);40 41 /* Open a write cursor on that root */42 memset(pCur, 0, sqlite3BtreeCursorSize());43 rc = sqlite3BtreeCursor(pBt, root, BTREE_WRCSR, 0, pCur);44 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);45 46 /* Insert one record with rowid and data */47 {48 BtreePayload x;49 memset(&x, 0, sizeof(x));50 x.nKey = rowid;51 x.pKey = 0;52 x.pData = (void*)pData;53 x.nData = nData;54 x.nZero = 0;55 rc = sqlite3BtreeInsert(pCur, &x, 0, 0);56 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);57 }58 59 /* Move to that row to ensure cursor is positioned */60 {61 int res = 0;62 rc = sqlite3BtreeTableMoveto(pCur, rowid, 0, &res);63 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);64 TEST_ASSERT_EQUAL_INT(0, res);65 }66 67 /* Mark cursor as incrblob */68 sqlite3BtreeIncrblobCursor(pCur);69 70 sqlite3_mutex_leave(db->mutex);71 72 *pDb = db;73 *ppBt = pBt;74 *pRoot = root;75}76 77/* Helper: read back payload of current row into buffer (must be large enough) */78static int helper_read_payload(sqlite3 *db, BtCursor *pCur, unsigned char *outBuf, int *pnOut){79 int rc;80 sqlite3_mutex_enter(db->mutex);81 TEST_ASSERT_EQUAL_INT(1, sqlite3_mutex_held(db->mutex));82 83 /* Ensure positioned on row */84 /* pCur is expected to already be valid on the row */85 {86 u32 n = sqlite3BtreePayloadSize(pCur);87 if( pnOut ) *pnOut = (int)n;88 rc = sqlite3BtreePayload(pCur, 0, n, outBuf);89 }90 91 sqlite3_mutex_leave(db->mutex);92 return rc;93}94 95/* Helper: cleanup cursor and close db, commit transaction */96static void helper_cleanup(sqlite3 *db, Btree *pBt, BtCursor *pCur){97 int rc;98 sqlite3_mutex_enter(db->mutex);99 rc = sqlite3BtreeCloseCursor(pCur);100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101 rc = sqlite3BtreeCommit(pBt);102 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);103 sqlite3_mutex_leave(db->mutex);104 sqlite3_close(db);105}106 107void setUp(void) { }108void tearDown(void) { }109 110/* Test 1: basic in-place write within local payload, no overflow */111void test_sqlite3BtreePutData_basic_write(void){112 sqlite3 *db = NULL;113 Btree *pBt = NULL;114 BtCursor cur;115 Pgno root = 0;116 const i64 rowid = 1;117 unsigned char initData[16];118 for(int i=0;i<16;i++) initData[i] = (unsigned char)(0x10 + i);119 120 helper_make_table_and_cursor(&db, &pBt, &cur, &root, rowid, initData, 16);121 122 /* Modify 3 bytes starting at offset 5 */123 unsigned char newBytes[3] = { 0xAA, 0xBB, 0xCC };124 125 sqlite3_mutex_enter(db->mutex);126 int rc = sqlite3BtreePutData(&cur, 5, 3, newBytes);127 sqlite3_mutex_leave(db->mutex);128 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);129 130 /* Check result */131 unsigned char got[32]; int n=0;132 rc = helper_read_payload(db, &cur, got, &n);133 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);134 TEST_ASSERT_EQUAL_INT(16, n);135 /* Verify modified segment */136 for(int i=0;i<16;i++){137 unsigned char expect = initData[i];138 if( i>=5 && i<8 ){139 expect = newBytes[i-5];140 }141 TEST_ASSERT_EQUAL_HEX8_MESSAGE(expect, got[i], "Payload byte mismatch");142 }143 144 helper_cleanup(db, pBt, &cur);145}146 147/* Test 2: read-only cursor returns SQLITE_READONLY */148void test_sqlite3BtreePutData_readonly_cursor(void){149 sqlite3 *db = NULL;150 Btree *pBt = NULL;151 BtCursor writeCur;152 Pgno root = 0;153 const i64 rowid = 7;154 unsigned char initData[12];155 for(int i=0;i<12;i++) initData[i] = (unsigned char)(i);156 157 helper_make_table_and_cursor(&db, &pBt, &writeCur, &root, rowid, initData, 12);158 159 /* Open a read-only cursor on the same table and move to the row */160 BtCursor roCur;161 memset(&roCur, 0, sqlite3BtreeCursorSize());162 163 sqlite3_mutex_enter(db->mutex);164 int rc = sqlite3BtreeCursor(pBt, root, 0 /* read-only */, 0, &roCur);165 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);166 int res = 0;167 rc = sqlite3BtreeTableMoveto(&roCur, rowid, 0, &res);168 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);169 TEST_ASSERT_EQUAL_INT(0, res);170 sqlite3BtreeIncrblobCursor(&roCur);171 172 unsigned char bytes[2] = {0x11, 0x22};173 rc = sqlite3BtreePutData(&roCur, 2, 2, bytes);174 sqlite3_mutex_leave(db->mutex);175 176 TEST_ASSERT_EQUAL_INT(SQLITE_READONLY, rc);177 178 /* Cleanup */179 helper_cleanup(db, pBt, &writeCur);180}181 182/* Test 3: writing past end returns SQLITE_CORRUPT (or equivalent) */183void test_sqlite3BtreePutData_past_end_returns_corrupt(void){184 sqlite3 *db = NULL;185 Btree *pBt = NULL;186 BtCursor cur;187 Pgno root = 0;188 const i64 rowid = 2;189 unsigned char initData[8] = {0,1,2,3,4,5,6,7};190 helper_make_table_and_cursor(&db, &pBt, &cur, &root, rowid, initData, 8);191 192 unsigned char two[2] = {0xFE, 0xED};193 sqlite3_mutex_enter(db->mutex);194 int rc = sqlite3BtreePutData(&cur, 7, 2, two); /* 7..8 -> last byte ok, +1 past end */195 sqlite3_mutex_leave(db->mutex);196 197 TEST_ASSERT_NOT_EQUAL_MESSAGE(SQLITE_OK, rc, "Expected corruption error");198 199 helper_cleanup(db, pBt, &cur);200}201 202/* Test 4: invalid (non-restored) cursor returns SQLITE_ABORT */203void test_sqlite3BtreePutData_invalid_cursor_returns_abort(void){204 sqlite3 *db = NULL;205 Btree *pBt = NULL;206 BtCursor cur;207 Pgno root = 0;208 const i64 rowid = 3;209 unsigned char initData[6] = {9,9,9,9,9,9};210 helper_make_table_and_cursor(&db, &pBt, &cur, &root, rowid, initData, 6);211 212 /* Invalidate cursor without making it require-seek */213 sqlite3_mutex_enter(db->mutex);214 sqlite3BtreeClearCursor(&cur); /* sets eState=CURSOR_INVALID */215 unsigned char b = 0xAB;216 int rc = sqlite3BtreePutData(&cur, 0, 1, &b);217 sqlite3_mutex_leave(db->mutex);218 219 TEST_ASSERT_EQUAL_INT(SQLITE_ABORT, rc);220 221 helper_cleanup(db, pBt, &cur);222}223 224/* Test 5: write on large payload crossing overflow pages */225void test_sqlite3BtreePutData_overflow_write(void){226 sqlite3 *db = NULL;227 Btree *pBt = NULL;228 BtCursor cur;229 Pgno root = 0;230 const i64 rowid = 100;231 /* Create a payload large enough to require overflow pages (e.g., 6000 bytes) */232 int nData = 6000;233 unsigned char *big = (unsigned char*)sqlite3_malloc(nData);234 TEST_ASSERT_NOT_NULL(big);235 for(int i=0;i<nData;i++) big[i] = (unsigned char)(i & 0xFF);236 237 helper_make_table_and_cursor(&db, &pBt, &cur, &root, rowid, big, nData);238 239 /* Write 10 bytes starting at offset 4090 (likely crosses local->overflow boundary) */240 unsigned char patch[10];241 for(int i=0;i<10;i++) patch[i] = (unsigned char)(0xF0 + i);242 243 sqlite3_mutex_enter(db->mutex);244 int rc = sqlite3BtreePutData(&cur, 4090, 10, patch);245 sqlite3_mutex_leave(db->mutex);246 247 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);248 249 /* Read back and verify */250 unsigned char *back = (unsigned char*)sqlite3_malloc(nData);251 TEST_ASSERT_NOT_NULL(back);252 int n=0;253 rc = helper_read_payload(db, &cur, back, &n);254 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);255 TEST_ASSERT_EQUAL_INT(nData, n);256 for(int i=0;i<nData;i++){257 unsigned char expect = big[i];258 if( i>=4090 && i<4100 ) expect = patch[i-4090];259 TEST_ASSERT_EQUAL_HEX8(expect, back[i]);260 }261 262 sqlite3_free(back);263 sqlite3_free(big);264 helper_cleanup(db, pBt, &cur);265}266 267int main(void){268 UNITY_BEGIN();269 RUN_TEST(test_sqlite3BtreePutData_basic_write);270 RUN_TEST(test_sqlite3BtreePutData_readonly_cursor);271 RUN_TEST(test_sqlite3BtreePutData_past_end_returns_corrupt);272 RUN_TEST(test_sqlite3BtreePutData_invalid_cursor_returns_abort);273 RUN_TEST(test_sqlite3BtreePutData_overflow_write);274 return UNITY_END();275}