AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6#include <stdio.h>7 8/* Helpers */9 10static void cleanup_db_files(const char *path){11 if( path==0 ) return;12 /* Remove main db file */13 remove(path);14 /* Remove possible -wal and -shm sidecars */15 char buf[1024];16 sqlite3_snprintf(sizeof(buf), buf, "%s-wal", path);17 remove(buf);18 sqlite3_snprintf(sizeof(buf), buf, "%s-shm", path);19 remove(buf);20}21 22static void exec_sql_or_fail(sqlite3 *db, const char *sql){23 char *errmsg = 0;24 int rc = sqlite3_exec(db, sql, 0, 0, &errmsg);25 if( rc!=SQLITE_OK ){26 const char *em = errmsg ? errmsg : "sqlite3_exec failed";27 /* Ensure errmsg freed before failing */28 if( errmsg ) sqlite3_free(errmsg);29 TEST_FAIL_MESSAGE(em);30 }31 if( errmsg ) sqlite3_free(errmsg);32}33 34static int get_device_characteristics(sqlite3 *db){35 sqlite3_stmt *stmt = 0;36 int dev = 0;37 int rc = sqlite3_prepare_v2(db, "PRAGMA main.device_characteristics;", -1, &stmt, 0);38 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);39 if( sqlite3_step(stmt)==SQLITE_ROW ){40 dev = sqlite3_column_int(stmt, 0);41 }42 sqlite3_finalize(stmt);43 return dev;44}45 46static Pager *get_main_pager(sqlite3 *db){47 /* Ensure main database is opened by touching it */48 exec_sql_or_fail(db, "PRAGMA main.journal_mode;");49 Btree *pBt = db->aDb[0].pBt;50 TEST_ASSERT_NOT_NULL_MESSAGE(pBt, "Main database Btree not initialized");51 Pager *pPager = sqlite3BtreePager(pBt);52 TEST_ASSERT_NOT_NULL_MESSAGE(pPager, "Pager is NULL");53 return pPager;54}55 56static sqlite3* open_db_file(const char *path){57 sqlite3 *db = 0;58 int rc;59 cleanup_db_files(path);60 rc = sqlite3_open(path, &db);61 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");62 TEST_ASSERT_NOT_NULL(db);63 return db;64}65 66/* Unity fixtures */67 68void setUp(void) {69 /* No global setup required */70}71 72void tearDown(void) {73 /* No global teardown required */74}75 76/* Tests */77 78void test_sqlite3PagerDirectReadOk_clean_delete_mode(void){79 const char *path = "test_pager_directread_clean.sqlite";80 sqlite3 *db = open_db_file(path);81 82 /* Ensure DELETE mode (no WAL), and DB is clean (no dirty pages) */83 exec_sql_or_fail(db, "PRAGMA journal_mode=DELETE;");84 /* Touch the database minimally to ensure it exists and pager is initialized */85 exec_sql_or_fail(db, "CREATE TABLE IF NOT EXISTS tclean(x);");86 /* Ensure we're not in a transaction and cache is clean */87 exec_sql_or_fail(db, "COMMIT;");88 89 Pager *pPager = get_main_pager(db);90 int dev = get_device_characteristics(db);91 int expected = ( (dev & SQLITE_IOCAP_SUBPAGE_READ) ? 1 : 0 );92 93 int rc = sqlite3PagerDirectReadOk(pPager, (Pgno)1);94 TEST_ASSERT_EQUAL_INT(expected, rc);95 96 sqlite3_close(db);97 cleanup_db_files(path);98}99 100void test_sqlite3PagerDirectReadOk_dirty_cache_returns_zero(void){101 const char *path = "test_pager_directread_dirty.sqlite";102 sqlite3 *db = open_db_file(path);103 104 exec_sql_or_fail(db, "PRAGMA journal_mode=DELETE;");105 /* Begin a write txn and create a table to ensure dirty pages in cache */106 exec_sql_or_fail(db, "BEGIN IMMEDIATE;");107 exec_sql_or_fail(db, "CREATE TABLE tdirty(x);");108 109 Pager *pPager = get_main_pager(db);110 int rc = sqlite3PagerDirectReadOk(pPager, (Pgno)1);111 TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Expected 0 when dirty pages are present");112 113 /* Cleanup transaction */114 exec_sql_or_fail(db, "ROLLBACK;");115 116 sqlite3_close(db);117 cleanup_db_files(path);118}119 120void test_sqlite3PagerDirectReadOk_wal_page1_in_wal_returns_zero(void){121 if( sqlite3_compileoption_used("OMIT_WAL") ){122 TEST_IGNORE_MESSAGE("SQLite compiled without WAL support; skipping WAL tests.");123 }124 125 const char *path = "test_pager_directread_wal.sqlite";126 sqlite3 *db = open_db_file(path);127 128 /* Enable WAL mode */129 exec_sql_or_fail(db, "PRAGMA journal_mode=WAL;");130 131 /* Perform a transaction that will put page 1 into the WAL (change-counter update) */132 exec_sql_or_fail(db, "BEGIN;");133 exec_sql_or_fail(db, "CREATE TABLE twal(x);");134 exec_sql_or_fail(db, "COMMIT;");135 136 /* Now cache should be clean, but page 1 should be present in WAL */137 Pager *pPager = get_main_pager(db);138 int rc = sqlite3PagerDirectReadOk(pPager, (Pgno)1);139 TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Expected 0 when requested page is present in WAL");140 141 sqlite3_close(db);142 cleanup_db_files(path);143}144 145void test_sqlite3PagerDirectReadOk_wal_page_not_present_matches_device_caps(void){146 if( sqlite3_compileoption_used("OMIT_WAL") ){147 TEST_IGNORE_MESSAGE("SQLite compiled without WAL support; skipping WAL tests.");148 }149 150 const char *path = "test_pager_directread_wal2.sqlite";151 sqlite3 *db = open_db_file(path);152 153 /* Enable WAL mode */154 exec_sql_or_fail(db, "PRAGMA journal_mode=WAL;");155 /* Make some changes and commit so cache is clean and WAL exists */156 exec_sql_or_fail(db, "BEGIN;");157 exec_sql_or_fail(db, "CREATE TABLE twal2(y);");158 exec_sql_or_fail(db, "COMMIT;");159 160 Pager *pPager = get_main_pager(db);161 int dev = get_device_characteristics(db);162 int expected = ( (dev & SQLITE_IOCAP_SUBPAGE_READ) ? 1 : 0 );163 164 /* Use a page number that is almost certainly not present in the WAL */165 Pgno farPg = (Pgno)999999u;166 int rc = sqlite3PagerDirectReadOk(pPager, farPg);167 TEST_ASSERT_EQUAL_INT(expected, rc);168 169 sqlite3_close(db);170 cleanup_db_files(path);171}172 173/* Main */174 175int main(void){176 UNITY_BEGIN();177 178 RUN_TEST(test_sqlite3PagerDirectReadOk_clean_delete_mode);179 RUN_TEST(test_sqlite3PagerDirectReadOk_dirty_cache_returns_zero);180 RUN_TEST(test_sqlite3PagerDirectReadOk_wal_page1_in_wal_returns_zero);181 RUN_TEST(test_sqlite3PagerDirectReadOk_wal_page_not_present_matches_device_caps);182 183 return UNITY_END();184}