AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6/* Helper: execute SQL and assert SQLITE_OK */7static void exec_or_fail(sqlite3 *db, const char *sql){8 char *zErr = 0;9 int rc = sqlite3_exec(db, sql, 0, 0, &zErr);10 if( rc!=SQLITE_OK ){11 const char *msg = zErr ? zErr : sqlite3_errmsg(db);12 printf("SQL error: %s\n", msg);13 }14 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);15 sqlite3_free(zErr);16}17 18/* Helper: get rootpage of a table from sqlite_master */19static Pgno get_rootpage(sqlite3 *db, const char *zName){20 Pgno root = 0;21 sqlite3_stmt *pStmt = 0;22 const char *sql = "SELECT rootpage FROM sqlite_master WHERE name=?1";23 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_prepare_v2(db, sql, -1, &pStmt, 0));24 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC));25 if( sqlite3_step(pStmt)==SQLITE_ROW ){26 root = (Pgno)sqlite3_column_int(pStmt, 0);27 }28 sqlite3_finalize(pStmt);29 TEST_ASSERT_MESSAGE(root!=0, "Failed to fetch rootpage");30 return root;31}32 33/* Helper: open a read-only BtCursor on given root page */34static void open_read_cursor(sqlite3 *db, Pgno root, BtCursor **ppCur){35 Btree *pBt = db->aDb[0].pBt;36 int n = sqlite3BtreeCursorSize();37 BtCursor *pCur = (BtCursor*)sqlite3_malloc(n);38 TEST_ASSERT_NOT_NULL(pCur);39 memset(pCur, 0, n);40 sqlite3BtreeCursorZero(pCur);41 42 /* Start a read transaction to satisfy assertions in sqlite3BtreeCursor */43 sqlite3_mutex_enter(db->mutex);44 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeBeginTrans(pBt, 0, 0));45 sqlite3_mutex_leave(db->mutex);46 47 /* Use API wrapper that manages BtShared mutex internally */48 TEST_ASSERT_EQUAL_INT(SQLITE_OK,49 sqlite3BtreeCursor(pBt, root, 0 /* read-only */, 0, pCur)50 );51 *ppCur = pCur;52}53 54/* Helper: close cursor and free memory */55static void close_cursor(BtCursor *pCur){56 if( pCur ){57 sqlite3BtreeCloseCursor(pCur);58 sqlite3_free(pCur);59 }60}61 62/* setUp/tearDown */63void setUp(void) { }64void tearDown(void) { }65 66/* Test 1: Empty table -> sqlite3BtreeLast returns *pRes==1, SQLITE_OK */67void test_sqlite3BtreeLast_empty_table(void){68 sqlite3 *db = 0;69 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));70 exec_or_fail(db, "PRAGMA auto_vacuum=0");71 exec_or_fail(db, "CREATE TABLE t1(x)");72 73 Pgno root = get_rootpage(db, "t1");74 75 BtCursor *pCur = 0;76 open_read_cursor(db, root, &pCur);77 78 int res = -1;79 /* Hold db mutex and BtShared mutex as required by implementation asserts */80 sqlite3_mutex_enter(db->mutex);81 sqlite3BtreeEnter(db->aDb[0].pBt);82 int rc = sqlite3BtreeLast(pCur, &res);83 sqlite3BtreeLeave(db->aDb[0].pBt);84 sqlite3_mutex_leave(db->mutex);85 86 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);87 TEST_ASSERT_EQUAL_INT(1, res);88 89 close_cursor(pCur);90 sqlite3_close(db);91}92 93/* Test 2: Non-empty table -> *pRes==0, cursor at last row, AtLast flag set */94void test_sqlite3BtreeLast_nonempty_table_simple(void){95 sqlite3 *db = 0;96 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));97 exec_or_fail(db, "PRAGMA auto_vacuum=0");98 exec_or_fail(db, "CREATE TABLE t2(x)");99 exec_or_fail(db, "INSERT INTO t2 VALUES('a'),('b'),('c'),('d'),('e')");100 101 Pgno root = get_rootpage(db, "t2");102 103 BtCursor *pCur = 0;104 open_read_cursor(db, root, &pCur);105 106 int res = -1;107 sqlite3_mutex_enter(db->mutex);108 sqlite3BtreeEnter(db->aDb[0].pBt);109 int rc = sqlite3BtreeLast(pCur, &res);110 sqlite3BtreeLeave(db->aDb[0].pBt);111 sqlite3_mutex_leave(db->mutex);112 113 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);114 TEST_ASSERT_EQUAL_INT(0, res);115 116 /* Verify last rowid == 5 and BTCF_AtLast flag set */117 sqlite3BtreeEnter(db->aDb[0].pBt);118 i64 lastRowid = sqlite3BtreeIntegerKey(pCur);119 sqlite3BtreeLeave(db->aDb[0].pBt);120 TEST_ASSERT_EQUAL_INT64(5, lastRowid);121 TEST_ASSERT_TRUE((pCur->curFlags & BTCF_AtLast)!=0);122 123 /* Call again to exercise the fast-path; still *pRes==0 and AtLast flag */124 sqlite3_mutex_enter(db->mutex);125 sqlite3BtreeEnter(db->aDb[0].pBt);126 res = -1;127 rc = sqlite3BtreeLast(pCur, &res);128 sqlite3BtreeLeave(db->aDb[0].pBt);129 sqlite3_mutex_leave(db->mutex);130 131 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);132 TEST_ASSERT_EQUAL_INT(0, res);133 TEST_ASSERT_TRUE((pCur->curFlags & BTCF_AtLast)!=0);134 135 close_cursor(pCur);136 sqlite3_close(db);137}138 139/* Test 3: Deep btree (force internal nodes) -> last row correct */140void test_sqlite3BtreeLast_deep_tree(void){141 sqlite3 *db = 0;142 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));143 /* Force small page size to get internal nodes quickly */144 exec_or_fail(db, "PRAGMA page_size=512");145 exec_or_fail(db, "PRAGMA auto_vacuum=0");146 exec_or_fail(db, "CREATE TABLE t3(x)");147 /* Insert many rows with large payload to split pages and create height>1 */148 {149 sqlite3_stmt *pIns = 0;150 TEST_ASSERT_EQUAL_INT(SQLITE_OK,151 sqlite3_prepare_v2(db, "INSERT INTO t3(rowid,x) VALUES(?,?)", -1, &pIns, 0)152 );153 char big[1000];154 memset(big, 'x', sizeof(big));155 int N = 200;156 for(int i=1; i<=N; i++){157 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_bind_int64(pIns, 1, (sqlite3_int64)i));158 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_bind_text(pIns, 2, big, (int)sizeof(big), SQLITE_STATIC));159 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, sqlite3_step(pIns));160 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_reset(pIns));161 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_clear_bindings(pIns));162 }163 sqlite3_finalize(pIns);164 }165 166 Pgno root = get_rootpage(db, "t3");167 168 BtCursor *pCur = 0;169 open_read_cursor(db, root, &pCur);170 171 int res = -1;172 sqlite3_mutex_enter(db->mutex);173 sqlite3BtreeEnter(db->aDb[0].pBt);174 int rc = sqlite3BtreeLast(pCur, &res);175 sqlite3BtreeLeave(db->aDb[0].pBt);176 sqlite3_mutex_leave(db->mutex);177 178 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);179 TEST_ASSERT_EQUAL_INT(0, res);180 181 sqlite3BtreeEnter(db->aDb[0].pBt);182 i64 lastRowid = sqlite3BtreeIntegerKey(pCur);183 sqlite3BtreeLeave(db->aDb[0].pBt);184 TEST_ASSERT_EQUAL_INT64(200, lastRowid);185 186 close_cursor(pCur);187 sqlite3_close(db);188}189 190int main(void){191 UNITY_BEGIN();192 RUN_TEST(test_sqlite3BtreeLast_empty_table);193 RUN_TEST(test_sqlite3BtreeLast_nonempty_table_simple);194 RUN_TEST(test_sqlite3BtreeLast_deep_tree);195 return UNITY_END();196}