AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <string.h>6 7/* Global DB and Btree for each test */8static sqlite3 *gDb = NULL;9static Btree *gBtree = NULL;10 11/* Simple helper to assert SQLite OK */12static void assert_ok_int(int rc, const char *msg){13 if( rc!=SQLITE_OK ){14 printf("Unexpected rc=%d at %s\n", rc, msg);15 }16 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, msg);17}18 19/* Create a new INTKEY table and return its root page number */20static Pgno create_intkey_table(void){21 Pgno root = 0;22 int rc = sqlite3BtreeCreateTable(gBtree, &root, BTREE_INTKEY|BTREE_LEAFDATA);23 assert_ok_int(rc, "sqlite3BtreeCreateTable");24 TEST_ASSERT_TRUE_MESSAGE(root>=2, "Root page should be >= 2");25 return root;26}27 28/* Insert a set of integer keys (rowids) into the intkey table. */29static void insert_keys(Pgno root, const i64 *keys, int nKeys){30 BtCursor cur;31 int rc = sqlite3BtreeCursor(gBtree, root, BTREE_WRCSR, NULL, &cur);32 assert_ok_int(rc, "sqlite3BtreeCursor(open write)");33 34 for(int i=0;i<nKeys;i++){35 BtreePayload x;36 memset(&x, 0, sizeof(x));37 x.nKey = keys[i];38 /* Store a 1-byte payload for each row */39 char data = (char)('A' + (i%26));40 x.pData = &data;41 x.nData = 1;42 x.nZero = 0;43 rc = sqlite3BtreeInsert(&cur, &x, 0, 0);44 assert_ok_int(rc, "sqlite3BtreeInsert");45 }46 47 rc = sqlite3BtreeCloseCursor(&cur);48 assert_ok_int(rc, "sqlite3BtreeCloseCursor");49}50 51/* Open a read-only cursor on root page */52static void open_read_cursor(Pgno root, BtCursor *pCur){53 int rc = sqlite3BtreeCursor(gBtree, root, 0, NULL, pCur);54 assert_ok_int(rc, "sqlite3BtreeCursor(open read)");55}56 57/* Close a cursor */58static void close_cursor(BtCursor *pCur){59 int rc = sqlite3BtreeCloseCursor(pCur);60 assert_ok_int(rc, "sqlite3BtreeCloseCursor");61}62 63void setUp(void){64 int rc = sqlite3_open(":memory:", &gDb);65 assert_ok_int(rc, "sqlite3_open");66 TEST_ASSERT_NOT_NULL(gDb);67 /* Main database btree handle */68 gBtree = gDb->aDb[0].pBt;69 TEST_ASSERT_NOT_NULL(gBtree);70 /* Begin write transaction */71 rc = sqlite3BtreeBeginTrans(gBtree, 1, 0);72 assert_ok_int(rc, "sqlite3BtreeBeginTrans");73}74 75void tearDown(void){76 if( gBtree ){77 sqlite3BtreeCommit(gBtree);78 gBtree = NULL;79 }80 if( gDb ){81 sqlite3_close(gDb);82 gDb = NULL;83 }84}85 86/* Test: sqlite3BtreeTableMoveto on empty table */87void test_sqlite3BtreeTableMoveto_empty_table(void){88 Pgno root = create_intkey_table();89 90 BtCursor cur;91 open_read_cursor(root, &cur);92 93 int res = 12345;94 int rc = sqlite3BtreeTableMoveto(&cur, 1, 0, &res);95 assert_ok_int(rc, "sqlite3BtreeTableMoveto(empty)");96 TEST_ASSERT_EQUAL_INT_MESSAGE(-1, res, "Empty table should yield res=-1");97 98 close_cursor(&cur);99}100 101/* Test: exact matches and between-range searches */102void test_sqlite3BtreeTableMoveto_exact_and_range(void){103 Pgno root = create_intkey_table();104 const i64 keys[] = {10, 20, 30, 40};105 insert_keys(root, keys, (int)(sizeof(keys)/sizeof(keys[0])));106 107 BtCursor cur;108 open_read_cursor(root, &cur);109 110 /* Exact hits */111 for(int i=0;i<4;i++){112 int res = 999;113 int rc = sqlite3BtreeTableMoveto(&cur, keys[i], 0, &res);114 assert_ok_int(rc, "sqlite3BtreeTableMoveto(exact)");115 TEST_ASSERT_EQUAL_INT_MESSAGE(0, res, "Exact match should set res=0");116 /* Verify cursor points to that rowid */117 i64 k = sqlite3BtreeIntegerKey(&cur);118 TEST_ASSERT_EQUAL_INT64(keys[i], k);119 }120 121 /* Between-range: search 25 should point to 30 with res>0 */122 {123 int res = 999;124 int rc = sqlite3BtreeTableMoveto(&cur, 25, 0, &res);125 assert_ok_int(rc, "sqlite3BtreeTableMoveto(25)");126 TEST_ASSERT_TRUE_MESSAGE(res>0, "25 should yield res>0 (points to larger key)");127 i64 k = sqlite3BtreeIntegerKey(&cur);128 TEST_ASSERT_EQUAL_INT64(30, k);129 }130 131 /* Below all: search 5 should point to 10 with res>0 */132 {133 int res = 999;134 int rc = sqlite3BtreeTableMoveto(&cur, 5, 0, &res);135 assert_ok_int(rc, "sqlite3BtreeTableMoveto(5)");136 TEST_ASSERT_TRUE_MESSAGE(res>0, "5 should yield res>0 (points to larger key)");137 i64 k = sqlite3BtreeIntegerKey(&cur);138 TEST_ASSERT_EQUAL_INT64(10, k);139 }140 141 close_cursor(&cur);142}143 144/* Test: search greater-than-last returns res<0 and leaves cursor at last */145void test_sqlite3BtreeTableMoveto_greater_than_last(void){146 Pgno root = create_intkey_table();147 const i64 keys[] = {10, 20, 30, 40};148 insert_keys(root, keys, (int)(sizeof(keys)/sizeof(keys[0])));149 150 BtCursor cur;151 open_read_cursor(root, &cur);152 153 int res = 999;154 int rc = sqlite3BtreeTableMoveto(&cur, 100, 0, &res);155 assert_ok_int(rc, "sqlite3BtreeTableMoveto(>last)");156 TEST_ASSERT_TRUE_MESSAGE(res<0, ">last should yield res<0");157 /* Cursor should be at last key (40) */158 i64 k = sqlite3BtreeIntegerKey(&cur);159 TEST_ASSERT_EQUAL_INT64(40, k);160 161 close_cursor(&cur);162}163 164/* Test: biasRight flag should not change search result */165void test_sqlite3BtreeTableMoveto_biasRight_independence(void){166 Pgno root = create_intkey_table();167 const i64 keys[] = {10, 20, 30, 40};168 insert_keys(root, keys, (int)(sizeof(keys)/sizeof(keys[0])));169 170 BtCursor cur;171 open_read_cursor(root, &cur);172 173 int res0 = 0, res1 = 0;174 i64 k0 = 0, k1 = 0;175 176 int rc = sqlite3BtreeTableMoveto(&cur, 25, 0, &res0);177 assert_ok_int(rc, "sqlite3BtreeTableMoveto(25,biasRight=0)");178 k0 = sqlite3BtreeIntegerKey(&cur);179 180 rc = sqlite3BtreeTableMoveto(&cur, 25, 1, &res1);181 assert_ok_int(rc, "sqlite3BtreeTableMoveto(25,biasRight=1)");182 k1 = sqlite3BtreeIntegerKey(&cur);183 184 TEST_ASSERT_EQUAL_INT(res0, res1);185 TEST_ASSERT_EQUAL_INT64(k0, k1);186 187 close_cursor(&cur);188}189 190/* Test: AtLast optimization (cursor already at last and key greater) */191void test_sqlite3BtreeTableMoveto_atlast_optimization(void){192 Pgno root = create_intkey_table();193 const i64 keys[] = {10, 20, 30, 40};194 insert_keys(root, keys, (int)(sizeof(keys)/sizeof(keys[0])));195 196 BtCursor cur;197 open_read_cursor(root, &cur);198 199 int pres = 999;200 int rc = sqlite3BtreeLast(&cur, &pres);201 assert_ok_int(rc, "sqlite3BtreeLast");202 TEST_ASSERT_EQUAL_INT(0, pres);203 i64 lastKey = sqlite3BtreeIntegerKey(&cur);204 TEST_ASSERT_EQUAL_INT64(40, lastKey);205 206 /* Now search for a key greater than last; expect res<0 and to remain at last */207 int res = 888;208 rc = sqlite3BtreeTableMoveto(&cur, 1000, 0, &res);209 assert_ok_int(rc, "sqlite3BtreeTableMoveto(atlast greater)");210 TEST_ASSERT_TRUE_MESSAGE(res<0, "At-last and greater search: res<0");211 i64 stillLast = sqlite3BtreeIntegerKey(&cur);212 TEST_ASSERT_EQUAL_INT64(40, stillLast);213 214 close_cursor(&cur);215}216 217/* Test: Fast path when cursor valid at key K and search K+1 with K+1 present */218void test_sqlite3BtreeTableMoveto_fastpath_increment(void){219 Pgno root = create_intkey_table();220 const i64 keys[] = {20, 21, 30};221 insert_keys(root, keys, (int)(sizeof(keys)/sizeof(keys[0])));222 223 BtCursor cur;224 open_read_cursor(root, &cur);225 226 /* Position exactly at 20 first */227 int res = 999;228 int rc = sqlite3BtreeTableMoveto(&cur, 20, 0, &res);229 assert_ok_int(rc, "sqlite3BtreeTableMoveto(20)");230 TEST_ASSERT_EQUAL_INT(0, res);231 TEST_ASSERT_EQUAL_INT64(20, sqlite3BtreeIntegerKey(&cur));232 233 /* Now search for 21, should use fast path and land on 21 */234 res = 777;235 rc = sqlite3BtreeTableMoveto(&cur, 21, 0, &res);236 assert_ok_int(rc, "sqlite3BtreeTableMoveto(21 fastpath)");237 TEST_ASSERT_EQUAL_INT(0, res);238 TEST_ASSERT_EQUAL_INT64(21, sqlite3BtreeIntegerKey(&cur));239 240 close_cursor(&cur);241}242 243int main(void){244 UNITY_BEGIN();245 RUN_TEST(test_sqlite3BtreeTableMoveto_empty_table);246 RUN_TEST(test_sqlite3BtreeTableMoveto_exact_and_range);247 RUN_TEST(test_sqlite3BtreeTableMoveto_greater_than_last);248 RUN_TEST(test_sqlite3BtreeTableMoveto_biasRight_independence);249 RUN_TEST(test_sqlite3BtreeTableMoveto_atlast_optimization);250 RUN_TEST(test_sqlite3BtreeTableMoveto_fastpath_increment);251 return UNITY_END();252}