AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <string.h>5 6/* Unity setup/teardown */7void setUp(void) {8 /* Nothing needed */9}10void tearDown(void) {11 /* Nothing needed */12}13 14/* Helper: open an in-memory database and return db pointer */15static sqlite3* helper_open_memdb(void){16 sqlite3 *db = NULL;17 int rc = sqlite3_open(":memory:", &db);18 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");19 TEST_ASSERT_NOT_NULL_MESSAGE(db, "db is NULL after sqlite3_open");20 return db;21}22 23/* Helper: get main database Btree from a sqlite3* */24static Btree* helper_get_main_btree(sqlite3 *db){25 TEST_ASSERT_NOT_NULL(db);26 /* aDb[0] is "main". pBt is Btree* */27 TEST_ASSERT_NOT_NULL_MESSAGE(db->aDb, "db->aDb is NULL");28 TEST_ASSERT_NOT_NULL_MESSAGE(db->aDb[0].pBt, "main Btree is NULL");29 return db->aDb[0].pBt;30}31 32/* Helper: begin write transaction on Btree */33static void helper_btree_begin_write(sqlite3 *db, Btree *pBt){34 int rc;35 int schemaVersion = 0;36 /* Start a write transaction at the btree layer */37 rc = sqlite3BtreeBeginTrans(pBt, 1, &schemaVersion);38 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3BtreeBeginTrans failed");39 /* Check that Btree thinks it is in write state */40 TEST_ASSERT_TRUE_MESSAGE(pBt->inTrans == TRANS_WRITE || pBt->inTrans == TRANS_READ,41 "BtShared inTransaction should be set");42 TEST_ASSERT_EQUAL_INT_MESSAGE(TRANS_WRITE, pBt->inTrans, "BtShared not in write state");43}44 45/* Helper: end transaction by doing phase two commit if needed */46static void helper_btree_commit_phase_two(sqlite3 *db, Btree *pBt){47 /* If not in write transaction, nothing to do */48 if( pBt->inTrans==TRANS_WRITE ){49 int rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);50 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "CommitPhaseTwo failed");51 }52}53 54/* Test: CommitPhaseOne returns SQLITE_OK when not in write transaction */55void test_sqlite3BtreeCommitPhaseOne_no_write_trans_returns_OK(void){56 sqlite3 *db = helper_open_memdb();57 Btree *pBt = helper_get_main_btree(db);58 59 /* Ensure not in write transaction */60 TEST_ASSERT_TRUE_MESSAGE(pBt->inTrans==TRANS_NONE || pBt->inTrans==TRANS_READ,61 "Unexpected initial transaction state");62 63 int rc = sqlite3BtreeCommitPhaseOne(pBt, NULL);64 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);65 66 /* Cleanup */67 sqlite3_close(db);68}69 70/* Test: CommitPhaseOne during a write transaction returns SQLITE_OK */71void test_sqlite3BtreeCommitPhaseOne_write_trans_returns_OK(void){72 sqlite3 *db = helper_open_memdb();73 Btree *pBt = helper_get_main_btree(db);74 75 helper_btree_begin_write(db, pBt);76 77 int rc = sqlite3BtreeCommitPhaseOne(pBt, NULL);78 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);79 80 /* Should still be in a write transaction; phase two finishes it */81 TEST_ASSERT_EQUAL_INT(TRANS_WRITE, pBt->inTrans);82 83 helper_btree_commit_phase_two(db, pBt);84 85 sqlite3_close(db);86}87 88/* Test: CommitPhaseOne can be called twice in a row (idempotent-ish) */89void test_sqlite3BtreeCommitPhaseOne_twice_returns_OK(void){90 sqlite3 *db = helper_open_memdb();91 Btree *pBt = helper_get_main_btree(db);92 93 helper_btree_begin_write(db, pBt);94 95 int rc1 = sqlite3BtreeCommitPhaseOne(pBt, NULL);96 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc1);97 98 int rc2 = sqlite3BtreeCommitPhaseOne(pBt, NULL);99 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc2);100 101 helper_btree_commit_phase_two(db, pBt);102 103 sqlite3_close(db);104}105 106/* Test: CommitPhaseOne with autovacuum flag set returns SQLITE_OK */107void test_sqlite3BtreeCommitPhaseOne_with_autovacuum_flag(void){108 sqlite3 *db = helper_open_memdb();109 Btree *pBt = helper_get_main_btree(db);110 111 /* Force a write txn */112 helper_btree_begin_write(db, pBt);113 114#ifndef SQLITE_OMIT_AUTOVACUUM115 /* Manually enable autovacuum flag in BtShared to exercise path */116 pBt->pBt->autoVacuum = 1;117 /* Also set nPage to a sane value for truncate-image call path (can be 0 too). */118 /* Not strictly necessary, but keeps invariants reasonable. */119 if( pBt->pBt->nPage == 0 ){120 /* Ensure page 1 is initialized; create a trivial schema by executing a no-op */121 /* Using SQL here is safe; DB mutexing handled by core. */122 int ignored = 0;123 (void)ignored;124 }125#endif126 127 int rc = sqlite3BtreeCommitPhaseOne(pBt, NULL);128 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);129 130 helper_btree_commit_phase_two(db, pBt);131 132 sqlite3_close(db);133}134 135/* Test: CommitPhaseOne after creating and inserting via SQL (transaction by SQL) */136void test_sqlite3BtreeCommitPhaseOne_after_sql_writes(void){137 sqlite3 *db = helper_open_memdb();138 Btree *pBt = helper_get_main_btree(db);139 140 /* Begin and perform writes using SQL */141 char *zErr = NULL;142 int rc = sqlite3_exec(db,143 "BEGIN IMMEDIATE; "144 "CREATE TABLE t(x); "145 "INSERT INTO t VALUES(1);",146 0, 0, &zErr);147 if( zErr ){ sqlite3_free(zErr); zErr=NULL; }148 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "SQL setup failed");149 150 /* Now call commit phase one directly at btree layer */151 rc = sqlite3BtreeCommitPhaseOne(pBt, NULL);152 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);153 154 /* Finish commit to cleanup */155 rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);156 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);157 158 sqlite3_close(db);159}160 161int main(void) {162 UNITY_BEGIN();163 164 RUN_TEST(test_sqlite3BtreeCommitPhaseOne_no_write_trans_returns_OK);165 RUN_TEST(test_sqlite3BtreeCommitPhaseOne_write_trans_returns_OK);166 RUN_TEST(test_sqlite3BtreeCommitPhaseOne_twice_returns_OK);167 RUN_TEST(test_sqlite3BtreeCommitPhaseOne_with_autovacuum_flag);168 RUN_TEST(test_sqlite3BtreeCommitPhaseOne_after_sql_writes);169 170 return UNITY_END();171}