AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdio.h>4#include <stdlib.h>5#include <string.h>6 7/* Unity setup/teardown */8void setUp(void) {9 /* No global setup needed */10}11void tearDown(void) {12 /* No global teardown needed */13}14 15/* Helpers */16static void exec_sql(sqlite3 *db, const char *sql){17 char *zErr = 0;18 int rc = sqlite3_exec(db, sql, 0, 0, &zErr);19 if( rc!=SQLITE_OK ){20 fprintf(stderr, "SQL error (%d): %s\n", rc, zErr ? zErr : "(null)");21 }22 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);23 if( zErr ) sqlite3_free(zErr);24}25 26static Pager* get_main_pager(sqlite3 *db){27 TEST_ASSERT_NOT_NULL(db);28 TEST_ASSERT_TRUE(db->nDb > 0);29 TEST_ASSERT_NOT_NULL(db->aDb);30 Btree *pBt = db->aDb[0].pBt;31 TEST_ASSERT_NOT_NULL(pBt);32 Pager *pPager = sqlite3BtreePager(pBt);33 TEST_ASSERT_NOT_NULL(pPager);34 return pPager;35}36 37/* Test 1: Early return on pre-existing error (NEVER(pPager->errCode)) */38void test_sqlite3PagerCommitPhaseTwo_error_early_return(void){39 /* Allocate a minimal Pager just for this test. It is safe because40 sqlite3PagerCommitPhaseTwo returns immediately when errCode is set,41 before accessing other fields or running asserts. */42 Pager *p = (Pager *)sqlite3_malloc(sizeof(*p));43 TEST_ASSERT_NOT_NULL(p);44 memset(p, 0, sizeof(*p));45 46 /* Set a persistent error and a sentinel iDataVersion. */47 p->errCode = SQLITE_IOERR; /* trigger early return */48 p->iDataVersion = 123456u;49 50 int rc = sqlite3PagerCommitPhaseTwo(p);51 TEST_ASSERT_EQUAL_INT(SQLITE_IOERR, rc);52 /* iDataVersion must not be incremented on error. */53 TEST_ASSERT_EQUAL_UINT(123456u, p->iDataVersion);54 55 sqlite3_free(p);56}57 58/* Test 2: Optimization no-op path: WRITER_LOCKED + exclusive + PERSIST + no changes */59void test_sqlite3PagerCommitPhaseTwo_optimization_noop_persist_exclusive_writer_locked(void){60 sqlite3 *db = 0;61 int rc = sqlite3_open("test_pager_commit2_opt.db", &db);62 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);63 TEST_ASSERT_NOT_NULL(db);64 65 /* Ensure rollback-journal mode: PERSIST, and exclusive locking mode. */66 exec_sql(db, "PRAGMA journal_mode=PERSIST;");67 exec_sql(db, "PRAGMA locking_mode=EXCLUSIVE;");68 /* Start a RESERVED/WRITE lock without making any changes. */69 exec_sql(db, "BEGIN IMMEDIATE;");70 71 Pager *pPager = get_main_pager(db);72 73 /* Confirm preconditions for optimization branch. We expect:74 - eState == PAGER_WRITER_LOCKED (no page modified)75 - exclusiveMode true76 - journalMode == PAGER_JOURNALMODE_PERSIST77 */78 TEST_ASSERT_EQUAL_INT(PAGER_WRITER_LOCKED, pPager->eState);79 TEST_ASSERT_TRUE(pPager->exclusiveMode!=0);80 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_PERSIST, pPager->journalMode);81 82 unsigned int beforeDV = pPager->iDataVersion;83 84 rc = sqlite3PagerCommitPhaseTwo(pPager);85 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);86 /* After optimization path, pager moves to READER state and dataVersion increments. */87 TEST_ASSERT_EQUAL_INT(PAGER_READER, pPager->eState);88 TEST_ASSERT_EQUAL_UINT(beforeDV + 1u, pPager->iDataVersion);89 90 /* Connection should now be out of the write-transaction. */91 exec_sql(db, "ROLLBACK;"); /* no-op if already committed, but ensure clean state */92 93 sqlite3_close(db);94}95 96/* Test 3: Normal path after commit-phase-one (DELETE journal mode, with changes) */97void test_sqlite3PagerCommitPhaseTwo_normal_after_phase_one(void){98 sqlite3 *db = 0;99 int rc = sqlite3_open("test_pager_commit2_normal.db", &db);100 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);101 TEST_ASSERT_NOT_NULL(db);102 103 /* Ensure rollback journal (not WAL) and normal locking. */104 exec_sql(db, "PRAGMA journal_mode=DELETE;");105 exec_sql(db, "PRAGMA locking_mode=NORMAL;");106 107 exec_sql(db, "BEGIN IMMEDIATE;");108 /* Make a change to ensure there is something to commit. */109 exec_sql(db, "CREATE TABLE IF NOT EXISTS t(x);");110 111 Pager *pPager = get_main_pager(db);112 113 /* Run commit phase one directly on the pager to reach WRITER_FINISHED state. */114 /* Note: sqlite3PagerCommitPhaseOne(Pager*, const char*, int) is an internal API. */115 rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0);116 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);117 TEST_ASSERT_EQUAL_INT(PAGER_WRITER_FINISHED, pPager->eState);118 119 unsigned int beforeDV = pPager->iDataVersion;120 121 /* Now run the target function: commit phase two. */122 rc = sqlite3PagerCommitPhaseTwo(pPager);123 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);124 TEST_ASSERT_EQUAL_INT(PAGER_READER, pPager->eState);125 TEST_ASSERT_EQUAL_UINT(beforeDV + 1u, pPager->iDataVersion);126 127 /* Verify the table exists (transaction was committed). */128 exec_sql(db, "SELECT count(*) FROM t;");129 130 sqlite3_close(db);131}132 133/* Unity main runner */134int main(void){135 UNITY_BEGIN();136 RUN_TEST(test_sqlite3PagerCommitPhaseTwo_error_early_return);137 RUN_TEST(test_sqlite3PagerCommitPhaseTwo_optimization_noop_persist_exclusive_writer_locked);138 RUN_TEST(test_sqlite3PagerCommitPhaseTwo_normal_after_phase_one);139 return UNITY_END();140}