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#include <time.h>8 9static int g_counter = 0;10 11/* Unity hooks */12void setUp(void) {13 /* Optional setup */14}15void tearDown(void) {16 /* Optional teardown */17}18 19/* Helper: generate a unique filename for a database file */20static void make_filename(char *buf, size_t n, const char *tag){21 ++g_counter;22 snprintf(buf, n, "test_snapshot_get_%s_%ld_%d.db",23 tag ? tag : "db", (long)time(NULL), g_counter);24}25 26/* Helper: remove db and its associated -wal and -shm files */27static void cleanup_db_files(const char *dbfile){28 if( dbfile && dbfile[0] ){29 char wal[1024], shm[1024];30 snprintf(wal, sizeof(wal), "%s-wal", dbfile);31 snprintf(shm, sizeof(shm), "%s-shm", dbfile);32 remove(wal);33 remove(shm);34 remove(dbfile);35 }36}37 38/* Helper: exec SQL and assert success */39static void exec_or_fail(sqlite3 *db, const char *sql){40 char *errmsg = NULL;41 int rc = sqlite3_exec(db, sql, 0, 0, &errmsg);42 if( rc!=SQLITE_OK ){43 const char *msg = errmsg ? errmsg : "sqlite3_exec failed";44 TEST_FAIL_MESSAGE(msg);45 }46 sqlite3_free(errmsg);47}48 49/* Helper: set journal mode to WAL for a given database schema (NULL->main).50 Returns 1 if journal_mode is "wal", 0 otherwise. */51static int ensure_wal(sqlite3 *db, const char *zDb){52 int rc;53 sqlite3_stmt *pStmt = NULL;54 char sql[256];55 if( zDb==NULL || zDb[0]==0 ){56 snprintf(sql, sizeof(sql), "PRAGMA journal_mode=WAL;");57 }else{58 snprintf(sql, sizeof(sql), "PRAGMA %s.journal_mode=WAL;", zDb);59 }60 rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);61 if( rc!=SQLITE_OK ) return 0;62 rc = sqlite3_step(pStmt);63 if( rc!=SQLITE_ROW ){64 sqlite3_finalize(pStmt);65 return 0;66 }67 const unsigned char *z = sqlite3_column_text(pStmt, 0);68 int isWal = (z && sqlite3_stricmp((const char*)z, "wal")==0);69 sqlite3_finalize(pStmt);70 return isWal;71}72 73/* Test: calling without an explicit transaction should return SQLITE_ERROR */74void test_sqlite3_snapshot_get_not_in_txn(void){75 sqlite3 *db = NULL;76 char dbfile[512];77 make_filename(dbfile, sizeof(dbfile), "notx");78 int rc = sqlite3_open(dbfile, &db);79 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);80 81 sqlite3_snapshot *pSnap = NULL;82 rc = sqlite3_snapshot_get(db, "main", &pSnap);83 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);84 85 sqlite3_close(db);86 cleanup_db_files(dbfile);87}88 89/* Test: successful snapshot retrieval on main in read transaction with WAL */90void test_sqlite3_snapshot_get_read_txn_main_wal(void){91 sqlite3 *db = NULL;92 char dbfile[512];93 make_filename(dbfile, sizeof(dbfile), "mainwal");94 int rc = sqlite3_open(dbfile, &db);95 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);96 97 if( !ensure_wal(db, NULL) ){98 sqlite3_close(db);99 cleanup_db_files(dbfile);100 TEST_IGNORE_MESSAGE("WAL not supported on this build or VFS. Skipping.");101 }102 103 exec_or_fail(db, "CREATE TABLE t(x);");104 exec_or_fail(db, "INSERT INTO t VALUES(1);");105 exec_or_fail(db, "INSERT INTO t VALUES(2);");106 exec_or_fail(db, "COMMIT;"); /* ensure there's a commit point */107 exec_or_fail(db, "BEGIN;"); /* start explicit transaction to satisfy autoCommit==0 */108 109 sqlite3_snapshot *pSnap = NULL;110 rc = sqlite3_snapshot_get(db, "main", &pSnap);111 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);112 TEST_ASSERT_NOT_NULL(pSnap);113 114 sqlite3_snapshot_free(pSnap);115 exec_or_fail(db, "COMMIT;");116 117 sqlite3_close(db);118 cleanup_db_files(dbfile);119}120 121/* Test: snapshot_get on temp schema should return SQLITE_ERROR */122void test_sqlite3_snapshot_get_temp_schema_error(void){123 sqlite3 *db = NULL;124 char dbfile[512];125 make_filename(dbfile, sizeof(dbfile), "temp");126 int rc = sqlite3_open(dbfile, &db);127 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);128 129 if( !ensure_wal(db, NULL) ){130 sqlite3_close(db);131 cleanup_db_files(dbfile);132 TEST_IGNORE_MESSAGE("WAL not supported on this build or VFS. Skipping.");133 }134 135 exec_or_fail(db, "BEGIN;");136 sqlite3_snapshot *pSnap = NULL;137 rc = sqlite3_snapshot_get(db, "temp", &pSnap);138 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);139 exec_or_fail(db, "COMMIT;");140 141 sqlite3_close(db);142 cleanup_db_files(dbfile);143}144 145/* Test: snapshot_get during a write transaction should return SQLITE_ERROR */146void test_sqlite3_snapshot_get_write_txn_error(void){147 sqlite3 *db = NULL;148 char dbfile[512];149 make_filename(dbfile, sizeof(dbfile), "writetx");150 int rc = sqlite3_open(dbfile, &db);151 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);152 153 if( !ensure_wal(db, NULL) ){154 sqlite3_close(db);155 cleanup_db_files(dbfile);156 TEST_IGNORE_MESSAGE("WAL not supported on this build or VFS. Skipping.");157 }158 159 exec_or_fail(db, "CREATE TABLE t(x);");160 exec_or_fail(db, "INSERT INTO t VALUES(1);");161 exec_or_fail(db, "COMMIT;");162 exec_or_fail(db, "BEGIN IMMEDIATE;"); /* enter write transaction */163 164 sqlite3_snapshot *pSnap = NULL;165 rc = sqlite3_snapshot_get(db, "main", &pSnap);166 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);167 168 exec_or_fail(db, "ROLLBACK;");169 170 sqlite3_close(db);171 cleanup_db_files(dbfile);172}173 174/* Test: snapshot_get with an unknown schema name should return SQLITE_ERROR */175void test_sqlite3_snapshot_get_unknown_schema_error(void){176 sqlite3 *db = NULL;177 char dbfile[512];178 make_filename(dbfile, sizeof(dbfile), "unknown");179 int rc = sqlite3_open(dbfile, &db);180 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);181 182 if( !ensure_wal(db, NULL) ){183 sqlite3_close(db);184 cleanup_db_files(dbfile);185 TEST_IGNORE_MESSAGE("WAL not supported on this build or VFS. Skipping.");186 }187 188 exec_or_fail(db, "BEGIN;");189 sqlite3_snapshot *pSnap = NULL;190 rc = sqlite3_snapshot_get(db, "nosuchdb", &pSnap);191 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);192 exec_or_fail(db, "COMMIT;");193 194 sqlite3_close(db);195 cleanup_db_files(dbfile);196}197 198/* Test: successful snapshot retrieval on an attached database in WAL mode */199void test_sqlite3_snapshot_get_attached_db_wal(void){200 sqlite3 *db = NULL;201 char mainfile[512], auxfile[512];202 make_filename(mainfile, sizeof(mainfile), "main_attach");203 make_filename(auxfile, sizeof(auxfile), "aux");204 205 int rc = sqlite3_open(mainfile, &db);206 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);207 208 if( !ensure_wal(db, NULL) ){209 sqlite3_close(db);210 cleanup_db_files(mainfile);211 cleanup_db_files(auxfile);212 TEST_IGNORE_MESSAGE("WAL not supported on this build or VFS. Skipping.");213 }214 215 /* Attach auxiliary database */216 char attachsql[1024];217 snprintf(attachsql, sizeof(attachsql), "ATTACH '%s' AS aux;", auxfile);218 exec_or_fail(db, attachsql);219 220 /* Ensure WAL for attached database */221 if( !ensure_wal(db, "aux") ){222 exec_or_fail(db, "DETACH aux;");223 sqlite3_close(db);224 cleanup_db_files(mainfile);225 cleanup_db_files(auxfile);226 TEST_IGNORE_MESSAGE("WAL not supported for attached database. Skipping.");227 }228 229 exec_or_fail(db, "CREATE TABLE aux.t2(y);");230 exec_or_fail(db, "INSERT INTO aux.t2 VALUES(10);");231 exec_or_fail(db, "COMMIT;");232 exec_or_fail(db, "BEGIN;");233 234 sqlite3_snapshot *pSnap = NULL;235 rc = sqlite3_snapshot_get(db, "aux", &pSnap);236 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);237 TEST_ASSERT_NOT_NULL(pSnap);238 sqlite3_snapshot_free(pSnap);239 240 exec_or_fail(db, "COMMIT;");241 exec_or_fail(db, "DETACH aux;");242 243 sqlite3_close(db);244 cleanup_db_files(mainfile);245 cleanup_db_files(auxfile);246}247 248int main(void){249 UNITY_BEGIN();250 RUN_TEST(test_sqlite3_snapshot_get_not_in_txn);251 RUN_TEST(test_sqlite3_snapshot_get_read_txn_main_wal);252 RUN_TEST(test_sqlite3_snapshot_get_temp_schema_error);253 RUN_TEST(test_sqlite3_snapshot_get_write_txn_error);254 RUN_TEST(test_sqlite3_snapshot_get_unknown_schema_error);255 RUN_TEST(test_sqlite3_snapshot_get_attached_db_wal);256 return UNITY_END();257}