1From 8bda6b2d11068a0449bbce7e24adbcb5b4707330 Mon Sep 17 00:00:00 2001 2From: MartinChoo <214582617@qq.com> 3Date: Wed, 23 Jul 2025 17:48:45 +0800 4Subject: [PATCH 11/12] Support compress db 5 6--- 7 ext/misc/cksumvfs.c | 4 +- 8 src/compressvfs.c | 1044 +++++++++++++++++++++++++++++++++++++++++++ 9 src/sqlite3.c | 575 +++++++++++++++++++++--- 10 3 files changed, 1552 insertions(+), 71 deletions(-) 11 create mode 100644 src/compressvfs.c 12 13diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c 14index d7a2431..b756ebb 100644 15--- a/ext/misc/cksumvfs.c 16+++ b/ext/misc/cksumvfs.c 17@@ -966,7 +966,7 @@ static int cksmRegisterVfs(void){ 18 cksm_vfs.iVersion = pOrig->iVersion; 19 cksm_vfs.pAppData = pOrig; 20 cksm_vfs.szOsFile = pOrig->szOsFile + sizeof(CksmFile); 21- rc = sqlite3_vfs_register(&cksm_vfs, 1); 22+ rc = sqlite3_vfs_register(&cksm_vfs, 0); 23 if( rc==SQLITE_OK ){ 24 rc = sqlite3_auto_extension((void(*)(void))cksmRegisterFunc); 25 } 26@@ -1019,7 +1019,7 @@ int sqlite3_cksumvfs_init( 27 #endif /* !defined(SQLITE_CKSUMVFS_STATIC) */ 28 29 #ifdef SQLITE_CKSUMVFS_STATIC 30-sqlite3_file *sqlite3_get_orig_file(sqlite3_file *file) { 31+sqlite3_file *cksmvfsGetOrigFile(sqlite3_file *file) { 32 return ORIGFILE(file); 33 } 34 35diff --git a/src/compressvfs.c b/src/compressvfs.c 36new file mode 100644 37index 0000000..90a3f44 38--- /dev/null 39+++ b/src/compressvfs.c 40@@ -0,0 +1,1044 @@ 41+/* 42+** 2025-06-10 43+** 44+** The author disclaims copyright to this source code. In place of 45+** a legal notice, here is a blessing: 46+** 47+** May you do good and not evil. 48+** May you find forgiveness for yourself and forgive others. 49+** May you share freely, never taking more than you give. 50+** 51+************************************************************************* 52+** 53+** This file implements a VFS shim that compress each page's data 54+** of an SQLite database file. It will Open a OutterDB which used to 55+** manager compressed page data and create a vfs_pages table into OutterDB. 56+** When read pages's data, the data will be selected from vfs_pages and 57+** decompress by compression. 58+** 59+** COMPILING 60+** 61+** This extension requires SQLite 3.44.4 or later. It uses the 62+** sqlite3_database_file_object() interface which was added in 63+** version 3.44.4, so it will not link with an earlier version of 64+** SQLite. 65+** 66+** To build this extension as a separately loaded shared library or 67+** DLL, use compiler command-lines similar to the following: 68+** 69+** (linux) gcc -fPIC -shared compressvfs.c -o sqlitecompressvfs.z.so 70+** 71+** You may want to add additional compiler options, of course, 72+** according to the needs of your project. 73+** 74+** LOADING 75+** 76+** To use this extension as a shared library, you first have to 77+** open SQLite database connection with compressvfs. 78+** It will load autometically when connect to OutterDB. 79+** And then compressvfs will be reegistered before open db connection. 80+** After first used , all subsequent databse connections that are opened 81+** will include this extension. 82+** 83+** Compressvfs is a VFS Shim. When loaded, "compressvfs" will registered based on 84+** default VFS. This is normally what you want. 85+** 86+** USING 87+** 88+** Open database connections using the sqlite3_open() with uri "?vfs=compressvfs" or 89+** sqlite3_open_v2() interfaces with zVfs parameter. 90+** 91+** For example: 92+** 93+** sqlite3 *db; 94+** sqlite3_open("file:example.db?vfs=compressvfs", &db); 95+** or 96+** sqlite3_open_v2("example.db", &db, flag, "compressvfs"); 97+** 98+** After open databse connections with compressvfs, page data will be auto compressed. 99+** And data can be read and written normally. 100+** 101+*/ 102+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 103+#include <sqlite3sym.h> 104+SQLITE_EXTENSION_INIT1 105+#include <stdio.h> 106+#include <stdlib.h> 107+#include <string.h> 108+#include <assert.h> 109+#include <stddef.h> 110+#include "securec.h" 111+#ifndef _WIN32 112+#include <dlfcn.h> 113+#endif 114+ 115+// export the symbols 116+#ifdef SQLITE_EXPORT_SYMBOLS 117+#if defined(__GNUC__) 118+# define EXPORT_SYMBOLS __attribute__ ((visibility ("default"))) 119+#elif defined(_MSC_VER) 120+# define EXPORT_SYMBOLS __declspec(dllexport) 121+#else 122+# define EXPORT_SYMBOLS 123+#endif 124+#endif 125+ 126+/* 127+** Useful datatype 128+*/ 129+#ifndef UINT32_TYPE 130+# ifdef HAVE_UINT32_T 131+# define UINT32_TYPE uint32_t 132+# else 133+# define UINT32_TYPE unsigned int 134+# endif 135+#endif 136+#ifndef UINT16_TYPE 137+# ifdef HAVE_UINT16_T 138+# define UINT16_TYPE uint16_t 139+# else 140+# define UINT16_TYPE unsigned short int 141+# endif 142+#endif 143+#ifndef INT16_TYPE 144+# ifdef HAVE_INT16_T 145+# define INT16_TYPE int16_t 146+# else 147+# define INT16_TYPE short int 148+# endif 149+#endif 150+#ifndef UINT8_TYPE 151+# ifdef HAVE_UINT8_T 152+# define UINT8_TYPE uint8_t 153+# else 154+# define UINT8_TYPE unsigned char 155+# endif 156+#endif 157+#ifndef INT8_TYPE 158+# ifdef HAVE_INT8_T 159+# define INT8_TYPE int8_t 160+# else 161+# define INT8_TYPE signed char 162+# endif 163+#endif 164+#ifndef LONGDOUBLE_TYPE 165+# define LONGDOUBLE_TYPE long double 166+#endif 167+#ifdef SQLITE_INT64_TYPE 168+ typedef SQLITE_INT64_TYPE sqlite_int64; 169+# ifdef SQLITE_UINT64_TYPE 170+ typedef SQLITE_UINT64_TYPE sqlite_uint64; 171+# else 172+ typedef unsigned SQLITE_INT64_TYPE sqlite_uint64; 173+# endif 174+#elif defined(_MSC_VER) || defined(__BORLANDC__) 175+ typedef __int64 sqlite_int64; 176+ typedef unsigned __int64 sqlite_uint64; 177+#else 178+ typedef long long int sqlite_int64; 179+ typedef unsigned long long int sqlite_uint64; 180+#endif 181+typedef sqlite_int64 sqlite3_int64; 182+typedef sqlite_uint64 sqlite3_uint64; 183+typedef sqlite_int64 i64; /* 8-byte signed integer */ 184+typedef sqlite_uint64 u64; /* 8-byte unsigned integer */ 185+typedef UINT32_TYPE u32; /* 4-byte unsigned integer */ 186+typedef UINT16_TYPE u16; /* 2-byte unsigned integer */ 187+typedef INT16_TYPE i16; /* 2-byte signed integer */ 188+typedef UINT8_TYPE u8; /* 1-byte unsigned integer */ 189+typedef INT8_TYPE i8; /* 1-byte signed integer */ 190+ 191+typedef u32 Pgno; 192+/* VFS's name */ 193+#define COMPRESS_VFS_NAME "compressvfs" 194+ 195+/* COMPRESSION OPTIONS */ 196+#define COMPRESSION_UNDEFINED 0 197+#define COMPRESSION_BROTLI 1 198+#define COMPRESSION_ZSTD 2 199+ 200+#define COMPRESSION_SQL_MAX_LENGTH 100 201+ 202+#define SQLITE_SHMMAP_IS_WRITE 0x00000001 /* Flag for xShmMap, extend file if necessary */ 203+#define SQLITE_OPEN_COMPRESS_SHM 0x00010000 /* Flag for xShmMap, need to rename shm file */ 204+ 205+/* An open file */ 206+typedef struct{ 207+ sqlite3_file base; /* IO methods */ 208+ sqlite3* pDb; /* Ptr to OutterDB */ 209+ u8 bOutterDbOpen; /* True to OutterDB is opened */ 210+ u8 bSubDbOpen; /* True to SubDB is opened */ 211+ u8 bBegin; /* True to xSync() need commit */ 212+ u8 compression; /* Compression options */ 213+ int page_size; /* Uncompressed page size */ 214+ int persistWalFlag; /* Flag to persist flag */ 215+} CompressFile; 216+ 217+ 218+static int compressClose(sqlite3_file *pFile); 219+static int compressRead(sqlite3_file *pFile, void *pBuf, int iAmt, sqlite_int64 iOfst); 220+static int compressWrite(sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite_int64 iOfst); 221+static int compressTruncate(sqlite3_file *pFile, sqlite_int64 size); 222+static int compressSync(sqlite3_file *pFile, int flags); 223+static int compressFileSize(sqlite3_file *pFile, i64 *pSize); 224+static int compressLock(sqlite3_file *pFile, int eFileLock); 225+static int compressUnlock(sqlite3_file *pFile, int eFileLock); 226+static int compressCheckReservedLock(sqlite3_file *pFile, int *pResOut); 227+static int compressFileControl(sqlite3_file *pFile, int op, void *pArg); 228+static int compressSectorSize(sqlite3_file *pFile); 229+static int compressDeviceCharacteristics(sqlite3_file *pFile); 230+static int compressShmMap(sqlite3_file *pFile, int iPg, int pgsz, int fileFlag, void volatile **pp); 231+static int compressShmLock(sqlite3_file *pFile, int offset, int n, int flags); 232+static void compressShmBarrier(sqlite3_file *pFile); 233+static int compressShmUnmap(sqlite3_file *pFile, int deleteFlag); 234+static int compressFetch(sqlite3_file *pFile, sqlite3_int64 iOfst, int iAmt, void **pp); 235+static int compressUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage); 236+ 237+static sqlite3_vfs compress_vfs = {0}; 238+static sqlite3_io_methods compress_io_methods = { 239+ 3, 240+ compressClose, 241+ compressRead, 242+ compressWrite, 243+ compressTruncate, 244+ compressSync, 245+ compressFileSize, 246+ compressLock, 247+ compressUnlock, 248+ compressCheckReservedLock, 249+ compressFileControl, 250+ compressSectorSize, 251+ compressDeviceCharacteristics, 252+ compressShmMap, 253+ compressShmLock, 254+ compressShmBarrier, 255+ compressShmUnmap, 256+ compressFetch, 257+ compressUnfetch 258+}; 259+ 260+/*----------------------------brotli header begin----------------------------*/ 261+#define BROTLI_BOOL int 262+#define BROTLI_TRUE 1 263+#define BROTLI_FALSE 0 264+#define BROTLI_MAX_WINDOW_BITS 24 265+typedef enum{ 266+ // Decoding error 267+ BROTLI_DECODER_RESULT_ERROR = 0, 268+ // Decoding successfully completed. 269+ BROTLI_DECODER_RESULT_SUCCESS = 1, 270+ // Should be called again more input. 271+ BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2, 272+ // Should be called again more output. 273+ BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3, 274+} BrotliDecoderResult; 275+ 276+typedef BROTLI_BOOL (*brotliCompress_ptr)(u32, u32, size_t, const u8*, size_t*, u8*); 277+typedef BrotliDecoderResult (*brotliDecompress_ptr)(size_t, const u8*, size_t*, u8*); 278+/*----------------------------brotli header end----------------------------*/ 279+ 280+/*----------------------------zstd header begin----------------------------*/ 281+typedef size_t (*zstdCompress_ptr)(void*, size_t, const void*, size_t, int); 282+typedef size_t (*zstdDecompress_ptr)(void*, size_t, const void*, size_t); 283+/*----------------------------zstd header begin----------------------------*/ 284+ 285+/* 286+** Access to a lower-level VFS that (might) implement dynamic loading, access to randomness, etc. 287+*/ 288+#define ORIGFILE(p) ((sqlite3_file*)(((CompressFile*)(p))+1)) 289+#define ORIGVFS(p) ((sqlite3_vfs*)((p)->pAppData)) 290+ 291+static u32 g_compress_algo_load = COMPRESSION_UNDEFINED; 292+static void *g_compress_algo_library = NULL; 293+typedef size_t (*compressBound_ptr)(size_t); 294+static compressBound_ptr compressBoundPtr = NULL; 295+ 296+static brotliCompress_ptr brotliCompressPtr = NULL; 297+static brotliDecompress_ptr brotliDecompressPtr = NULL; 298+static zstdCompress_ptr zstdCompressPtr = NULL; 299+static zstdDecompress_ptr zstdDecompressPtr = NULL; 300+ 301+static int loadBrotliExtension(){ 302+ g_compress_algo_library = dlopen("libbrotli_shared.z.so", RTLD_LAZY); 303+ if( g_compress_algo_library==NULL ){ 304+ sqlite3_log(SQLITE_NOTICE, "load brotli so failed: %s", dlerror()); 305+ return SQLITE_ERROR; 306+ } 307+ compressBoundPtr = (compressBound_ptr)dlsym(g_compress_algo_library, "BrotliEncoderMaxCompressedSize"); 308+ if( compressBoundPtr==NULL ){ 309+ goto failed; 310+ } 311+ brotliCompressPtr = (brotliCompress_ptr)dlsym(g_compress_algo_library, "BrotliEncoderCompress"); 312+ if( brotliCompressPtr==NULL ){ 313+ goto failed; 314+ } 315+ brotliDecompressPtr = (brotliDecompress_ptr)dlsym(g_compress_algo_library, "BrotliDecoderDecompress"); 316+ if( brotliDecompressPtr==NULL ){ 317+ goto failed; 318+ } 319+ g_compress_algo_load = COMPRESSION_BROTLI; 320+ return SQLITE_OK; 321+ 322+ failed: 323+ sqlite3_log(SQLITE_NOTICE, "load brotli dlsym failed :%s", dlerror()); 324+ compressBoundPtr = NULL; 325+ brotliCompressPtr = NULL; 326+ brotliDecompressPtr = NULL; 327+ dlclose(g_compress_algo_library); 328+ return SQLITE_ERROR; 329+} 330+ 331+static int loadZstdExtension(){ 332+ g_compress_algo_library = dlopen("libzstd_shared.z.so", RTLD_LAZY); 333+ if( g_compress_algo_library==NULL ){ 334+ sqlite3_log(SQLITE_NOTICE, "load zstd so failed :%s", dlerror()); 335+ return SQLITE_ERROR; 336+ } 337+ compressBoundPtr = (compressBound_ptr)dlsym(g_compress_algo_library, "ZSTD_compressBound"); 338+ if( compressBoundPtr==NULL ){ 339+ goto failed; 340+ } 341+ zstdCompressPtr = (zstdCompress_ptr)dlsym(g_compress_algo_library, "ZSTD_compress"); 342+ if( zstdCompressPtr==NULL ){ 343+ goto failed; 344+ } 345+ zstdDecompressPtr = (zstdDecompress_ptr)dlsym(g_compress_algo_library, "ZSTD_decompress"); 346+ if( zstdDecompressPtr==NULL ){ 347+ goto failed; 348+ } 349+ g_compress_algo_load = COMPRESSION_ZSTD; 350+ return SQLITE_OK; 351+ 352+ failed: 353+ sqlite3_log(SQLITE_NOTICE, "load zstd dlsym failed :%s", dlerror()); 354+ compressBoundPtr = NULL; 355+ zstdCompressPtr = NULL; 356+ zstdDecompressPtr = NULL; 357+ dlclose(g_compress_algo_library); 358+ return SQLITE_ERROR; 359+} 360+ 361+static int loadCompressAlgorithmExtension(u8 compression){ 362+ if( g_compress_algo_load!=0u ){ 363+ return SQLITE_OK; 364+ } 365+#ifndef _WIN32 366+ if( compression==COMPRESSION_UNDEFINED ){ 367+ if( loadZstdExtension()==SQLITE_ERROR ){ 368+ sqlite3_log(SQLITE_NOTICE, "load zstd failed :%s", dlerror()); 369+ if( loadBrotliExtension()==SQLITE_ERROR ){ 370+ sqlite3_log(SQLITE_ERROR, "load compress so failed :%s", dlerror()); 371+ return SQLITE_ERROR; 372+ } 373+ } 374+ }else if( compression==COMPRESSION_BROTLI ){ 375+ if( loadBrotliExtension()==SQLITE_ERROR ){ 376+ sqlite3_log(SQLITE_ERROR, "load brotli so failed :%s", dlerror()); 377+ return SQLITE_ERROR; 378+ } 379+ }else if( compression==COMPRESSION_ZSTD ){ 380+ if( loadZstdExtension()==SQLITE_ERROR ){ 381+ sqlite3_log(SQLITE_ERROR, "load zstd so failed :%s", dlerror()); 382+ return SQLITE_ERROR; 383+ } 384+ }else{ 385+ sqlite3_log(SQLITE_ERROR, "load compress so failed, compression is invalid :%u", compression); 386+ return SQLITE_ERROR; 387+ } 388+#endif 389+ return SQLITE_OK; 390+} 391+ 392+/* Get compress bound */ 393+static int compressLen(int src_len, int compression){ 394+ if( compression==COMPRESSION_BROTLI || compression==COMPRESSION_ZSTD ){ 395+ return compressBoundPtr(src_len); 396+ } 397+ return -1; 398+} 399+ 400+/* Compress buf with compression */ 401+static int compressBuf( 402+ u8 *dst, 403+ int dst_buf_len, 404+ int *dst_written_len, 405+ const u8 *src, 406+ int src_len, 407+ int compression 408+){ 409+ int ret_len = 0; 410+ if( g_compress_algo_load==COMPRESSION_UNDEFINED && 411+ loadCompressAlgorithmExtension(compression)==SQLITE_ERROR ){ 412+ return SQLITE_ERROR; 413+ } 414+ if( g_compress_algo_load!=(u32)compression ){ 415+ sqlite3_log(SQLITE_MISUSE, "already load %u, but need load %d", g_compress_algo_load, compression); 416+ return SQLITE_MISUSE; 417+ } 418+ if( compression==COMPRESSION_BROTLI ){ 419+ size_t dst_len = dst_buf_len; 420+ int ret = brotliCompressPtr( 421+ 3, // COMPRESS QUALITY (1-11) 422+ BROTLI_MAX_WINDOW_BITS, // WINDOWS SIZE (10-24) 423+ (size_t)src_len, 424+ (const u8 *)src, 425+ &dst_len, 426+ (u8 *)dst); 427+ if( ret!=BROTLI_TRUE ){ 428+ return SQLITE_ERROR; 429+ } 430+ ret_len = dst_len; 431+ }else if( compression==COMPRESSION_ZSTD ){ 432+ ret_len = (int)zstdCompressPtr(dst, dst_buf_len, src, src_len, 3); 433+ } 434+ if( ret_len<=0 ){ 435+ return SQLITE_ERROR; 436+ } 437+ *dst_written_len = ret_len; 438+ return SQLITE_OK; 439+} 440+ 441+/* Decompress buf with compression */ 442+EXPORT_SYMBOLS int decompressBuf( 443+ u8 *dst, 444+ int dst_buf_len, 445+ int *dst_written_len, 446+ const u8 *src, 447+ int src_len, 448+ int compression 449+){ 450+ int ret_len = -1; 451+ if( g_compress_algo_load==COMPRESSION_UNDEFINED && 452+ loadCompressAlgorithmExtension(compression)==SQLITE_ERROR ){ 453+ return SQLITE_ERROR; 454+ } 455+ if( g_compress_algo_load!=(u32)compression ){ 456+ sqlite3_log(SQLITE_MISUSE, "already load %u, but need load %d", g_compress_algo_load, compression); 457+ return SQLITE_MISUSE; 458+ } 459+ if( compression==COMPRESSION_BROTLI ){ 460+ size_t dst_len = dst_buf_len; 461+ int ret = (int)brotliDecompressPtr(src_len, src, &dst_len, dst); 462+ if( ret!=BROTLI_TRUE ){ 463+ return SQLITE_ERROR; 464+ } 465+ ret_len = dst_len; 466+ }else if( compression==COMPRESSION_ZSTD ){ 467+ ret_len = (int)zstdDecompressPtr(dst, dst_buf_len, src, src_len); 468+ } 469+ if( ret_len<=0 ){ 470+ return SQLITE_ERROR; 471+ } 472+ *dst_written_len = ret_len; 473+ return SQLITE_OK; 474+} 475+ 476+/* Check whether the table exists in the OutterDB. */ 477+static int tableExists(sqlite3 *db, const char *table_name){ 478+ sqlite3_stmt *stmt = NULL; 479+ const char *sql = "SELECT 1 FROM sqlite_master WHERE type='table' AND name=?;"; 480+ int exists = 0; 481+ 482+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 483+ sqlite3_bind_text(stmt, 1, table_name, -1, SQLITE_STATIC); 484+ if( sqlite3_step(stmt)==SQLITE_ROW ){ 485+ exists = 1; 486+ } 487+ sqlite3_finalize(stmt); 488+ } 489+ return exists; 490+} 491+ 492+/* Get page size before compressed from OutterDB. */ 493+static int getCompressPgsize(sqlite3 *db, int *pagesize){ 494+ int rc = SQLITE_OK; 495+ if( *pagesize!=0 ){ 496+ return rc; 497+ } 498+ sqlite3_stmt *stmt = NULL; 499+ const char *sql = "SELECT pagesize FROM vfs_compression;"; 500+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 501+ if( sqlite3_step(stmt)==SQLITE_ROW ){ 502+ *pagesize = sqlite3_column_int(stmt, 0); 503+ }else{ 504+ rc = SQLITE_ERROR; 505+ } 506+ sqlite3_finalize(stmt); 507+ } 508+ return rc; 509+} 510+ 511+/* Set page size before compressed to OutterDB. */ 512+static int setCompressPgsize(sqlite3 *db, int pagesize){ 513+ int rc = SQLITE_OK; 514+ sqlite3_stmt *stmt = NULL; 515+ const char *sql = "UPDATE vfs_compression SET pagesize=?;"; 516+ 517+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 518+ sqlite3_bind_int(stmt, 1, pagesize); 519+ if( sqlite3_step(stmt)!=SQLITE_DONE ){ 520+ rc = SQLITE_ERROR; 521+ } 522+ sqlite3_finalize(stmt); 523+ } 524+ return rc; 525+} 526+ 527+/* Get max page number from OutterDB. */ 528+static int getMaxCompressPgno(sqlite3 *db){ 529+ sqlite3_stmt *stmt = NULL; 530+ const char *sql = "SELECT MAX(pageno) FROM vfs_pages;"; 531+ int max_pgno = 0; 532+ 533+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 534+ if( sqlite3_step(stmt)==SQLITE_ROW ){ 535+ max_pgno = sqlite3_column_int(stmt, 0); 536+ } 537+ sqlite3_finalize(stmt); 538+ } 539+ return max_pgno; 540+} 541+ 542+/* Get Compression option from OutterDB. */ 543+static void getCompression(sqlite3 *db, CompressFile *pCompress){ 544+ sqlite3_stmt *stmt = NULL; 545+ const char *sql = "SELECT count(*), compression, pagesize FROM vfs_compression;"; 546+ int count = 0; 547+ pCompress->compression = COMPRESSION_UNDEFINED; 548+ pCompress->page_size = 0; 549+ 550+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 551+ if( sqlite3_step(stmt)==SQLITE_ROW ){ 552+ count = sqlite3_column_int(stmt, 0); 553+ if( count==1 ){ 554+ pCompress->compression = sqlite3_column_int(stmt, 1); 555+ pCompress->page_size = sqlite3_column_int(stmt, 2); 556+ } 557+ } 558+ sqlite3_finalize(stmt); 559+ } 560+ return; 561+} 562+ 563+/* 564+** Sync a compress file. If need commit a transaction 565+** which begin in compressWrite or compressTruncate. 566+*/ 567+static int compressSync(sqlite3_file *pFile, int flags){ 568+ assert( pFile ); 569+ CompressFile *pCompress = (CompressFile *)pFile; 570+ sqlite3 *db = pCompress->pDb; 571+ if( pCompress->bBegin==1 ){ 572+ int rc = sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); 573+ if( rc!=SQLITE_OK ){ 574+ return SQLITE_IOERR_WRITE; 575+ } 576+ pCompress->bBegin = 0; 577+ } 578+ return SQLITE_OK; 579+} 580+ 581+/* 582+** Check if another file-handle holds a RESERVED lock on a compress file. 583+*/ 584+static int compressCheckReservedLock(sqlite3_file *pFile, int *pResOut){ 585+ assert( pFile ); 586+ *pResOut = 0; 587+ return SQLITE_OK; 588+} 589+ 590+/* 591+** File control method. Use default VFS's xFileControl. 592+*/ 593+static int compressFileControl(sqlite3_file *pFile, int op, void *pArg){ 594+ assert( pFile ); 595+ CompressFile *pCompress = (CompressFile *)pFile; 596+ if( op==SQLITE_FCNTL_PERSIST_WAL ){ 597+ int persistFlag = *(int*)pArg; 598+ if( persistFlag<0 ){ 599+ *(int*)pArg = pCompress->persistWalFlag; 600+ }else{ 601+ pCompress->persistWalFlag = persistFlag>0 ? 1 : 0; 602+ } 603+ return SQLITE_OK; 604+ } 605+ pFile = ORIGFILE(pFile); 606+ return pFile->pMethods->xFileControl(pFile, op, pArg); 607+} 608+ 609+/* 610+** Return the sector-size in bytes for a file. Use default VFS's xSectorSize. 611+*/ 612+static int compressSectorSize(sqlite3_file *pFile){ 613+ assert( pFile ); 614+ pFile = ORIGFILE(pFile); 615+ return pFile->pMethods->xSectorSize(pFile); 616+} 617+ 618+/* 619+** Return the device characteristic flags supported by a file. 620+** Use default VFS's xDeviceCharacteristics. 621+*/ 622+static int compressDeviceCharacteristics(sqlite3_file *pFile){ 623+ assert( pFile ); 624+ pFile = ORIGFILE(pFile); 625+ return pFile->pMethods->xDeviceCharacteristics(pFile); 626+} 627+ 628+/* 629+** Lock a compress file.Never lock InnerDB, because it wil not control the database file. 630+*/ 631+static int compressLock(sqlite3_file *pFile, int eFileLock){ 632+ assert( pFile ); 633+ return SQLITE_OK; 634+} 635+ 636+/* 637+** Unlock a compress file.Never unlock InnerDB, because it wil not control the database file. 638+*/ 639+static int compressUnlock(sqlite3_file *pFile, int eFileLock){ 640+ assert( pFile ); 641+ return SQLITE_OK; 642+} 643+ 644+/* 645+** Get File's size. Here wil return InnerDB's file size. 646+*/ 647+static int compressFileSize(sqlite3_file *pFile, i64 *pSize){ 648+ assert( pFile ); 649+ CompressFile *pCompress = (CompressFile *)pFile; 650+ sqlite3 *db = pCompress->pDb; 651+ int rc = getCompressPgsize(db, &pCompress->page_size); 652+ if( rc!=SQLITE_OK ){ 653+ return SQLITE_IOERR_FSTAT; 654+ } 655+ int pgsize = pCompress->page_size; 656+ int maxpgno = getMaxCompressPgno(db); 657+ *pSize = (i64)maxpgno * pgsize; 658+ return SQLITE_OK; 659+} 660+ 661+/* 662+** Truncate a compress file by delete from vfs_pages. 663+*/ 664+static int compressTruncate(sqlite3_file *pFile, sqlite_int64 size){ 665+ assert( pFile ); 666+ CompressFile *pCompress = (CompressFile *)pFile; 667+ sqlite3 *db = pCompress->pDb; 668+ int rc = getCompressPgsize(db, &pCompress->page_size); 669+ if( rc!=SQLITE_OK || pCompress->page_size==0 ){ 670+ return SQLITE_IOERR_TRUNCATE; 671+ } 672+ int pgsize = pCompress->page_size; 673+ int pgno = size / pgsize; 674+ if( size % pgsize!=0 || getMaxCompressPgno(db) < pgno ){ 675+ return SQLITE_IOERR_TRUNCATE; 676+ } 677+ if( pCompress->bBegin!=1 ){ 678+ if( sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL)!=SQLITE_OK ){ 679+ return SQLITE_IOERR_TRUNCATE; 680+ } 681+ pCompress->bBegin = 1; 682+ } 683+ sqlite3_stmt *stmt = NULL; 684+ const char *sql = "DELETE FROM vfs_pages WHERE pageno > ?;"; 685+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 686+ sqlite3_bind_int(stmt, 1, pgno); 687+ if( sqlite3_step(stmt)!=SQLITE_DONE ){ 688+ rc = SQLITE_IOERR_TRUNCATE; 689+ } 690+ sqlite3_finalize(stmt); 691+ } 692+ return rc; 693+} 694+ 695+/* 696+** Write one page of data to compress file at a time. 697+** It will be compressed and insert into vfs_pages in OutterDB. 698+*/ 699+static int compressWrite(sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite_int64 iOfst){ 700+ assert( pFile ); 701+ assert( iAmt>0 ); 702+ CompressFile *pCompress = (CompressFile *)pFile; 703+ sqlite3 *db = pCompress->pDb; 704+ int rc = getCompressPgsize(db, &pCompress->page_size); 705+ if( rc!=SQLITE_OK ){ 706+ return SQLITE_IOERR_WRITE; 707+ } 708+ 709+ if( pCompress->page_size<=0 && iAmt >= 512 && iAmt <= 64*1024 && !(iAmt & (iAmt - 1)) ){ 710+ // new compress db need set orignal db's pagesize 711+ rc = setCompressPgsize(db, iAmt); 712+ if( rc!=SQLITE_OK ){ 713+ return SQLITE_IOERR_WRITE; 714+ } 715+ pCompress->page_size = iAmt; 716+ } 717+ int pgsize = pCompress->page_size; 718+ int pgno = iOfst / pgsize + 1; 719+ if( iAmt!=pgsize || iOfst % pgsize!=0 ){ 720+ return SQLITE_IOERR_WRITE; 721+ } 722+ 723+ int max_compress_size = compressLen(iAmt, pCompress->compression); 724+ if( max_compress_size<=0 ){ 725+ return SQLITE_IOERR_WRITE; 726+ } 727+ u8 *compressed_data = sqlite3_malloc(max_compress_size); 728+ if( compressed_data==NULL ){ 729+ return SQLITE_NOMEM; 730+ } 731+ int compress_data_len = 0; 732+ if( compressBuf(compressed_data, max_compress_size, &compress_data_len, pBuf, iAmt, pCompress->compression) ){ 733+ sqlite3_free(compressed_data); 734+ return SQLITE_IOERR_WRITE; 735+ } 736+ if( pCompress->bBegin!=1 ){ 737+ if( sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL)!=SQLITE_OK ){ 738+ sqlite3_free(compressed_data); 739+ return SQLITE_IOERR_WRITE; 740+ } 741+ pCompress->bBegin = 1; 742+ } 743+ sqlite3_stmt *stmt = NULL; 744+ const char *sql = "INSERT OR REPLACE INTO vfs_pages(data, pageno) VALUES (?,?);"; 745+ if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){ 746+ sqlite3_bind_blob(stmt, 1, compressed_data, compress_data_len, SQLITE_STATIC); 747+ sqlite3_bind_int(stmt, 2, pgno); 748+ if( sqlite3_step(stmt)!=SQLITE_DONE ){ 749+ sqlite3_free(compressed_data); 750+ sqlite3_finalize(stmt); 751+ return SQLITE_IOERR_WRITE; 752+ } 753+ sqlite3_finalize(stmt); 754+ } 755+ sqlite3_free(compressed_data); 756+ return SQLITE_OK; 757+} 758+ 759+/* 760+** Read data from compress file. 761+** It will be selected from vfs_pages in OutterDB and return a decompressed buf. 762+*/ 763+static int compressRead(sqlite3_file *pFile, void *pBuf, int iAmt, sqlite_int64 iOfst){ 764+ assert( pFile ); 765+ assert( iOfst>=0 ); 766+ assert( iAmt>0 ); 767+ CompressFile *pCompress = (CompressFile *)pFile; 768+ if( pCompress->compression!=COMPRESSION_BROTLI && pCompress->compression!=COMPRESSION_ZSTD ){ 769+ // nowaday only support brotli/zstd. 770+ return SQLITE_CORRUPT; 771+ } 772+ (void)memset_s(pBuf, iAmt, 0, iAmt); 773+ sqlite3 *db = pCompress->pDb; 774+ int rc = getCompressPgsize(db, &pCompress->page_size); 775+ if( rc!=SQLITE_OK || pCompress->page_size==0 ){ 776+ return SQLITE_IOERR_SHORT_READ; 777+ } 778+ int pgsize = pCompress->page_size; 779+ int pgno = iOfst / pgsize + 1; 780+ int dataidx = iOfst % pgsize; 781+ sqlite3_stmt *stmt = NULL; 782+ const char *sql = "SELECT data, length(data) FROM vfs_pages WHERE pageno=?;"; 783+ const void *data = NULL; 784+ int data_len = 0; 785+ rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); 786+ if( rc!=SQLITE_OK ){ 787+ return SQLITE_CORRUPT; 788+ } 789+ u8 *decompressed_data = NULL; 790+ if( pgsize!=iAmt ){ 791+ decompressed_data = sqlite3_malloc(pgsize); 792+ if( decompressed_data==NULL ){ 793+ sqlite3_finalize(stmt); 794+ return SQLITE_NOMEM; 795+ } 796+ }else{ 797+ decompressed_data = (u8*)pBuf; 798+ } 799+ 800+ int decompress_data_len = 0; 801+ sqlite3_bind_int(stmt, 1, pgno); 802+ rc = sqlite3_step(stmt); 803+ if( rc==SQLITE_ROW ){ 804+ data = sqlite3_column_blob(stmt, 0); 805+ if( data==NULL ){ 806+ rc = SQLITE_IOERR_SHORT_READ; 807+ goto failed; 808+ } 809+ data_len = sqlite3_column_int(stmt, 1); 810+ if( data_len==0 ){ 811+ rc = SQLITE_IOERR_SHORT_READ; 812+ goto failed; 813+ } 814+ if( decompressBuf(decompressed_data, pgsize, &decompress_data_len, data, data_len, 815+ pCompress->compression)!=SQLITE_OK ){ 816+ rc = SQLITE_IOERR_SHORT_READ; 817+ goto failed; 818+ } 819+ if( decompress_data_len!=pgsize ){ 820+ rc = SQLITE_IOERR_SHORT_READ; 821+ goto failed; 822+ } 823+ if( pgsize!=iAmt ){ 824+ rc = memcpy_s(pBuf, iAmt, decompressed_data+dataidx, iAmt); 825+ if( rc!=SQLITE_OK ){ 826+ rc = SQLITE_IOERR_SHORT_READ; 827+ goto failed; 828+ } 829+ } 830+ rc = SQLITE_OK; 831+ }else if( rc==SQLITE_DONE ){ 832+ rc = SQLITE_IOERR_SHORT_READ; 833+ } 834+ 835+ failed: 836+ sqlite3_finalize(stmt); 837+ if( pgsize!=iAmt ){ 838+ sqlite3_free(decompressed_data); 839+ } 840+ 841+ return rc; 842+} 843+ 844+/* 845+** Close compress file. Need sync data and close OutterDB and InnerDB. 846+*/ 847+static int compressClose(sqlite3_file *pFile){ 848+ assert( pFile ); 849+ CompressFile *pCompress = (CompressFile *)pFile; 850+ sqlite3 *db = pCompress->pDb; 851+ int rc = compressSync(pFile, 0); 852+ if( rc!=SQLITE_OK ){ 853+ return rc; 854+ } 855+ if( pCompress->bOutterDbOpen ){ 856+ if( db!=NULL ){ 857+ rc = sqlite3_close_v2(db); 858+ if( rc!=SQLITE_OK ){ 859+ return rc; 860+ } 861+ pCompress->pDb = NULL; 862+ } 863+ } 864+ if( pCompress->bSubDbOpen ){ 865+ pFile = ORIGFILE(pFile); 866+ rc = pFile->pMethods->xClose(pFile); 867+ } 868+ return rc; 869+} 870+ 871+/* 872+** Create a shared memory file mapping. Need to rename the shm file, append a tail named "compress." 873+*/ 874+static int compressShmMap( 875+ sqlite3_file *pFile, 876+ int iPg, 877+ int pgsz, 878+ int fileFlag, 879+ void volatile **pp 880+){ 881+ assert( pFile ); 882+ pFile = ORIGFILE(pFile); 883+ fileFlag |= SQLITE_OPEN_COMPRESS_SHM; 884+ return pFile->pMethods->xShmMap(pFile, iPg, pgsz, fileFlag, pp); 885+} 886+ 887+/* Perform locking on a shared-memory segment */ 888+static int compressShmLock(sqlite3_file *pFile, int offset, int n, int flags){ 889+ assert( pFile ); 890+ pFile = ORIGFILE(pFile); 891+ flags |= SQLITE_OPEN_COMPRESS_SHM; 892+ return pFile->pMethods->xShmLock(pFile, offset, n, flags); 893+} 894+ 895+/* Memory barrier operation on shared memory */ 896+static void compressShmBarrier(sqlite3_file *pFile){ 897+ assert( pFile ); 898+ pFile = ORIGFILE(pFile); 899+ return pFile->pMethods->xShmBarrier(pFile); 900+} 901+ 902+/* Unmap a shared memory segment */ 903+static int compressShmUnmap(sqlite3_file *pFile, int deleteFlag){ 904+ assert( pFile ); 905+ pFile = ORIGFILE(pFile); 906+ deleteFlag |= SQLITE_OPEN_COMPRESS_SHM; 907+ return pFile->pMethods->xShmUnmap(pFile, deleteFlag); 908+} 909+ 910+/* Fetch a page of a memory-mapped file */ 911+static int compressFetch(sqlite3_file *pFile, sqlite3_int64 iOfst, int iAmt, void **pp){ 912+ assert( pFile ); 913+ pFile = ORIGFILE(pFile); 914+ if( pFile->pMethods->iVersion>2 && pFile->pMethods->xFetch ){ 915+ return pFile->pMethods->xFetch(pFile, iOfst, iAmt, pp); 916+ } 917+ *pp = 0; 918+ return SQLITE_OK; 919+} 920+ 921+/* Release a memory-mapped page */ 922+static int compressUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage){ 923+ assert( pFile ); 924+ pFile = ORIGFILE(pFile); 925+ if( pFile->pMethods->iVersion>2 && pFile->pMethods->xUnfetch ){ 926+ return pFile->pMethods->xUnfetch(pFile, iOfst, pPage); 927+ } 928+ return SQLITE_OK; 929+} 930+ 931+/* 932+** Open a compress file.If this file is not a journal or wal file, 933+** it will open a OutterDB and create vfs_pages and vfs_compression table 934+** which used to manager compressed pages's data. 935+*/ 936+static int compressOpen( 937+ sqlite3_vfs *pVfs, 938+ const char *zName, 939+ sqlite3_file *pFile, 940+ int flags, 941+ int *pOutFlags 942+){ 943+ sqlite3_file *pSubFile = ORIGFILE(pFile); 944+ CompressFile *pCompress = (CompressFile *)pFile; 945+ int rc = SQLITE_OK; 946+ if( !(flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB)) ){ 947+ return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags); 948+ } 949+ rc = ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pSubFile, flags, pOutFlags); 950+ if( rc!=SQLITE_OK ){ 951+ return rc; 952+ } 953+ pCompress->bSubDbOpen = 1; 954+ sqlite3_int64 fileSize = 0; 955+ rc = pSubFile->pMethods->xFileSize(pSubFile, &fileSize); 956+ if( rc!=SQLITE_OK ){ 957+ rc = SQLITE_CANTOPEN; 958+ goto open_end; 959+ } 960+ 961+ pFile->pMethods = &compress_io_methods; 962+ rc = sqlite3_open_v2(zName, &pCompress->pDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, ORIGVFS(pVfs)->zName); 963+ if( rc!=SQLITE_OK ){ 964+ rc = SQLITE_CANTOPEN; 965+ goto open_end; 966+ } 967+ pCompress->bOutterDbOpen = 1; 968+ sqlite3 *db = pCompress->pDb; 969+ const char *pre_pragma = "PRAGMA page_size=4096;"\ 970+ "PRAGMA auto_vacuum=INCREMENTAL;PRAGMA journal_mode=OFF;"; 971+ sqlite3_busy_timeout(db, 2000); // Set time out:2s 972+ rc = sqlite3_exec(db, pre_pragma, NULL, NULL, NULL); 973+ if( rc!=SQLITE_OK ){ 974+ rc = SQLITE_CANTOPEN; 975+ goto open_end; 976+ } 977+ if( tableExists(db, "vfs_compression") ){ 978+ getCompression(db, pCompress); 979+ if( pCompress->compression!=COMPRESSION_BROTLI && pCompress->compression!=COMPRESSION_ZSTD ){ 980+ rc = SQLITE_CANTOPEN; 981+ goto open_end; 982+ } 983+ if( loadCompressAlgorithmExtension(pCompress->compression)==SQLITE_ERROR ){ 984+ rc = SQLITE_CANTOPEN; 985+ goto open_end; 986+ } 987+ }else if( flags&SQLITE_OPEN_MAIN_DB && fileSize!=0 ){ 988+ rc = SQLITE_WARNING_NOTCOMPRESSDB; 989+ sqlite3_log(rc, "open compress database go wrong, it should be a compressed db"); 990+ goto open_end; 991+ }else{ 992+ if( loadCompressAlgorithmExtension(COMPRESSION_UNDEFINED)==SQLITE_ERROR ){ 993+ rc = SQLITE_CANTOPEN; 994+ goto open_end; 995+ } 996+ const char *init_compression_sql = "CREATE TABLE vfs_compression (compression INTEGER, pagesize INTEGER);"; 997+ rc = sqlite3_exec(db, init_compression_sql, NULL, NULL, NULL); 998+ if( rc!=SQLITE_OK ){ 999+ rc = SQLITE_CANTOPEN; 1000+ goto open_end; 1001+ } 1002+ char set_compression_sql[COMPRESSION_SQL_MAX_LENGTH] = {0}; 1003+ if( sprintf_s(set_compression_sql, COMPRESSION_SQL_MAX_LENGTH, 1004+ "INSERT INTO vfs_compression(compression, pagesize) VALUES (%u, 0);", g_compress_algo_load)<=0 ){ 1005+ rc = SQLITE_CANTOPEN; 1006+ goto open_end; 1007+ } 1008+ rc = sqlite3_exec(db, set_compression_sql, NULL, NULL, NULL); 1009+ if( rc!=SQLITE_OK ){ 1010+ rc = SQLITE_CANTOPEN; 1011+ goto open_end; 1012+ } 1013+ pCompress->compression = g_compress_algo_load; 1014+ pCompress->page_size = 0; 1015+ } 1016+ if( tableExists(db, "vfs_pages") ){ 1017+ goto open_end; 1018+ } 1019+ const char *create_sql = "CREATE TABLE vfs_pages (pageno INTEGER PRIMARY KEY, data BLOB NOT NULL);"; 1020+ rc = sqlite3_exec(db, create_sql, NULL, NULL, NULL); 1021+ if( rc!=SQLITE_OK ){ 1022+ rc = SQLITE_CANTOPEN; 1023+ } 1024+ 1025+ open_end: 1026+ return rc; 1027+} 1028+ 1029+/* 1030+** Delete compress file. If this file is journal or wal, need to delete the InnerDB's renamed file. 1031+*/ 1032+static int compressDelete(sqlite3_vfs *pVfs, const char *zName, int syncDir){ 1033+ return ORIGVFS(pVfs)->xDelete(ORIGVFS(pVfs), zName, syncDir); 1034+} 1035+ 1036+/* 1037+** Access compress file. If this file is journal or wal need to access the InnerDB's renamed file. 1038+*/ 1039+static int compressAccess( 1040+ sqlite3_vfs *pVfs, 1041+ const char *zName, 1042+ int flags, 1043+ int *pResOut 1044+){ 1045+ return ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), zName, flags, pResOut); 1046+} 1047+ 1048+ 1049+/* 1050+** Init compressvfs and register the vfs 1051+*/ 1052+EXPORT_SYMBOLS int sqlite3CompressvfsInit(){ 1053+ // copy default vfs for compressvfs 1054+ if( sqlite3_vfs_find(COMPRESS_VFS_NAME) ){ 1055+ return SQLITE_OK; 1056+ } 1057+ sqlite3_vfs *default_vfs = sqlite3_vfs_find(0); 1058+ if( default_vfs==0 ){ 1059+ sqlite3_log(SQLITE_ERROR, "Default vfs not found."); 1060+ return SQLITE_ERROR; 1061+ } 1062+ sqlite3_vfs *pVfs = &compress_vfs; 1063+ *pVfs = *default_vfs; 1064+ pVfs->pNext = 0; 1065+ pVfs->zName = COMPRESS_VFS_NAME; 1066+ pVfs->pAppData = default_vfs; 1067+ pVfs->szOsFile = pVfs->szOsFile+sizeof(CompressFile); 1068+ 1069+ pVfs->xOpen = compressOpen; 1070+ pVfs->xDelete = compressDelete; 1071+ pVfs->xAccess = compressAccess; 1072+ int rc = sqlite3_vfs_register(pVfs, 0); 1073+ if( rc!=SQLITE_OK ){ 1074+ sqlite3_log(SQLITE_ERROR, "Compress vfs register error!"); 1075+ return rc; 1076+ } 1077+ return SQLITE_OK; 1078+} 1079+ 1080+EXPORT_SYMBOLS sqlite3_file *compressvfsGetOrigFile(sqlite3_file *file){ 1081+ return ORIGFILE(file); 1082+} 1083+ 1084+#endif /* SQLITE_ENABLE_PAGE_COMPRESS */ 1085\ No newline at end of file 1086diff --git a/src/sqlite3.c b/src/sqlite3.c 1087index c84348b..1abddb3 100644 1088--- a/src/sqlite3.c 1089+++ b/src/sqlite3.c 1090@@ -881,6 +881,7 @@ SQLITE_API int sqlite3_exec( 1091 #define SQLITE_NOTICE_RBU (SQLITE_NOTICE | (3<<8)) 1092 #define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8)) 1093 #define SQLITE_WARNING_DUMP (SQLITE_WARNING | (2<<8)) 1094+#define SQLITE_WARNING_NOTCOMPRESSDB (SQLITE_WARNING | (3<<8)) 1095 #define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8)) 1096 #define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8)) 1097 #define SQLITE_OK_SYMLINK (SQLITE_OK | (2<<8)) /* internal use only */ 1098@@ -1877,6 +1878,14 @@ struct sqlite3_vfs { 1099 */ 1100 #define SQLITE_SHM_NLOCK 8 1101 1102+/* 1103+** CAPI3REF: Maximum xShmLock index 1104+** 1105+** The xShmMap method on [sqlite3_io_methods] may use values 1106+** between 0 and this upper bound as its "offset" argument. 1107+*/ 1108+#define SQLITE_SHMMAP_IS_WRITE 0x00000001 /* Flag for xShmMap, extend file if necessary */ 1109+#define SQLITE_OPEN_COMPRESS_SHM 0x00010000 /* Flag for xShmMap, need to rename shm file */ 1110 1111 /* 1112 ** CAPI3REF: Initialize The SQLite Library 1113@@ -38370,6 +38379,9 @@ static void MarkLockStatusByRc(int rc, u32 lockIdx, u32 lockLen, u8 lockType, u8 1114 #define MarkLockStatusByRc(A, B, C, D, E) 1115 #endif 1116 /************** End define dump function *************************************/ 1117+#if !SQLITE_OS_UNIX && defined(SQLITE_ENABLE_PAGE_COMPRESS) 1118+#undef SQLITE_ENABLE_PAGE_COMPRESS 1119+#endif 1120 /************** Begin file os_unix.c *****************************************/ 1121 /* 1122 ** 2004 May 22 1123@@ -38639,6 +38651,9 @@ struct unixFile { 1124 UnixUnusedFd *pPreallocatedUnused; /* Pre-allocated UnixUnusedFd */ 1125 const char *zPath; /* Name of the file */ 1126 unixShm *pShm; /* Shared memory segment information */ 1127+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1128+ unixShm *pCompressShm; 1129+#endif 1130 int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ 1131 #if SQLITE_MAX_MMAP_SIZE>0 1132 int nFetchOut; /* Number of outstanding xFetch refs */ 1133@@ -39640,6 +39655,9 @@ struct unixInodeInfo { 1134 UnixUnusedFd *pUnused; /* Unused file descriptors to close */ 1135 int nRef; /* Number of pointers to this structure */ 1136 unixShmNode *pShmNode; /* Shared memory associated with this inode */ 1137+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1138+ unixShmNode *pCompressShmNode; /* Shared memory associated with this inode */ 1139+#endif 1140 unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ 1141 unixInodeInfo *pPrev; /* .... doubly linked */ 1142 #if SQLITE_ENABLE_LOCKING_STYLE 1143@@ -42889,14 +42907,23 @@ static int unixShmSystemLock( 1144 unixFile *pFile, /* Open connection to the WAL file */ 1145 int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ 1146 int ofst, /* First byte of the locking range */ 1147- int n /* Number of bytes to lock */ 1148+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1149+ int n, /* Number of bytes to lock */ 1150+ int bCompress /* True to compress shm */ 1151+#else 1152+ int n 1153+#endif 1154 ){ 1155 unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ 1156 struct flock f; /* The posix advisory locking structure */ 1157 int rc = SQLITE_OK; /* Result code form fcntl() */ 1158 1159 /* Access to the unixShmNode object is serialized by the caller */ 1160+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1161+ pShmNode = bCompress ? pFile->pInode->pCompressShmNode : pFile->pInode->pShmNode; 1162+#else 1163 pShmNode = pFile->pInode->pShmNode; 1164+#endif 1165 assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) ); 1166 assert( pShmNode->nRef>0 || unixMutexHeld() ); 1167 1168@@ -42984,9 +43011,11 @@ static int unixShmRegionPerMap(void){ 1169 ** This is not a VFS shared-memory method; it is a utility function called 1170 ** by VFS shared-memory methods. 1171 */ 1172-static void unixShmPurge(unixFile *pFd){ 1173- unixShmNode *p = pFd->pInode->pShmNode; 1174- assert( unixMutexHeld() ); 1175+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1176+static void unixShmPurgeInner(unixFile *pFd, unixShmNode *p, int bCompressShm){ 1177+#else 1178+static void unixShmPurgeInner(unixFile *pFd, unixShmNode *p){ 1179+#endif 1180 if( p && ALWAYS(p->nRef==0) ){ 1181 int nShmPerMap = unixShmRegionPerMap(); 1182 int i; 1183@@ -43004,11 +43033,35 @@ static void unixShmPurge(unixFile *pFd){ 1184 robust_close(pFd, p->hShm, __LINE__); 1185 p->hShm = -1; 1186 } 1187+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1188+ if( bCompressShm ){ 1189+ p->pInode->pCompressShmNode = 0; 1190+ }else{ 1191+ p->pInode->pShmNode = 0; 1192+ } 1193+#else 1194 p->pInode->pShmNode = 0; 1195+#endif 1196 sqlite3_free(p); 1197 } 1198 } 1199 1200+/* 1201+** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0. 1202+** 1203+** This is not a VFS shared-memory method; it is a utility function called 1204+** by VFS shared-memory methods. 1205+*/ 1206+static void unixShmPurge(unixFile *pFd){ 1207+ assert( unixMutexHeld() ); 1208+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1209+ unixShmPurgeInner(pFd, pFd->pInode->pCompressShmNode, 1); 1210+ unixShmPurgeInner(pFd, pFd->pInode->pShmNode, 0); 1211+#else 1212+ unixShmPurgeInner(pFd, pFd->pInode->pShmNode); 1213+#endif 1214+} 1215+ 1216 /* 1217 ** The DMS lock has not yet been taken on shm file pShmNode. Attempt to 1218 ** take it now. Return SQLITE_OK if successful, or an SQLite error 1219@@ -43018,7 +43071,11 @@ static void unixShmPurge(unixFile *pFd){ 1220 ** connection and no other process already holds a lock, return 1221 ** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. 1222 */ 1223+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1224+static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode, int bCompress){ 1225+#else 1226 static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ 1227+#endif 1228 struct flock lock; 1229 int rc = SQLITE_OK; 1230 1231@@ -43051,7 +43108,11 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ 1232 pShmNode->isUnlocked = 1; 1233 rc = SQLITE_READONLY_CANTINIT; 1234 }else{ 1235+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1236+ rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1, bCompress); 1237+#else 1238 rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1); 1239+#endif 1240 MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, EXCLUSIVE_LOCK, LOCK_BY_PROCESS); 1241 MARK_LAST_BUSY_LINE(rc); 1242 /* The first connection to attach must truncate the -shm file. We 1243@@ -43071,7 +43132,11 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ 1244 1245 if( rc==SQLITE_OK ){ 1246 assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK ); 1247+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1248+ rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1, bCompress); 1249+#else 1250 rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); 1251+#endif 1252 MarkLockStatusByRc(rc, SHM_DMS_IDX, 1, SHARED_LOCK, LOCK_BY_PROCESS); 1253 MARK_LAST_BUSY_LINE(rc); 1254 } 1255@@ -43112,8 +43177,15 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){ 1256 ** that no other processes are able to read or write the database. In 1257 ** that case, we do not really need shared memory. No shared memory 1258 ** file is created. The shared memory will be simulated with heap memory. 1259+** 1260+** If bCompressShm is true, it indicates that the shmFile needs to be renamed by 1261+** adding a suffix named "compress". 1262 */ 1263+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1264+static int unixOpenSharedMemory(unixFile *pDbFd, int bCompressShm){ 1265+#else 1266 static int unixOpenSharedMemory(unixFile *pDbFd){ 1267+#endif 1268 struct unixShm *p = 0; /* The connection to be opened */ 1269 struct unixShmNode *pShmNode; /* The underlying mmapped file */ 1270 int rc = SQLITE_OK; /* Result code */ 1271@@ -43133,7 +43205,11 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ 1272 assert( unixFileMutexNotheld(pDbFd) ); 1273 unixEnterMutex(); 1274 pInode = pDbFd->pInode; 1275+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1276+ pShmNode = bCompressShm ? pInode->pCompressShmNode : pInode->pShmNode; 1277+#else 1278 pShmNode = pInode->pShmNode; 1279+#endif 1280 if( pShmNode==0 ){ 1281 struct stat sStat; /* fstat() info for database file */ 1282 #ifndef SQLITE_SHM_DIRECTORY 1283@@ -43153,6 +43229,11 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ 1284 nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; 1285 #else 1286 nShmFilename = 6 + (int)strlen(zBasePath); 1287+#endif 1288+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1289+ if( bCompressShm ){ 1290+ nShmFilename += 8; 1291+ } 1292 #endif 1293 pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); 1294 if( pShmNode==0 ){ 1295@@ -43169,8 +43250,17 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ 1296 sqlite3_snprintf(nShmFilename, zShm, "%s-shm", zBasePath); 1297 sqlite3FileSuffix3(pDbFd->zPath, zShm); 1298 #endif 1299- pShmNode->hShm = -1; 1300+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1301+ if( bCompressShm ){ 1302+ sqlite3_snprintf(nShmFilename, zShm, "%scompress", zShm); 1303+ pDbFd->pInode->pCompressShmNode = pShmNode; 1304+ }else{ 1305+ pDbFd->pInode->pShmNode = pShmNode; 1306+ } 1307+#else 1308 pDbFd->pInode->pShmNode = pShmNode; 1309+#endif 1310+ pShmNode->hShm = -1; 1311 pShmNode->pInode = pDbFd->pInode; 1312 if( sqlite3GlobalConfig.bCoreMutex ){ 1313 pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); 1314@@ -43207,7 +43297,11 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ 1315 */ 1316 robustFchown(pShmNode->hShm, sStat.st_uid, sStat.st_gid); 1317 1318+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1319+ rc = unixLockSharedMemory(pDbFd, pShmNode, bCompressShm); 1320+#else 1321 rc = unixLockSharedMemory(pDbFd, pShmNode); 1322+#endif 1323 if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; 1324 } 1325 } 1326@@ -43218,7 +43312,15 @@ static int unixOpenSharedMemory(unixFile *pDbFd){ 1327 p->id = pShmNode->nextShmId++; 1328 #endif 1329 pShmNode->nRef++; 1330+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1331+ if( bCompressShm ){ 1332+ pDbFd->pCompressShm = p; 1333+ }else{ 1334+ pDbFd->pShm = p; 1335+ } 1336+#else 1337 pDbFd->pShm = p; 1338+#endif 1339 unixLeaveMutex(); 1340 1341 /* The reference count on pShmNode has already been incremented under 1342@@ -43250,6 +43352,10 @@ shm_open_err: 1343 ** 1344 ** If an error occurs, an error code is returned and *pp is set to NULL. 1345 ** 1346+** The fileFlag parameter has two means: 1347+** bExtend = fileFlag & SQLITE_SHMMAP_IS_WRITE 1348+** bCompressShm = fileFlag & SQLITE_OPEN_COMPRESS_SHM 1349+** 1350 ** Otherwise, if the bExtend parameter is 0 and the requested shared-memory 1351 ** region has not been allocated (by any client, including one running in a 1352 ** separate process), then *pp is set to NULL and SQLITE_OK returned. If 1353@@ -43260,12 +43366,15 @@ shm_open_err: 1354 ** this call as described above, then it is mapped into this processes 1355 ** address space (if it is not already), *pp is set to point to the mapped 1356 ** memory and SQLITE_OK returned. 1357+** 1358+** If bCompressShm is true, it indicates that the shmFile needs to be renamed by 1359+** adding a suffix named "compress" 1360 */ 1361 static int unixShmMap( 1362 sqlite3_file *fd, /* Handle open on database file */ 1363 int iRegion, /* Region to retrieve */ 1364 int szRegion, /* Size of regions */ 1365- int bExtend, /* True to extend file if necessary */ 1366+ int fileFlag, /* True to extend file if necessary */ 1367 void volatile **pp /* OUT: Mapped memory */ 1368 ){ 1369 unixFile *pDbFd = (unixFile*)fd; 1370@@ -43274,18 +43383,33 @@ static int unixShmMap( 1371 int rc = SQLITE_OK; 1372 int nShmPerMap = unixShmRegionPerMap(); 1373 int nReqRegion; 1374+ int bExtend = (fileFlag & SQLITE_SHMMAP_IS_WRITE); // True to extend file if necessary 1375+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1376+ int bCompressShm = (fileFlag & SQLITE_OPEN_COMPRESS_SHM); // True to rename shm file 1377+#endif 1378 1379 /* If the shared-memory file has not yet been opened, open it now. */ 1380+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1381+ if( (bCompressShm && pDbFd->pCompressShm==0) || ( !bCompressShm && pDbFd->pShm==0 )){ 1382+ rc = unixOpenSharedMemory(pDbFd, bCompressShm); 1383+ if( rc!=SQLITE_OK ) return rc; 1384+ } 1385+ p = bCompressShm ? pDbFd->pCompressShm : pDbFd->pShm; 1386+#else 1387 if( pDbFd->pShm==0 ){ 1388 rc = unixOpenSharedMemory(pDbFd); 1389 if( rc!=SQLITE_OK ) return rc; 1390 } 1391- 1392 p = pDbFd->pShm; 1393+#endif 1394 pShmNode = p->pShmNode; 1395 sqlite3_mutex_enter(pShmNode->pShmMutex); 1396 if( pShmNode->isUnlocked ){ 1397+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1398+ rc = unixLockSharedMemory(pDbFd, pShmNode, bCompressShm); 1399+#else 1400 rc = unixLockSharedMemory(pDbFd, pShmNode); 1401+#endif 1402 if( rc!=SQLITE_OK ) goto shmpage_out; 1403 pShmNode->isUnlocked = 0; 1404 } 1405@@ -43448,8 +43572,13 @@ static int unixShmLock( 1406 int rc = SQLITE_OK; /* Result code */ 1407 u16 mask; /* Mask of locks to take or release */ 1408 int *aLock; 1409- 1410+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1411+ int bCompress = (flags & SQLITE_OPEN_COMPRESS_SHM); 1412+ flags &= ~SQLITE_OPEN_COMPRESS_SHM; 1413+ p = bCompress ? pDbFd->pCompressShm : pDbFd->pShm; 1414+#else 1415 p = pDbFd->pShm; 1416+#endif 1417 if( p==0 ) { 1418 #ifdef LOG_DUMP 1419 sqlite3_log(SQLITE_IOERR_SHMLOCK, "unixShmLock-pShm, fd[%d], ofst[%d], n[%d], flags[%d]", pDbFd->h, ofst, n, flags); 1420@@ -43516,7 +43645,11 @@ static int unixShmLock( 1421 } 1422 1423 if( bUnlock ){ 1424+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1425+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n, bCompress); 1426+#else 1427 rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); 1428+#endif 1429 if( rc==SQLITE_OK ){ 1430 memset(&aLock[ofst], 0, sizeof(int)*n); 1431 } 1432@@ -43540,7 +43673,11 @@ static int unixShmLock( 1433 if( aLock[ofst]<0 ){ 1434 rc = SQLITE_BUSY; 1435 }else if( aLock[ofst]==0 ){ 1436+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1437+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n, bCompress); 1438+#else 1439 rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); 1440+#endif 1441 useProcessLock = LOCK_BY_PROCESS; 1442 } 1443 MarkLockStatusByRc(rc, ofst, n, SHARED_LOCK, useProcessLock); 1444@@ -43566,7 +43703,11 @@ static int unixShmLock( 1445 /* Get the exclusive locks at the system level. Then if successful 1446 ** also update the in-memory values. */ 1447 if( rc==SQLITE_OK ){ 1448+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1449+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n, bCompress); 1450+#else 1451 rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); 1452+#endif 1453 useProcessLock = LOCK_BY_PROCESS; 1454 if( rc==SQLITE_OK ){ 1455 assert( (p->sharedMask & mask)==0 ); 1456@@ -43619,9 +43760,14 @@ static int unixShmUnmap( 1457 unixShmNode *pShmNode; /* The underlying shared-memory file */ 1458 unixShm **pp; /* For looping over sibling connections */ 1459 unixFile *pDbFd; /* The underlying database file */ 1460- 1461 pDbFd = (unixFile*)fd; 1462+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1463+ int bCompressShm = (deleteFlag & SQLITE_OPEN_COMPRESS_SHM); // True means compress shm file 1464+ deleteFlag &= 1; 1465+ p = bCompressShm ? pDbFd->pCompressShm : pDbFd->pShm; 1466+#else 1467 p = pDbFd->pShm; 1468+#endif 1469 if( p==0 ) return SQLITE_OK; 1470 pShmNode = p->pShmNode; 1471 1472@@ -43636,7 +43782,15 @@ static int unixShmUnmap( 1473 1474 /* Free the connection p */ 1475 sqlite3_free(p); 1476+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1477+ if( bCompressShm ){ 1478+ pDbFd->pCompressShm = 0; 1479+ }else{ 1480+ pDbFd->pShm = 0; 1481+ } 1482+#else 1483 pDbFd->pShm = 0; 1484+#endif 1485 sqlite3_mutex_leave(pShmNode->pShmMutex); 1486 1487 /* If pShmNode->nRef has reached 0, then close the underlying 1488@@ -50788,8 +50942,15 @@ static int winLockSharedMemory(winShmNode *pShmNode){ 1489 ** When opening a new shared-memory file, if no other instances of that 1490 ** file are currently open, in this process or in other processes, then 1491 ** the file must be truncated to zero length or have its header cleared. 1492+** 1493+** If bCompressShm is true, it indicates that the shmFile needs to be renamed by 1494+** adding a suffix named "compress" 1495 */ 1496+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1497+static int winOpenSharedMemory(winFile *pDbFd, int bCompressShm){ 1498+#else 1499 static int winOpenSharedMemory(winFile *pDbFd){ 1500+#endif 1501 struct winShm *p; /* The connection to be opened */ 1502 winShmNode *pShmNode = 0; /* The underlying mmapped file */ 1503 int rc = SQLITE_OK; /* Result code */ 1504@@ -50804,6 +50965,11 @@ static int winOpenSharedMemory(winFile *pDbFd){ 1505 p = sqlite3MallocZero( sizeof(*p) ); 1506 if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; 1507 nName = sqlite3Strlen30(pDbFd->zPath); 1508+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1509+ if( bCompressShm ){ 1510+ nName += 8; 1511+ } 1512+#endif 1513 pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); 1514 if( pNew==0 ){ 1515 sqlite3_free(p); 1516@@ -50812,6 +50978,11 @@ static int winOpenSharedMemory(winFile *pDbFd){ 1517 pNew->zFilename = (char*)&pNew[1]; 1518 sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); 1519 sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); 1520+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1521+ if( bCompressShm ){ 1522+ sqlite3_snprintf(nName+15, pNew->zFilename, "%scompress", pNew->zFilename); 1523+ } 1524+#endif 1525 1526 /* Look to see if there is an existing winShmNode that can be used. 1527 ** If no matching winShmNode currently exists, create a new one. 1528@@ -51069,6 +51240,10 @@ static void winShmBarrier( 1529 ** 1530 ** If an error occurs, an error code is returned and *pp is set to NULL. 1531 ** 1532+** The fileFlag parameter has two means: 1533+** bExtend = fileFlag & SQLITE_SHMMAP_IS_WRITE 1534+** bCompressShm = fileFlag & SQLITE_OPEN_COMPRESS_SHM 1535+** 1536 ** Otherwise, if the isWrite parameter is 0 and the requested shared-memory 1537 ** region has not been allocated (by any client, including one running in a 1538 ** separate process), then *pp is set to NULL and SQLITE_OK returned. If 1539@@ -51079,12 +51254,15 @@ static void winShmBarrier( 1540 ** this call as described above, then it is mapped into this processes 1541 ** address space (if it is not already), *pp is set to point to the mapped 1542 ** memory and SQLITE_OK returned. 1543+** 1544+** If bCompressShm is true, it indicates that the shmFile needs to be renamed by 1545+** adding a suffix named "compress" 1546 */ 1547 static int winShmMap( 1548 sqlite3_file *fd, /* Handle open on database file */ 1549 int iRegion, /* Region to retrieve */ 1550 int szRegion, /* Size of regions */ 1551- int isWrite, /* True to extend file if necessary */ 1552+ int fileFlag, /* True to extend file if necessary */ 1553 void volatile **pp /* OUT: Mapped memory */ 1554 ){ 1555 winFile *pDbFd = (winFile*)fd; 1556@@ -51092,10 +51270,18 @@ static int winShmMap( 1557 winShmNode *pShmNode; 1558 DWORD protect = PAGE_READWRITE; 1559 DWORD flags = FILE_MAP_WRITE | FILE_MAP_READ; 1560+ int isWrite = (fileFlag & SQLITE_SHMMAP_IS_WRITE); // True to extend file if necessary 1561+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1562+ int bCompressShm = (fileFlag & SQLITE_OPEN_COMPRESS_SHM); // True to rename shm file 1563+#endif 1564 int rc = SQLITE_OK; 1565 1566 if( !pShm ){ 1567+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1568+ rc = winOpenSharedMemory(pDbFd, bCompressShm); 1569+#else 1570 rc = winOpenSharedMemory(pDbFd); 1571+#endif 1572 if( rc!=SQLITE_OK ) return rc; 1573 pShm = pDbFd->pShm; 1574 assert( pShm!=0 ); 1575@@ -57775,7 +57961,7 @@ struct PagerSavepoint { 1576 #endif 1577 }; 1578 1579-#if !defined(SQLITE_OS_UNIX) && defined(SQLITE_META_DWR) 1580+#if !SQLITE_OS_UNIX && defined(SQLITE_META_DWR) 1581 #undef SQLITE_META_DWR 1582 #endif 1583 1584@@ -62360,7 +62546,30 @@ SQLITE_PRIVATE int sqlite3PagerOpen( 1585 ** that is used by sqlite3_filename_database() and kin also depend on the 1586 ** specific formatting and order of the various filenames, so if the format 1587 ** changes here, be sure to change it there as well. 1588+ ** 1589+ ** Addition, in case of enable page compression, journal&WAL filename will add a file extension:"compress" 1590+ ** So the final layout in memory is as follows: 1591+ ** 1592+ ** Pager object (sizeof(Pager) bytes) 1593+ ** PCache object (sqlite3PcacheSize() bytes) 1594+ ** Database file handle (pVfs->szOsFile bytes) 1595+ ** Sub-journal file handle (journalFileSize bytes) 1596+ ** Main journal file handle (journalFileSize bytes) 1597+ ** Ptr back to the Pager (sizeof(Pager*) bytes) 1598+ ** \0\0\0\0 database prefix (4 bytes) 1599+ ** Database file name (nPathname+1 bytes) 1600+ ** URI query parameters (nUriByte bytes) 1601+ ** Journal filename (nPathname+16+1 bytes) 1602+ ** WAL filename (nPathname+12+1 bytes) 1603+ ** \0\0\0 terminator (3 bytes) 1604+ ** 1605 */ 1606+ int fileExtSz = 0; 1607+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1608+ if( sqlite3_stricmp(pVfs->zName, "compressvfs")==0 ){ 1609+ fileExtSz = 8; /* 8 means the size of suffix:"compress" */ 1610+ } 1611+#endif 1612 assert( SQLITE_PTRSIZE==sizeof(Pager*) ); 1613 pPtr = (u8 *)sqlite3MallocZero( 1614 ROUND8(sizeof(*pPager)) + /* Pager structure */ 1615@@ -62371,9 +62580,9 @@ SQLITE_PRIVATE int sqlite3PagerOpen( 1616 4 + /* Database prefix */ 1617 nPathname + 1 + /* database filename */ 1618 nUriByte + /* query parameters */ 1619- nPathname + 8 + 1 + /* Journal filename */ 1620+ nPathname + 8 + fileExtSz + 1 + /* Journal filename */ 1621 #ifndef SQLITE_OMIT_WAL 1622- nPathname + 4 + 1 + /* WAL filename */ 1623+ nPathname + 4 + fileExtSz + 1 + /* WAL filename */ 1624 #endif 1625 3 /* Terminator */ 1626 ); 1627@@ -62407,7 +62616,13 @@ SQLITE_PRIVATE int sqlite3PagerOpen( 1628 if( nPathname>0 ){ 1629 pPager->zJournal = (char*)pPtr; 1630 memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; 1631- memcpy(pPtr, "-journal",8); pPtr += 8 + 1; 1632+ memcpy(pPtr, "-journal",8); pPtr += 8; 1633+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1634+ if( fileExtSz>0 ){ /* 8 means the size of string:"compress" */ 1635+ memcpy(pPtr, "compress", 8); pPtr += fileExtSz; 1636+ } 1637+#endif 1638+ pPtr += 1; /* Skip zero suffix */ 1639 #ifdef SQLITE_ENABLE_8_3_NAMES 1640 sqlite3FileSuffix3(zFilename,pPager->zJournal); 1641 pPtr = (u8*)(pPager->zJournal + sqlite3Strlen30(pPager->zJournal)+1); 1642@@ -62421,7 +62636,13 @@ SQLITE_PRIVATE int sqlite3PagerOpen( 1643 if( nPathname>0 ){ 1644 pPager->zWal = (char*)pPtr; 1645 memcpy(pPtr, zPathname, nPathname); pPtr += nPathname; 1646- memcpy(pPtr, "-wal", 4); pPtr += 4 + 1; 1647+ memcpy(pPtr, "-wal", 4); pPtr += 4; 1648+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1649+ if( fileExtSz>0 ){ /* 8 means the size of string:"compress" */ 1650+ memcpy(pPtr, "compress", 8); pPtr += fileExtSz; 1651+ } 1652+#endif 1653+ pPtr += 1; /* Skip zero suffix */ 1654 #ifdef SQLITE_ENABLE_8_3_NAMES 1655 sqlite3FileSuffix3(zFilename, pPager->zWal); 1656 pPtr = (u8*)(pPager->zWal + sqlite3Strlen30(pPager->zWal)+1); 1657@@ -66177,6 +66398,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc( 1658 pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ); 1659 if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT; 1660 }else{ 1661+ assert( pWal->writeLock==0 || pWal->writeLock==1 ); 1662 rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ, 1663 pWal->writeLock, (void volatile **)&pWal->apWiData[iPage] 1664 ); 1665@@ -137863,6 +138085,9 @@ typedef int (*sqlite3_loadext_entry)( 1666 /* clean the binlog of the db */ 1667 #define sqlite3_clean_binlog sqlite3_api->clean_binlog 1668 #endif /* SQLITE_ENABLE_BINLOG */ 1669+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1670+#define sqlite3_compressdb_backup sqlite3_api->compressdb_backup 1671+#endif /* SQLITE_ENABLE_PAGE_COMPRESS */ 1672 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ 1673 1674 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) 1675@@ -183070,6 +183295,188 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ 1676 return oldLimit; /* IMP: R-53341-35419 */ 1677 } 1678 1679+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1680+/************** Include sqlitecompressvfs.h in the middle of main.c ******************/ 1681+/************** Begin file sqlitecompressvfs.h ***************************************/ 1682+/* 1683+** 2025 June 27 1684+** 1685+** The author disclaims copyright to this source code. In place of 1686+** a legal notice, here is a blessing: 1687+** 1688+** May you do good and not evil. 1689+** May you find forgiveness for yourself and forgive others. 1690+** May you share freely, never taking more than you give. 1691+** 1692+****************************************************************************** 1693+** 1694+** This header file is used by programs that want to link against the 1695+** compressvfs extension. All it does is declare the sqlite3_compressvfs_init() interface. 1696+*/ 1697+ 1698+#ifndef _WIN32 1699+#include <dlfcn.h> 1700+#endif 1701+ 1702+typedef int (*sqlite3CompressVFSInit_ptr)(); 1703+static sqlite3CompressVFSInit_ptr compressvfsInitPtr = NULL; 1704+typedef int (*sqlite3DecompressBuf_ptr)( 1705+ u8 *dst, 1706+ int dst_buf_len, 1707+ int *dst_written_len, 1708+ const u8 *src, 1709+ int src_len, 1710+ int compression 1711+); 1712+static sqlite3DecompressBuf_ptr decompressBufPtr = NULL; 1713+typedef sqlite3_file *(*sqlite3CompressGetOriFile_ptr)(sqlite3_file *); 1714+static sqlite3CompressGetOriFile_ptr compressvfsGetOrigFilePtr = NULL; 1715+static u32 compressInit = 0u; 1716+static u32 compressSoLoad = 0u; 1717+static void *g_compress_library = NULL; 1718+ 1719+int sqlite3LoadCompressExtension(){ 1720+ if( compressSoLoad ){ 1721+ return SQLITE_OK; 1722+ } 1723+#ifndef _WIN32 1724+ g_compress_library = dlopen("libsqlitecompressvfs.z.so", RTLD_LAZY); 1725+ if( g_compress_library==NULL ){ 1726+ sqlite3_log(SQLITE_ERROR, "load compressvfs so failed: %s\n", dlerror()); 1727+ return SQLITE_ERROR; 1728+ } 1729+ compressvfsInitPtr = (sqlite3CompressVFSInit_ptr)dlsym(g_compress_library, "sqlite3CompressvfsInit"); 1730+ if( compressvfsInitPtr==NULL ){ 1731+ sqlite3_log(SQLITE_ERROR, "load compressvfs init func failed: %s\n", dlerror()); 1732+ dlclose(g_compress_library); 1733+ return SQLITE_ERROR; 1734+ } 1735+ compressvfsGetOrigFilePtr = (sqlite3CompressGetOriFile_ptr)dlsym(g_compress_library, "compressvfsGetOrigFile"); 1736+ if( compressvfsGetOrigFilePtr==NULL ){ 1737+ sqlite3_log(SQLITE_ERROR, "load compressvfs get orig file func failed: %s\n", dlerror()); 1738+ dlclose(g_compress_library); 1739+ return SQLITE_ERROR; 1740+ } 1741+ decompressBufPtr = (sqlite3DecompressBuf_ptr)dlsym(g_compress_library, "decompressBuf"); 1742+ if( decompressBufPtr==NULL ){ 1743+ sqlite3_log(SQLITE_ERROR, "load decompress func failed: %s\n", dlerror()); 1744+ dlclose(g_compress_library); 1745+ return SQLITE_ERROR; 1746+ } 1747+ compressSoLoad = 1u; 1748+#endif 1749+ return SQLITE_OK; 1750+} 1751+ 1752+/* 1753+** Load compressvfs so and register compressvfs 1754+*/ 1755+int sqlite3CompressVFSModuleInit(){ 1756+ int rc = SQLITE_OK; 1757+ if( compressInit ){ 1758+ return rc; 1759+ } 1760+#ifndef _WIN32 1761+ rc = sqlite3LoadCompressExtension(); 1762+ if( rc!=SQLITE_OK ){ 1763+ return rc; 1764+ } 1765+ 1766+ rc = compressvfsInitPtr(); 1767+ if( rc!=SQLITE_OK ){ 1768+ dlclose(g_compress_library); 1769+ compressSoLoad = 0u; 1770+ return rc; 1771+ } 1772+ compressInit = 1u; 1773+#endif 1774+ return rc; 1775+} 1776+ 1777+/* 1778+** The backup API copies the content of one compressed database into another decompressed file. 1779+** It is useful either for creating backups database or 1780+** for copying in-memory databases to or from persistent files. 1781+** The source database must be comrpessed, the destination database will be decomrpessed. 1782+** IMPORTANT: Before use this API, must use a normal vfs which is not compressvfs to open source comrpessed db. 1783+*/ 1784+SQLITE_API int sqlite3_compressdb_backup(sqlite3 *srcDb, const char *destDbPath){ 1785+ int rc = sqlite3LoadCompressExtension(); 1786+ if( rc!=SQLITE_OK ){ 1787+ return rc; 1788+ } 1789+ int pagesize = 0; 1790+ int compression = 0; 1791+ sqlite3_stmt *stmt = NULL; 1792+ u8 *decompressed_data = NULL; 1793+ int openFlags = (O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY|O_NOFOLLOW); /* Flags to pass to open() */ 1794+ int fd = robust_open(destDbPath, openFlags, 0); 1795+ if( fd<0 ){ 1796+ sqlite3_log(SQLITE_ERROR, "Failed to open destDb file!"); 1797+ return SQLITE_CANTOPEN_BKPT; 1798+ } 1799+ const char *compression_sql = "SELECT pagesize, compression FROM vfs_compression;"; 1800+ if( sqlite3_prepare_v2(srcDb, compression_sql, -1, &stmt, NULL) == SQLITE_OK ){ 1801+ if( sqlite3_step(stmt) == SQLITE_ROW ){ 1802+ pagesize = sqlite3_column_int(stmt, 0); 1803+ compression = sqlite3_column_int(stmt, 1); 1804+ }else{ 1805+ sqlite3_log(SQLITE_ERROR, "Failed to select compression!"); 1806+ rc = SQLITE_ERROR; 1807+ } 1808+ sqlite3_finalize(stmt); 1809+ stmt = NULL; 1810+ } 1811+ if( rc!=SQLITE_OK || pagesize==0 ){ 1812+ rc = SQLITE_WARNING_NOTCOMPRESSDB; 1813+ goto failed; 1814+ } 1815+ int dst_len = pagesize; 1816+ const char *data_sql = "SELECT data FROM vfs_pages;"; 1817+ rc = sqlite3_prepare_v2(srcDb, data_sql, -1, &stmt, NULL); 1818+ if( rc!=SQLITE_OK ){ 1819+ sqlite3_log(SQLITE_ERROR, "Failed to exec data_sql!"); 1820+ goto failed; 1821+ } 1822+ decompressed_data = (u8 *)sqlite3_malloc(pagesize); 1823+ int pagecount = 0; 1824+ while( (rc = sqlite3_step(stmt)) == SQLITE_ROW ){ 1825+ const void *data_ptr = sqlite3_column_blob(stmt, 0); 1826+ int data_size = sqlite3_column_bytes(stmt, 0); 1827+ rc = decompressBufPtr(decompressed_data, pagesize, &dst_len, data_ptr, data_size, compression); 1828+ if( rc!=SQLITE_OK || pagesize!=dst_len ){ 1829+ sqlite3_log(rc, "Failed to decompress buf in src db!"); 1830+ rc = SQLITE_ERROR; 1831+ goto failed; 1832+ } 1833+ int nWrite = seekAndWriteFd(fd, (i64)pagecount*pagesize, decompressed_data, pagesize, &rc); 1834+ if( nWrite!=dst_len || rc!=SQLITE_OK ){ 1835+ sqlite3_log(rc, "Write dest db error at page %d!", pagecount); 1836+ rc = SQLITE_IOERR_WRITE; 1837+ goto failed; 1838+ } 1839+ pagecount++; 1840+ if( pagecount==(PENDING_BYTE/pagesize)+1 ){ 1841+ pagecount++; 1842+ } 1843+ } 1844+ if( rc!=SQLITE_DONE ){ 1845+ sqlite3_log(rc, "Querry error: %s!", sqlite3_errmsg(srcDb)); 1846+ } 1847+ 1848+failed: 1849+ osClose(fd); 1850+ if( decompressed_data ){ 1851+ sqlite3_free(decompressed_data); 1852+ } 1853+ sqlite3_finalize(stmt); 1854+ return rc; 1855+} 1856+ 1857+/************** End of sqlitecompressvfs.h ***************************************/ 1858+/************** Continuing where we left off in main.c ***********************/ 1859+#endif /* SQLITE_ENABLE_PAGE_COMPRESS */ 1860+ 1861 /* 1862 ** This function is used to parse both URIs and non-URI filenames passed by the 1863 ** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database 1864@@ -183314,6 +183721,17 @@ SQLITE_PRIVATE int sqlite3ParseUri( 1865 flags &= ~SQLITE_OPEN_URI; 1866 } 1867 1868+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1869+ if( sqlite3_stricmp(zVfs, "compressvfs")==0 ){ 1870+ rc = sqlite3CompressVFSModuleInit(); 1871+ if( rc!=SQLITE_OK ){ 1872+ *pzErrMsg = sqlite3_mprintf("load so of compressvfs error."); 1873+ rc = SQLITE_ERROR; 1874+ goto parse_uri_out; 1875+ } 1876+ } 1877+#endif 1878+ 1879 *ppVfs = sqlite3_vfs_find(zVfs); 1880 if( *ppVfs==0 ){ 1881 *pzErrMsg = sqlite3_mprintf("no such vfs: %s", zVfs); 1882@@ -254405,6 +254823,50 @@ SQLITE_API int sqlite3_stmt_init( 1883 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } 1884 /************************** End of sqlite3.c ******************************/ 1885 1886+#ifdef SQLITE_CKSUMVFS_STATIC 1887+extern sqlite3_file *cksmvfsGetOrigFile(sqlite3_file *file); 1888+#else 1889+static sqlite3_file *cksmvfsGetOrigFile(sqlite3_file *file) { 1890+ return file; 1891+} 1892+#endif /* SQLITE_CKSUMVFS_STATIC */ 1893+ 1894+#define SQLITE_CHECK_FILE_ID_UNIX 1 1895+#define SQLITE_CHECK_FILE_ID_CKSM 2 1896+#define SQLITE_CHECK_FILE_ID_COMPRESS 3 1897+ 1898+static u8 Sqlite3GetCheckFileId(const sqlite3_vfs *vfs){ 1899+ if( vfs==NULL ){ 1900+ return 0; 1901+ }else if( sqlite3_stricmp(vfs->zName, "unix")==0 ){ 1902+ return SQLITE_CHECK_FILE_ID_UNIX; 1903+ }else if( sqlite3_stricmp(vfs->zName, "cksmvfs")==0 ){ 1904+ return SQLITE_CHECK_FILE_ID_CKSM; 1905+ }else if( sqlite3_stricmp(vfs->zName, "compressvfs")==0 ){ 1906+ return SQLITE_CHECK_FILE_ID_COMPRESS; 1907+ } 1908+ sqlite3_log(SQLITE_WARNING_DUMP, "Not support pVfs type %s", vfs->zName); 1909+ return 0; 1910+} 1911+ 1912+#if SQLITE_OS_UNIX 1913+// checkFileId should not be 0 1914+// it must be SQLITE_CHECK_FILE_ID_UNIX(1) or SQLITE_CHECK_FILE_ID_CKSM(2) or SQLITE_CHECK_FILE_ID_COMPRESS(3) 1915+static unixFile *Sqlite3GetUnixFile(sqlite3_file *file, u8 checkFileId){ 1916+ if( checkFileId==SQLITE_CHECK_FILE_ID_UNIX ){ 1917+ return (unixFile*)file; 1918+ }else if( checkFileId==SQLITE_CHECK_FILE_ID_CKSM ){ 1919+ return (unixFile*)cksmvfsGetOrigFile(file); 1920+ } 1921+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 1922+ if( compressvfsGetOrigFilePtr!=NULL ){ 1923+ return (unixFile*)compressvfsGetOrigFilePtr(file); 1924+ } 1925+#endif 1926+ return (unixFile*)file; 1927+} 1928+#endif /* SQLITE_OS_UNIX */ 1929+ 1930 #ifdef SQLITE_HAS_CODEC 1931 /************** Begin file hw_codec_openssl.h *******************************/ 1932 #ifndef EXPOSE_INTERNAL_FUNC 1933@@ -255290,7 +255752,7 @@ CODEC_STATIC void sqlite3CodecTransPgno(Pgno input, unsigned char *output){ 1934 output[1] = (u8)(input>>8); 1935 output[2] = (u8)(input>>16); 1936 output[3] = (u8)(input>>24); 1937-#endif 1938+#endif /* CIPHER_BIG_ENDAIN */ 1939 } 1940 1941 CODEC_STATIC int sqlite3CodecHmac(KeyContext *ctx, Pgno pgno, int bufferSize, unsigned char *input, unsigned char *output){ 1942@@ -256147,26 +256609,8 @@ static int PragmaCksumPersistEnable(sqlite3 *db, int iDb, Parse *parse, const ch 1943 } 1944 return 1; 1945 } 1946-extern sqlite3_file *sqlite3_get_orig_file(sqlite3_file *file); 1947-#else 1948-static sqlite3_file *sqlite3_get_orig_file(sqlite3_file *file) { 1949- return file; 1950-} 1951 #endif /* SQLITE_CKSUMVFS_STATIC */ 1952 1953-#if SQLITE_OS_UNIX 1954-#define SQLITE_CHECK_FILE_ID_UNIX 1 1955-#define SQLITE_CHECK_FILE_ID_CKSM 2 1956- 1957-// checkFileId should not be 0, it must be SQLITE_CHECK_FILE_ID_UNIX(1) or SQLITE_CHECK_FILE_ID_CKSM(2) 1958-static unixFile *Sqlite3GetUnixFile(sqlite3_file *file, u8 checkFileId) { 1959- if (checkFileId == SQLITE_CHECK_FILE_ID_UNIX) { 1960- return (unixFile*)file; 1961- } 1962- return (unixFile*)sqlite3_get_orig_file(file); 1963-} 1964-#endif /* SQLITE_OS_UNIX */ 1965- 1966 #ifdef SQLITE_META_DWR 1967 #define META_DWR_MAX_PAGES 500 1968 #define META_DWR_MAGIC 0x234A86D9 1969@@ -256454,15 +256898,7 @@ static MetaDwrHdr *AllocInitMetaHeaderDwr(Pager *pPager) { 1970 MetaDwrReleaseHdr(hdr); 1971 return NULL; 1972 } 1973- if (pPager->pVfs==NULL) { 1974- hdr->checkFileId = 0; 1975- } else if (sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0) { 1976- hdr->checkFileId = SQLITE_CHECK_FILE_ID_UNIX; 1977- } else if (sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") == 0) { 1978- hdr->checkFileId = SQLITE_CHECK_FILE_ID_CKSM; 1979- } else { 1980- hdr->checkFileId = 0; 1981- } 1982+ hdr->checkFileId = Sqlite3GetCheckFileId(pPager->pVfs); 1983 return hdr; 1984 } 1985 1986@@ -257156,25 +257592,19 @@ CHK_RESTORE_OUT: 1987 static inline u8 IsConnectionValidForCheck(Pager *pPager) 1988 { 1989 #if SQLITE_OS_UNIX 1990- if (pPager->pVfs == NULL) { 1991+ u8 checkFileId = Sqlite3GetCheckFileId(pPager->pVfs); 1992+ if( checkFileId==0 ){ 1993 return 0; 1994 } 1995- if (sqlite3_stricmp(pPager->pVfs->zName, "unix") != 0 && sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") != 0) { 1996- return 0; 1997- } 1998- u8 checkFileId = SQLITE_CHECK_FILE_ID_UNIX; 1999- if (sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") == 0) { 2000- checkFileId = SQLITE_CHECK_FILE_ID_CKSM; 2001- } 2002 unixFile *fd = Sqlite3GetUnixFile(pPager->fd, checkFileId); 2003 // unix and only one connection exist 2004- if (fd == NULL || fd->pInode == NULL || fd->pInode->nRef != 1) { 2005+ if (fd == NULL || fd->pInode == NULL || fd->pInode->nRef != (checkFileId==SQLITE_CHECK_FILE_ID_COMPRESS ? 2 : 1)) { 2006 return 0; 2007 } 2008 return 1; 2009 #else 2010 return 0; 2011-#endif 2012+#endif /* SQLITE_OS_UNIX */ 2013 } 2014 2015 static int MetaDwrOpenAndCheck(Btree *pBt) 2016@@ -257407,14 +257837,23 @@ static void DumpTrxProcessLocks(unixFile *file, char *dumpBuf, int dumpBufLen) 2017 2018 static void DumpWalLocks(unixFile *file, u8 walEnabled, char *dumpBuf, int dumpBufLen) 2019 { 2020+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 2021+ if( (file->pShm == NULL || file->pShm->pShmNode == NULL) && 2022+ (file->pCompressShm == NULL || file->pCompressShm->pShmNode == NULL) ){ 2023+#else 2024 if (file->pShm == NULL || file->pShm->pShmNode == NULL) { 2025+#endif 2026 sqlite3_log(SQLITE_ERROR, "[SQLite]Wal mode disabled! pShm or pShmNode is NULL"); 2027 return; 2028 } 2029 if (!walEnabled) { 2030 sqlite3_log(SQLITE_ERROR, "[SQLite] walEnabled false"); 2031 } 2032+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 2033+ unixShmNode *pShmNode = (file->pShm == NULL || file->pShm->pShmNode == NULL) ? file->pCompressShm->pShmNode : file->pShm->pShmNode; 2034+#else 2035 unixShmNode *pShmNode = file->pShm->pShmNode; 2036+#endif 2037 char *tmp = dumpBuf; 2038 int availLen = dumpBufLen - 1; 2039 dumpBuf[availLen] = '\0'; 2040@@ -257458,14 +257897,8 @@ static void DumpLocksByWal(Wal *pWal) 2041 sqlite3_log(SQLITE_ERROR, "Wal ptr is NULL!"); 2042 return; 2043 } 2044- u8 checkFileId = 0; 2045- if (pWal->pVfs==NULL) { 2046- return; 2047- } else if (sqlite3_stricmp(pWal->pVfs->zName, "unix") == 0) { 2048- checkFileId = SQLITE_CHECK_FILE_ID_UNIX; 2049- } else if (sqlite3_stricmp(pWal->pVfs->zName, "cksmvfs") == 0) { 2050- checkFileId = SQLITE_CHECK_FILE_ID_CKSM; 2051- } else { 2052+ u8 checkFileId = Sqlite3GetCheckFileId(pWal->pVfs); 2053+ if( checkFileId==0 ){ 2054 return; 2055 } 2056 DumpLocksInfo(Sqlite3GetUnixFile(pWal->pDbFd, checkFileId), 1); 2057@@ -257478,14 +257911,8 @@ static void DumpLocksByPager(Pager *pPager) 2058 sqlite3_log(SQLITE_ERROR, "Pager ptr is NULL!"); 2059 return; 2060 } 2061- u8 checkFileId = 0; 2062- if (pPager->pVfs==NULL) { 2063- return; 2064- } else if (sqlite3_stricmp(pPager->pVfs->zName, "unix") == 0) { 2065- checkFileId = SQLITE_CHECK_FILE_ID_UNIX; 2066- } else if (sqlite3_stricmp(pPager->pVfs->zName, "cksmvfs") == 0) { 2067- checkFileId = SQLITE_CHECK_FILE_ID_CKSM; 2068- } else { 2069+ u8 checkFileId = Sqlite3GetCheckFileId(pPager->pVfs); 2070+ if( checkFileId==0 ){ 2071 return; 2072 } 2073 #ifndef SQLITE_OMIT_WAL 2074@@ -258615,6 +259042,11 @@ struct sqlite3_api_routines_extra { 2075 int (*is_support_binlog)(void); 2076 int (*replay_binlog)(sqlite3*, sqlite3*); 2077 int (*clean_binlog)(sqlite3*, BinlogFileCleanModeE); 2078+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 2079+ int (*compressdb_backup)(sqlite3*, const char*); 2080+#else 2081+ void *dymmyFunc1; 2082+#endif /* SQLITE_ENABLE_PAGE_COMPRESS */ 2083 }; 2084 2085 typedef struct sqlite3_api_routines_extra sqlite3_api_routines_extra; 2086@@ -258641,6 +259073,11 @@ static const sqlite3_api_routines_extra sqlite3ExtraApis = { 2087 0, 2088 0, 2089 #endif/* SQLITE_ENABLE_BINLOG */ 2090+#ifdef SQLITE_ENABLE_PAGE_COMPRESS 2091+ sqlite3_compressdb_backup, 2092+#else 2093+ 0, 2094+#endif/* SQLITE_ENABLE_PAGE_COMPRESS */ 2095 }; 2096 2097 EXPORT_SYMBOLS const sqlite3_api_routines *sqlite3_export_symbols = &sqlite3Apis; 2098-- 20992.47.0.windows.2 2100 2101