AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdio.h>6 7/* Prototype of the provided test wrapper for the static function */8int test_pagerOpenSavepoint(Pager *pPager, int nSavepoint);9 10static void exec_sql_ok(sqlite3 *db, const char *sql){11 char *zErr = 0;12 int rc = sqlite3_exec(db, sql, 0, 0, &zErr);13 if( rc!=SQLITE_OK ){14 fprintf(stderr, "SQL error (%d): %s while executing: %s\n", rc, zErr?zErr:"(null)", sql);15 }16 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);17 if( zErr ) sqlite3_free(zErr);18}19 20static sqlite3* open_file_db(const char *zFilename){21 sqlite3 *db = 0;22 int rc = sqlite3_open(zFilename, &db);23 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);24 TEST_ASSERT_NOT_NULL(db);25 return db;26}27 28static Pager* getPagerFromDb(sqlite3 *db){29 TEST_ASSERT_NOT_NULL(db);30 /* Main database at schema index 0 */31 TEST_ASSERT_NOT_NULL(db->aDb);32 TEST_ASSERT_NOT_NULL(db->aDb[0].pBt);33 return sqlite3BtreePager(db->aDb[0].pBt);34}35 36static void prepare_db_baseline(sqlite3 *db){37 /* Ensure rollback-journal mode (not WAL) and create content so dbSize>0 */38 exec_sql_ok(db, "PRAGMA journal_mode=DELETE;");39 exec_sql_ok(db, "BEGIN IMMEDIATE;"); /* Ensure db file exists/writable if needed */40 exec_sql_ok(db, "CREATE TABLE IF NOT EXISTS t(x);");41 exec_sql_ok(db, "COMMIT;"); /* Persist initial content */42}43 44static void begin_immediate(sqlite3 *db){45 exec_sql_ok(db, "BEGIN IMMEDIATE;");46}47 48static void rollback_and_close(sqlite3 *db, const char *zFilename){49 /* Safe rollback (in case transaction is active) and close */50 if( db ){51 sqlite3_exec(db, "ROLLBACK;", 0, 0, 0);52 sqlite3_close(db);53 }54 if( zFilename ){55 /* Best-effort cleanup */56 remove(zFilename);57 }58}59 60void setUp(void) {61 /* No-op */62}63 64void tearDown(void) {65 /* No-op */66}67 68/* Test 1: Create a single savepoint with no prior journal activity (journalOff==0).69 Validate that the savepoint is created and initialized correctly. */70void test_pagerOpenSavepoint_basic_no_dirty(void){71 const char *zDb = "test_pagerOpenSavepoint_basic_no_dirty.sqlite";72 sqlite3 *db = open_file_db(zDb);73 prepare_db_baseline(db);74 75 begin_immediate(db);76 Pager *pPager = getPagerFromDb(db);77 78 /* Preconditions expected by the function/asserts */79 TEST_ASSERT_TRUE(pPager->useJournal!=0);80 TEST_ASSERT_TRUE(pPager->eState>=PAGER_WRITER_LOCKED);81 82 int n0 = pPager->nSavepoint;83 TEST_ASSERT_EQUAL_INT(0, n0);84 85 Pgno dbSizeAtCall = pPager->dbSize;86 i64 joBefore = pPager->journalOff; /* likely 0 since no dirty pages yet */87 88 int rc = test_pagerOpenSavepoint(pPager, 1);89 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);90 91 TEST_ASSERT_EQUAL_INT(1, pPager->nSavepoint);92 TEST_ASSERT_NOT_NULL(pPager->aSavepoint);93 94 PagerSavepoint *ps0 = &pPager->aSavepoint[0];95 TEST_ASSERT_NOT_NULL(ps0->pInSavepoint);96 TEST_ASSERT_EQUAL_INT(1, ps0->bTruncateOnRelease);97 98 TEST_ASSERT_EQUAL_UINT(dbSizeAtCall, ps0->nOrig);99 TEST_ASSERT_EQUAL_INT(pPager->nSubRec, ps0->iSubRec);100 101 if( joBefore>0 ){102 TEST_ASSERT_TRUE(((sqlite3_int64)ps0->iOffset) == joBefore);103 }else{104 /* When journalOff==0 at call time, pagerOpenSavepoint uses header size.105 We can at least assert it is non-zero and not negative. */106 TEST_ASSERT_TRUE(ps0->iOffset > 0);107 }108 109 rollback_and_close(db, zDb);110}111 112/* Test 2: Create a savepoint after performing a write, ensuring journalOff>0.113 Validate iOffset captured equals journalOff at time of creation and other fields match. */114void test_pagerOpenSavepoint_after_journal_activity(void){115 const char *zDb = "test_pagerOpenSavepoint_after_journal.sqlite";116 sqlite3 *db = open_file_db(zDb);117 prepare_db_baseline(db);118 119 begin_immediate(db);120 Pager *pPager = getPagerFromDb(db);121 122 /* Perform a write to ensure journal file is opened and journalOff>0 */123 exec_sql_ok(db, "INSERT INTO t VALUES(1);");124 125 Pgno dbSizeAtCall = pPager->dbSize;126 i64 joBefore = pPager->journalOff;127 int subRecBefore = pPager->nSubRec;128 129 TEST_ASSERT_TRUE_MESSAGE(joBefore>0, "Expected journalOff>0 after write");130 131 int rc = test_pagerOpenSavepoint(pPager, 1);132 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);133 134 TEST_ASSERT_EQUAL_INT(1, pPager->nSavepoint);135 TEST_ASSERT_NOT_NULL(pPager->aSavepoint);136 137 PagerSavepoint *ps0 = &pPager->aSavepoint[0];138 TEST_ASSERT_NOT_NULL(ps0->pInSavepoint);139 TEST_ASSERT_EQUAL_INT(1, ps0->bTruncateOnRelease);140 141 TEST_ASSERT_EQUAL_UINT(dbSizeAtCall, ps0->nOrig);142 TEST_ASSERT_EQUAL_INT(subRecBefore, ps0->iSubRec);143 TEST_ASSERT_TRUE(((sqlite3_int64)ps0->iOffset) == joBefore);144 145 rollback_and_close(db, zDb);146}147 148/* Test 3: Grow the savepoint array (realloc path). Ensure prior entries are preserved and149 new entries reflect the current pager state at the time of growth. */150void test_pagerOpenSavepoint_grow_and_preserve(void){151 const char *zDb = "test_pagerOpenSavepoint_grow.sqlite";152 sqlite3 *db = open_file_db(zDb);153 prepare_db_baseline(db);154 155 begin_immediate(db);156 Pager *pPager = getPagerFromDb(db);157 158 /* First write to open journal, then create first savepoint */159 exec_sql_ok(db, "INSERT INTO t VALUES(10);");160 Pgno dbSize0 = pPager->dbSize;161 i64 jo0 = pPager->journalOff;162 int subRec0 = pPager->nSubRec;163 164 TEST_ASSERT_TRUE_MESSAGE(jo0>0, "Expected journalOff>0 after first write");165 int rc = test_pagerOpenSavepoint(pPager, 1);166 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);167 TEST_ASSERT_EQUAL_INT(1, pPager->nSavepoint);168 169 /* Snapshot the first savepoint's fields */170 PagerSavepoint ps0 = pPager->aSavepoint[0];171 172 /* Perform additional write to advance journal state, then grow to 3 savepoints */173 exec_sql_ok(db, "INSERT INTO t VALUES(20);");174 Pgno dbSize1 = pPager->dbSize;175 i64 jo1 = pPager->journalOff;176 int subRec1 = pPager->nSubRec;177 TEST_ASSERT_TRUE_MESSAGE(jo1>0, "Expected journalOff>0 after second write");178 179 rc = test_pagerOpenSavepoint(pPager, 3);180 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);181 TEST_ASSERT_EQUAL_INT(3, pPager->nSavepoint);182 TEST_ASSERT_NOT_NULL(pPager->aSavepoint);183 184 /* Verify first savepoint preserved */185 TEST_ASSERT_NOT_NULL(pPager->aSavepoint[0].pInSavepoint);186 TEST_ASSERT_EQUAL_INT(ps0.bTruncateOnRelease, pPager->aSavepoint[0].bTruncateOnRelease);187 TEST_ASSERT_EQUAL_UINT(ps0.nOrig, pPager->aSavepoint[0].nOrig);188 TEST_ASSERT_EQUAL_INT(ps0.iSubRec, pPager->aSavepoint[0].iSubRec);189 TEST_ASSERT_TRUE(((sqlite3_int64)ps0.iOffset) == (sqlite3_int64)pPager->aSavepoint[0].iOffset);190 191 /* Verify new savepoints [1] and [2] reflect current pager state at creation */192 PagerSavepoint *ps1 = &pPager->aSavepoint[1];193 PagerSavepoint *ps2 = &pPager->aSavepoint[2];194 195 TEST_ASSERT_NOT_NULL(ps1->pInSavepoint);196 TEST_ASSERT_NOT_NULL(ps2->pInSavepoint);197 TEST_ASSERT_EQUAL_INT(1, ps1->bTruncateOnRelease);198 TEST_ASSERT_EQUAL_INT(1, ps2->bTruncateOnRelease);199 200 TEST_ASSERT_EQUAL_UINT(dbSize1, ps1->nOrig);201 TEST_ASSERT_EQUAL_UINT(dbSize1, ps2->nOrig);202 203 TEST_ASSERT_EQUAL_INT(subRec1, ps1->iSubRec);204 TEST_ASSERT_EQUAL_INT(subRec1, ps2->iSubRec);205 206 TEST_ASSERT_TRUE(((sqlite3_int64)ps1->iOffset) == jo1);207 TEST_ASSERT_TRUE(((sqlite3_int64)ps2->iOffset) == jo1);208 209 rollback_and_close(db, zDb);210}211 212int main(void) {213 UNITY_BEGIN();214 RUN_TEST(test_pagerOpenSavepoint_basic_no_dirty);215 RUN_TEST(test_pagerOpenSavepoint_after_journal_activity);216 RUN_TEST(test_pagerOpenSavepoint_grow_and_preserve);217 return UNITY_END();218}