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/* Unity setup/teardown */9void setUp(void) {10 /* No-op */11}12void tearDown(void) {13 /* No-op */14}15 16/* Helper: build a temporary database filename. Caller must sqlite3_free(). */17static char *make_temp_db_filename(const char *tag){18 char tmp[L_tmpnam];19 if( tmpnam(tmp)==NULL ){20 /* Fallback */21 return sqlite3_mprintf("test_%s_%ld.db", tag?tag:"db", (long)sqlite3OsRandomness(0,0));22 }23 return sqlite3_mprintf("%s_%s.db", tmp, tag?tag:"db");24}25 26/* Helper: execute SQL and assert success */27static void exec_ok(sqlite3 *db, const char *sql){28 char *err = 0;29 int rc = sqlite3_exec(db, sql, 0, 0, &err);30 if( err ){31 /* If rc not OK, include message in assertion output */32 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, err);33 }else{34 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);35 }36 if( err ) sqlite3_free(err);37}38 39/* Helper: prepare/step/finalize and return single int result */40static int query_int(sqlite3 *db, const char *sql){41 sqlite3_stmt *stmt = 0;42 int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, 0);43 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");44 rc = sqlite3_step(stmt);45 TEST_ASSERT_MESSAGE(rc==SQLITE_ROW || rc==SQLITE_DONE, "step not ROW/DONE");46 int val = 0;47 if( rc==SQLITE_ROW ){48 val = sqlite3_column_int(stmt, 0);49 rc = sqlite3_step(stmt);50 TEST_ASSERT_MESSAGE(rc==SQLITE_DONE, "expected single-row result");51 }52 rc = sqlite3_finalize(stmt);53 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "finalize failed");54 return val;55}56 57/* Helper: open database and set pragmas for predictable behavior */58static void open_db_with_pragmas(const char *filename, sqlite3 **pDb){59 int rc = sqlite3_open(filename, pDb);60 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);61 /* Use rollback journal mode for predictable uncommitted visibility semantics */62 exec_ok(*pDb, "PRAGMA journal_mode=DELETE;");63 /* Avoid long waits in case of lock contentions */64 exec_ok(*pDb, "PRAGMA busy_timeout=0;");65 exec_ok(*pDb, "PRAGMA synchronous=OFF;");66}67 68/* Test 1: No transaction, expect SQLITE_OK. */69static void test_sqlite3_db_cacheflush_no_txn_ok(void){70 char *fname = make_temp_db_filename("no_txn");71 sqlite3 *db = 0;72 73 open_db_with_pragmas(fname, &db);74 /* Create a simple schema, but no active write transaction */75 exec_ok(db, "CREATE TABLE IF NOT EXISTS t(x);");76 77 int rc = sqlite3_db_cacheflush(db);78 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);79 80 sqlite3_close(db);81 remove(fname);82 sqlite3_free(fname);83}84 85/* Test 2: Write transaction with uncommitted change; flush returns OK and does not commit. */86static void test_sqlite3_db_cacheflush_uncommitted_changes_not_visible(void){87 char *fname = make_temp_db_filename("txn");88 sqlite3 *db1 = 0;89 sqlite3 *db2 = 0;90 91 open_db_with_pragmas(fname, &db1);92 open_db_with_pragmas(fname, &db2);93 94 /* Setup schema and baseline committed row */95 exec_ok(db1, "DROP TABLE IF EXISTS t;");96 exec_ok(db1, "CREATE TABLE t(x);");97 exec_ok(db1, "INSERT INTO t VALUES(1);");98 exec_ok(db1, "COMMIT;"); /* ensure autocommit */99 /* Ensure db2 sees baseline */100 TEST_ASSERT_EQUAL_INT(1, query_int(db2, "SELECT count(*) FROM t;"));101 102 /* Start write transaction and add uncommitted row */103 exec_ok(db1, "BEGIN;");104 exec_ok(db1, "INSERT INTO t VALUES(2);");105 106 /* Flush caches */107 int rc = sqlite3_db_cacheflush(db1);108 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);109 110 /* Within same connection, uncommitted row is visible */111 TEST_ASSERT_EQUAL_INT(2, query_int(db1, "SELECT count(*) FROM t;"));112 /* From another connection, uncommitted row must NOT be visible */113 TEST_ASSERT_EQUAL_INT(1, query_int(db2, "SELECT count(*) FROM t;"));114 115 /* Rollback and ensure row 2 is gone on both */116 exec_ok(db1, "ROLLBACK;");117 TEST_ASSERT_EQUAL_INT(1, query_int(db1, "SELECT count(*) FROM t;"));118 TEST_ASSERT_EQUAL_INT(1, query_int(db2, "SELECT count(*) FROM t;"));119 120 /* Now try again, but commit after flush to verify normal visibility */121 exec_ok(db1, "BEGIN;");122 exec_ok(db1, "INSERT INTO t VALUES(2);");123 rc = sqlite3_db_cacheflush(db1);124 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);125 exec_ok(db1, "COMMIT;");126 127 /* After commit, both connections should see both rows */128 TEST_ASSERT_EQUAL_INT(2, query_int(db1, "SELECT count(*) FROM t;"));129 TEST_ASSERT_EQUAL_INT(2, query_int(db2, "SELECT count(*) FROM t;"));130 131 sqlite3_close(db1);132 sqlite3_close(db2);133 remove(fname);134 sqlite3_free(fname);135}136 137/* Test 3: Attached database: uncommitted changes in both schemas; flush OK; not visible elsewhere until commit. */138static void test_sqlite3_db_cacheflush_with_attached_db(void){139 char *fname_main = make_temp_db_filename("main");140 char *fname_aux = make_temp_db_filename("aux");141 sqlite3 *dbA = 0;142 sqlite3 *dbB = 0;143 144 open_db_with_pragmas(fname_main, &dbA);145 open_db_with_pragmas(fname_main, &dbB);146 147 /* Attach auxiliary db to both connections */148 {149 char *sqlAttachA = sqlite3_mprintf("ATTACH '%q' AS aux;", fname_aux);150 char *sqlAttachB = sqlite3_mprintf("ATTACH '%q' AS aux;", fname_aux);151 exec_ok(dbA, sqlAttachA);152 exec_ok(dbB, sqlAttachB);153 sqlite3_free(sqlAttachA);154 sqlite3_free(sqlAttachB);155 }156 /* Ensure aux uses DELETE journal mode as well */157 exec_ok(dbA, "PRAGMA aux.journal_mode=DELETE;");158 exec_ok(dbB, "PRAGMA aux.journal_mode=DELETE;");159 160 /* Create schemas and baseline committed rows in both dbs */161 exec_ok(dbA, "DROP TABLE IF EXISTS main.m;");162 exec_ok(dbA, "DROP TABLE IF EXISTS aux.a;");163 exec_ok(dbA, "CREATE TABLE main.m(x);");164 exec_ok(dbA, "CREATE TABLE aux.a(y);");165 exec_ok(dbA, "INSERT INTO main.m VALUES(10);");166 exec_ok(dbA, "INSERT INTO aux.a VALUES(20);");167 exec_ok(dbA, "COMMIT;"); /* ensure autocommit */168 169 /* Confirm baselines visible from dbB */170 TEST_ASSERT_EQUAL_INT(1, query_int(dbB, "SELECT count(*) FROM main.m;"));171 TEST_ASSERT_EQUAL_INT(1, query_int(dbB, "SELECT count(*) FROM aux.a;"));172 173 /* Start a write transaction and insert into both schemas without commit */174 exec_ok(dbA, "BEGIN;");175 exec_ok(dbA, "INSERT INTO main.m VALUES(11);");176 exec_ok(dbA, "INSERT INTO aux.a VALUES(21);");177 178 /* Flush caches */179 int rc = sqlite3_db_cacheflush(dbA);180 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);181 182 /* dbA sees uncommitted rows */183 TEST_ASSERT_EQUAL_INT(2, query_int(dbA, "SELECT count(*) FROM main.m;"));184 TEST_ASSERT_EQUAL_INT(2, query_int(dbA, "SELECT count(*) FROM aux.a;"));185 /* dbB must NOT see uncommitted rows from either schema */186 TEST_ASSERT_EQUAL_INT(1, query_int(dbB, "SELECT count(*) FROM main.m;"));187 TEST_ASSERT_EQUAL_INT(1, query_int(dbB, "SELECT count(*) FROM aux.a;"));188 189 /* Rollback and verify baseline remains */190 exec_ok(dbA, "ROLLBACK;");191 TEST_ASSERT_EQUAL_INT(1, query_int(dbA, "SELECT count(*) FROM main.m;"));192 TEST_ASSERT_EQUAL_INT(1, query_int(dbA, "SELECT count(*) FROM aux.a;"));193 TEST_ASSERT_EQUAL_INT(1, query_int(dbB, "SELECT count(*) FROM main.m;"));194 TEST_ASSERT_EQUAL_INT(1, query_int(dbB, "SELECT count(*) FROM aux.a;"));195 196 sqlite3_close(dbA);197 sqlite3_close(dbB);198 /* Cleanup files */199 remove(fname_main);200 remove(fname_aux);201 sqlite3_free(fname_main);202 sqlite3_free(fname_aux);203}204 205int main(void){206 UNITY_BEGIN();207 RUN_TEST(test_sqlite3_db_cacheflush_no_txn_ok);208 RUN_TEST(test_sqlite3_db_cacheflush_uncommitted_changes_not_visible);209 RUN_TEST(test_sqlite3_db_cacheflush_with_attached_db);210 return UNITY_END();211}