From b4cf7ef2eed6882300b1f8750ae37db46e24632c Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 10 Dec 2024 10:07:13 +0200 Subject: [PATCH 1/3] libsql-sqlite3: Add libsql_wal_checkpoint_seq_count() API This adds libsql_wal_checkpoint_seq_count() API function for determining the checkpoint sequence counter of a database WAL. --- libsql-sqlite3/doc/libsql_extensions.md | 1 + libsql-sqlite3/src/main.c | 24 +++++++++++++++++++ libsql-sqlite3/src/pager.c | 12 ++++++++++ libsql-sqlite3/src/sqlite.h.in | 9 +++++++ libsql-sqlite3/src/test_walapi.c | 31 +++++++++++++++++++++++++ libsql-sqlite3/src/wal.c | 6 +++++ libsql-sqlite3/src/wal.h | 3 +++ 7 files changed, 86 insertions(+) diff --git a/libsql-sqlite3/doc/libsql_extensions.md b/libsql-sqlite3/doc/libsql_extensions.md index d4602ac542..f2be5b2dfb 100644 --- a/libsql-sqlite3/doc/libsql_extensions.md +++ b/libsql-sqlite3/doc/libsql_extensions.md @@ -362,6 +362,7 @@ The libSQL has the following WAL API functions, which are useful for syncing the WAL between databases: * `libsql_wal_disable_checkpoint` -- Disable checkpointing, including on database close. +* `libsql_wal_checkpoint_seq_count` -- Return the checkpoint sequence number of the WAL. * `libsql_wal_frame_count` -- Get the number of frames in the WAL. * `libsql_wal_get_frame` -- Get a frame from the WAL. * `libsql_wal_insert_begin` -- Begin WAL insertion. diff --git a/libsql-sqlite3/src/main.c b/libsql-sqlite3/src/main.c index 62a4a63276..d431577d76 100644 --- a/libsql-sqlite3/src/main.c +++ b/libsql-sqlite3/src/main.c @@ -2449,6 +2449,30 @@ int libsql_wal_disable_checkpoint(sqlite3 *db) { return SQLITE_OK; } +/* +** Return the checkpoint sequence counter of the given database. +*/ +int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt) { + int rc = SQLITE_OK; + Pager *pPager; + +#ifdef SQLITE_OMIT_WAL + *pnFrame = 0; + return SQLITE_OK; +#else +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerWalCheckpointSeqCount(pPager, pnCkpt); + sqlite3Error(db, rc); + sqlite3_mutex_leave(db->mutex); + return rc; +#endif +} + /* ** Return the number of frames in the WAL of the given database. */ diff --git a/libsql-sqlite3/src/pager.c b/libsql-sqlite3/src/pager.c index ed8c99ee13..d99cd81569 100644 --- a/libsql-sqlite3/src/pager.c +++ b/libsql-sqlite3/src/pager.c @@ -7771,6 +7771,18 @@ int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ return rc; } +/** +** Return the current checkpoint generation in the WAL file. +**/ +int sqlite3PagerWalCheckpointSeqCount(Pager *pPager, unsigned int *pnCkpt){ + if( pagerUseWal(pPager) ){ + return pPager->wal->methods.xCheckpointSeqCount(pPager->wal->pData, pnCkpt); + } else { + *pnCkpt = 0; + return SQLITE_OK; + } +} + /** ** Return the number of frames in the WAL file. ** diff --git a/libsql-sqlite3/src/sqlite.h.in b/libsql-sqlite3/src/sqlite.h.in index eb96b55254..dab6c31db4 100644 --- a/libsql-sqlite3/src/sqlite.h.in +++ b/libsql-sqlite3/src/sqlite.h.in @@ -10564,6 +10564,15 @@ void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlite3 *db), vo */ int libsql_wal_disable_checkpoint(sqlite3 *db); +/* +** CAPI3REF: Get the checkpoint sequence counter of the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_checkpoint_seq_count(D,P)] interface returns the checkpoint sequence counter +** of the WAL file for [database connection] D into *P. +*/ +int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt); + /* ** CAPI3REF: Get the number of frames in the WAL file ** METHOD: sqlite3 diff --git a/libsql-sqlite3/src/test_walapi.c b/libsql-sqlite3/src/test_walapi.c index 4eb66b7cfa..b593ad72df 100644 --- a/libsql-sqlite3/src/test_walapi.c +++ b/libsql-sqlite3/src/test_walapi.c @@ -145,6 +145,34 @@ void test_sync_while_reading() { cmp_data(db_primary, db_backup); } +// This test case writes to a local database, force checkpoint, and then +//verifies that checkpoint sequence number incremented, and the WAL is +//truncated. +static void test_checkpoint(void) { + sqlite3 *db_primary; + unsigned int max_frame, ckpt_seq; + + open_db("primary_test_checkpoint.db", &db_primary); + + ensure(sqlite3_exec(db_primary, "CREATE TABLE t (x)", 0, 0, 0) == SQLITE_OK, "failed to insert data\n"); + ensure(sqlite3_exec(db_primary, "INSERT INTO t VALUES (randomblob(4 * 1024))", 0, 0, 0) == SQLITE_OK, "failed to insert data\n"); + ensure(sqlite3_exec(db_primary, "INSERT INTO t VALUES (randomblob(1 * 1024))", 0, 0, 0) == SQLITE_OK, "failed to insert data\n"); + ensure(sqlite3_exec(db_primary, "INSERT INTO t VALUES (randomblob(1 * 1024))", 0, 0, 0) == SQLITE_OK, "failed to insert data\n"); + + ensure(libsql_wal_checkpoint_seq_count(db_primary, &ckpt_seq) == SQLITE_OK, "failed to get checkpoint sequence count: %s\n", sqlite3_errmsg(db_primary)); + assert(ckpt_seq == 0); + ensure(libsql_wal_frame_count(db_primary, &max_frame) == SQLITE_OK, "failed to get frames count: %s\n", sqlite3_errmsg(db_primary)); + assert(max_frame == 7); + + // force checkpoint + ensure(sqlite3_wal_checkpoint_v2(db_primary, NULL, SQLITE_CHECKPOINT_TRUNCATE, NULL, NULL) == SQLITE_OK, "failed to checkpoint: %s\n", sqlite3_errmsg(db_primary)); + + ensure(libsql_wal_checkpoint_seq_count(db_primary, &ckpt_seq) == SQLITE_OK, "failed to get checkpoint sequence count: %s\n", sqlite3_errmsg(db_primary)); + assert(ckpt_seq == 1); + ensure(libsql_wal_frame_count(db_primary, &max_frame) == SQLITE_OK, "failed to get frames count: %s\n", sqlite3_errmsg(db_primary)); + assert(max_frame == 0); +} + int main(int argc, char *argv[]) { test_huge_payload(); @@ -156,5 +184,8 @@ int main(int argc, char *argv[]) test_sync_while_reading(); printf("============= OK test_sync_while_reading\n"); + test_checkpoint(); + printf("============= OK test_checkpoint\n"); + return 0; } diff --git a/libsql-sqlite3/src/wal.c b/libsql-sqlite3/src/wal.c index 64d4524760..3c47584fe5 100644 --- a/libsql-sqlite3/src/wal.c +++ b/libsql-sqlite3/src/wal.c @@ -4024,6 +4024,11 @@ static int walFrames( return rc; } +int sqlite3WalCheckpointSeqCount(Wal *pWal, unsigned int *pnCkpt){ + *pnCkpt = pWal->nCkpt; + return SQLITE_OK; +} + int sqlite3WalFrameCount(Wal *pWal, int locked, unsigned int *pnFrames){ int rc = SQLITE_OK; if( locked==0 ) { @@ -4543,6 +4548,7 @@ static int sqlite3WalOpen( out->methods.xUndo = (int (*)(wal_impl *, int (*)(void *, unsigned int), void *))sqlite3WalUndo; out->methods.xSavepoint = (void (*)(wal_impl *, unsigned int *))sqlite3WalSavepoint; out->methods.xSavepointUndo = (int (*)(wal_impl *, unsigned int *))sqlite3WalSavepointUndo; + out->methods.xCheckpointSeqCount = (int (*)(wal_impl *, unsigned int *))sqlite3WalCheckpointSeqCount; out->methods.xFrameCount = (int (*)(wal_impl *, int, unsigned int *))sqlite3WalFrameCount; out->methods.xFrames = (int (*)(wal_impl *, int, libsql_pghdr *, unsigned int, int, int, int *))sqlite3WalFrames; out->methods.xCheckpoint = (int (*)(wal_impl *, sqlite3 *, int, int (*)(void *), void *, int, int, unsigned char *, int *, int *, int (*)(void*, int, const unsigned char*, int, int, int), void*))sqlite3WalCheckpoint; diff --git a/libsql-sqlite3/src/wal.h b/libsql-sqlite3/src/wal.h index 69df6716d8..2cb8624a41 100644 --- a/libsql-sqlite3/src/wal.h +++ b/libsql-sqlite3/src/wal.h @@ -76,6 +76,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the current checkpoint generation in the WAL file. */ + int (*xCheckpointSeqCount)(wal_impl* pWal, unsigned int *pnCkpt); + /* Return the number of frames in the WAL */ int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); From e9e0405a7aee4432f57ab497c34cc41f9b6a3229 Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 10 Dec 2024 12:26:19 +0200 Subject: [PATCH 2/3] libsql-ffi: Update bundled SQLite --- .../SQLite3MultipleCiphers/src/sqlite3.c | 57 +++++++++++++++++++ libsql-ffi/bundled/bindings/bindgen.rs | 28 +++++---- .../bundled/bindings/session_bindgen.rs | 12 ++++ libsql-ffi/bundled/src/sqlite3.c | 57 +++++++++++++++++++ libsql-ffi/bundled/src/sqlite3.h | 12 ++++ 5 files changed, 154 insertions(+), 12 deletions(-) diff --git a/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c b/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c index 69d2a9eb93..f8c5cc347d 100644 --- a/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c +++ b/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c @@ -10953,6 +10953,15 @@ SQLITE_API void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlit */ SQLITE_API int libsql_wal_disable_checkpoint(sqlite3 *db); +/* +** CAPI3REF: Get the checkpoint sequence counter of the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_checkpoint_seq_count(D,P)] interface returns the checkpoint sequence counter +** of the WAL file for [database connection] D into *P. +*/ +SQLITE_API int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt); + /* ** CAPI3REF: Get the number of frames in the WAL file ** METHOD: sqlite3 @@ -14036,6 +14045,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the current checkpoint generation in the WAL file. */ + int (*xCheckpointSeqCount)(wal_impl* pWal, unsigned int *pnCkpt); + /* Return the number of frames in the WAL */ int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); @@ -57354,6 +57366,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the current checkpoint generation in the WAL file. */ + int (*xCheckpointSeqCount)(wal_impl* pWal, unsigned int *pnCkpt); + /* Return the number of frames in the WAL */ int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); @@ -65282,6 +65297,18 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ return rc; } +/** +** Return the current checkpoint generation in the WAL file. +**/ +SQLITE_PRIVATE int sqlite3PagerWalCheckpointSeqCount(Pager *pPager, unsigned int *pnCkpt){ + if( pagerUseWal(pPager) ){ + return pPager->wal->methods.xCheckpointSeqCount(pPager->wal->pData, pnCkpt); + } else { + *pnCkpt = 0; + return SQLITE_OK; + } +} + /** ** Return the number of frames in the WAL file. ** @@ -69533,6 +69560,11 @@ static int walFrames( return rc; } +SQLITE_PRIVATE int sqlite3WalCheckpointSeqCount(Wal *pWal, unsigned int *pnCkpt){ + *pnCkpt = pWal->nCkpt; + return SQLITE_OK; +} + SQLITE_PRIVATE int sqlite3WalFrameCount(Wal *pWal, int locked, unsigned int *pnFrames){ int rc = SQLITE_OK; if( locked==0 ) { @@ -70052,6 +70084,7 @@ static int sqlite3WalOpen( out->methods.xUndo = (int (*)(wal_impl *, int (*)(void *, unsigned int), void *))sqlite3WalUndo; out->methods.xSavepoint = (void (*)(wal_impl *, unsigned int *))sqlite3WalSavepoint; out->methods.xSavepointUndo = (int (*)(wal_impl *, unsigned int *))sqlite3WalSavepointUndo; + out->methods.xCheckpointSeqCount = (int (*)(wal_impl *, unsigned int *))sqlite3WalCheckpointSeqCount; out->methods.xFrameCount = (int (*)(wal_impl *, int, unsigned int *))sqlite3WalFrameCount; out->methods.xFrames = (int (*)(wal_impl *, int, libsql_pghdr *, unsigned int, int, int, int *))sqlite3WalFrames; out->methods.xCheckpoint = (int (*)(wal_impl *, sqlite3 *, int, int (*)(void *), void *, int, int, unsigned char *, int *, int *, int (*)(void*, int, const unsigned char*, int, int, int), void*))sqlite3WalCheckpoint; @@ -183206,6 +183239,30 @@ int libsql_wal_disable_checkpoint(sqlite3 *db) { return SQLITE_OK; } +/* +** Return the checkpoint sequence counter of the given database. +*/ +int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt) { + int rc = SQLITE_OK; + Pager *pPager; + +#ifdef SQLITE_OMIT_WAL + *pnFrame = 0; + return SQLITE_OK; +#else +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerWalCheckpointSeqCount(pPager, pnCkpt); + sqlite3Error(db, rc); + sqlite3_mutex_leave(db->mutex); + return rc; +#endif +} + /* ** Return the number of frames in the WAL of the given database. */ diff --git a/libsql-ffi/bundled/bindings/bindgen.rs b/libsql-ffi/bundled/bindings/bindgen.rs index 9a8fa22ca2..09d7393d27 100644 --- a/libsql-ffi/bundled/bindings/bindgen.rs +++ b/libsql-ffi/bundled/bindings/bindgen.rs @@ -940,7 +940,7 @@ extern "C" { extern "C" { pub fn sqlite3_vmprintf( arg1: *const ::std::os::raw::c_char, - arg2: *mut __va_list_tag, + arg2: va_list, ) -> *mut ::std::os::raw::c_char; } extern "C" { @@ -956,7 +956,7 @@ extern "C" { arg1: ::std::os::raw::c_int, arg2: *mut ::std::os::raw::c_char, arg3: *const ::std::os::raw::c_char, - arg4: *mut __va_list_tag, + arg4: va_list, ) -> *mut ::std::os::raw::c_char; } extern "C" { @@ -2503,7 +2503,7 @@ extern "C" { pub fn sqlite3_str_vappendf( arg1: *mut sqlite3_str, zFormat: *const ::std::os::raw::c_char, - arg2: *mut __va_list_tag, + arg2: va_list, ); } extern "C" { @@ -2866,6 +2866,12 @@ extern "C" { extern "C" { pub fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> ::std::os::raw::c_int; } +extern "C" { + pub fn libsql_wal_checkpoint_seq_count( + db: *mut sqlite3, + pnCkpt: *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} extern "C" { pub fn libsql_wal_frame_count( arg1: *mut sqlite3, @@ -3355,6 +3361,12 @@ pub struct libsql_wal_methods { aWalData: *mut ::std::os::raw::c_uint, ) -> ::std::os::raw::c_int, >, + pub xCheckpointSeqCount: ::std::option::Option< + unsafe extern "C" fn( + pWal: *mut wal_impl, + pnCkpt: *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int, + >, pub xFrameCount: ::std::option::Option< unsafe extern "C" fn( pWal: *mut wal_impl, @@ -3570,12 +3582,4 @@ extern "C" { extern "C" { pub static sqlite3_wal_manager: libsql_wal_manager; } -pub type __builtin_va_list = [__va_list_tag; 1usize]; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct __va_list_tag { - pub gp_offset: ::std::os::raw::c_uint, - pub fp_offset: ::std::os::raw::c_uint, - pub overflow_arg_area: *mut ::std::os::raw::c_void, - pub reg_save_area: *mut ::std::os::raw::c_void, -} +pub type __builtin_va_list = *mut ::std::os::raw::c_char; diff --git a/libsql-ffi/bundled/bindings/session_bindgen.rs b/libsql-ffi/bundled/bindings/session_bindgen.rs index 88dd5d2fe2..eb9cbf9f06 100644 --- a/libsql-ffi/bundled/bindings/session_bindgen.rs +++ b/libsql-ffi/bundled/bindings/session_bindgen.rs @@ -2883,6 +2883,12 @@ extern "C" { extern "C" { pub fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> ::std::os::raw::c_int; } +extern "C" { + pub fn libsql_wal_checkpoint_seq_count( + db: *mut sqlite3, + pnCkpt: *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} extern "C" { pub fn libsql_wal_frame_count( arg1: *mut sqlite3, @@ -3867,6 +3873,12 @@ pub struct libsql_wal_methods { aWalData: *mut ::std::os::raw::c_uint, ) -> ::std::os::raw::c_int, >, + pub xCheckpointSeqCount: ::std::option::Option< + unsafe extern "C" fn( + pWal: *mut wal_impl, + pnCkpt: *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int, + >, pub xFrameCount: ::std::option::Option< unsafe extern "C" fn( pWal: *mut wal_impl, diff --git a/libsql-ffi/bundled/src/sqlite3.c b/libsql-ffi/bundled/src/sqlite3.c index 69d2a9eb93..f8c5cc347d 100644 --- a/libsql-ffi/bundled/src/sqlite3.c +++ b/libsql-ffi/bundled/src/sqlite3.c @@ -10953,6 +10953,15 @@ SQLITE_API void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlit */ SQLITE_API int libsql_wal_disable_checkpoint(sqlite3 *db); +/* +** CAPI3REF: Get the checkpoint sequence counter of the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_checkpoint_seq_count(D,P)] interface returns the checkpoint sequence counter +** of the WAL file for [database connection] D into *P. +*/ +SQLITE_API int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt); + /* ** CAPI3REF: Get the number of frames in the WAL file ** METHOD: sqlite3 @@ -14036,6 +14045,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the current checkpoint generation in the WAL file. */ + int (*xCheckpointSeqCount)(wal_impl* pWal, unsigned int *pnCkpt); + /* Return the number of frames in the WAL */ int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); @@ -57354,6 +57366,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the current checkpoint generation in the WAL file. */ + int (*xCheckpointSeqCount)(wal_impl* pWal, unsigned int *pnCkpt); + /* Return the number of frames in the WAL */ int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); @@ -65282,6 +65297,18 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ return rc; } +/** +** Return the current checkpoint generation in the WAL file. +**/ +SQLITE_PRIVATE int sqlite3PagerWalCheckpointSeqCount(Pager *pPager, unsigned int *pnCkpt){ + if( pagerUseWal(pPager) ){ + return pPager->wal->methods.xCheckpointSeqCount(pPager->wal->pData, pnCkpt); + } else { + *pnCkpt = 0; + return SQLITE_OK; + } +} + /** ** Return the number of frames in the WAL file. ** @@ -69533,6 +69560,11 @@ static int walFrames( return rc; } +SQLITE_PRIVATE int sqlite3WalCheckpointSeqCount(Wal *pWal, unsigned int *pnCkpt){ + *pnCkpt = pWal->nCkpt; + return SQLITE_OK; +} + SQLITE_PRIVATE int sqlite3WalFrameCount(Wal *pWal, int locked, unsigned int *pnFrames){ int rc = SQLITE_OK; if( locked==0 ) { @@ -70052,6 +70084,7 @@ static int sqlite3WalOpen( out->methods.xUndo = (int (*)(wal_impl *, int (*)(void *, unsigned int), void *))sqlite3WalUndo; out->methods.xSavepoint = (void (*)(wal_impl *, unsigned int *))sqlite3WalSavepoint; out->methods.xSavepointUndo = (int (*)(wal_impl *, unsigned int *))sqlite3WalSavepointUndo; + out->methods.xCheckpointSeqCount = (int (*)(wal_impl *, unsigned int *))sqlite3WalCheckpointSeqCount; out->methods.xFrameCount = (int (*)(wal_impl *, int, unsigned int *))sqlite3WalFrameCount; out->methods.xFrames = (int (*)(wal_impl *, int, libsql_pghdr *, unsigned int, int, int, int *))sqlite3WalFrames; out->methods.xCheckpoint = (int (*)(wal_impl *, sqlite3 *, int, int (*)(void *), void *, int, int, unsigned char *, int *, int *, int (*)(void*, int, const unsigned char*, int, int, int), void*))sqlite3WalCheckpoint; @@ -183206,6 +183239,30 @@ int libsql_wal_disable_checkpoint(sqlite3 *db) { return SQLITE_OK; } +/* +** Return the checkpoint sequence counter of the given database. +*/ +int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt) { + int rc = SQLITE_OK; + Pager *pPager; + +#ifdef SQLITE_OMIT_WAL + *pnFrame = 0; + return SQLITE_OK; +#else +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; +#endif + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerWalCheckpointSeqCount(pPager, pnCkpt); + sqlite3Error(db, rc); + sqlite3_mutex_leave(db->mutex); + return rc; +#endif +} + /* ** Return the number of frames in the WAL of the given database. */ diff --git a/libsql-ffi/bundled/src/sqlite3.h b/libsql-ffi/bundled/src/sqlite3.h index 916f3a0b68..16de3a2939 100644 --- a/libsql-ffi/bundled/src/sqlite3.h +++ b/libsql-ffi/bundled/src/sqlite3.h @@ -10564,6 +10564,15 @@ SQLITE_API void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlit */ SQLITE_API int libsql_wal_disable_checkpoint(sqlite3 *db); +/* +** CAPI3REF: Get the checkpoint sequence counter of the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_checkpoint_seq_count(D,P)] interface returns the checkpoint sequence counter +** of the WAL file for [database connection] D into *P. +*/ +SQLITE_API int libsql_wal_checkpoint_seq_count(sqlite3 *db, unsigned int *pnCkpt); + /* ** CAPI3REF: Get the number of frames in the WAL file ** METHOD: sqlite3 @@ -13647,6 +13656,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the current checkpoint generation in the WAL file. */ + int (*xCheckpointSeqCount)(wal_impl* pWal, unsigned int *pnCkpt); + /* Return the number of frames in the WAL */ int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); From bb7f011f7974ccb5c402624d549aa56641bbb33b Mon Sep 17 00:00:00 2001 From: Pekka Enberg Date: Tue, 10 Dec 2024 12:35:05 +0200 Subject: [PATCH 3/3] libsql: Wire up xCheckpointSeqCount --- .../injector/sqlite_injector/injector_wal.rs | 4 ++++ .../test/rust_suite/src/virtual_wal.rs | 4 ++++ libsql-storage/src/lib.rs | 4 ++++ libsql-sys/src/wal/either.rs | 6 ++++++ libsql-sys/src/wal/ffi.rs | 19 +++++++++++++++++++ libsql-sys/src/wal/mod.rs | 2 ++ libsql-sys/src/wal/sqlite3_wal.rs | 12 ++++++++++++ libsql-sys/src/wal/wrapper.rs | 12 ++++++++++++ libsql-wal/src/wal.rs | 5 +++++ 9 files changed, 68 insertions(+) diff --git a/libsql-replication/src/injector/sqlite_injector/injector_wal.rs b/libsql-replication/src/injector/sqlite_injector/injector_wal.rs index 545899d57a..9262c8dea2 100644 --- a/libsql-replication/src/injector/sqlite_injector/injector_wal.rs +++ b/libsql-replication/src/injector/sqlite_injector/injector_wal.rs @@ -138,6 +138,10 @@ impl Wal for InjectorWal { self.inner.savepoint_undo(rollback_data) } + fn checkpoint_seq_count(&self) -> Result { + self.inner.checkpoint_seq_count() + } + fn frame_count(&self, locked: i32) -> Result { self.inner.frame_count(locked) } diff --git a/libsql-sqlite3/test/rust_suite/src/virtual_wal.rs b/libsql-sqlite3/test/rust_suite/src/virtual_wal.rs index 2395a34b76..6e936d4176 100644 --- a/libsql-sqlite3/test/rust_suite/src/virtual_wal.rs +++ b/libsql-sqlite3/test/rust_suite/src/virtual_wal.rs @@ -106,6 +106,10 @@ mod tests { self.0.read_frame_raw(page_no, buffer) } + fn checkpoint_seq_count(&self) -> libsql_sys::wal::Result { + self.0.checkpoint_seq_count() + } + fn frame_count(&self, locked: i32) -> libsql_sys::wal::Result { self.0.frame_count(locked) } diff --git a/libsql-storage/src/lib.rs b/libsql-storage/src/lib.rs index ce65fe1307..20672cdc90 100644 --- a/libsql-storage/src/lib.rs +++ b/libsql-storage/src/lib.rs @@ -270,6 +270,10 @@ impl Wal for DurableWal { todo!() } + fn checkpoint_seq_count(&self) -> Result { + todo!() + } + fn frame_count(&self, _locked: i32) -> Result { todo!() } diff --git a/libsql-sys/src/wal/either.rs b/libsql-sys/src/wal/either.rs index dbad73f861..654b80c137 100644 --- a/libsql-sys/src/wal/either.rs +++ b/libsql-sys/src/wal/either.rs @@ -81,6 +81,12 @@ macro_rules! create_either { } } + fn checkpoint_seq_count(&self) -> super::Result { + match self { + $( $name::$t(inner) => inner.checkpoint_seq_count() ),* + } + } + fn frame_count(&self, locked: i32) -> super::Result { match self { $( $name::$t(inner) => inner.frame_count(locked) ),* diff --git a/libsql-sys/src/wal/ffi.rs b/libsql-sys/src/wal/ffi.rs index 6b1d303d8f..7526874ef2 100644 --- a/libsql-sys/src/wal/ffi.rs +++ b/libsql-sys/src/wal/ffi.rs @@ -30,6 +30,7 @@ pub(crate) fn construct_libsql_wal(wal: *mut W) -> libsql_wal { xUndo: Some(undo::), xSavepoint: Some(savepoint::), xSavepointUndo: Some(savepoint_undo::), + xCheckpointSeqCount: Some(checkpoint_seq_count::), xFrameCount: Some(frame_count::), xFrames: Some(frames::), xCheckpoint: Some(checkpoint::), @@ -295,6 +296,24 @@ pub unsafe extern "C" fn savepoint_undo(wal: *mut wal_impl, wal_data: *m } } +pub unsafe extern "C" fn checkpoint_seq_count( + wal: *mut wal_impl, + out: *mut c_uint, +) -> c_int { + let this = &mut (*(wal as *mut T)); + match this.checkpoint_seq_count() { + Ok(n) => { + if !out.is_null() { + unsafe { + *out = n as _; + } + } + SQLITE_OK + } + Err(code) => code.extended_code, + } +} + pub unsafe extern "C" fn frame_count( wal: *mut wal_impl, locked: i32, diff --git a/libsql-sys/src/wal/mod.rs b/libsql-sys/src/wal/mod.rs index 71e0c21ee3..4a7cbce447 100644 --- a/libsql-sys/src/wal/mod.rs +++ b/libsql-sys/src/wal/mod.rs @@ -199,6 +199,8 @@ pub trait Wal { fn savepoint(&mut self, rollback_data: &mut [u32]); fn savepoint_undo(&mut self, rollback_data: &mut [u32]) -> Result<()>; + fn checkpoint_seq_count(&self) -> Result; + fn frame_count(&self, locked: i32) -> Result; /// Insert frames in the wal. On commit, returns the number of inserted frames for that diff --git a/libsql-sys/src/wal/sqlite3_wal.rs b/libsql-sys/src/wal/sqlite3_wal.rs index 0764a79cd9..d89da362b8 100644 --- a/libsql-sys/src/wal/sqlite3_wal.rs +++ b/libsql-sys/src/wal/sqlite3_wal.rs @@ -288,6 +288,18 @@ impl Wal for Sqlite3Wal { } } + fn checkpoint_seq_count(&self) -> Result { + let mut out: u32 = 0; + let rc = unsafe { + (self.inner.methods.xCheckpointSeqCount.unwrap())(self.inner.pData, &mut out) + }; + if rc != 0 { + Err(Error::new(rc)) + } else { + Ok(out) + } + } + fn frame_count(&self, locked: i32) -> Result { let mut out: u32 = 0; let rc = unsafe { diff --git a/libsql-sys/src/wal/wrapper.rs b/libsql-sys/src/wal/wrapper.rs index 4f3fdb7159..af12d6463d 100644 --- a/libsql-sys/src/wal/wrapper.rs +++ b/libsql-sys/src/wal/wrapper.rs @@ -89,6 +89,10 @@ impl, W: Wal> Wal for WalRef { unsafe { (*self.wrapper).savepoint_undo(&mut *self.wrapped, rollback_data) } } + fn checkpoint_seq_count(&self) -> super::Result { + unsafe { (*self.wrapper).checkpoint_seq_count(&*self.wrapped) } + } + fn frame_count(&self, locked: i32) -> super::Result { unsafe { (*self.wrapper).frame_count(&*self.wrapped, locked) } } @@ -272,6 +276,10 @@ where .savepoint_undo(&mut self.wrapped, rollback_data) } + fn checkpoint_seq_count(&self) -> super::Result { + self.wrapper.checkpoint_seq_count(&self.wrapped) + } + fn frame_count(&self, locked: i32) -> super::Result { self.wrapper.frame_count(&self.wrapped, locked) } @@ -409,6 +417,10 @@ pub trait WrapWal { wrapped.savepoint_undo(rollback_data) } + fn checkpoint_seq_count(&self, wrapped: &W) -> super::Result { + wrapped.checkpoint_seq_count() + } + fn frame_count(&self, wrapped: &W, locked: i32) -> super::Result { wrapped.frame_count(locked) } diff --git a/libsql-wal/src/wal.rs b/libsql-wal/src/wal.rs index 6c89ce848f..8398adcbd6 100644 --- a/libsql-wal/src/wal.rs +++ b/libsql-wal/src/wal.rs @@ -275,6 +275,11 @@ where } } + #[tracing::instrument(skip_all, fields(id = self.conn_id))] + fn checkpoint_seq_count(&self) -> libsql_sys::wal::Result { + Err(libsql_sys::wal::Error::new(10)) // SQLITE_IOERR + } + #[tracing::instrument(skip_all, fields(id = self.conn_id))] fn frame_count(&self, _locked: i32) -> libsql_sys::wal::Result { Err(libsql_sys::wal::Error::new(10)) // SQLITE_IOERR