AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdio.h>4#include <string.h>5 6/* Utilities */7 8static void unlink_if_exists(sqlite3_vfs *pVfs, const char *zPath){9 if( zPath==NULL ) return;10 int exists = 0;11 (void)sqlite3OsAccess(pVfs, zPath, SQLITE_ACCESS_EXISTS, &exists);12 if( exists ){13 (void)sqlite3OsDelete(pVfs, zPath, 0);14 }15}16 17static sqlite3* open_db_filename(const char *zFilename){18 sqlite3 *db = 0;19 int rc = sqlite3_open(zFilename, &db);20 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);21 TEST_ASSERT_NOT_NULL(db);22 return db;23}24 25static void close_db_and_cleanup(sqlite3 *db, const char *zFilename){26 if( db ){27 sqlite3_close(db);28 }29 if( zFilename ){30 sqlite3_vfs *pVfs = sqlite3_vfs_find(0);31 if( pVfs ){32 /* Remove both the database and its journal if they exist */33 unlink_if_exists(pVfs, zFilename);34 /* Also try removing the "-journal" file if any (use Pager if possible). */35 /* We cannot get Pager after closing db easily, so derive journal name. */36 /* SQLite uses "-journal" suffix by default */37 char zJournal[1024];38 memset(zJournal, 0, sizeof(zJournal));39 size_t n = strlen(zFilename);40 if( n + 9 < sizeof(zJournal) ){41 memcpy(zJournal, zFilename, n);42 memcpy(zJournal + n, "-journal", 9);43 unlink_if_exists(pVfs, zJournal);44 }45 /* Also try wal file */46 char zWal[1024];47 memset(zWal, 0, sizeof(zWal));48 if( n + 4 < sizeof(zWal) ){49 memcpy(zWal, zFilename, n);50 memcpy(zWal + n, "-wal", 4);51 unlink_if_exists(pVfs, zWal);52 }53 }54 }55}56 57static Pager* get_main_pager(sqlite3 *db){58 /* Main database is index 0 */59 Btree *pBt = db->aDb[0].pBt;60 TEST_ASSERT_NOT_NULL(pBt);61 Pager *pPager = sqlite3BtreePager(pBt);62 TEST_ASSERT_NOT_NULL(pPager);63 return pPager;64}65 66static int file_exists(sqlite3_vfs *pVfs, const char *zPath){67 int exists = 0;68 if( zPath==NULL ) return 0;69 (void)sqlite3OsAccess(pVfs, zPath, SQLITE_ACCESS_EXISTS, &exists);70 return exists;71}72 73/* Open the pager's journal file explicitly via VFS (and create it if needed) */74static void open_pager_journal(Pager *pPager){75 TEST_ASSERT_NOT_NULL(pPager);76 TEST_ASSERT_NOT_NULL(pPager->pVfs);77 TEST_ASSERT_NOT_NULL(pPager->jfd);78 TEST_ASSERT_NOT_NULL(pPager->zJournal);79 80 int fout = 0;81 int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_MAIN_JOURNAL;82 int rc = sqlite3OsOpen(pPager->pVfs, pPager->zJournal, pPager->jfd, flags, &fout);83 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);84 TEST_ASSERT_TRUE(isOpen(pPager->jfd));85}86 87/* Unity fixtures */88 89void setUp(void) {90 /* Nothing for now */91}92 93void tearDown(void) {94 /* Nothing for now */95}96 97/* Tests */98 99/* Basic mode change to DELETE on an on-disk DB */100void test_sqlite3PagerSetJournalMode_basic_change(void){101 const char *zDb = "test_pager_jm_basic.db";102 sqlite3 *db = open_db_filename(zDb);103 Pager *pPager = get_main_pager(db);104 105 /* Ensure we are not in WAL for this test */106 TEST_ASSERT_FALSE(pagerUseWal(pPager));107 108 int rcMode = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_DELETE);109 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_DELETE, rcMode);110 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_DELETE, (int)pPager->journalMode);111 112 close_db_and_cleanup(db, zDb);113}114 115/* Transition from PERSIST to DELETE should delete the on-disk journal file */116void test_sqlite3PagerSetJournalMode_persist_to_delete_removes_journal(void){117 const char *zDb = "test_pager_jm_persist_del.db";118 sqlite3 *db = open_db_filename(zDb);119 Pager *pPager = get_main_pager(db);120 sqlite3_vfs *pVfs = pPager->pVfs;121 122 TEST_ASSERT_FALSE(pagerUseWal(pPager));123 124 /* Set to PERSIST, create/open a journal file, and verify it exists */125 int ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_PERSIST);126 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_PERSIST, ret);127 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_PERSIST, (int)pPager->journalMode);128 129 open_pager_journal(pPager);130 TEST_ASSERT_NOT_NULL(pPager->zJournal);131 TEST_ASSERT_TRUE(file_exists(pVfs, pPager->zJournal));132 133 /* Switch to DELETE: this should close and delete the journal file */134 ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_DELETE);135 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_DELETE, ret);136 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_DELETE, (int)pPager->journalMode);137 138 int exists = file_exists(pVfs, pPager->zJournal);139 TEST_ASSERT_FALSE_MESSAGE(exists, "Journal file should have been deleted on PERSIST->DELETE transition");140 141 close_db_and_cleanup(db, zDb);142}143 144/* No-op when setting to the same mode (TRUNCATE): existing journal should not be deleted */145void test_sqlite3PagerSetJournalMode_noop_does_not_delete_existing_journal(void){146 const char *zDb = "test_pager_jm_noop.db";147 sqlite3 *db = open_db_filename(zDb);148 Pager *pPager = get_main_pager(db);149 sqlite3_vfs *pVfs = pPager->pVfs;150 151 TEST_ASSERT_FALSE(pagerUseWal(pPager));152 153 /* Set to TRUNCATE and create a journal file */154 int ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_TRUNCATE);155 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_TRUNCATE, ret);156 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_TRUNCATE, (int)pPager->journalMode);157 158 open_pager_journal(pPager);159 TEST_ASSERT_TRUE(file_exists(pVfs, pPager->zJournal));160 161 /* Set to same mode again - should be a no-op (journal should remain) */162 ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_TRUNCATE);163 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_TRUNCATE, ret);164 TEST_ASSERT_TRUE(file_exists(pVfs, pPager->zJournal));165 166 /* Cleanup */167 unlink_if_exists(pVfs, pPager->zJournal);168 close_db_and_cleanup(db, zDb);169}170 171/* Setting to OFF from a non-(TRUNCATE/PERSIST) previous mode should at least close jfd */172void test_sqlite3PagerSetJournalMode_off_closes_jfd_when_no_delete_branch(void){173 const char *zDb = "test_pager_jm_off_closes.db";174 sqlite3 *db = open_db_filename(zDb);175 Pager *pPager = get_main_pager(db);176 177 TEST_ASSERT_FALSE(pagerUseWal(pPager));178 179 /* Ensure previous mode is DELETE (bitwise & 5 == 0) to skip delete-branch */180 int ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_DELETE);181 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_DELETE, ret);182 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_DELETE, (int)pPager->journalMode);183 184 /* Open the journal file descriptor explicitly */185 open_pager_journal(pPager);186 TEST_ASSERT_TRUE(isOpen(pPager->jfd));187 188 /* Now set mode to OFF; since previous mode is not TRUNCATE/PERSIST,189 the else-if branch should run and close jfd */190 ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);191 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_OFF, ret);192 TEST_ASSERT_FALSE(isOpen(pPager->jfd));193 194 close_db_and_cleanup(db, zDb);195}196 197/* In-memory database only allows MEMORY or OFF. Other modes are ignored. */198void test_sqlite3PagerSetJournalMode_memdb_restricts_modes(void){199 sqlite3 *db = 0;200 int rc = sqlite3_open(":memory:", &db);201 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);202 TEST_ASSERT_NOT_NULL(db);203 204 Pager *pPager = get_main_pager(db);205 206 /* Verify MEMDB macro behavior: initial mode must be MEMORY or OFF */207 int eOld = (int)pPager->journalMode;208 TEST_ASSERT_TRUE_MESSAGE(209 eOld==PAGER_JOURNALMODE_MEMORY || eOld==PAGER_JOURNALMODE_OFF,210 "In-memory pager initial journalMode must be MEMORY or OFF"211 );212 213 /* Attempt to set to DELETE - should be ignored and return current mode */214 int ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_DELETE);215 TEST_ASSERT_EQUAL_INT(eOld, ret);216 TEST_ASSERT_EQUAL_INT(eOld, (int)pPager->journalMode);217 218 /* Setting to MEMORY should be allowed and result in MEMORY */219 ret = sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_MEMORY);220 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_MEMORY, ret);221 TEST_ASSERT_EQUAL_INT(PAGER_JOURNALMODE_MEMORY, (int)pPager->journalMode);222 223 sqlite3_close(db);224}225 226/* Main */227 228int main(void) {229 UNITY_BEGIN();230 RUN_TEST(test_sqlite3PagerSetJournalMode_basic_change);231 RUN_TEST(test_sqlite3PagerSetJournalMode_persist_to_delete_removes_journal);232 RUN_TEST(test_sqlite3PagerSetJournalMode_noop_does_not_delete_existing_journal);233 RUN_TEST(test_sqlite3PagerSetJournalMode_off_closes_jfd_when_no_delete_branch);234 RUN_TEST(test_sqlite3PagerSetJournalMode_memdb_restricts_modes);235 return UNITY_END();236}