AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8/* Helper: execute SQL and assert success */9static void exec_sql_ok(sqlite3 *db, const char *sql){10 char *errmsg = 0;11 int rc = sqlite3_exec(db, sql, 0, 0, &errmsg);12 if( rc!=SQLITE_OK ){13 const char *msg = errmsg ? errmsg : "(null)";14 /* Use Unity to report the error and fail */15 char buf[512];16 snprintf(buf, sizeof(buf), "SQL error %d: %s while executing: %s", rc, msg, sql);17 if( errmsg ) sqlite3_free(errmsg);18 TEST_FAIL_MESSAGE(buf);19 }20 if( errmsg ) sqlite3_free(errmsg);21}22 23/* Helper: Execute a scalar int query and return the first column of first row (or 0 if no row) */24static int query_int(sqlite3 *db, const char *sql){25 sqlite3_stmt *stmt = 0;26 int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);27 if( rc!=SQLITE_OK ){28 char buf[512];29 snprintf(buf, sizeof(buf), "prepare failed %d for: %s", rc, sql);30 TEST_FAIL_MESSAGE(buf);31 }32 int val = 0;33 rc = sqlite3_step(stmt);34 if( rc==SQLITE_ROW ){35 val = sqlite3_column_int(stmt, 0);36 }else if( rc==SQLITE_DONE ){37 val = 0;38 }else{39 char buf[512];40 snprintf(buf, sizeof(buf), "step failed %d for: %s", rc, sql);41 sqlite3_finalize(stmt);42 TEST_FAIL_MESSAGE(buf);43 }44 sqlite3_finalize(stmt);45 return val;46}47 48/* Helper: open a new database connection to a given filename */49static sqlite3* open_db(const char *filename){50 sqlite3 *db = 0;51 int rc = sqlite3_open(filename, &db);52 if( rc!=SQLITE_OK ){53 char buf[256];54 snprintf(buf, sizeof(buf), "sqlite3_open failed %d for %s", rc, filename);55 TEST_FAIL_MESSAGE(buf);56 }57 return db;58}59 60/* Helper: close DB and assert success */61static void close_db(sqlite3 *db){62 int rc = sqlite3_close(db);63 if( rc!=SQLITE_OK ){64 char buf[256];65 snprintf(buf, sizeof(buf), "sqlite3_close failed %d", rc);66 TEST_FAIL_MESSAGE(buf);67 }68}69 70/* Helper: get Pager* from sqlite3* connection */71static Pager* getPager(sqlite3 *db){72 TEST_ASSERT_NOT_NULL(db);73 TEST_ASSERT_NOT_NULL(db->aDb);74 TEST_ASSERT_NOT_NULL(db->aDb[0].pBt);75 Pager *pPager = sqlite3BtreePager(db->aDb[0].pBt);76 TEST_ASSERT_NOT_NULL(pPager);77 return pPager;78}79 80void setUp(void) {81 /* No global setup required */82}83 84void tearDown(void) {85 /* No global teardown required */86}87 88/* Test: calling sqlite3PagerRollback on a freshly opened connection (no txn) is a no-op and returns SQLITE_OK. */89void test_sqlite3PagerRollback_noop_when_open(void){90 const char *fname = "test_pager_rb_open.db";91 sqlite3 *db = open_db(fname);92 93 Pager *pPager = getPager(db);94 int rc = sqlite3PagerRollback(pPager);95 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);96 97 /* Sanity: database usable after call */98 exec_sql_ok(db, "CREATE TABLE IF NOT EXISTS s(x);");99 100 close_db(db);101 remove(fname);102}103 104/* Test: rollback of uncommitted changes using rollback-journal (DELETE mode) */105void test_sqlite3PagerRollback_undo_changes_with_rollback_journal(void){106 const char *fname = "test_pager_rb_delete.db";107 sqlite3 *db = open_db(fname);108 109 /* Ensure rollback journal mode */110 exec_sql_ok(db, "PRAGMA journal_mode=DELETE;");111 112 /* Begin a write transaction and make changes, but do not commit */113 exec_sql_ok(db, "BEGIN IMMEDIATE;");114 exec_sql_ok(db, "CREATE TABLE t(x);");115 exec_sql_ok(db, "INSERT INTO t VALUES(1);");116 117 /* Directly rollback via pager */118 Pager *pPager = getPager(db);119 int rc = sqlite3PagerRollback(pPager);120 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);121 122 /* Bring the connection back to a consistent high-level state */123 exec_sql_ok(db, "ROLLBACK;");124 125 /* Verify that changes were not persisted */126 int n = query_int(db, "SELECT count(*) FROM sqlite_master WHERE name='t';");127 TEST_ASSERT_EQUAL_INT(0, n);128 129 /* Connection still usable */130 exec_sql_ok(db, "CREATE TABLE t2(y);");131 n = query_int(db, "SELECT count(*) FROM sqlite_master WHERE name='t2';");132 TEST_ASSERT_EQUAL_INT(1, n);133 134 close_db(db);135 remove(fname);136 /* Clean up possible -wal/-shm if created by VFS */137 remove("test_pager_rb_delete.db-journal");138}139 140/* Test: rollback of uncommitted changes in WAL mode */141void test_sqlite3PagerRollback_undo_changes_with_wal_mode(void){142 const char *fname = "test_pager_rb_wal.db";143 sqlite3 *db = open_db(fname);144 145 /* Switch to WAL mode */146 exec_sql_ok(db, "PRAGMA journal_mode=WAL;");147 148 /* Begin a write transaction and make changes, but do not commit */149 exec_sql_ok(db, "BEGIN;");150 exec_sql_ok(db, "CREATE TABLE w(x);");151 exec_sql_ok(db, "INSERT INTO w VALUES(10);");152 153 /* Directly rollback via pager */154 Pager *pPager = getPager(db);155 int rc = sqlite3PagerRollback(pPager);156 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);157 158 /* Synchronize high-level transaction state */159 exec_sql_ok(db, "ROLLBACK;");160 161 /* Verify that changes were not persisted */162 int n = query_int(db, "SELECT count(*) FROM sqlite_master WHERE name='w';");163 TEST_ASSERT_EQUAL_INT(0, n);164 165 /* Connection still usable */166 exec_sql_ok(db, "CREATE TABLE w2(y);");167 n = query_int(db, "SELECT count(*) FROM sqlite_master WHERE name='w2';");168 TEST_ASSERT_EQUAL_INT(1, n);169 170 close_db(db);171 remove(fname);172 remove("test_pager_rb_wal.db-wal");173 remove("test_pager_rb_wal.db-shm");174}175 176/* Test: sqlite3PagerRollback returns OK when in a read transaction (no write). */177void test_sqlite3PagerRollback_noop_in_reader_state(void){178 const char *fname = "test_pager_rb_reader.db";179 sqlite3 *db = open_db(fname);180 181 /* Start a deferred transaction and perform a read to ensure a read lock */182 exec_sql_ok(db, "BEGIN;");183 (void)query_int(db, "SELECT count(*) FROM sqlite_master;");184 185 Pager *pPager = getPager(db);186 int rc = sqlite3PagerRollback(pPager);187 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);188 189 /* Rollback at SQL level to clean up */190 exec_sql_ok(db, "ROLLBACK;");191 192 close_db(db);193 remove(fname);194}195 196int main(void){197 UNITY_BEGIN();198 RUN_TEST(test_sqlite3PagerRollback_noop_when_open);199 RUN_TEST(test_sqlite3PagerRollback_undo_changes_with_rollback_journal);200 RUN_TEST(test_sqlite3PagerRollback_undo_changes_with_wal_mode);201 RUN_TEST(test_sqlite3PagerRollback_noop_in_reader_state);202 return UNITY_END();203}