AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <string.h>5#include <stdlib.h>6 7/* Helpers to reduce boilerplate in tests */8 9static void open_ephemeral_btree(sqlite3 **ppDb, Btree **ppBt){10 int rc;11 sqlite3 *db = 0;12 Btree *pBt = 0;13 sqlite3_vfs *pVfs = sqlite3_vfs_find(0);14 15 rc = sqlite3_open(":memory:", &db);16 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);17 TEST_ASSERT_NOT_NULL(db);18 19 /* Open an ephemeral Btree (zFilename==NULL) */20 rc = sqlite3BtreeOpen(pVfs, 0, db, &pBt, 0, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY);21 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);22 TEST_ASSERT_NOT_NULL(pBt);23 24 *ppDb = db;25 *ppBt = pBt;26}27 28static void close_ephemeral_btree(sqlite3 *db, Btree *pBt){29 /* Close the btree and the database connection */30 if( pBt ){31 int rc = sqlite3BtreeClose(pBt);32 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);33 }34 if( db ){35 int rc = sqlite3_close(db);36 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);37 }38}39 40static Pgno create_intkey_table(Btree *pBt){41 int rc;42 Pgno root = 0;43 /* Begin a write transaction and create a table with intkey+leafdata */44 rc = sqlite3BtreeBeginTrans(pBt, 1, 0);45 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);46 47 rc = sqlite3BtreeCreateTable(pBt, &root, BTREE_INTKEY | BTREE_LEAFDATA);48 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);49 TEST_ASSERT_TRUE(root > 0);50 return root;51}52 53static BtCursor* open_cursor(Btree *pBt, Pgno root, int wr){54 int rc;55 int sz = sqlite3BtreeCursorSize();56 BtCursor *pCur = (BtCursor*)sqlite3MallocZero((u64)sz);57 TEST_ASSERT_NOT_NULL(pCur);58 sqlite3BtreeCursorZero(pCur);59 rc = sqlite3BtreeCursor(pBt, root, wr ? BTREE_WRCSR : 0, 0, pCur);60 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);61 return pCur;62}63 64static void close_cursor(BtCursor *pCur){65 if( pCur ){66 int rc = sqlite3BtreeCloseCursor(pCur);67 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);68 sqlite3_free(pCur);69 }70}71 72static void insert_intkey_row(BtCursor *pCur, sqlite3_int64 key){73 BtreePayload x;74 int rc;75 memset(&x, 0, sizeof(x));76 x.pKey = 0; /* intkey table => pKey must be NULL */77 x.nKey = key; /* rowid */78 x.pData = 0; /* zero-length payload */79 x.nData = 0;80 x.nZero = 0;81 rc = sqlite3BtreeInsert(pCur, &x, BTREE_APPEND, 0);82 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);83}84 85/* Unity boilerplate */86 87void setUp(void) {88 /* nothing */89}90void tearDown(void) {91 /* nothing */92}93 94/* Tests */95 96void test_sqlite3BtreeNext_on_empty_table_returns_done(void){97 sqlite3 *db = 0;98 Btree *pBt = 0;99 Pgno root;100 BtCursor *pRead = 0;101 102 open_ephemeral_btree(&db, &pBt);103 root = create_intkey_table(pBt);104 105 /* Open read transaction and cursor */106 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeBeginTrans(pBt, 0, 0));107 pRead = open_cursor(pBt, root, 0);108 109 int isEmpty = -1;110 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeFirst(pRead, &isEmpty));111 TEST_ASSERT_EQUAL_INT(1, isEmpty); /* table empty */112 113 /* Next on empty/invalid should return DONE and leave cursor invalid */114 int rc = sqlite3BtreeNext(pRead, 0);115 TEST_ASSERT_EQUAL_INT(SQLITE_DONE, rc);116 TEST_ASSERT_EQUAL_INT(CURSOR_INVALID, pRead->eState);117 118 close_cursor(pRead);119 /* Commit/cleanup */120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeCommit(pBt));121 close_ephemeral_btree(db, pBt);122}123 124void test_sqlite3BtreeNext_iterates_small_table_and_returns_done(void){125 sqlite3 *db = 0;126 Btree *pBt = 0;127 Pgno root;128 BtCursor *pWrite = 0;129 BtCursor *pRead = 0;130 131 open_ephemeral_btree(&db, &pBt);132 root = create_intkey_table(pBt);133 134 /* Open a write cursor and insert 10 ascending rowids */135 pWrite = open_cursor(pBt, root, 1);136 for(int i=1;i<=10;i++){137 insert_intkey_row(pWrite, i);138 }139 140 /* Commit writes so we can test read cursor alone or just leave open */141 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeCommit(pBt));142 143 /* Open a read transaction and read cursor */144 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeBeginTrans(pBt, 0, 0));145 pRead = open_cursor(pBt, root, 0);146 147 int res = -1;148 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeFirst(pRead, &res));149 TEST_ASSERT_EQUAL_INT(0, res);150 151 int count = 0;152 sqlite3_int64 expect = 1;153 do{154 sqlite3_int64 k = sqlite3BtreeIntegerKey(pRead);155 TEST_ASSERT_EQUAL_INT64(expect, k);156 count++;157 expect++;158 /* Make sure flags arg 1 is accepted (hint) */159 int rc = sqlite3BtreeNext(pRead, 1);160 if( rc==SQLITE_DONE ){161 break;162 }163 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);164 }while(1);165 166 TEST_ASSERT_EQUAL_INT(10, count);167 TEST_ASSERT_EQUAL_INT(CURSOR_INVALID, pRead->eState);168 169 close_cursor(pRead);170 close_cursor(pWrite);171 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeCommit(pBt));172 close_ephemeral_btree(db, pBt);173}174 175void test_sqlite3BtreeNext_iterates_across_multiple_pages(void){176 sqlite3 *db = 0;177 Btree *pBt = 0;178 Pgno root;179 BtCursor *pWrite = 0;180 BtCursor *pRead = 0;181 182 open_ephemeral_btree(&db, &pBt);183 root = create_intkey_table(pBt);184 185 /* Insert many rows to force page splits and internal nodes. */186 pWrite = open_cursor(pBt, root, 1);187 const int N = 2500; /* Should be enough to span multiple pages */188 for(int i=1;i<=N;i++){189 insert_intkey_row(pWrite, (sqlite3_int64)i);190 }191 192 /* Commit writes */193 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeCommit(pBt));194 195 /* Open a read cursor and iterate all rows */196 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeBeginTrans(pBt, 0, 0));197 pRead = open_cursor(pBt, root, 0);198 199 int res = -1;200 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeFirst(pRead, &res));201 TEST_ASSERT_EQUAL_INT(0, res);202 203 int count = 0;204 sqlite3_int64 expected = 1;205 while(1){206 sqlite3_int64 k = sqlite3BtreeIntegerKey(pRead);207 if( k!=expected ){208 /* Helpful assert message */209 TEST_FAIL_MESSAGE("Key mismatch during multi-page iteration");210 }211 count++;212 expected++;213 int rc = sqlite3BtreeNext(pRead, 0);214 if( rc==SQLITE_DONE ){215 break;216 }217 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);218 }219 220 TEST_ASSERT_EQUAL_INT(N, count);221 TEST_ASSERT_EQUAL_INT(CURSOR_INVALID, pRead->eState);222 223 close_cursor(pRead);224 close_cursor(pWrite);225 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3BtreeCommit(pBt));226 close_ephemeral_btree(db, pBt);227}228 229int main(void){230 UNITY_BEGIN();231 RUN_TEST(test_sqlite3BtreeNext_on_empty_table_returns_done);232 RUN_TEST(test_sqlite3BtreeNext_iterates_small_table_and_returns_done);233 RUN_TEST(test_sqlite3BtreeNext_iterates_across_multiple_pages);234 return UNITY_END();235}