Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_btree_btreeRestoreCursorPosition.c207 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdio.h>5 6/* Global test context */7static sqlite3 *gDb = NULL;8static Btree *gBt = NULL;9static Pgno gRoot = 0;10 11/* Wrapper for the static function under test (provided by build as per instructions) */12extern int test_btreeRestoreCursorPosition(BtCursor *pCur);13 14/* Helper: exec callback to capture integer from first column */15static int cb_int(void *pArg, int nCol, char **azVal, char **azCol){16  (void)nCol; (void)azCol;17  if( azVal && azVal[0] ){18    *(int*)pArg = atoi(azVal[0]);19  }20  return 0;21}22 23static void open_table_and_fill(void){24  char *zErr = NULL;25  int rc;26 27  rc = sqlite3_exec(gDb,28    "PRAGMA page_size=4096;"29    "CREATE TABLE t(x INTEGER PRIMARY KEY, y TEXT);"30    "INSERT INTO t(x,y) VALUES (1,'a'),(2,'b'),(4,'d');",31    0, 0, &zErr32  );33  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, zErr ? zErr : "exec failed");34  if( zErr ) sqlite3_free(zErr);35 36  /* Query rootpage of table t */37  int root = 0;38  rc = sqlite3_exec(gDb,39    "SELECT rootpage FROM sqlite_master WHERE type='table' AND name='t';",40    cb_int, &root, &zErr41  );42  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, zErr ? zErr : "root exec failed");43  if( zErr ) sqlite3_free(zErr);44  TEST_ASSERT_MESSAGE(root>1, "Expected valid root page > 1");45  gRoot = (Pgno)root;46}47 48static BtCursor* open_btree_cursor_readonly(void){49  int rc;50  int schemaVersion = 0;51  BtCursor *pCur = NULL;52 53  /* Must hold the db mutex for Btree APIs */54  sqlite3_mutex_enter(gDb->mutex);55 56  /* Begin a read transaction */57  rc = sqlite3BtreeBeginTrans(gBt, 0, &schemaVersion);58  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);59 60  int sz = sqlite3BtreeCursorSize();61  pCur = (BtCursor*)sqlite3MallocZero(sz);62  TEST_ASSERT_NOT_NULL(pCur);63  sqlite3BtreeCursorZero(pCur);64 65  rc = sqlite3BtreeCursor(gBt, gRoot, 0, NULL, pCur);66  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);67 68  /* Position at first entry to initialize internal fields */69  int isEmpty = 0;70  rc = sqlite3BtreeFirst(pCur, &isEmpty);71  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);72  TEST_ASSERT_EQUAL_INT(0, isEmpty);73 74  sqlite3_mutex_leave(gDb->mutex);75  return pCur;76}77 78static void close_btree_cursor(BtCursor *pCur){79  int rc;80  sqlite3_mutex_enter(gDb->mutex);81  rc = sqlite3BtreeCloseCursor(pCur);82  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);83  sqlite3_mutex_leave(gDb->mutex);84  sqlite3_free(pCur);85}86 87/* Unity setUp/tearDown */88void setUp(void) {89  int rc = sqlite3_open(":memory:", &gDb);90  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);91  TEST_ASSERT_NOT_NULL(gDb);92 93  /* Access internal Btree for main db */94  gBt = gDb->aDb[0].pBt;95  TEST_ASSERT_NOT_NULL(gBt);96 97  open_table_and_fill();98}99 100void tearDown(void) {101  if( gDb ){102    int rc = sqlite3_close(gDb);103    TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);104    gDb = NULL;105    gBt = NULL;106    gRoot = 0;107  }108}109 110/* Test 1: If cursor is in CURSOR_FAULT state, function returns skipNext value */111void test_btreeRestoreCursorPosition_fault_returns_skipNext(void){112  BtCursor *pCur = open_btree_cursor_readonly();113 114  /* Put cursor into fault state */115  sqlite3_mutex_enter(gDb->mutex);116  sqlite3BtreeEnter(gBt);117 118  pCur->eState = CURSOR_FAULT;119  pCur->skipNext = -12345;120 121  int rc = test_btreeRestoreCursorPosition(pCur);122 123  sqlite3BtreeLeave(gBt);124  sqlite3_mutex_leave(gDb->mutex);125 126  TEST_ASSERT_EQUAL_INT(-12345, rc);127 128  close_btree_cursor(pCur);129}130 131/* Test 2: REQUIRESEEK to an existing rowid seeks correctly and yields VALID state */132void test_btreeRestoreCursorPosition_seek_existing_rowid(void){133  BtCursor *pCur = open_btree_cursor_readonly();134 135  /* Prepare cursor to require seek to existing key 2 */136  sqlite3_mutex_enter(gDb->mutex);137  sqlite3BtreeEnter(gBt);138 139  pCur->eState = CURSOR_REQUIRESEEK;140  pCur->nKey = 2;   /* existing rowid */141  pCur->pKey = NULL;142  pCur->skipNext = 0;143 144  int rc = test_btreeRestoreCursorPosition(pCur);145 146  sqlite3BtreeLeave(gBt);147  sqlite3_mutex_leave(gDb->mutex);148 149  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);150  TEST_ASSERT_EQUAL_INT(CURSOR_VALID, pCur->eState);151  /* Confirm pointing at rowid 2 */152  sqlite3_mutex_enter(gDb->mutex);153  sqlite3BtreeEnter(gBt);154  i64 k = sqlite3BtreeIntegerKey(pCur);155  sqlite3BtreeLeave(gBt);156  sqlite3_mutex_leave(gDb->mutex);157  TEST_ASSERT_EQUAL_INT64(2, k);158 159  close_btree_cursor(pCur);160}161 162/* Test 3: REQUIRESEEK to a non-existing rowid results in SKIPNEXT state */163void test_btreeRestoreCursorPosition_seek_nonexisting_rowid_sets_skipnext(void){164  BtCursor *pCur = open_btree_cursor_readonly();165 166  /* We have rows 1,2,4. Seek to 3 (non-existent). */167  sqlite3_mutex_enter(gDb->mutex);168  sqlite3BtreeEnter(gBt);169 170  pCur->eState = CURSOR_REQUIRESEEK;171  pCur->nKey = 3;   /* non-existing rowid between 2 and 4 */172  pCur->pKey = NULL;173  pCur->skipNext = 0;174 175  int rc = test_btreeRestoreCursorPosition(pCur);176 177  sqlite3BtreeLeave(gBt);178  sqlite3_mutex_leave(gDb->mutex);179 180  TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);181  TEST_ASSERT_EQUAL_INT(CURSOR_SKIPNEXT, pCur->eState);182  TEST_ASSERT_TRUE_MESSAGE(pCur->skipNext==1 || pCur->skipNext==-1,183                           "skipNext should be +/-1 for non-existing key");184 185  /* Verify cursor is on neighbor: either 2 (skipNext<0) or 4 (skipNext>0) */186  sqlite3_mutex_enter(gDb->mutex);187  sqlite3BtreeEnter(gBt);188  i64 k = sqlite3BtreeIntegerKey(pCur);189  sqlite3BtreeLeave(gBt);190  sqlite3_mutex_leave(gDb->mutex);191 192  if( pCur->skipNext < 0 ){193    TEST_ASSERT_EQUAL_INT64(2, k);194  }else{195    TEST_ASSERT_EQUAL_INT64(4, k);196  }197 198  close_btree_cursor(pCur);199}200 201int main(void){202  UNITY_BEGIN();203  RUN_TEST(test_btreeRestoreCursorPosition_fault_returns_skipNext);204  RUN_TEST(test_btreeRestoreCursorPosition_seek_existing_rowid);205  RUN_TEST(test_btreeRestoreCursorPosition_seek_nonexisting_rowid_sets_skipnext);206  return UNITY_END();207}