AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6 7/* Helper: open an in-memory DB and get Btree handle */8static int open_memory_db(sqlite3 **ppDb, Btree **ppBt){9 int rc = sqlite3_open(":memory:", ppDb);10 if( rc!=SQLITE_OK ) return rc;11 *ppBt = (*ppDb)->aDb[0].pBt;12 if( *ppBt==0 ) return SQLITE_ERROR;13 return SQLITE_OK;14}15 16/* Helper: create a new intkey+leafdata table and open a write cursor */17static int make_table_and_cursor(Btree *pBt, Pgno *pRoot, BtCursor *pCur){18 int rc;19 int schemaVersion = 0;20 sqlite3BtreeCursorZero(pCur);21 rc = sqlite3BtreeBeginTrans(pBt, 1, &schemaVersion);22 if( rc!=SQLITE_OK ) return rc;23 rc = sqlite3BtreeCreateTable(pBt, pRoot, BTREE_INTKEY|BTREE_LEAFDATA);24 if( rc!=SQLITE_OK ) return rc;25 rc = sqlite3BtreeCursor(pBt, *pRoot, BTREE_WRCSR, NULL, pCur);26 return rc;27}28 29/* Helper: drop table and close cursor, commit and close db */30static void cleanup_all(sqlite3 *db, Btree *pBt, BtCursor *pCur, Pgno root){31 if( pCur && pCur->pBtree ){32 sqlite3BtreeCloseCursor(pCur);33 }34 if( pBt ){35 int moved = 0;36 /* Ignore error from drop if already gone */37 (void)sqlite3BtreeDropTable(pBt, (int)root, &moved);38 (void)sqlite3BtreeCommit(pBt);39 }40 if( db ){41 sqlite3_close(db);42 }43}44 45/* Helper: insert a row (intkey, data) */46static int insert_row(BtCursor *pCur, i64 rowid, const void *pData, int nData,47 int flags, int seekResult){48 BtreePayload x;49 memset(&x, 0, sizeof(x));50 x.pKey = NULL;51 x.nKey = rowid;52 x.pData = (void*)pData;53 x.nData = nData;54 x.nZero = 0;55 return sqlite3BtreeInsert(pCur, &x, flags, seekResult);56}57 58/* Helper: fetch a row payload into buf, returning payload size (or <0 on err) */59static int fetch_row(BtCursor *pCur, i64 rowid, void *pBuf, int bufCap){60 int rc, res = 0;61 rc = sqlite3BtreeTableMoveto(pCur, rowid, 0, &res);62 if( rc!=SQLITE_OK ) return -1;63 if( res!=0 ) return -2; /* not found */64 {65 u32 sz = sqlite3BtreePayloadSize(pCur);66 if( (int)sz>bufCap ) return -3;67 rc = sqlite3BtreePayload(pCur, 0, (int)sz, pBuf);68 if( rc!=SQLITE_OK ) return -4;69 return (int)sz;70 }71}72 73void setUp(void) {74 /* nothing */75}76void tearDown(void) {77 /* nothing */78}79 80/* Test 1: basic insert and fetch */81void test_sqlite3BtreeInsert_basic_insert_and_fetch(void){82 sqlite3 *db = NULL;83 Btree *pBt = NULL;84 BtCursor cur;85 Pgno root = 0;86 87 TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_memory_db(&db, &pBt));88 TEST_ASSERT_NOT_NULL(pBt);89 90 TEST_ASSERT_EQUAL_INT(SQLITE_OK, make_table_and_cursor(pBt, &root, &cur));91 92 const char *msg = "hello";93 TEST_ASSERT_EQUAL_INT(SQLITE_OK, insert_row(&cur, 1, msg, (int)strlen(msg), 0, 0));94 95 char buf[16] = {0};96 int got = fetch_row(&cur, 1, buf, sizeof(buf));97 TEST_ASSERT_GREATER_OR_EQUAL(0, got);98 TEST_ASSERT_EQUAL_INT((int)strlen(msg), got);99 buf[got] = '\0';100 TEST_ASSERT_EQUAL_STRING(msg, buf);101 102 cleanup_all(db, pBt, &cur, root);103}104 105/* Test 2: overwrite same-size payload on same key */106void test_sqlite3BtreeInsert_overwrite_same_size(void){107 sqlite3 *db = NULL;108 Btree *pBt = NULL;109 BtCursor cur;110 Pgno root = 0;111 112 TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_memory_db(&db, &pBt));113 TEST_ASSERT_EQUAL_INT(SQLITE_OK, make_table_and_cursor(pBt, &root, &cur));114 115 const char *msg1 = "hello";116 const char *msg2 = "HELLO"; /* same length */117 TEST_ASSERT_EQUAL_INT(SQLITE_OK, insert_row(&cur, 5, msg1, (int)strlen(msg1), 0, 0));118 119 /* Overwrite with same size data – should take overwrite optimization path */120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, insert_row(&cur, 5, msg2, (int)strlen(msg2), 0, 0));121 122 char buf[16] = {0};123 int got = fetch_row(&cur, 5, buf, sizeof(buf));124 TEST_ASSERT_GREATER_OR_EQUAL(0, got);125 TEST_ASSERT_EQUAL_INT((int)strlen(msg2), got);126 buf[got] = '\0';127 TEST_ASSERT_EQUAL_STRING(msg2, buf);128 129 cleanup_all(db, pBt, &cur, root);130}131 132/* Test 3: insert large payload to force overflow pages and verify */133void test_sqlite3BtreeInsert_large_payload_overflow(void){134 sqlite3 *db = NULL;135 Btree *pBt = NULL;136 BtCursor cur;137 Pgno root = 0;138 139 TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_memory_db(&db, &pBt));140 TEST_ASSERT_EQUAL_INT(SQLITE_OK, make_table_and_cursor(pBt, &root, &cur));141 142 const int N = 10000; /* large to ensure overflow */143 char *big = (char*)sqlite3_malloc(N);144 TEST_ASSERT_NOT_NULL(big);145 for(int i=0;i<N;i++) big[i] = (char)('a' + (i%26));146 147 TEST_ASSERT_EQUAL_INT(SQLITE_OK, insert_row(&cur, 42, big, N, 0, 0));148 149 /* Fetch back and verify */150 char *back = (char*)sqlite3_malloc(N);151 TEST_ASSERT_NOT_NULL(back);152 int got = fetch_row(&cur, 42, back, N);153 TEST_ASSERT_EQUAL_INT(N, got);154 TEST_ASSERT_EQUAL_INT(0, memcmp(big, back, N));155 156 sqlite3_free(big);157 sqlite3_free(back);158 159 cleanup_all(db, pBt, &cur, root);160}161 162/* Test 4: BTREE_SAVEPOSITION overwrite while positioned on the row */163void test_sqlite3BtreeInsert_saveposition_overwrite(void){164 sqlite3 *db = NULL;165 Btree *pBt = NULL;166 BtCursor cur;167 Pgno root = 0;168 169 TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_memory_db(&db, &pBt));170 TEST_ASSERT_EQUAL_INT(SQLITE_OK, make_table_and_cursor(pBt, &root, &cur));171 172 const char *msg1 = "abcde";173 const char *msg2 = "12345"; /* same length */174 175 /* Insert initial */176 TEST_ASSERT_EQUAL_INT(SQLITE_OK, insert_row(&cur, 7, msg1, (int)strlen(msg1), 0, 0));177 178 /* Position cursor exactly on the rowid 7 */179 int res=0;180 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeTableMoveto(&cur, 7, 0, &res));181 TEST_ASSERT_EQUAL_INT(0, res);182 183 /* Overwrite using BTREE_SAVEPOSITION: cursor is already at rowid 7 */184 BtreePayload x;185 memset(&x, 0, sizeof(x));186 x.nKey = 7;187 x.pData = (void*)msg2;188 x.nData = (int)strlen(msg2);189 x.nZero = 0;190 int rc = sqlite3BtreeInsert(&cur, &x, BTREE_SAVEPOSITION, 0);191 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);192 193 /* Verify */194 char buf[16]={0};195 int got = fetch_row(&cur, 7, buf, sizeof(buf));196 TEST_ASSERT_EQUAL_INT((int)strlen(msg2), got);197 buf[got] = '\0';198 TEST_ASSERT_EQUAL_STRING(msg2, buf);199 200 cleanup_all(db, pBt, &cur, root);201}202 203/* Test 5: Append hint with ascending keys */204void test_sqlite3BtreeInsert_append_hint(void){205 sqlite3 *db = NULL;206 Btree *pBt = NULL;207 BtCursor cur;208 Pgno root = 0;209 210 TEST_ASSERT_EQUAL_INT(SQLITE_OK, open_memory_db(&db, &pBt));211 TEST_ASSERT_EQUAL_INT(SQLITE_OK, make_table_and_cursor(pBt, &root, &cur));212 213 const char *msg = "x";214 for(int i=1;i<=100;i++){215 TEST_ASSERT_EQUAL_INT(SQLITE_OK, insert_row(&cur, i, msg, 1, BTREE_APPEND, 0));216 }217 218 /* Move to last and verify */219 int pres = 0;220 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeLast(&cur, &pres));221 TEST_ASSERT_EQUAL_INT(0, pres); /* table not empty */222 223 /* The last row should be key 100 */224 /* Get current key by reading payload after moving to specific key */225 int res = 0;226 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeTableMoveto(&cur, 100, 0, &res));227 TEST_ASSERT_EQUAL_INT(0, res);228 u32 sz = sqlite3BtreePayloadSize(&cur);229 TEST_ASSERT_EQUAL_UINT32(1, sz);230 char c;231 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreePayload(&cur, 0, 1, &c));232 TEST_ASSERT_EQUAL_INT('x', c);233 234 cleanup_all(db, pBt, &cur, root);235}236 237int main(void){238 UNITY_BEGIN();239 RUN_TEST(test_sqlite3BtreeInsert_basic_insert_and_fetch);240 RUN_TEST(test_sqlite3BtreeInsert_overwrite_same_size);241 RUN_TEST(test_sqlite3BtreeInsert_large_payload_overflow);242 RUN_TEST(test_sqlite3BtreeInsert_saveposition_overwrite);243 RUN_TEST(test_sqlite3BtreeInsert_append_hint);244 return UNITY_END();245}