• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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