diff --git a/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c b/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c index 82db050d36..c3f15b1dd7 100644 --- a/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c +++ b/libsql-ffi/bundled/SQLite3MultipleCiphers/src/sqlite3.c @@ -10938,6 +10938,59 @@ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); */ SQLITE_API void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlite3 *db), void *arg); +/* +** CAPI3REF: Disable WAL checkpointing +** METHOD: sqlite3 +** +** ^The [libsql_wal_disable_checkpoint(D)] interface disables automatic +** checkpointing of the WAL file for [database connection] D. +** +** Note: This function disables WAL checkpointing entirely, including when +** the last database connection is closed. This is different from +** sqlite3_wal_autocheckpoint() which only disables automatic checkpoints +** for the current connection, but still allows checkpointing when the +** connection is closed. +*/ +SQLITE_API int libsql_wal_disable_checkpoint(sqlite3 *db); + +/* +** CAPI3REF: Get the number of frames in the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_frame_count(D,P)] interface returns the number of frames +** in the WAL file for [database connection] D into *P. +*/ +SQLITE_API int libsql_wal_frame_count(sqlite3*, unsigned int*); + +/* +** CAPI3REF: Get a frame from the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_get_frame(D,I,P,S)] interface extracts frame I from +** the WAL file for [database connection] D into memory obtained from +** [sqlite3_malloc64()] and returns a pointer to that memory. The size of +** the memory allocated is given by S. +*/ +SQLITE_API int libsql_wal_get_frame(sqlite3*, unsigned int, void*, unsigned int); + +/* +** CAPI3REF: Begin frame insertion into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_begin(sqlite3*); + +/* +** CAPI3REF: End frame insertion into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_end(sqlite3*); + +/* +** CAPI3REF: Insert a frame into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_frame(sqlite3*, unsigned int, void *, unsigned int); + /* ** CAPI3REF: Low-level system error code ** METHOD: sqlite3 @@ -13963,6 +14016,7 @@ typedef struct libsql_wal_methods { /* Read a page from the write-ahead log, if it is present. */ int (*xFindFrame)(wal_impl* pWal, unsigned int, unsigned int *); int (*xReadFrame)(wal_impl* pWal, unsigned int, int, unsigned char *); + int (*xReadFrameRaw)(wal_impl* pWal, unsigned int, int, unsigned char *); /* If the WAL is not empty, return the size of the database. */ unsigned int (*xDbsize)(wal_impl* pWal); @@ -13982,6 +14036,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the number of frames in the WAL */ + int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); + /* Write a frame or frames to the log. */ int (*xFrames)(wal_impl* pWal, int, libsql_pghdr *, unsigned int, int, int, int*); @@ -16378,6 +16435,12 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); +SQLITE_PRIVATE int sqlite3PagerWalFrameCount(Pager *, unsigned int *); +SQLITE_PRIVATE int sqlite3PagerWalReadFrame(Pager *, unsigned int, void *, unsigned int); +SQLITE_PRIVATE int sqlite3PagerWalBeginCommit(Pager*); +SQLITE_PRIVATE int sqlite3PagerWalEndCommit(Pager*); +SQLITE_PRIVATE int sqlite3PagerWalInsert(Pager*, unsigned int, void *, unsigned int); + SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); @@ -18223,6 +18286,7 @@ struct sqlite3 { PreUpdate *pPreUpdate; /* Context for active pre-update callback */ #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifndef SQLITE_OMIT_WAL + int walCheckPointDisabled; int (*xWalCallback)(void *, sqlite3 *, const char *, int); void *pWalArg; #endif @@ -57270,6 +57334,7 @@ typedef struct libsql_wal_methods { /* Read a page from the write-ahead log, if it is present. */ int (*xFindFrame)(wal_impl* pWal, unsigned int, unsigned int *); int (*xReadFrame)(wal_impl* pWal, unsigned int, int, unsigned char *); + int (*xReadFrameRaw)(wal_impl* pWal, unsigned int, int, unsigned char *); /* If the WAL is not empty, return the size of the database. */ unsigned int (*xDbsize)(wal_impl* pWal); @@ -57289,6 +57354,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the number of frames in the WAL */ + int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); + /* Write a frame or frames to the log. */ int (*xFrames)(wal_impl* pWal, int, libsql_pghdr *, unsigned int, int, int, int*); @@ -65214,6 +65282,94 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ return rc; } +/** +** Return the number of frames in the WAL file. +** +** If the pager is not in WAL mode or we failed to obtain an exclusive write lock, returns -1. +**/ +SQLITE_PRIVATE int sqlite3PagerWalFrameCount(Pager *pPager, unsigned int *pnFrames){ + if( pagerUseWal(pPager) ){ + return pPager->wal->methods.xFrameCount(pPager->wal->pData, 0, pnFrames); + }else{ + return SQLITE_ERROR; + } +} + +SQLITE_PRIVATE int sqlite3PagerWalReadFrameRaw( + Pager *pPager, + unsigned int iFrame, + void *pFrameOut, + unsigned int nFrameOutLen +){ + if( pagerUseWal(pPager) ){ + unsigned int nFrameLen = 24+pPager->pageSize; + if( nFrameOutLen!=nFrameLen ) return SQLITE_MISUSE; + return pPager->wal->methods.xReadFrameRaw(pPager->wal->pData, iFrame, nFrameOutLen, pFrameOut); + }else{ + return SQLITE_ERROR; + } +} + +SQLITE_PRIVATE int sqlite3PagerWalBeginCommit(Pager *pPager) { + int rc; + if (!pagerUseWal(pPager)) { + return SQLITE_ERROR; + } + rc = pagerBeginReadTransaction(pPager); + if (rc != SQLITE_OK) { + return rc; + } + return pPager->wal->methods.xBeginWriteTransaction(pPager->wal->pData); +} + +SQLITE_PRIVATE int sqlite3PagerWalEndCommit(Pager *pPager) { + if (!pagerUseWal(pPager)) { + return SQLITE_ERROR; + } + return pPager->wal->methods.xEndWriteTransaction(pPager->wal->pData); +} + +SQLITE_PRIVATE int sqlite3PagerWalInsert(Pager *pPager, unsigned int iFrame, void *pBuf, unsigned int nBuf) { + int rc = SQLITE_OK; + + if (!pagerUseWal(pPager)) { + return SQLITE_ERROR; + } + unsigned int mxFrame; + rc = pPager->wal->methods.xFrameCount(pPager->wal->pData, 1, &mxFrame); + if (rc != SQLITE_OK) { + return rc; + } + if (iFrame <= mxFrame) { + return SQLITE_OK; + } + u8 *aFrame = (u8*)pBuf; + u32 pgno = sqlite3Get4byte(&aFrame[0]); + u32 nTruncate = sqlite3Get4byte(&aFrame[4]); + u8 *pData = aFrame + 24; + + PgHdr pghdr; + memset(&pghdr, 0, sizeof(PgHdr)); + pghdr.pPage = NULL; + pghdr.pData = pData; + pghdr.pExtra = NULL; + pghdr.pgno = pgno; + pghdr.flags = 0; + + int isCommit = (nTruncate != 0); + + int nFrames = 0; + rc = pPager->wal->methods.xFrames(pPager->wal->pData, + pPager->pageSize, + &pghdr, + nTruncate, + isCommit, + pPager->walSyncFlags, + &nFrames); + assert( nFrames == 1 ); + return rc; +} + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* ** If pager pPager is a wal-mode database not in exclusive locking mode, @@ -67601,9 +67757,14 @@ static int sqlite3WalClose( if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } - rc = sqlite3WalCheckpoint(pWal, db, - SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0, NULL, NULL - ); + /* Don't checkpoint on close if automatic WAL checkpointing is disabled. */ + if( !db->walCheckPointDisabled ){ + rc = sqlite3WalCheckpoint(pWal, db, + SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0, NULL, NULL + ); + } else { + rc = SQLITE_ERROR; + } if( rc==SQLITE_OK ){ int bPersist = -1; sqlite3OsFileControlHint( @@ -68731,6 +68892,29 @@ static int sqlite3WalReadFrame( return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); } +/* +** Read the contents of frame iRead from the wal file into buffer pOut +** (which is nOut bytes in size). Return SQLITE_OK if successful, or an +** error code otherwise. +*/ +static int sqlite3WalReadFrameRaw( + Wal *pWal, /* WAL handle */ + u32 iRead, /* Frame to read */ + int nOut, /* Size of buffer pOut in bytes */ + u8 *pOut /* Buffer to write page data to */ +){ + int sz; + i64 iOffset; + sz = pWal->hdr.szPage; + sz = (sz&0xfe00) + ((sz&0x0001)<<16); + testcase( sz<=32768 ); + testcase( sz>=65536 ); + iOffset = walFrameOffset(iRead, sz); + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ + sz += WAL_FRAME_HDRSIZE; + return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); +} + /* ** Return the size of the database in pages (or zero, if unknown). */ @@ -69341,6 +69525,19 @@ static int walFrames( return rc; } +SQLITE_PRIVATE int sqlite3WalFrameCount(Wal *pWal, int locked, unsigned int *pnFrames){ + int rc = SQLITE_OK; + if( locked==0 ) { + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + if (rc != SQLITE_OK) return rc; + } + *pnFrames = pWal->hdr.mxFrame; + if( locked==0 ) { + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + } + return SQLITE_OK; +} + /* ** Write a set of frames to the log. The caller must hold the write-lock ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). @@ -69840,12 +70037,14 @@ static int sqlite3WalOpen( out->methods.xEndReadTransaction = (void (*)(wal_impl *))sqlite3WalEndReadTransaction; out->methods.xFindFrame = (int (*)(wal_impl *, unsigned int, unsigned int *))sqlite3WalFindFrame; out->methods.xReadFrame = (int (*)(wal_impl *, unsigned int, int, unsigned char *))sqlite3WalReadFrame; + out->methods.xReadFrameRaw = (int (*)(wal_impl *, unsigned int, int, unsigned char *))sqlite3WalReadFrameRaw; out->methods.xDbsize = (unsigned int (*)(wal_impl *))sqlite3WalDbsize; out->methods.xBeginWriteTransaction = (int (*)(wal_impl *))sqlite3WalBeginWriteTransaction; out->methods.xEndWriteTransaction = (int (*)(wal_impl *))sqlite3WalEndWriteTransaction; 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.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; out->methods.xCallback = (int (*)(wal_impl *))sqlite3WalCallback; @@ -182972,6 +183171,160 @@ void *libsql_close_hook( return pRet; } +/* +** Disable WAL checkpointing. +** +** Note: This function disables WAL checkpointing entirely, including when +** the last database connection is closed. This is different from +** sqlite3_wal_autocheckpoint() which only disables automatic checkpoints +** for the current connection, but still allows checkpointing when the +** connection is closed. +**/ +int libsql_wal_disable_checkpoint(sqlite3 *db) { +#ifndef SQLITE_OMIT_WAL +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlite3_mutex_enter(db->mutex); + db->walCheckPointDisabled = 1; + db->xWalCallback = 0; + sqlite3_mutex_leave(db->mutex); +#endif + return SQLITE_OK; +} + +/* +** Return the number of frames in the WAL of the given database. +*/ +int libsql_wal_frame_count( + sqlite3* db, + unsigned int *pnFrame +){ + 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 = sqlite3PagerWalFrameCount(pPager, pnFrame); + sqlite3_mutex_leave(db->mutex); + return rc; +#endif +} + +int libsql_wal_get_frame( + sqlite3* db, + unsigned int iFrame, + void *pBuf, + unsigned int nBuf +){ + int rc = SQLITE_OK; + Pager *pPager; + +#ifdef SQLITE_OMIT_WAL + UNUSED_PARAMETER(iFrame); + UNUSED_PARAMETER(nBuf); + UNUSED_PARAMETER(pBuf); + 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 = sqlite3PagerWalReadFrameRaw(pPager, iFrame, pBuf, nBuf); + sqlite3_mutex_leave(db->mutex); + + return rc; +#endif +} + +/* +** Begin a WAL commit. +*/ +int libsql_wal_insert_begin(sqlite3 *db) { + Pager *pPager; + int rc; + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerSharedLock(pPager); + if (rc != SQLITE_OK) { + goto out_unlock; + } + int isOpen = 0; + rc = sqlite3PagerOpenWal(pPager, &isOpen); + if (rc != SQLITE_OK) { + goto out_unlock; + } + rc = sqlite3PagerWalBeginCommit(pPager); + if (rc != SQLITE_OK) { + goto out_unlock; + } +out_unlock: + sqlite3_mutex_leave(db->mutex); + return rc; +} + +int libsql_wal_insert_end(sqlite3 *db) { + Pager *pPager; + int rc; + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerWalEndCommit(pPager); + if (rc != SQLITE_OK) { + goto out_unlock; + } +out_unlock: + sqlite3_mutex_leave(db->mutex); + return rc; +} + +/* +** Insert a frame into the WAL. +*/ +int libsql_wal_insert_frame( + sqlite3* db, + unsigned int iFrame, + void *pBuf, + unsigned int nBuf +){ + 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 = sqlite3PagerWalInsert(pPager, iFrame, pBuf, nBuf); + if (rc != SQLITE_OK) { + goto out_unlock; + } +out_unlock: + sqlite3_mutex_leave(db->mutex); + + return rc; +#endif +} + /* ** Register a function to be invoked prior to each autovacuum that ** determines the number of pages to vacuum. @@ -183069,6 +183422,7 @@ SQLITE_API void *sqlite3_wal_hook( #endif sqlite3_mutex_enter(db->mutex); pRet = db->pWalArg; + db->walCheckPointDisabled = 0; db->xWalCallback = xCallback; db->pWalArg = pArg; sqlite3_mutex_leave(db->mutex); diff --git a/libsql-ffi/bundled/bindings/bindgen.rs b/libsql-ffi/bundled/bindings/bindgen.rs index 05a6782ead..9606aed509 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" { @@ -2863,6 +2863,37 @@ extern "C" { arg: *mut ::std::os::raw::c_void, ) -> *mut ::std::os::raw::c_void; } +extern "C" { + pub fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_frame_count( + arg1: *mut sqlite3, + arg2: *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_get_frame( + arg1: *mut sqlite3, + arg2: ::std::os::raw::c_uint, + arg3: *mut ::std::os::raw::c_void, + arg4: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_insert_begin(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_insert_end(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_insert_frame( + arg1: *mut sqlite3, + arg2: ::std::os::raw::c_uint, + arg3: *mut ::std::os::raw::c_void, + arg4: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} extern "C" { pub fn sqlite3_system_errno(arg1: *mut sqlite3) -> ::std::os::raw::c_int; } @@ -3539,12 +3570,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 4caa0b14a4..88dd5d2fe2 100644 --- a/libsql-ffi/bundled/bindings/session_bindgen.rs +++ b/libsql-ffi/bundled/bindings/session_bindgen.rs @@ -23,10 +23,11 @@ extern "C" { ) -> ::std::os::raw::c_int; } -pub const SQLITE_VERSION: &[u8; 7] = b"3.44.0\0"; -pub const SQLITE_VERSION_NUMBER: i32 = 3044000; +pub const __GNUC_VA_LIST: i32 = 1; +pub const SQLITE_VERSION: &[u8; 7] = b"3.45.1\0"; +pub const SQLITE_VERSION_NUMBER: i32 = 3045001; pub const SQLITE_SOURCE_ID: &[u8; 85] = - b"2023-11-01 11:23:50 17129ba1ff7f0daf37100ee82d507aef7827cf38de1866e2633096ae6ad8alt1\0"; + b"2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257ccalt1\0"; pub const LIBSQL_VERSION: &[u8; 6] = b"0.2.3\0"; pub const SQLITE_OK: i32 = 0; pub const SQLITE_ERROR: i32 = 1; @@ -355,6 +356,7 @@ pub const SQLITE_DETERMINISTIC: i32 = 2048; pub const SQLITE_DIRECTONLY: i32 = 524288; pub const SQLITE_SUBTYPE: i32 = 1048576; pub const SQLITE_INNOCUOUS: i32 = 2097152; +pub const SQLITE_RESULT_SUBTYPE: i32 = 16777216; pub const SQLITE_WIN32_DATA_DIRECTORY_TYPE: i32 = 1; pub const SQLITE_WIN32_TEMP_DIRECTORY_TYPE: i32 = 2; pub const SQLITE_TXN_NONE: i32 = 0; @@ -407,6 +409,7 @@ pub const SQLITE_TESTCTRL_PENDING_BYTE: i32 = 11; pub const SQLITE_TESTCTRL_ASSERT: i32 = 12; pub const SQLITE_TESTCTRL_ALWAYS: i32 = 13; pub const SQLITE_TESTCTRL_RESERVE: i32 = 14; +pub const SQLITE_TESTCTRL_JSON_SELFCHECK: i32 = 14; pub const SQLITE_TESTCTRL_OPTIMIZATIONS: i32 = 15; pub const SQLITE_TESTCTRL_ISKEYWORD: i32 = 16; pub const SQLITE_TESTCTRL_SCRATCHMALLOC: i32 = 17; @@ -516,8 +519,8 @@ pub const FTS5_TOKENIZE_DOCUMENT: i32 = 4; pub const FTS5_TOKENIZE_AUX: i32 = 8; pub const FTS5_TOKEN_COLOCATED: i32 = 1; pub const WAL_SAVEPOINT_NDATA: i32 = 4; -pub type __gnuc_va_list = __builtin_va_list; pub type va_list = __builtin_va_list; +pub type __gnuc_va_list = __builtin_va_list; extern "C" { pub static sqlite3_version: [::std::os::raw::c_char; 0usize]; } @@ -2278,11 +2281,7 @@ pub struct sqlite3_module { pzErr: *mut *mut ::std::os::raw::c_char, ) -> ::std::os::raw::c_int, >, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct libsql_module { - pub iVersion: ::std::os::raw::c_int, + pub reserved: [::std::option::Option; 5usize], pub xPreparedSql: ::std::option::Option< unsafe extern "C" fn( arg1: *mut sqlite3_vtab_cursor, @@ -2344,16 +2343,6 @@ extern "C" { xDestroy: ::std::option::Option, ) -> ::std::os::raw::c_int; } -extern "C" { - pub fn libsql_create_module( - db: *mut sqlite3, - zName: *const ::std::os::raw::c_char, - p: *const sqlite3_module, - pLibsql: *const libsql_module, - pClientData: *mut ::std::os::raw::c_void, - xDestroy: ::std::option::Option, - ) -> ::std::os::raw::c_int; -} extern "C" { pub fn sqlite3_drop_modules( db: *mut sqlite3, @@ -2364,7 +2353,6 @@ extern "C" { #[derive(Debug, Copy, Clone)] pub struct sqlite3_vtab { pub pModule: *const sqlite3_module, - pub pLibsqlModule: *const libsql_module, pub nRef: ::std::os::raw::c_int, pub zErrMsg: *mut ::std::os::raw::c_char, } @@ -2892,6 +2880,37 @@ extern "C" { arg: *mut ::std::os::raw::c_void, ) -> *mut ::std::os::raw::c_void; } +extern "C" { + pub fn libsql_wal_disable_checkpoint(db: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_frame_count( + arg1: *mut sqlite3, + arg2: *mut ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_get_frame( + arg1: *mut sqlite3, + arg2: ::std::os::raw::c_uint, + arg3: *mut ::std::os::raw::c_void, + arg4: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_insert_begin(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_insert_end(arg1: *mut sqlite3) -> ::std::os::raw::c_int; +} +extern "C" { + pub fn libsql_wal_insert_frame( + arg1: *mut sqlite3, + arg2: ::std::os::raw::c_uint, + arg3: *mut ::std::os::raw::c_void, + arg4: ::std::os::raw::c_uint, + ) -> ::std::os::raw::c_int; +} extern "C" { pub fn sqlite3_system_errno(arg1: *mut sqlite3) -> ::std::os::raw::c_int; } @@ -3659,6 +3678,24 @@ pub struct Fts5ExtensionApi { piCol: *mut ::std::os::raw::c_int, ), >, + pub xQueryToken: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut Fts5Context, + iPhrase: ::std::os::raw::c_int, + iToken: ::std::os::raw::c_int, + ppToken: *mut *const ::std::os::raw::c_char, + pnToken: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, + pub xInstToken: ::std::option::Option< + unsafe extern "C" fn( + arg1: *mut Fts5Context, + iIdx: ::std::os::raw::c_int, + iToken: ::std::os::raw::c_int, + arg2: *mut *const ::std::os::raw::c_char, + arg3: *mut ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, + >, } #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/libsql-ffi/bundled/src/sqlite3.c b/libsql-ffi/bundled/src/sqlite3.c index 82db050d36..c3f15b1dd7 100644 --- a/libsql-ffi/bundled/src/sqlite3.c +++ b/libsql-ffi/bundled/src/sqlite3.c @@ -10938,6 +10938,59 @@ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); */ SQLITE_API void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlite3 *db), void *arg); +/* +** CAPI3REF: Disable WAL checkpointing +** METHOD: sqlite3 +** +** ^The [libsql_wal_disable_checkpoint(D)] interface disables automatic +** checkpointing of the WAL file for [database connection] D. +** +** Note: This function disables WAL checkpointing entirely, including when +** the last database connection is closed. This is different from +** sqlite3_wal_autocheckpoint() which only disables automatic checkpoints +** for the current connection, but still allows checkpointing when the +** connection is closed. +*/ +SQLITE_API int libsql_wal_disable_checkpoint(sqlite3 *db); + +/* +** CAPI3REF: Get the number of frames in the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_frame_count(D,P)] interface returns the number of frames +** in the WAL file for [database connection] D into *P. +*/ +SQLITE_API int libsql_wal_frame_count(sqlite3*, unsigned int*); + +/* +** CAPI3REF: Get a frame from the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_get_frame(D,I,P,S)] interface extracts frame I from +** the WAL file for [database connection] D into memory obtained from +** [sqlite3_malloc64()] and returns a pointer to that memory. The size of +** the memory allocated is given by S. +*/ +SQLITE_API int libsql_wal_get_frame(sqlite3*, unsigned int, void*, unsigned int); + +/* +** CAPI3REF: Begin frame insertion into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_begin(sqlite3*); + +/* +** CAPI3REF: End frame insertion into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_end(sqlite3*); + +/* +** CAPI3REF: Insert a frame into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_frame(sqlite3*, unsigned int, void *, unsigned int); + /* ** CAPI3REF: Low-level system error code ** METHOD: sqlite3 @@ -13963,6 +14016,7 @@ typedef struct libsql_wal_methods { /* Read a page from the write-ahead log, if it is present. */ int (*xFindFrame)(wal_impl* pWal, unsigned int, unsigned int *); int (*xReadFrame)(wal_impl* pWal, unsigned int, int, unsigned char *); + int (*xReadFrameRaw)(wal_impl* pWal, unsigned int, int, unsigned char *); /* If the WAL is not empty, return the size of the database. */ unsigned int (*xDbsize)(wal_impl* pWal); @@ -13982,6 +14036,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the number of frames in the WAL */ + int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); + /* Write a frame or frames to the log. */ int (*xFrames)(wal_impl* pWal, int, libsql_pghdr *, unsigned int, int, int, int*); @@ -16378,6 +16435,12 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); SQLITE_PRIVATE void sqlite3PagerSetBusyHandler(Pager*, int(*)(void *), void *); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int); SQLITE_PRIVATE Pgno sqlite3PagerMaxPageCount(Pager*, Pgno); +SQLITE_PRIVATE int sqlite3PagerWalFrameCount(Pager *, unsigned int *); +SQLITE_PRIVATE int sqlite3PagerWalReadFrame(Pager *, unsigned int, void *, unsigned int); +SQLITE_PRIVATE int sqlite3PagerWalBeginCommit(Pager*); +SQLITE_PRIVATE int sqlite3PagerWalEndCommit(Pager*); +SQLITE_PRIVATE int sqlite3PagerWalInsert(Pager*, unsigned int, void *, unsigned int); + SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64); @@ -18223,6 +18286,7 @@ struct sqlite3 { PreUpdate *pPreUpdate; /* Context for active pre-update callback */ #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ #ifndef SQLITE_OMIT_WAL + int walCheckPointDisabled; int (*xWalCallback)(void *, sqlite3 *, const char *, int); void *pWalArg; #endif @@ -57270,6 +57334,7 @@ typedef struct libsql_wal_methods { /* Read a page from the write-ahead log, if it is present. */ int (*xFindFrame)(wal_impl* pWal, unsigned int, unsigned int *); int (*xReadFrame)(wal_impl* pWal, unsigned int, int, unsigned char *); + int (*xReadFrameRaw)(wal_impl* pWal, unsigned int, int, unsigned char *); /* If the WAL is not empty, return the size of the database. */ unsigned int (*xDbsize)(wal_impl* pWal); @@ -57289,6 +57354,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the number of frames in the WAL */ + int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); + /* Write a frame or frames to the log. */ int (*xFrames)(wal_impl* pWal, int, libsql_pghdr *, unsigned int, int, int, int*); @@ -65214,6 +65282,94 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3 *db){ return rc; } +/** +** Return the number of frames in the WAL file. +** +** If the pager is not in WAL mode or we failed to obtain an exclusive write lock, returns -1. +**/ +SQLITE_PRIVATE int sqlite3PagerWalFrameCount(Pager *pPager, unsigned int *pnFrames){ + if( pagerUseWal(pPager) ){ + return pPager->wal->methods.xFrameCount(pPager->wal->pData, 0, pnFrames); + }else{ + return SQLITE_ERROR; + } +} + +SQLITE_PRIVATE int sqlite3PagerWalReadFrameRaw( + Pager *pPager, + unsigned int iFrame, + void *pFrameOut, + unsigned int nFrameOutLen +){ + if( pagerUseWal(pPager) ){ + unsigned int nFrameLen = 24+pPager->pageSize; + if( nFrameOutLen!=nFrameLen ) return SQLITE_MISUSE; + return pPager->wal->methods.xReadFrameRaw(pPager->wal->pData, iFrame, nFrameOutLen, pFrameOut); + }else{ + return SQLITE_ERROR; + } +} + +SQLITE_PRIVATE int sqlite3PagerWalBeginCommit(Pager *pPager) { + int rc; + if (!pagerUseWal(pPager)) { + return SQLITE_ERROR; + } + rc = pagerBeginReadTransaction(pPager); + if (rc != SQLITE_OK) { + return rc; + } + return pPager->wal->methods.xBeginWriteTransaction(pPager->wal->pData); +} + +SQLITE_PRIVATE int sqlite3PagerWalEndCommit(Pager *pPager) { + if (!pagerUseWal(pPager)) { + return SQLITE_ERROR; + } + return pPager->wal->methods.xEndWriteTransaction(pPager->wal->pData); +} + +SQLITE_PRIVATE int sqlite3PagerWalInsert(Pager *pPager, unsigned int iFrame, void *pBuf, unsigned int nBuf) { + int rc = SQLITE_OK; + + if (!pagerUseWal(pPager)) { + return SQLITE_ERROR; + } + unsigned int mxFrame; + rc = pPager->wal->methods.xFrameCount(pPager->wal->pData, 1, &mxFrame); + if (rc != SQLITE_OK) { + return rc; + } + if (iFrame <= mxFrame) { + return SQLITE_OK; + } + u8 *aFrame = (u8*)pBuf; + u32 pgno = sqlite3Get4byte(&aFrame[0]); + u32 nTruncate = sqlite3Get4byte(&aFrame[4]); + u8 *pData = aFrame + 24; + + PgHdr pghdr; + memset(&pghdr, 0, sizeof(PgHdr)); + pghdr.pPage = NULL; + pghdr.pData = pData; + pghdr.pExtra = NULL; + pghdr.pgno = pgno; + pghdr.flags = 0; + + int isCommit = (nTruncate != 0); + + int nFrames = 0; + rc = pPager->wal->methods.xFrames(pPager->wal->pData, + pPager->pageSize, + &pghdr, + nTruncate, + isCommit, + pPager->walSyncFlags, + &nFrames); + assert( nFrames == 1 ); + return rc; +} + #ifdef SQLITE_ENABLE_SETLK_TIMEOUT /* ** If pager pPager is a wal-mode database not in exclusive locking mode, @@ -67601,9 +67757,14 @@ static int sqlite3WalClose( if( pWal->exclusiveMode==WAL_NORMAL_MODE ){ pWal->exclusiveMode = WAL_EXCLUSIVE_MODE; } - rc = sqlite3WalCheckpoint(pWal, db, - SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0, NULL, NULL - ); + /* Don't checkpoint on close if automatic WAL checkpointing is disabled. */ + if( !db->walCheckPointDisabled ){ + rc = sqlite3WalCheckpoint(pWal, db, + SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0, NULL, NULL + ); + } else { + rc = SQLITE_ERROR; + } if( rc==SQLITE_OK ){ int bPersist = -1; sqlite3OsFileControlHint( @@ -68731,6 +68892,29 @@ static int sqlite3WalReadFrame( return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); } +/* +** Read the contents of frame iRead from the wal file into buffer pOut +** (which is nOut bytes in size). Return SQLITE_OK if successful, or an +** error code otherwise. +*/ +static int sqlite3WalReadFrameRaw( + Wal *pWal, /* WAL handle */ + u32 iRead, /* Frame to read */ + int nOut, /* Size of buffer pOut in bytes */ + u8 *pOut /* Buffer to write page data to */ +){ + int sz; + i64 iOffset; + sz = pWal->hdr.szPage; + sz = (sz&0xfe00) + ((sz&0x0001)<<16); + testcase( sz<=32768 ); + testcase( sz>=65536 ); + iOffset = walFrameOffset(iRead, sz); + /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */ + sz += WAL_FRAME_HDRSIZE; + return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset); +} + /* ** Return the size of the database in pages (or zero, if unknown). */ @@ -69341,6 +69525,19 @@ static int walFrames( return rc; } +SQLITE_PRIVATE int sqlite3WalFrameCount(Wal *pWal, int locked, unsigned int *pnFrames){ + int rc = SQLITE_OK; + if( locked==0 ) { + rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + if (rc != SQLITE_OK) return rc; + } + *pnFrames = pWal->hdr.mxFrame; + if( locked==0 ) { + walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1); + } + return SQLITE_OK; +} + /* ** Write a set of frames to the log. The caller must hold the write-lock ** on the log file (obtained using sqlite3WalBeginWriteTransaction()). @@ -69840,12 +70037,14 @@ static int sqlite3WalOpen( out->methods.xEndReadTransaction = (void (*)(wal_impl *))sqlite3WalEndReadTransaction; out->methods.xFindFrame = (int (*)(wal_impl *, unsigned int, unsigned int *))sqlite3WalFindFrame; out->methods.xReadFrame = (int (*)(wal_impl *, unsigned int, int, unsigned char *))sqlite3WalReadFrame; + out->methods.xReadFrameRaw = (int (*)(wal_impl *, unsigned int, int, unsigned char *))sqlite3WalReadFrameRaw; out->methods.xDbsize = (unsigned int (*)(wal_impl *))sqlite3WalDbsize; out->methods.xBeginWriteTransaction = (int (*)(wal_impl *))sqlite3WalBeginWriteTransaction; out->methods.xEndWriteTransaction = (int (*)(wal_impl *))sqlite3WalEndWriteTransaction; 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.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; out->methods.xCallback = (int (*)(wal_impl *))sqlite3WalCallback; @@ -182972,6 +183171,160 @@ void *libsql_close_hook( return pRet; } +/* +** Disable WAL checkpointing. +** +** Note: This function disables WAL checkpointing entirely, including when +** the last database connection is closed. This is different from +** sqlite3_wal_autocheckpoint() which only disables automatic checkpoints +** for the current connection, but still allows checkpointing when the +** connection is closed. +**/ +int libsql_wal_disable_checkpoint(sqlite3 *db) { +#ifndef SQLITE_OMIT_WAL +#ifdef SQLITE_ENABLE_API_ARMOR + if( !sqlite3SafetyCheckOk(db) ){ + return SQLITE_MISUSE_BKPT; + } +#endif + sqlite3_mutex_enter(db->mutex); + db->walCheckPointDisabled = 1; + db->xWalCallback = 0; + sqlite3_mutex_leave(db->mutex); +#endif + return SQLITE_OK; +} + +/* +** Return the number of frames in the WAL of the given database. +*/ +int libsql_wal_frame_count( + sqlite3* db, + unsigned int *pnFrame +){ + 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 = sqlite3PagerWalFrameCount(pPager, pnFrame); + sqlite3_mutex_leave(db->mutex); + return rc; +#endif +} + +int libsql_wal_get_frame( + sqlite3* db, + unsigned int iFrame, + void *pBuf, + unsigned int nBuf +){ + int rc = SQLITE_OK; + Pager *pPager; + +#ifdef SQLITE_OMIT_WAL + UNUSED_PARAMETER(iFrame); + UNUSED_PARAMETER(nBuf); + UNUSED_PARAMETER(pBuf); + 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 = sqlite3PagerWalReadFrameRaw(pPager, iFrame, pBuf, nBuf); + sqlite3_mutex_leave(db->mutex); + + return rc; +#endif +} + +/* +** Begin a WAL commit. +*/ +int libsql_wal_insert_begin(sqlite3 *db) { + Pager *pPager; + int rc; + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerSharedLock(pPager); + if (rc != SQLITE_OK) { + goto out_unlock; + } + int isOpen = 0; + rc = sqlite3PagerOpenWal(pPager, &isOpen); + if (rc != SQLITE_OK) { + goto out_unlock; + } + rc = sqlite3PagerWalBeginCommit(pPager); + if (rc != SQLITE_OK) { + goto out_unlock; + } +out_unlock: + sqlite3_mutex_leave(db->mutex); + return rc; +} + +int libsql_wal_insert_end(sqlite3 *db) { + Pager *pPager; + int rc; + + sqlite3_mutex_enter(db->mutex); + pPager = sqlite3BtreePager(db->aDb[0].pBt); + rc = sqlite3PagerWalEndCommit(pPager); + if (rc != SQLITE_OK) { + goto out_unlock; + } +out_unlock: + sqlite3_mutex_leave(db->mutex); + return rc; +} + +/* +** Insert a frame into the WAL. +*/ +int libsql_wal_insert_frame( + sqlite3* db, + unsigned int iFrame, + void *pBuf, + unsigned int nBuf +){ + 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 = sqlite3PagerWalInsert(pPager, iFrame, pBuf, nBuf); + if (rc != SQLITE_OK) { + goto out_unlock; + } +out_unlock: + sqlite3_mutex_leave(db->mutex); + + return rc; +#endif +} + /* ** Register a function to be invoked prior to each autovacuum that ** determines the number of pages to vacuum. @@ -183069,6 +183422,7 @@ SQLITE_API void *sqlite3_wal_hook( #endif sqlite3_mutex_enter(db->mutex); pRet = db->pWalArg; + db->walCheckPointDisabled = 0; db->xWalCallback = xCallback; db->pWalArg = pArg; sqlite3_mutex_leave(db->mutex); diff --git a/libsql-ffi/bundled/src/sqlite3.h b/libsql-ffi/bundled/src/sqlite3.h index d526834332..916f3a0b68 100644 --- a/libsql-ffi/bundled/src/sqlite3.h +++ b/libsql-ffi/bundled/src/sqlite3.h @@ -10549,6 +10549,59 @@ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *); */ SQLITE_API void *libsql_close_hook(sqlite3 *db, void (*xClose)(void *pCtx, sqlite3 *db), void *arg); +/* +** CAPI3REF: Disable WAL checkpointing +** METHOD: sqlite3 +** +** ^The [libsql_wal_disable_checkpoint(D)] interface disables automatic +** checkpointing of the WAL file for [database connection] D. +** +** Note: This function disables WAL checkpointing entirely, including when +** the last database connection is closed. This is different from +** sqlite3_wal_autocheckpoint() which only disables automatic checkpoints +** for the current connection, but still allows checkpointing when the +** connection is closed. +*/ +SQLITE_API int libsql_wal_disable_checkpoint(sqlite3 *db); + +/* +** CAPI3REF: Get the number of frames in the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_frame_count(D,P)] interface returns the number of frames +** in the WAL file for [database connection] D into *P. +*/ +SQLITE_API int libsql_wal_frame_count(sqlite3*, unsigned int*); + +/* +** CAPI3REF: Get a frame from the WAL file +** METHOD: sqlite3 +** +** ^The [libsql_wal_get_frame(D,I,P,S)] interface extracts frame I from +** the WAL file for [database connection] D into memory obtained from +** [sqlite3_malloc64()] and returns a pointer to that memory. The size of +** the memory allocated is given by S. +*/ +SQLITE_API int libsql_wal_get_frame(sqlite3*, unsigned int, void*, unsigned int); + +/* +** CAPI3REF: Begin frame insertion into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_begin(sqlite3*); + +/* +** CAPI3REF: End frame insertion into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_end(sqlite3*); + +/* +** CAPI3REF: Insert a frame into the WAL +** METHOD: sqlite3 +*/ +SQLITE_API int libsql_wal_insert_frame(sqlite3*, unsigned int, void *, unsigned int); + /* ** CAPI3REF: Low-level system error code ** METHOD: sqlite3 @@ -13574,6 +13627,7 @@ typedef struct libsql_wal_methods { /* Read a page from the write-ahead log, if it is present. */ int (*xFindFrame)(wal_impl* pWal, unsigned int, unsigned int *); int (*xReadFrame)(wal_impl* pWal, unsigned int, int, unsigned char *); + int (*xReadFrameRaw)(wal_impl* pWal, unsigned int, int, unsigned char *); /* If the WAL is not empty, return the size of the database. */ unsigned int (*xDbsize)(wal_impl* pWal); @@ -13593,6 +13647,9 @@ typedef struct libsql_wal_methods { ** response to a ROLLBACK TO command. */ int (*xSavepointUndo)(wal_impl* pWal, unsigned int *aWalData); + /* Return the number of frames in the WAL */ + int (*xFrameCount)(wal_impl* pWal, int, unsigned int *); + /* Write a frame or frames to the log. */ int (*xFrames)(wal_impl* pWal, int, libsql_pghdr *, unsigned int, int, int, int*);