AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <stdlib.h>5#include <stdio.h>6 7/* Utility: execute SQL and assert success */8static void execSQL(sqlite3 *db, const char *zSql){9 char *zErr = 0;10 int rc = sqlite3_exec(db, zSql, 0, 0, &zErr);11 if( rc!=SQLITE_OK ){12 /* Print error to help debugging */13 if( zErr ){14 fprintf(stderr, "SQL error: %s\n", zErr);15 }16 }17 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, zErr ? zErr : "exec failed");18 sqlite3_free(zErr);19}20 21/* Utility: open a read cursor for a table by name in "main" schema */22static int openReadCursorForTable(23 sqlite3 *db,24 const char *zTabName,25 Btree **ppBtree,26 BtCursor **ppCur,27 Pgno *pRoot28){29 TEST_ASSERT_NOT_NULL(db);30 TEST_ASSERT_NOT_NULL(ppBtree);31 TEST_ASSERT_NOT_NULL(ppCur);32 TEST_ASSERT_NOT_NULL(pRoot);33 34 /* Find the table root page (tnum) via internal schema */35 Table *pTab = sqlite3FindTable(db, zTabName, "main");36 TEST_ASSERT_NOT_NULL_MESSAGE(pTab, "Table not found in schema");37 *pRoot = (Pgno)pTab->tnum;38 39 /* Get the Btree for "main" */40 *ppBtree = db->aDb[0].pBt;41 TEST_ASSERT_NOT_NULL_MESSAGE(*ppBtree, "Main Btree is NULL");42 43 /* Begin a read transaction */44 int rc = sqlite3BtreeBeginTrans(*ppBtree, 0, 0);45 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);46 47 /* Allocate and open a cursor */48 int nSize = sqlite3BtreeCursorSize();49 BtCursor *pCur = (BtCursor*)sqlite3_malloc(nSize);50 TEST_ASSERT_NOT_NULL_MESSAGE(pCur, "malloc BtCursor failed");51 sqlite3BtreeCursorZero(pCur);52 rc = sqlite3BtreeCursor(*ppBtree, *pRoot, 0, 0, pCur);53 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);54 55 *ppCur = pCur;56 return SQLITE_OK;57}58 59/* Utility: close and free BtCursor */60static void closeAndFreeCursor(BtCursor *pCur){61 if( pCur ){62 sqlite3BtreeCloseCursor(pCur);63 sqlite3_free(pCur);64 }65}66 67void setUp(void){68 /* nothing */69}70void tearDown(void){71 /* nothing */72}73 74/* Test 1: Empty table should count 0 entries */75void test_sqlite3BtreeCount_empty_table(void){76 sqlite3 *db = 0;77 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));78 79 execSQL(db, "CREATE TABLE t_empty(x)");80 81 Btree *pBt = 0;82 BtCursor *pCur = 0;83 Pgno root = 0;84 TEST_ASSERT_EQUAL_INT(SQLITE_OK,85 openReadCursorForTable(db, "t_empty", &pBt, &pCur, &root)86 );87 88 i64 n = -1;89 int rc = sqlite3BtreeCount(db, pCur, &n);90 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);91 TEST_ASSERT_EQUAL_INT64(0, n);92 93 closeAndFreeCursor(pCur);94 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));95}96 97/* Test 2: Small table count */98void test_sqlite3BtreeCount_small_table(void){99 sqlite3 *db = 0;100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));101 102 execSQL(db, "CREATE TABLE t_small(a)");103 /* Insert 10 rows */104 for(int i=0;i<10;i++){105 char sql[64];106 sqlite3_snprintf(sizeof(sql), sql, "INSERT INTO t_small VALUES(%d);", i);107 execSQL(db, sql);108 }109 110 Btree *pBt = 0;111 BtCursor *pCur = 0;112 Pgno root = 0;113 TEST_ASSERT_EQUAL_INT(SQLITE_OK,114 openReadCursorForTable(db, "t_small", &pBt, &pCur, &root)115 );116 117 i64 n = -1;118 int rc = sqlite3BtreeCount(db, pCur, &n);119 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);120 TEST_ASSERT_EQUAL_INT64(10, n);121 122 closeAndFreeCursor(pCur);123 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));124}125 126/* Test 3: Large table to force multiple pages */127void test_sqlite3BtreeCount_large_table_multi_page(void){128 sqlite3 *db = 0;129 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));130 131 execSQL(db, "CREATE TABLE t_big(a)");132 /* Insert 2000 rows - enough to span several pages in most default builds */133 const int N = 2000;134 execSQL(db, "BEGIN");135 for(int i=0;i<N;i++){136 char sql[64];137 sqlite3_snprintf(sizeof(sql), sql, "INSERT INTO t_big VALUES(%d);", i);138 execSQL(db, sql);139 }140 execSQL(db, "COMMIT");141 142 Btree *pBt = 0;143 BtCursor *pCur = 0;144 Pgno root = 0;145 TEST_ASSERT_EQUAL_INT(SQLITE_OK,146 openReadCursorForTable(db, "t_big", &pBt, &pCur, &root)147 );148 149 i64 n = -1;150 int rc = sqlite3BtreeCount(db, pCur, &n);151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);152 TEST_ASSERT_EQUAL_INT64(N, n);153 154 closeAndFreeCursor(pCur);155 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));156}157 158/* Test 4: Interrupt flag set causes traversal loop to skip; expect count 0 */159void test_sqlite3BtreeCount_interrupted_returns_zero(void){160 sqlite3 *db = 0;161 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", &db));162 163 execSQL(db, "CREATE TABLE t_int(a)");164 /* Insert some rows */165 for(int i=0;i<50;i++){166 char sql[64];167 sqlite3_snprintf(sizeof(sql), sql, "INSERT INTO t_int VALUES(%d);", i);168 execSQL(db, sql);169 }170 171 Btree *pBt = 0;172 BtCursor *pCur = 0;173 Pgno root = 0;174 TEST_ASSERT_EQUAL_INT(SQLITE_OK,175 openReadCursorForTable(db, "t_int", &pBt, &pCur, &root)176 );177 178 /* Set the interrupt flag before calling count */179 db->u1.isInterrupted = 1;180 181 i64 n = -1;182 int rc = sqlite3BtreeCount(db, pCur, &n);183 /* Function still returns SQLITE_OK but should have skipped traversal */184 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);185 TEST_ASSERT_EQUAL_INT64(0, n);186 187 /* Clear interrupt flag for cleanliness */188 db->u1.isInterrupted = 0;189 190 closeAndFreeCursor(pCur);191 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_close(db));192}193 194int main(void){195 UNITY_BEGIN();196 RUN_TEST(test_sqlite3BtreeCount_empty_table);197 RUN_TEST(test_sqlite3BtreeCount_small_table);198 RUN_TEST(test_sqlite3BtreeCount_large_table_multi_page);199 RUN_TEST(test_sqlite3BtreeCount_interrupted_returns_zero);200 return UNITY_END();201}