From 7108a95a00062eb180993e1b11498b734ebd448f Mon Sep 17 00:00:00 2001
From: chenwei_1994 <1606056289@qq.com>
Date: Sun, 12 Jul 2026 14:43:02 +0800
Subject: [PATCH] Bugfix on current version

---
 ext/misc/cksumvfs.c |  11 +-
 src/compressvfs.c   | 514 +++++++++++++++++++++---------
 src/sqlite3.c       | 752 ++++++++++++++++++++++++++++++++++----------
 3 files changed, 953 insertions(+), 324 deletions(-)

diff --git a/ext/misc/cksumvfs.c b/ext/misc/cksumvfs.c
index 27b1028..e89edcd 100644
--- a/ext/misc/cksumvfs.c
+++ b/ext/misc/cksumvfs.c
@@ -559,17 +559,17 @@ static int cksmRead(
     **    (1) the size indicates that we are dealing with a complete
     **        database page, only support pageSize:4K
     **    (2) checksum verification is enabled
-    **    (3) we are not in the middle of checkpoint
+    **    (3) Skip the WAL log write, it might write segment as iSyncPoint, do checksum at checkpoint
     **    (4) magic number should be 0xff
     **    (5) checksum type should be 1, 0 means without checksum
     */
-    if( iAmt==4096           /* (1) */
+    if( iAmt==4096          /* (1) */
      && p->verifyCksm       /* (2) */
-     && !p->inCkpt          /* (3) */
+     && !p->isWal           /* (3) */
     ){
       if( ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE]!=CKSUMVFS_MAGIC_NUM ){ /* (4) */
         sqlite3_log(SQLITE_IOERR_DATA, "unrecognized format, offset %lld of \"%s\", amt:%d", iOfst, p->zFName, iAmt);
-        return SQLITE_IOERR_DATA;
+        return rc;
       }
       if( ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE+1]==CKSUMVFS_WITHOUT_CHECKSUM ){ /* (5) */
         return rc;
@@ -584,7 +584,6 @@ static int cksmRead(
         EncodeReservedBytesIntoBase16(cksum, CKSUMVFS_CHECKSUM_SIZE, actual, CKSUM_HEX_LEN);
         sqlite3_log(SQLITE_IOERR_DATA, "checksum fault offset %lld of \"%s\", amt:%d, expect:%s, actual:%s",
            iOfst, p->zFName, iAmt, expect, actual);
-        rc = SQLITE_IOERR_DATA;
       }
     }
   }
@@ -617,7 +616,7 @@ static int cksmWrite(
   */
   if( iAmt==4096
    && p->computeCksm
-   && !p->inCkpt
+   && !p->isWal
   ){
     ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE]=CKSUMVFS_MAGIC_NUM;
     ((u8*)zBuf)[iAmt-CKSUMVFS_RESERVED_SIZE+1]=p->verifyCksm ? CKSUMVFS_CALC_CHECKSUM : CKSUMVFS_WITHOUT_CHECKSUM;
diff --git a/src/compressvfs.c b/src/compressvfs.c
index 8f2e908..53b9967 100644
--- a/src/compressvfs.c
+++ b/src/compressvfs.c
@@ -151,6 +151,7 @@ typedef INT8_TYPE i8;              /* 1-byte signed integer */
 typedef u32 Pgno;
 /* VFS's name */
 #define COMPRESS_VFS_NAME "compressvfs"
+#define CKSM_VFS_NAME "cksmvfs"
 
 /* COMPRESSION OPTIONS */
 #define COMPRESSION_UNDEFINED 0
@@ -162,6 +163,8 @@ typedef u32 Pgno;
 #define SQLITE_SHMMAP_IS_WRITE       0x00000001  /* Flag for xShmMap, extend file if necessary */
 #define SQLITE_OPEN_COMPRESS_SHM     0x00010000  /* Flag for xShmMap, need to rename shm file */
 
+#define SQLITE_WARNING_DUMP          (SQLITE_WARNING | (2<<8))
+
 /* An open file */
 typedef struct{
   sqlite3_file base;        /* IO methods */
@@ -170,8 +173,10 @@ typedef struct{
   u8 bSubDbOpen;            /* True to SubDB is opened */
   u8 bBegin;                /* True to xSync() need commit */
   u8 compression;           /* Compression options */
-  int page_size;            /* Uncompressed page size */
+  int pageSize;             /* Uncompressed page size */
   int persistWalFlag;       /* Flag to persist flag */
+  int openFlags;            /* Flag to open file */
+  sqlite3_file *pLockFd;    /* File handle to lock file */
 } CompressFile;
 
 
@@ -233,7 +238,24 @@ typedef enum{
   BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3,
 } BrotliDecoderResult;
 
-typedef BROTLI_BOOL (*brotliCompress_ptr)(u32, u32, size_t, const u8*, size_t*, u8*);
+/** Options for ::BROTLI_PARAM_MODE parameter. */
+typedef enum BrotliEncoderMde {
+  /**
+   * Default compression mode.
+   *
+   * In this mode compressor does not know anything in advance about the
+   * properties of the input.
+   */
+  BROTLI_MODE_GENERIC = 0,
+  /** Compression mode for UTF-8 formatted text input. */
+  BROTLI_MODE_TEXT = 1,
+  /** Compression mode used in WOFF 2.0 */
+  BROTLI_MODE_FONT = 2
+} BrotliEncoderMde;
+/** Default value for ::BROTLI_PARAM_MODE parameter. */
+#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC
+
+typedef BROTLI_BOOL (*brotliCompress_ptr)(int, int, BrotliEncoderMde, size_t, const u8*, size_t*, u8*);
 typedef BrotliDecoderResult (*brotliDecompress_ptr)(size_t, const u8*, size_t*, u8*);
 /*----------------------------brotli header end----------------------------*/
 
@@ -380,6 +402,7 @@ static int compressBuf(
     int ret = brotliCompressPtr(
       3,                        // COMPRESS QUALITY (1-11)
       BROTLI_MAX_WINDOW_BITS,   // WINDOWS SIZE (10-24)
+      BROTLI_DEFAULT_MODE,      // MODE(BROTLI_MODE_GENERIC, TEXT, FONT)
       (size_t)src_len,
       (const u8 *)src,
       &dst_len,
@@ -433,20 +456,32 @@ EXPORT_SYMBOLS int decompressBuf(
   return SQLITE_OK;
 }
 
+static int compressConvertErrCode(int errCode){
+  if( errCode==SQLITE_CORRUPT || errCode==SQLITE_BUSY ){
+    return errCode;
+  }
+  return SQLITE_IOERR;
+}
+
 /* Check whether the table exists in the OutterDB. */
-static int tableExists(sqlite3 *db, const char *table_name){
+static int tableExists(sqlite3 *db, const char *table_name, u8 *isExist){
   sqlite3_stmt *stmt = NULL;
   const char *sql = "SELECT 1 FROM sqlite_master WHERE type='table' AND name=?;";
-  int exists = 0;
 
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    sqlite3_bind_text(stmt, 1, table_name, -1, SQLITE_STATIC);
-    if( sqlite3_step(stmt)==SQLITE_ROW ){
-      exists = 1;
-    }
-    sqlite3_finalize(stmt);
+  int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Check table wrong while prepare stat");
+    return compressConvertErrCode(rc);
   }
-  return exists;
+  sqlite3_bind_text(stmt, 1, table_name, -1, SQLITE_STATIC);
+  rc = sqlite3_step(stmt);
+  sqlite3_finalize(stmt);
+  if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
+    sqlite3_log(rc, "Check table wrong while step stat");
+    return compressConvertErrCode(rc);
+  }
+  *isExist = (rc==SQLITE_ROW) ? 1 : 0;
+  return SQLITE_OK;
 }
 
 /* Get page size before compressed from OutterDB. */
@@ -457,15 +492,20 @@ static int getCompressPgsize(sqlite3 *db, int *pagesize){
   }
   sqlite3_stmt *stmt = NULL;
   const char *sql = "SELECT pagesize FROM vfs_compression;";
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    if( sqlite3_step(stmt)==SQLITE_ROW ){
-      *pagesize = sqlite3_column_int(stmt, 0);
-    }else{
-      rc = SQLITE_ERROR;
-    }
+  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Compress db prepare to get pgsz wrong");
+    return compressConvertErrCode(rc);
+  }
+  rc = sqlite3_step(stmt);
+  if( rc!=SQLITE_ROW ){
+    sqlite3_log(rc, "Compress db get pgsz wrong, expect at least one row");
     sqlite3_finalize(stmt);
+    return compressConvertErrCode(rc);
   }
-  return rc;
+  *pagesize = sqlite3_column_int(stmt, 0);
+  sqlite3_finalize(stmt);
+  return SQLITE_OK;
 }
 
 /* Set page size before compressed to OutterDB. */
@@ -474,50 +514,77 @@ static int setCompressPgsize(sqlite3 *db, int pagesize){
   sqlite3_stmt *stmt = NULL;
   const char *sql = "UPDATE vfs_compression SET pagesize=?;";
 
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    sqlite3_bind_int(stmt, 1, pagesize);
-    if( sqlite3_step(stmt)!=SQLITE_DONE ){
-      rc = SQLITE_ERROR;
-    }
-    sqlite3_finalize(stmt);
+  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Compress db prepare update pgsz wrong, size:%d", pagesize);
+    return compressConvertErrCode(rc);
   }
-  return rc;
+  sqlite3_bind_int(stmt, 1, pagesize);
+  rc = sqlite3_step(stmt);
+  sqlite3_finalize(stmt);
+  if( rc!=SQLITE_DONE ){
+    sqlite3_log(rc, "Compress db update pgsz wrong, size:%d", pagesize);
+    return compressConvertErrCode(rc);
+  }
+  return SQLITE_OK;
 }
 
 /* Get max page number from OutterDB. */
 static int getMaxCompressPgno(sqlite3 *db){
   sqlite3_stmt *stmt = NULL;
   const char *sql = "SELECT MAX(pageno) FROM vfs_pages;";
-  int max_pgno = 0;
+  int mxValue = 0;
 
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    if( sqlite3_step(stmt)==SQLITE_ROW ){
-      max_pgno = sqlite3_column_int(stmt, 0);
-    }
+  int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(SQLITE_WARNING_DUMP, "try get max pgno wrong while prepare stat, rc:%d", rc);
+    return mxValue;
+  }
+  rc = sqlite3_step(stmt);
+  if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
+    sqlite3_log(SQLITE_WARNING_DUMP, "try get max pgno wrong while step stat, rc:%d", rc);
     sqlite3_finalize(stmt);
+    return mxValue;
   }
-  return max_pgno;
+  if( rc==SQLITE_ROW ){  // May not exist any pages, if so, return 0, else fetch the value
+    mxValue = sqlite3_column_int(stmt, 0);
+  }
+  sqlite3_finalize(stmt);
+  return mxValue;
 }
 
 /* Get Compression option from OutterDB. */
-static void getCompression(sqlite3 *db, CompressFile *pCompress){
+static int getCompression(sqlite3 *db, CompressFile *pCompress){
   sqlite3_stmt *stmt = NULL;
   const char *sql = "SELECT count(*), compression, pagesize FROM vfs_compression;";
   int count = 0;
-  pCompress->compression = COMPRESSION_UNDEFINED;
-  pCompress->page_size = 0;
-
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    if( sqlite3_step(stmt)==SQLITE_ROW ){
-      count = sqlite3_column_int(stmt, 0);
-      if( count==1 ){
-        pCompress->compression = sqlite3_column_int(stmt, 1);
-        pCompress->page_size = sqlite3_column_int(stmt, 2);
-      }
-    }
+  pCompress->compression = COMPRESSION_ZSTD;
+  pCompress->pageSize = 0;
+
+  int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Compress db get compression wrong while prepare stat.");
+    return compressConvertErrCode(rc);
+  }
+  rc = sqlite3_step(stmt);
+  if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
+    sqlite3_log(rc, "Compress db get compression wrong while step stat.");
     sqlite3_finalize(stmt);
+    return compressConvertErrCode(rc);
   }
-  return;
+  if( rc==SQLITE_DONE ){
+    sqlite3_finalize(stmt);  // No compression options, use default
+    return SQLITE_OK;
+  }
+  count = sqlite3_column_int(stmt, 0);
+  if( count!=1 ){
+    sqlite3_finalize(stmt);
+    return SQLITE_IOERR;
+  }
+  pCompress->compression = sqlite3_column_int(stmt, 1);
+  pCompress->pageSize = sqlite3_column_int(stmt, 2);
+  sqlite3_finalize(stmt);
+  return SQLITE_OK;
 }
 
 /*
@@ -531,7 +598,7 @@ static int compressSync(sqlite3_file *pFile, int flags){
   if( pCompress->bBegin==1 ){
     int rc = sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL);
     if( rc!=SQLITE_OK ){
-      return SQLITE_IOERR_WRITE;
+      return compressConvertErrCode(rc);
     }
     pCompress->bBegin = 0;
   }
@@ -590,7 +657,9 @@ static int compressDeviceCharacteristics(sqlite3_file *pFile){
 */
 static int compressLock(sqlite3_file *pFile, int eFileLock){
   assert( pFile );
-  return SQLITE_OK;
+  CompressFile *pCompress = (CompressFile *)pFile;
+  sqlite3_file *pSubFile = ORIGFILE(pFile);
+  return pSubFile->pMethods->xLock(pCompress->pLockFd, eFileLock);
 }
 
 /*
@@ -598,7 +667,9 @@ static int compressLock(sqlite3_file *pFile, int eFileLock){
 */
 static int compressUnlock(sqlite3_file *pFile, int eFileLock){
   assert( pFile );
-  return SQLITE_OK;
+  CompressFile *pCompress = (CompressFile *)pFile;
+  sqlite3_file *pSubFile = ORIGFILE(pFile);
+  return pSubFile->pMethods->xUnlock(pCompress->pLockFd, eFileLock);
 }
 
 /*
@@ -608,11 +679,12 @@ static int compressFileSize(sqlite3_file *pFile, i64 *pSize){
   assert( pFile );
   CompressFile *pCompress = (CompressFile *)pFile;
   sqlite3 *db = pCompress->pDb;
-  int rc = getCompressPgsize(db, &pCompress->page_size);
+  int rc = getCompressPgsize(db, &pCompress->pageSize);
   if( rc!=SQLITE_OK ){
-    return SQLITE_IOERR_FSTAT;
+    sqlite3_log(rc, "compress db get pgsz wrong");
+    return rc;
   }
-  int pgsize = pCompress->page_size;
+  int pgsize = pCompress->pageSize;
   int maxpgno = getMaxCompressPgno(db);
   *pSize = (i64)maxpgno * pgsize;
   return SQLITE_OK;
@@ -625,31 +697,43 @@ static int compressTruncate(sqlite3_file *pFile, sqlite_int64 size){
   assert( pFile );
   CompressFile *pCompress = (CompressFile *)pFile;
   sqlite3 *db = pCompress->pDb;
-  int rc = getCompressPgsize(db, &pCompress->page_size);
-  if( rc!=SQLITE_OK || pCompress->page_size==0 ){
+  int rc = getCompressPgsize(db, &pCompress->pageSize);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Get pgsize wrong before truncate pages(%lld)", size);
+    return rc;
+  }
+  int pgsize = pCompress->pageSize;
+  if( pgsize==0 || size%pgsize!=0 ){
     return SQLITE_IOERR_TRUNCATE;
   }
-  int pgsize = pCompress->page_size;
   int pgno = size / pgsize;
-  if( size % pgsize!=0 || getMaxCompressPgno(db) < pgno ){
-    return SQLITE_IOERR_TRUNCATE;
+  int maxPgno = getMaxCompressPgno(db);
+  if( maxPgno<pgno ){
+    sqlite3_log(SQLITE_CORRUPT, "Get max(%d) wrong before truncate pages(%d)", maxPgno, pgno);
+    return SQLITE_CORRUPT;
   }
   if( pCompress->bBegin!=1 ){
-    if( sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL)!=SQLITE_OK ){
-      return SQLITE_IOERR_TRUNCATE;
+    rc = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL);
+    if( rc!=SQLITE_OK ){
+      return compressConvertErrCode(rc);
     }
     pCompress->bBegin = 1;
   }
   sqlite3_stmt *stmt = NULL;
   const char *sql = "DELETE FROM vfs_pages WHERE pageno > ?;";
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    sqlite3_bind_int(stmt, 1, pgno);
-    if( sqlite3_step(stmt)!=SQLITE_DONE ){
-      rc = SQLITE_IOERR_TRUNCATE;
-    }
-    sqlite3_finalize(stmt);
+  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Truncate page wrong while prepare stat, pgno:%d", pgno);
+    return compressConvertErrCode(rc);
   }
-  return rc;
+  sqlite3_bind_int(stmt, 1, pgno);
+  rc = sqlite3_step(stmt);
+  sqlite3_finalize(stmt);
+  if( rc!=SQLITE_DONE ){
+    sqlite3_log(rc, "Truncate page wrong while step stat, pgno:%d", pgno);
+    return compressConvertErrCode(rc);
+  }
+  return SQLITE_OK;
 }
 
 /*
@@ -661,58 +745,74 @@ static int compressWrite(sqlite3_file *pFile, const void *pBuf, int iAmt, sqlite
   assert( iAmt>0 );
   CompressFile *pCompress = (CompressFile *)pFile;
   sqlite3 *db = pCompress->pDb;
-  int rc = getCompressPgsize(db, &pCompress->page_size);
+  int rc = getCompressPgsize(db, &pCompress->pageSize);
   if( rc!=SQLITE_OK ){
-    return SQLITE_IOERR_WRITE;
+    sqlite3_log(rc, "Missing pgsz(%d), write ofst:%lld, iAmt:%d, flags:%d", pCompress->pageSize,
+      iOfst, iAmt, pCompress->openFlags);
+    return rc;
   }
 
-  if( pCompress->page_size<=0 && iAmt >= 512 && iAmt <= 64*1024 && !(iAmt & (iAmt - 1)) ){
+  if( pCompress->pageSize<=0 && iAmt >= 512 && iAmt <= 64*1024 && !(iAmt & (iAmt - 1)) ){
     // new compress db need set orignal db's pagesize
     rc = setCompressPgsize(db, iAmt);
     if( rc!=SQLITE_OK ){
-      return SQLITE_IOERR_WRITE;
+      sqlite3_log(rc, "Save page size(%d) wrong, ofst:%lld, flags:%d", iAmt, iOfst, pCompress->openFlags);
+      return rc;
     }
-    pCompress->page_size = iAmt;
+    pCompress->pageSize = iAmt;
+  }
+  int pgsize = pCompress->pageSize;
+  if( pgsize==0 ){
+    sqlite3_log(SQLITE_IOERR_WRITE, "Zero pageSize in compressWrite, iAmt:%d, iOfst:%lld", iAmt, iOfst);
+    return SQLITE_IOERR_WRITE;
   }
-  int pgsize = pCompress->page_size;
   int pgno = iOfst / pgsize + 1;
   if( iAmt!=pgsize || iOfst % pgsize!=0 ){
+    sqlite3_log(SQLITE_IOERR_WRITE, "Mismatch info, iAmt(%d), pgsz(%d), offset:%ld", iAmt, pgsize, iOfst);
     return SQLITE_IOERR_WRITE;
   }
 
-  int max_compress_size = compressLen(iAmt, pCompress->compression);
-  if( max_compress_size<=0 ){
+  int maxSize = compressLen(iAmt, pCompress->compression);
+  if( maxSize<=0 ){
+    sqlite3_log(SQLITE_IOERR_WRITE, "Get compress size(%d) wrong, compression(%d)", maxSize, pCompress->compression);
     return SQLITE_IOERR_WRITE;
   }
-  u8 *compressed_data = sqlite3_malloc(max_compress_size);
-  if( compressed_data==NULL ){
+  u8 *tmpData = sqlite3_malloc(maxSize);
+  if( tmpData==NULL ){
+    sqlite3_log(SQLITE_NOMEM, "Malloc size(%d) wrong", maxSize);
     return SQLITE_NOMEM;
   }
-  int compress_data_len = 0;
-  if( compressBuf(compressed_data, max_compress_size, &compress_data_len, pBuf, iAmt, pCompress->compression) ){
-    sqlite3_free(compressed_data);
+  int len = 0;
+  if( compressBuf(tmpData, maxSize, &len, pBuf, iAmt, pCompress->compression) ){
+    sqlite3_free(tmpData);
+    sqlite3_log(SQLITE_IOERR_WRITE, "Compress buf wrong, pgno(%d), amt(%d), ofst(%lld)", pgno, iAmt, iOfst);
     return SQLITE_IOERR_WRITE;
   }
   if( pCompress->bBegin!=1 ){
-    if( sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL)!=SQLITE_OK ){
-      sqlite3_free(compressed_data);
-      return SQLITE_IOERR_WRITE;
+    if( (rc = sqlite3_exec(db, "BEGIN;", NULL, NULL, NULL))!=SQLITE_OK ){
+      sqlite3_free(tmpData);
+      sqlite3_log(rc, "Begin transaction to insert compressed page wrong, pgno(%d), ofst(%lld)", pgno, iOfst);
+      return compressConvertErrCode(rc);
     }
     pCompress->bBegin = 1;
   }
   sqlite3_stmt *stmt = NULL;
   const char *sql = "INSERT OR REPLACE INTO vfs_pages(data, pageno) VALUES (?,?);";
-  if( sqlite3_prepare_v2(db, sql, -1, &stmt, NULL)==SQLITE_OK ){
-    sqlite3_bind_blob(stmt, 1, compressed_data, compress_data_len, SQLITE_STATIC);
-    sqlite3_bind_int(stmt, 2, pgno);
-    if( sqlite3_step(stmt)!=SQLITE_DONE ){
-      sqlite3_free(compressed_data);
-      sqlite3_finalize(stmt);
-      return SQLITE_IOERR_WRITE;
-    }
-    sqlite3_finalize(stmt);
+  rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_free(tmpData);
+    sqlite3_log(rc, "Prepare stat to insert page wrong, pgno(%d), ofst(%lld)", pgno, iOfst);
+    return compressConvertErrCode(rc);
+  }
+  sqlite3_bind_blob(stmt, 1, tmpData, len, SQLITE_STATIC);
+  sqlite3_bind_int(stmt, 2, pgno);
+  rc = sqlite3_step(stmt);
+  sqlite3_finalize(stmt);
+  sqlite3_free(tmpData);
+  if( rc!=SQLITE_DONE ){
+    sqlite3_log(rc, "Compress db insert page wrong, pgno(%d), ofst(%lld)", pgno, iOfst);
+    return compressConvertErrCode(rc);
   }
-  sqlite3_free(compressed_data);
   return SQLITE_OK;
 }
 
@@ -731,71 +831,85 @@ static int compressRead(sqlite3_file *pFile, void *pBuf, int iAmt, sqlite_int64
   }
   (void)memset_s(pBuf, iAmt, 0, iAmt);
   sqlite3 *db = pCompress->pDb;
-  int rc = getCompressPgsize(db, &pCompress->page_size);
-  if( rc!=SQLITE_OK || pCompress->page_size==0 ){
+  int rc = getCompressPgsize(db, &pCompress->pageSize);
+  if( rc!=SQLITE_OK || pCompress->pageSize==0 ){
+    sqlite3_log(SQLITE_WARNING_DUMP, "Missing pgsz(%d), read ofst(%lld), amt(%d), flags(%d)", pCompress->pageSize,
+      iOfst, iAmt, pCompress->openFlags);
     return SQLITE_IOERR_SHORT_READ;
   }
-  int pgsize = pCompress->page_size;
+  int pgsize = pCompress->pageSize;
   int pgno = iOfst / pgsize + 1;
   int dataidx = iOfst % pgsize;
   sqlite3_stmt *stmt = NULL;
   const char *sql = "SELECT data, length(data) FROM vfs_pages WHERE pageno=?;";
   const void *data = NULL;
-  int data_len = 0;
+  int dataLen = 0;
   rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
   if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Prepare to get compressed page(%d) wrong, ofst(%lld)", pgno, iOfst);
     return SQLITE_CORRUPT;
   }
-  u8 *decompressed_data = NULL;
+  u8 *decompressedData = NULL;
   if( pgsize!=iAmt ){
-    decompressed_data = sqlite3_malloc(pgsize);
-    if( decompressed_data==NULL ){
+    decompressedData = sqlite3_malloc(pgsize);
+    if( decompressedData==NULL ){
       sqlite3_finalize(stmt);
+      sqlite3_log(SQLITE_NOMEM, "Malloc for decompress size(%d) wrong, amt(%d), ofst(%lld)", pgsize, iAmt, iOfst);
       return SQLITE_NOMEM;
     }
   }else{
-    decompressed_data = (u8*)pBuf;
+    decompressedData = (u8*)pBuf;
   }
 
-  int decompress_data_len = 0;
+  int decompressLen = 0;
   sqlite3_bind_int(stmt, 1, pgno);
   rc = sqlite3_step(stmt);
   if( rc==SQLITE_ROW ){
     data = sqlite3_column_blob(stmt, 0);
     if( data==NULL ){
       rc = SQLITE_IOERR_SHORT_READ;
-      goto failed;
+      sqlite3_log(rc, "Get compress page(%d) wrong, empty data, amt(%d), ofst(%lld)", pgno, iAmt, iOfst);
+      goto END_OUT;
     }
-    data_len = sqlite3_column_int(stmt, 1);
-    if( data_len==0 ){
+    dataLen = sqlite3_column_int(stmt, 1);
+    if( dataLen==0 ){
       rc = SQLITE_IOERR_SHORT_READ;
-      goto failed;
+      sqlite3_log(rc, "Get compress page(%d) wrong, short data, amt(%d), ofst(%lld)", pgno, iAmt, iOfst);
+      goto END_OUT;
     }
-    if( decompressBuf(decompressed_data, pgsize, &decompress_data_len, data, data_len,
-      pCompress->compression)!=SQLITE_OK ){
+    if( decompressBuf(decompressedData, pgsize, &decompressLen, data, dataLen, pCompress->compression)!=SQLITE_OK ){
       rc = SQLITE_IOERR_SHORT_READ;
-      goto failed;
+      sqlite3_log(rc, "Decompress page(%d) wrong, compression(%d), len(%d), amt(%d), ofst(%lld)", pgno,
+        (int)pCompress->compression, dataLen, iAmt, iOfst);
+      goto END_OUT;
     }
-    if( decompress_data_len!=pgsize ){
+    if( decompressLen!=pgsize ){
       rc = SQLITE_IOERR_SHORT_READ;
-      goto failed;
+      sqlite3_log(rc, "Decompress page(%d) wrong, size(%d), pgsz(%d), amt(%d), ofst(%lld)", pgno,
+        decompressLen, pgsize, iAmt, iOfst);
+      goto END_OUT;
     }
     if( pgsize!=iAmt ){
-      rc = memcpy_s(pBuf, iAmt, decompressed_data+dataidx, iAmt);
+      rc = memcpy_s(pBuf, iAmt, decompressedData+dataidx, iAmt);
       if( rc!=SQLITE_OK ){
         rc = SQLITE_IOERR_SHORT_READ;
-        goto failed;
+        sqlite3_log(rc, "Copy decompress page(%d) wrong, size(%d), pgsz(%d), amt(%d), ofst(%lld)", pgno,
+          decompressLen, pgsize, iAmt, iOfst);
+        goto END_OUT;
       }
     }
     rc = SQLITE_OK;
   }else if( rc==SQLITE_DONE ){
-    rc = SQLITE_IOERR_SHORT_READ;
+    rc = SQLITE_BUSY;
+    sqlite3_log(SQLITE_WARNING_DUMP, "Missing page(%d) while try read, may busy now", pgno);
+  }else if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Got page(%d) from compress db wrong", pgno);
   }
 
- failed:
+END_OUT:
   sqlite3_finalize(stmt);
   if( pgsize!=iAmt ){
-    sqlite3_free(decompressed_data);
+    sqlite3_free(decompressedData);
   }
 
   return rc;
@@ -808,22 +922,28 @@ static int compressClose(sqlite3_file *pFile){
   assert( pFile );
   CompressFile *pCompress = (CompressFile *)pFile;
   sqlite3 *db = pCompress->pDb;
+  sqlite3_file *pSubFile = ORIGFILE(pFile);
   int rc = compressSync(pFile, 0);
   if( rc!=SQLITE_OK ){
-    return rc;
+    sqlite3_log(SQLITE_WARNING_DUMP, "Sync wrong while close compress file, rc:%d", rc);
   }
   if( pCompress->bOutterDbOpen ){
     if( db!=NULL ){
       rc = sqlite3_close_v2(db);
       if( rc!=SQLITE_OK ){
-        return rc;
+        sqlite3_log(rc, "compress db close wrong");
+        return compressConvertErrCode(rc);
       }
       pCompress->pDb = NULL;
     }
   }
+  if( pCompress->pLockFd ){
+    pSubFile->pMethods->xClose(pCompress->pLockFd);
+    sqlite3_free(pCompress->pLockFd);
+    pCompress->pLockFd = NULL;
+  }
   if( pCompress->bSubDbOpen ){
-    pFile = ORIGFILE(pFile);
-    rc = pFile->pMethods->xClose(pFile);
+    rc = pSubFile->pMethods->xClose(pSubFile);
   }
   return rc;
 }
@@ -888,6 +1008,69 @@ static int compressUnfetch(sqlite3_file *pFile, sqlite3_int64 iOfst, void *pPage
   return SQLITE_OK;
 }
 
+static int compressOpenLockFile(sqlite3_vfs *pVfs, const char *zName, int flags, sqlite3_file **pFile){
+  const char *walPath = sqlite3_filename_wal(zName);
+  const char *lockPath = walPath + strlen(walPath) + 1;  // For compress vfs, lock path place at this position
+  int isLockExist = 0;
+  int rc = ORIGVFS(pVfs)->xAccess(ORIGVFS(pVfs), lockPath, SQLITE_ACCESS_READWRITE, &isLockExist);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Access lock file failed, path:%s", lockPath);
+    return rc;
+  }
+  if( isLockExist==0 && ((flags&SQLITE_OPEN_CREATE)==0) ){
+    sqlite3_log(SQLITE_WARNING_NOTCOMPRESSDB, "Check lock wrong, compress db should have lock file");
+    return SQLITE_WARNING_NOTCOMPRESSDB;
+  }
+  if( pVfs->szOsFile<=(int)sizeof(CompressFile) ){
+    sqlite3_log(SQLITE_CANTOPEN, "Unexpected file handle size(%d), vfs:%s", pVfs->szOsFile, pVfs->zName);
+    return SQLITE_CANTOPEN;
+  }
+  sqlite3_file *pLockFd = sqlite3_malloc(pVfs->szOsFile - sizeof(CompressFile));
+  if( pLockFd==NULL ){
+    rc = SQLITE_NOMEM;
+    sqlite3_log(rc, "Malloc file handle for lock wrong, size(%d)", (int)(pVfs->szOsFile - sizeof(CompressFile)));
+    return rc;
+  }
+  memset(pLockFd, 0, pVfs->szOsFile - sizeof(CompressFile));
+  rc = ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), lockPath, pLockFd, flags, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Compress vfs lock file open wrong, path:%s, flag(%d)", lockPath, flags);
+    sqlite3_free(pLockFd);
+    return rc;
+  }
+  *pFile = pLockFd;
+  return rc;
+}
+
+static int compressDbInitCompression(CompressFile *pCompress, sqlite3 *db, u32 compression){
+  const char *pragmaStr = "PRAGMA checksum_persist_enable=ON;PRAGMA page_size=4096;PRAGMA auto_vacuum=INCREMENTAL;";
+  int rc = sqlite3_exec(db, pragmaStr, NULL, NULL, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Config compress db wrong, name");
+    return SQLITE_CANTOPEN;
+  }
+  const char *initCompressionStr = "CREATE TABLE IF NOT EXISTS vfs_compression(compression INTEGER, pagesize INTEGER);";
+  rc = sqlite3_exec(db, initCompressionStr, NULL, NULL, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Configure compress db wrong, create vfs_compression failed");
+    return SQLITE_CANTOPEN;
+  }
+  char compressionSql[COMPRESSION_SQL_MAX_LENGTH] = {0};
+  if( sprintf_s(compressionSql, COMPRESSION_SQL_MAX_LENGTH,
+    "INSERT OR IGNORE INTO vfs_compression(compression, pagesize) VALUES (%u, 0);", compression)<=0 ){
+    sqlite3_log(rc, "Concatenate config stat wrong, compression(%u)", compression);
+    return SQLITE_CANTOPEN;
+  }
+  rc = sqlite3_exec(db, compressionSql, NULL, NULL, NULL);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Config compress db wrong, insert compression(%u) failed", compression);
+    return SQLITE_CANTOPEN;
+  }
+  pCompress->compression = compression;
+  pCompress->pageSize = 0;
+  return SQLITE_OK;
+}
+
 /*
 ** Open a compress file.If this file is not a journal or wal file,
 ** it will open a OutterDB and create vfs_pages and vfs_compression table
@@ -902,87 +1085,106 @@ static int compressOpen(
 ){
   sqlite3_file *pSubFile = ORIGFILE(pFile);
   CompressFile *pCompress = (CompressFile *)pFile;
+  pCompress->openFlags = flags;
   int rc = SQLITE_OK;
   if( !(flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB)) ){
     return ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pFile, flags, pOutFlags);
   }
   rc = ORIGVFS(pVfs)->xOpen(ORIGVFS(pVfs), zName, pSubFile, flags, pOutFlags);
   if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Compress file open wrong, path:%s, flags:%d", zName, flags);
     return rc;
   }
   pCompress->bSubDbOpen = 1;
   sqlite3_int64 fileSize = 0;
+  u8 isExist = 0;
+  sqlite3 *db = NULL;
   rc = pSubFile->pMethods->xFileSize(pSubFile, &fileSize);
   if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Calculate compress file size wrong, path:%s", zName);
     rc = SQLITE_CANTOPEN;
-    goto open_end;
+    goto END_OUT;
   }
 
+  rc = compressOpenLockFile(pVfs, zName, flags, &pCompress->pLockFd);
+  if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Compress vfs lock file open wrong, path:%s, flag(%d)", zName, flags);
+    goto END_OUT;
+  }
   pFile->pMethods = &compress_io_methods;
-  rc = sqlite3_open_v2(zName, &pCompress->pDb, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, ORIGVFS(pVfs)->zName);
+  rc = sqlite3_open_v2(zName, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, CKSM_VFS_NAME);
   if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Open compress db wrong, name:%s", zName);
     rc = SQLITE_CANTOPEN;
-    goto open_end;
+    goto END_OUT;
   }
-  pCompress->bOutterDbOpen = 1;
-  sqlite3 *db = pCompress->pDb;
-  const char *pre_pragma = "PRAGMA page_size=4096;"\
-    "PRAGMA auto_vacuum=INCREMENTAL;PRAGMA journal_mode=OFF;";
   sqlite3_busy_timeout(db, 2000);  // Set time out:2s
-  rc = sqlite3_exec(db, pre_pragma, NULL, NULL, NULL);
+  rc = sqlite3_exec(db, "PRAGMA journal_mode=OFF;", NULL, NULL, NULL);
   if( rc!=SQLITE_OK ){
-    rc = SQLITE_CANTOPEN;
-    goto open_end;
+    sqlite3_log(rc, "Config compress db journal mode wrong, name:%s", zName);
+    goto END_OUT;
+  }
+  pCompress->bOutterDbOpen = 1;
+  rc = tableExists(db, "vfs_compression", &isExist);
+  if( rc!=SQLITE_OK ) {
+    goto END_OUT;
   }
-  if( tableExists(db, "vfs_compression") ){
-    getCompression(db, pCompress);
+  if( isExist ){
+    rc = getCompression(db, pCompress);
+    if( rc!=SQLITE_OK ){
+      goto END_OUT;
+    }
     if( pCompress->compression!=COMPRESSION_BROTLI && pCompress->compression!=COMPRESSION_ZSTD ){
       rc = SQLITE_CANTOPEN;
-      goto open_end;
+      sqlite3_log(rc, "Unrecognized compression(%d), name:%s", pCompress->compression, zName);
+      goto END_OUT;
     }
-    if( loadCompressAlgorithmExtension(pCompress->compression)==SQLITE_ERROR ){
+    if( loadCompressAlgorithmExtension(pCompress->compression)!=SQLITE_OK ){
       rc = SQLITE_CANTOPEN;
-      goto open_end;
+      goto END_OUT;
     }
   }else if( flags&SQLITE_OPEN_MAIN_DB && fileSize!=0 ){
     rc = SQLITE_WARNING_NOTCOMPRESSDB;
     sqlite3_log(rc, "open compress database go wrong, it should be a compressed db");
-    goto open_end;
+    goto END_OUT;
   }else{
     if( loadCompressAlgorithmExtension(COMPRESSION_UNDEFINED)==SQLITE_ERROR ){
       rc = SQLITE_CANTOPEN;
-      goto open_end;
-    }
-    const char *init_compression_sql = "CREATE TABLE vfs_compression (compression INTEGER, pagesize INTEGER);";
-    rc = sqlite3_exec(db, init_compression_sql, NULL, NULL, NULL);
-    if( rc!=SQLITE_OK ){
-      rc = SQLITE_CANTOPEN;
-      goto open_end;
-    }
-    char set_compression_sql[COMPRESSION_SQL_MAX_LENGTH] = {0};
-    if( sprintf_s(set_compression_sql, COMPRESSION_SQL_MAX_LENGTH,
-      "INSERT INTO vfs_compression(compression, pagesize) VALUES (%u, 0);", g_compress_algo_load)<=0 ){
-      rc = SQLITE_CANTOPEN;
-      goto open_end;
+      goto END_OUT;
     }
-    rc = sqlite3_exec(db, set_compression_sql, NULL, NULL, NULL);
+    rc = compressDbInitCompression(pCompress, db, g_compress_algo_load);
     if( rc!=SQLITE_OK ){
-      rc = SQLITE_CANTOPEN;
-      goto open_end;
+      sqlite3_log(rc, "Init compression info wrong, name:%s", zName);
+      goto END_OUT;
     }
-    pCompress->compression = g_compress_algo_load;
-    pCompress->page_size = 0;
   }
-  if( tableExists(db, "vfs_pages") ){
-    goto open_end;
+  rc = tableExists(db, "vfs_pages", &isExist);
+  if( rc!=SQLITE_OK ){
+    goto END_OUT;
+  }
+  if( isExist ){
+    goto END_OUT;
   }
-  const char *create_sql = "CREATE TABLE vfs_pages (pageno INTEGER PRIMARY KEY, data BLOB NOT NULL);";
-  rc = sqlite3_exec(db, create_sql, NULL, NULL, NULL);
+  const char *pageDdlStr = "CREATE TABLE IF NOT EXISTS vfs_pages (pageno INTEGER PRIMARY KEY, data BLOB NOT NULL);";
+  rc = sqlite3_exec(db, pageDdlStr, NULL, NULL, NULL);
   if( rc!=SQLITE_OK ){
+    sqlite3_log(rc, "Config compress db wrong, name:%s, create page table failed", zName);
     rc = SQLITE_CANTOPEN;
   }
 
- open_end:
+END_OUT:
+  if( rc==SQLITE_OK ){
+    pCompress->pDb = db;
+  }else{
+    if( db!=NULL ){
+      sqlite3_close_v2(db);
+    }
+    if( pCompress->pLockFd ){
+      pSubFile->pMethods->xClose(pCompress->pLockFd);
+      sqlite3_free(pCompress->pLockFd);
+      pCompress->pLockFd = NULL;
+    }
+  }
   return rc;
 }
 
diff --git a/src/sqlite3.c b/src/sqlite3.c
index 394e6bd..f220899 100644
--- a/src/sqlite3.c
+++ b/src/sqlite3.c
@@ -3661,6 +3661,12 @@ SQLITE_API int sqlite3_set_authorizer(
 #define SQLITE_COPY                  0   /* No longer used */
 #define SQLITE_RECURSIVE            33   /* NULL            NULL            */
 
+#ifdef SQLITE_ENABLE_BINLOG
+#define SQLITE_SEARCHABLE_DELETE            101   /* NULL            NULL            */
+#define SQLITE_SEARCHABLE_INSERT            102   /* NULL            NULL            */
+#define SQLITE_SEARCHABLE_UPDATE            103   /* NULL            NULL            */
+#endif/* SQLITE_ENABLE_BINLOG */
+
 /*
 ** CAPI3REF: Tracing And Profiling Functions
 ** METHOD: sqlite3
@@ -5363,7 +5369,7 @@ SQLITE_API int sqlite3_set_droptable_handle(sqlite3*, void (*xFunc)(sqlite3*,con
 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
 
 #ifdef SQLITE_ENABLE_BINLOG
-SQLITE_API int sqlite3_is_support_binlog(void);
+SQLITE_API int sqlite3_is_support_binlog(const char *notUsed);
 
 SQLITE_API int sqlite3_replay_binlog(sqlite3 *srcDb, sqlite3 *destDb);
 
@@ -5375,6 +5381,7 @@ typedef struct BinlogSearchResult {
   int fileIndex;
   sqlite3_uint64 readPos;
   char **nameAndValues; // pointer to the row data in record format
+  int *colTypes;        // array of SQL column types for each column
 } BinlogSearchResult;
 
 typedef struct BinlogSearchResultSet{
@@ -5410,8 +5417,10 @@ SQLITE_API int sqlite3_set_monitor_config_binlog(sqlite3 *srcDb, MonitorTablesCo
 SQLITE_API int sqlite3_set_xChange_callback_binlog(sqlite3 *srcDb, void (*xChangeCallback)(const char *dbPath, 
   char *tableName));
 
-SQLITE_API int sqlite3_set_json_parse_callback_binlog(sqlite3 *srcDb, 
+SQLITE_API int sqlite3_set_json_parse_callback_binlog(sqlite3 *srcDb,
   MonitorTablesConfig*(*jsonParseCallback)(const char *dbPath));
+SQLITE_API int sqlite3_free_json_parse_callback_binlog(sqlite3 *srcDb,
+  int(*freeJsonParseCallback)(MonitorTablesConfig *config));
 // get data from binlog file
 SQLITE_API int sqlite3_get_search_data_binlog(sqlite3 *srcDb, sqlite3 *destDb, BinlogSearchResultSet **rs);
 // free result data
@@ -15104,6 +15113,14 @@ typedef INT16_TYPE LogEst;
 #define LARGEST_UINT64 (0xffffffff|(((u64)0xffffffff)<<32))
 #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
 
+/*
+** Macro SMXV(n) return the maximum value that can be held in variable n,
+** assuming n is a signed integer type.  UMXV(n) is similar for unsigned
+** integer types.
+*/
+#define SMXV(n) ((((i64)1)<<(sizeof(n)*8-1))-1)
+#define UMXV(n) ((((i64)1)<<(sizeof(n)*8))-1)
+
 /*
 ** Round up a number to the next larger multiple of 8.  This is used
 ** to force 8-byte alignment on 64-bit architectures.
@@ -17708,8 +17725,9 @@ typedef struct CodecParameter {
 #define SQLITE_UUID_BLOB_LENGTH 16
  
 typedef enum {
-  ROW = 0,
-  ROW_FOR_SEARCH = 2
+  ROW = 1,
+  ROW_FOR_SEARCH = 2,
+  READ_FOR_SEARCH = 3
 } Sqlite3BinlogMode;
  
 typedef enum {
@@ -17814,9 +17832,10 @@ typedef struct Sqlite3BinlogConfig {
 
 void (*xChangeCallback)(const char *dbPath, char *tableName);
 MonitorTablesConfig*(*jsonParseCallback)(const char *dbPath);
+int(*freeJsonParseCallback)(MonitorTablesConfig *config);
 
 typedef struct BinlogRow {
-  int op;                // one of the SQLITE_INSERT, SQLITE_UPDATE, SQLITE_DELETE
+  int op;                // one of the SQLITE_INSERT, SQLITE_UPDATE, SQLITE_DELETE,SQLITE_SEARCH, SQLITE_DELETE_SEARCH 
   sqlite3_int64 rowid;   // rowid of changed data, -1 for without rowid table
   sqlite3_uint64 nData;  // size of pData
   int nZero;             // extra zero bytes at the end of pData
@@ -17856,6 +17875,8 @@ typedef struct Sqlite3BinlogHandle {
   void (*xLogFullCallback)(void *pCtx, u16 currentCount, const char *dbPath);
   void (*xChangeCallback)(const char *dbPath, char *tabelName);
   MonitorTablesConfig* (*jsonParseCallback)(const char *dbPath);
+  int (*freeJsonParseCallback)(MonitorTablesConfig *config);
+  Table* pTable;
   BinlogInstanceT *binlogConn;
   BinlogApi binlogApi;
   u8 isSkipTrigger;
@@ -17920,7 +17941,7 @@ SQLITE_PRIVATE Table *sqlite3BinlogFindTable(BtCursor *pC);
 SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor, const BinlogRow *pRow);
 SQLITE_PRIVATE void sqlite3FreeBinlogRowData(Vdbe *p);
 SQLITE_PRIVATE char *sqlite3BinlogGetNthCol(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol);
-SQLITE_PRIVATE char *sqlite3BinlogGetNthColForSearch(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol);
+SQLITE_PRIVATE char *sqlite3BinlogGetNthColForSearch(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol, int *pColType);
 /************** Binlog typedef in sqlite3 END ************************************/
 #endif
 
@@ -19154,7 +19175,7 @@ struct AggInfo {
                           ** from source tables rather than from accumulators */
   u8 useSortingIdx;       /* In direct mode, reference the sorting index rather
                           ** than the source table */
-  u16 nSortingColumn;     /* Number of columns in the sorting index */
+  u32 nSortingColumn;     /* Number of columns in the sorting index */
   int sortingIdx;         /* Cursor number of the sorting index */
   int sortingIdxPTab;     /* Cursor number of pseudo-table */
   int iFirstReg;          /* First register in range for aCol[] and aFunc[] */
@@ -19163,8 +19184,8 @@ struct AggInfo {
     Table *pTab;             /* Source table */
     Expr *pCExpr;            /* The original expression */
     int iTable;              /* Cursor number of the source table */
-    i16 iColumn;             /* Column number within the source table */
-    i16 iSorterColumn;       /* Column number in the sorting index */
+    int iColumn;             /* Column number within the source table */
+    int iSorterColumn;       /* Column number in the sorting index */
   } *aCol;
   int nColumn;            /* Number of used entries in aCol[] */
   int nAccumulator;       /* Number of columns that show through to the output.
@@ -23985,6 +24006,9 @@ struct Vdbe {
 /* begin of binlog related field */
   StmtType stmtType;              /* Type of binlog statement */
   BinlogDMLData *pBinlogDMLData;  /* Data for binlog row-based DML statement */
+  char **changedCols;
+  int changedColsCount;
+  int isInsertOrDelete;
   const char *pDataTmp;           /* Temporary binlog data for op_notfound */
   sqlite3_uint64 nDataTmp;        /* Size of pDataTmp */
 /* end of binlog related field */
@@ -42437,8 +42461,8 @@ static void enableDbFileDelMonitor(int32_t fd)
     }
     flags |= HMFS_MONITOR_FL;
     ret = ioctl(fd, HMFS_IOCTL_HW_SET_FLAGS, &flags);
-    if (ret < 0) {
-        sqlite3_log(SQLITE_WARNING, "Fd %d enable del monitor go wrong, errno = %d", fd, errno);
+    if (ret < 0 && errno != EACCES) {
+        sqlite3_log(SQLITE_WARNING, "Flags %u fd %d enable del monitor go wrong, errno = %d", flags, fd, errno);
     }
 }
 
@@ -45858,6 +45882,24 @@ static int openDirectory(const char *zFilename, int *pFd){
   return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname);
 }
 
+#ifdef LOG_DUMP
+static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow);
+static int dirSyncTimeCostAlert(int fd, int fullSync, int dataOnly)
+{
+  sqlite3_int64 startTimeMs = 0;
+  sqlite3_int64 endTimeMs = 0;
+  // unixCurrentTimeInt64 always return OK
+  (void)unixCurrentTimeInt64(0, &startTimeMs);
+  int rc = full_fsync(fd, 0, 0);
+  (void)unixCurrentTimeInt64(0, &endTimeMs);
+  // 1500 ms
+  if (unlikely(endTimeMs >= (startTimeMs + 1500))) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "[SQLite]Dir sync takes %lld ms", endTimeMs - startTimeMs);
+  }
+  return rc;
+}
+#endif
+
 /*
 ** Make sure all writes to a particular file are committed to disk.
 **
@@ -45909,7 +45951,11 @@ static int unixSync(sqlite3_file *id, int flags){
             HAVE_FULLFSYNC, isFullsync));
     rc = osOpenDirectory(pFile->zPath, &dirfd);
     if( rc==SQLITE_OK ){
+#ifndef LOG_DUMP
       full_fsync(dirfd, 0, 0);
+#else
+      dirSyncTimeCostAlert(dirfd, 0, 0);
+#endif
       robust_close(pFile, dirfd, __LINE__);
     }else{
       assert( rc==SQLITE_CANTOPEN );
@@ -61618,6 +61664,8 @@ static void MetaDwrCheckVacuum(BtShared *pBt);
 static int MetaDwrRecoverAndBeginTran(Btree *pBt, int wrflag, int *pSchemaVersion);
 static int MetaDwrOpenAndCheck(Btree *pBt);
 static void MetaDwrDisable(Btree *pBt);
+static void MetaDwrDumpInfo(Pager *pPager);
+static void MetaDwrCommitUpdate(Pager *pPager);
 #define META_HEADER_CHANGED 1
 #define META_SCHEMA_CHANGED 2
 #define META_IN_RECOVERY 1
@@ -61895,6 +61943,9 @@ struct Pager {
   void *metaMapPage;
   int (*xGetMethod)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
 #endif
+#ifdef SQLITE_ENABLE_PAGE_COMPRESS
+  char *zLock;                /* Point to the lock filename, use to replace main db to lock */
+#endif
 };
 
 /*
@@ -63396,11 +63447,12 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
     sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
   }
 #ifdef SQLITE_META_DWR
-  if (bCommit && pPager->metaChanged != 0) {
-    sqlite3BeginBenignMalloc();
-    (void)MetaDwrWriteHeader(pPager, pPager->metaHdr);
-    sqlite3EndBenignMalloc();
-    pPager->metaChanged = 0;
+  if (bCommit) {
+    MetaDwrCommitUpdate(pPager);
+    if (pPager->metaChanged != 0) {
+      (void)MetaDwrWriteHeader(pPager, pPager->metaHdr);
+      pPager->metaChanged = 0;
+    }
   }
 #endif
   if( pagerUseWal(pPager) ){
@@ -66189,7 +66241,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
   ** specific formatting and order of the various filenames, so if the format
   ** changes here, be sure to change it there as well.
   **
-  ** Addition, in case of enable page compression, journal&WAL filename will add a file extension:"compress"
+  ** Addition, in case of enable page compression, journal&WAL filename will add a file extension:"compress",
+  ** lock file need an other setting
   ** So the final layout in memory is as follows:
   **
   **     Pager object                    (sizeof(Pager) bytes)
@@ -66203,13 +66256,16 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
   **     URI query parameters            (nUriByte bytes)
   **     Journal filename                (nPathname+16+1 bytes)
   **     WAL filename                    (nPathname+12+1 bytes)
+  **     Lock filename                   (nPathname+13+1 bytes)
   **     \0\0\0 terminator               (3 bytes)
   **
   */
   int fileExtSz = 0;
+  int lockExtSz = 0;
 #ifdef SQLITE_ENABLE_PAGE_COMPRESS
   if( sqlite3_stricmp(pVfs->zName, "compressvfs")==0 ){
     fileExtSz = 8;  /* 8 means the size of suffix:"compress" */
+    lockExtSz = 13;  /* 13 means the size of suffix:"-lockcompress" */
   }
 #endif
   assert( SQLITE_PTRSIZE==sizeof(Pager*) );
@@ -66225,6 +66281,9 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
     nPathname + 8 + fileExtSz + 1 +      /* Journal filename */
 #ifndef SQLITE_OMIT_WAL
     nPathname + 4 + fileExtSz + 1 +      /* WAL filename */
+#endif
+#ifdef SQLITE_ENABLE_PAGE_COMPRESS
+    nPathname + lockExtSz + 1 +          /* Lock filename */
 #endif
     3                                    /* Terminator */
   );
@@ -66293,6 +66352,19 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
     pPager->zWal = 0;
   }
 #endif
+
+#ifdef SQLITE_ENABLE_PAGE_COMPRESS
+  if( nPathname>0 ){
+    pPager->zLock = (char*)pPtr;
+    memcpy(pPtr, zPathname, nPathname);   pPtr += nPathname;
+    if( lockExtSz>0 ){  /* 13 means the size of string:"-lockcompress" */
+      memcpy(pPtr, "-lockcompress", 13);  pPtr += lockExtSz;
+    }
+                                          pPtr += 1;  /* Skip zero suffix */
+  }else{
+    pPager->zLock = 0;
+  }
+#endif
   (void)pPtr;  /* Suppress warning about unused pPtr value */
 
   if( nPathname ) sqlite3DbFree(0, zPathname);
@@ -77465,7 +77537,13 @@ static void zeroPage(MemPage *pPage, int flags){
   data[hdr+7] = 0;
   put2byte(&data[hdr+5], pBt->usableSize);
   pPage->nFree = (u16)(pBt->usableSize - first);
-  decodeFlags(pPage, flags);
+  /*
+  ** pPage might not be a btree page, it might be ovf/ptrmap/free page
+  ** and decodeFlags() return SQLITE_CORRUPT, but no harm is done by this
+  */
+  if( decodeFlags(pPage, flags)==SQLITE_CORRUPT ){
+    sqlite3_log(SQLITE_WARNING_DUMP, "warn: zero page, might be ovf/ptrmap/free page:%d", pPage->pgno);
+  }
   pPage->cellOffset = first;
   pPage->aDataEnd = &data[pBt->pageSize];
   pPage->aCellIdx = &data[first];
@@ -77674,7 +77752,9 @@ static void pageReinit(DbPage *pData){
       ** But no harm is done by this.  And it is very important that
       ** btreeInitPage() be called on every btree page so we make
       ** the call for every page that comes in for re-initializing. */
-      btreeInitPage(pPage);
+      if( btreeInitPage(pPage)==SQLITE_CORRUPT ){
+        sqlite3_log(SQLITE_WARNING_DUMP, "warn: page reinit, might be ovf/ptrmap/free page:%d", pPage->pgno);
+      }
     }
   }
 }
@@ -78590,6 +78670,11 @@ static int lockBtree(BtShared *pBt){
     }
     if( nPage>nPageFile ){
       if( sqlite3WritableSchema(pBt->db)==0 ){
+        sqlite3_log(SQLITE_WARNING_DUMP, "sqlite3WritableSchema nPage %d nPageFile %d", nPage, nPageFile);
+#ifdef SQLITE_META_DWR
+        DumpLocksByPager(pBt->pPager);
+        MetaDwrDumpInfo(pBt->pPager);
+#endif
         rc = SQLITE_CORRUPT_BKPT;
         goto page1_init_failed;
       }else{
@@ -93152,6 +93237,23 @@ static void vdbeInvokeSqllog(Vdbe *v){
 # define vdbeInvokeSqllog(x)
 #endif
 
+#ifdef SQLITE_ENABLE_BINLOG
+SQLITE_PRIVATE void FreeChangedCols(Vdbe *p){
+  sqlite3 *db = p->db;
+  p->isInsertOrDelete = 0;
+  if (p->changedCols==NULL) {
+    return;
+  }
+  for (int i=0; i<p->changedColsCount; i++) {
+    sqlite3DbFree(db, p->changedCols[i]);
+    p->changedCols[i] = NULL;
+  }
+  sqlite3DbFree(db, p->changedCols);
+  p->changedCols = NULL;
+  p->changedColsCount = 0;
+}
+#endif
+
 /*
 ** Clean up a VDBE after execution but do not delete the VDBE just yet.
 ** Write any error messages into *pzErrMsg.  Return the result code.
@@ -93248,6 +93350,7 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
   }
 #endif
 #ifdef SQLITE_ENABLE_BINLOG
+  FreeChangedCols(p);
   sqlite3FreeBinlogRowData(p);
 #endif
   return p->rc & db->errMask;
@@ -93354,6 +93457,7 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
   }
 #endif
 #ifdef SQLITE_ENABLE_BINLOG
+  FreeChangedCols(p);
   sqlite3FreeBinlogRowData(p);
 #endif
 }
@@ -96091,8 +96195,9 @@ SQLITE_API int sqlite3_set_droptable_handle(sqlite3 *db, void (*xFunc)(sqlite3*,
 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
 
 #ifdef SQLITE_ENABLE_BINLOG
-SQLITE_API int sqlite3_is_support_binlog(void)
+SQLITE_API int sqlite3_is_support_binlog(const char *notUsed)
 {
+  (void)notUsed;
   return SQLITE_ERROR;
 }
 
@@ -96190,6 +96295,26 @@ SQLITE_API int sqlite3_set_json_parse_callback_binlog(sqlite3 *srcDb,
   return SQLITE_OK;
 }
 
+SQLITE_API int sqlite3_free_json_parse_callback_binlog(sqlite3 *srcDb,
+  int(*freeJsonParseCallback)(MonitorTablesConfig *config))
+{
+  if (srcDb == NULL) {
+    sqlite3_log(SQLITE_ERROR, "free json parse binlog parameter is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "free json parse binlog parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  if (srcDb->xBinlogHandle.freeJsonParseCallback != NULL) {
+      sqlite3_log(SQLITE_WARNING, "free json parse callback binlog freeJsonParseCallback is alreadySet");
+      return SQLITE_ERROR;
+  }
+  srcDb->xBinlogHandle.freeJsonParseCallback = freeJsonParseCallback;
+  return SQLITE_OK;
+}
+
+
 SQLITE_API int sqlite3_free_search_data_binlog(sqlite3 *srcDb, BinlogSearchResultSet **rs)
 {
   if (srcDb == NULL || rs == NULL || *rs == NULL) {
@@ -96240,9 +96365,13 @@ SQLITE_API int sqlite3_replay_binlog(sqlite3 *srcDb, sqlite3 *destDb)
   int rc = SQLITE_OK;
   sqlite3_mutex_enter(srcDb->mutex);
   rc = sqlite3BinlogReplay(srcDb, destDb);
-  if (rc != SQLITE_OK) {
+  if (rc != SQLITE_OK && rc != SQLITE_CORRUPT) {
     sqlite3_log(rc, "replay binlog err:%d", rc);
   }
+  if (rc == SQLITE_CORRUPT) {
+    rc = SQLITE_ERROR;
+    sqlite3_log(rc, "replay binlog err with not expect way");
+  }
   sqlite3_mutex_leave(srcDb->mutex);
   return rc;
 }
@@ -120799,7 +120928,9 @@ static void findOrCreateAggInfoColumn(
 ){
   struct AggInfo_col *pCol;
   int k;
+  int mxTerm = pParse->db->aLimit[SQLITE_LIMIT_COLUMN];
 
+  assert( mxTerm <= SMXV(i16) );
   assert( pAggInfo->iFirstReg==0 );
   pCol = pAggInfo->aCol;
   for(k=0; k<pAggInfo->nColumn; k++, pCol++){
@@ -120817,6 +120948,10 @@ static void findOrCreateAggInfoColumn(
     assert( pParse->db->mallocFailed );
     return;
   }
+  if( k>mxTerm ){
+    sqlite3ErrorMsg(pParse, "more than %d aggregate terms", mxTerm);
+    k = mxTerm;
+  }
   pCol = &pAggInfo->aCol[k];
   assert( ExprUseYTab(pExpr) );
   pCol->pTab = pExpr->y.pTab;
@@ -120850,6 +120985,7 @@ fix_up_expr:
   if( pExpr->op==TK_COLUMN ){
     pExpr->op = TK_AGG_COLUMN;
   }
+  assert( k <= SMXV(pExpr->iAgg) );
   pExpr->iAgg = (i16)k;
 }
 
@@ -120933,13 +121069,19 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
         ** function that is already in the pAggInfo structure
         */
         struct AggInfo_func *pItem = pAggInfo->aFunc;
+        int mxTerm = pParse->db->aLimit[SQLITE_LIMIT_COLUMN];
+        assert( mxTerm <= SMXV(i16) );
         for(i=0; i<pAggInfo->nFunc; i++, pItem++){
           if( pItem->pFExpr==pExpr ) break;
           if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
             break;
           }
         }
-        if( i>=pAggInfo->nFunc ){
+        if( i>mxTerm ){
+          sqlite3ErrorMsg(pParse, "more than %d aggregate terms", mxTerm);
+          i = mxTerm;
+          assert( i<pAggInfo->nFunc );
+        }else if( i>=pAggInfo->nFunc ){
           /* pExpr is original.  Make a new entry in pAggInfo->aFunc[]
           */
           u8 enc = ENC(pParse->db);
@@ -120991,6 +121133,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
         */
         assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
         ExprSetVVAProperty(pExpr, EP_NoReduce);
+        assert( i <= SMXV(pExpr->iAgg) );
         pExpr->iAgg = (i16)i;
         pExpr->pAggInfo = pAggInfo;
         return WRC_Prune;
@@ -125703,8 +125846,8 @@ static void attachFunc(
   if( rc==SQLITE_OK ){
     extern int sqlite3CodecAttach(sqlite3*, int, const void*, int);
     extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
-    int nKey;
-    char *zKey;
+    int nKey = 0;
+    char *zKey = NULL;
     int t = sqlite3_value_type(argv[2]);
     switch( t ){
       case SQLITE_INTEGER:
@@ -125721,14 +125864,7 @@ static void attachFunc(
         break;
 
       case SQLITE_NULL:
-        /* No key specified.  Use the key from URI filename, or if none,
-        ** use the key from the main database. */
-        if( sqlite3CodecQueryParameters(db, zName, zPath)==0 ){
-          sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
-          if( nKey || sqlite3BtreeGetRequestedReserve(db->aDb[0].pBt)>0 ){
-            rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
-          }
-        }
+        /* No key specified. just use null key. */
         break;
     }
   }
@@ -142163,6 +142299,8 @@ typedef int (*sqlite3_loadext_entry)(
 #define sqlite3_set_xChange_callback_binlog       sqlite3_api->set_xChange_callback
 /* set binlog json parse callback at client*/
 #define sqlite3_set_json_parse_callback_binlog    sqlite3_api->set_json_parse_callback
+/* free binlog json parse callback at client*/
+#define sqlite3_free_json_parse_callback_binlog    sqlite3_api->free_json_parse_callback
 /* get search data from binlog */
 #define sqlite3_get_search_data_binlog       sqlite3_api->get_search_data
 /* free search data from binlog */
@@ -159064,6 +159202,27 @@ SQLITE_PRIVATE void sqlite3Update(
   if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
   sqlite3BeginWriteOperation(pParse, pTrigger || hasFK, iDb);
 
+  #ifdef SQLITE_ENABLE_BINLOG
+    if (db->xBinlogHandle.mode == ROW_FOR_SEARCH) {
+      FreeChangedCols(v);
+      int changedColsCount = 0;
+      for (int l = 0; l<pTab->nCol; l++) {
+        if ( aXRef[l]>=0) changedColsCount++;
+      }
+      if (changedColsCount > 0) {
+        v->changedCols = sqlite3DbMallocZero(db, sizeof(char*) * changedColsCount);
+        if (v->changedCols) {
+          int colIndex = 0;
+          for (int l=0; l<pTab->nCol; l++) {
+            if (aXRef[l]>=0) {
+                v->changedCols[colIndex++] = sqlite3DbStrDup(db, pTab->aCol[l].zCnName);
+            }
+          }
+          v->changedColsCount = changedColsCount;
+        }
+      }
+    }
+  #endif
   /* Allocate required registers. */
   if( !IsVirtual(pTab) ){
     /* For now, regRowSet and aRegIdx[nAllIdx] share the same register.
@@ -184567,8 +184726,12 @@ int sqlite3IcuModuleInit(){
     sqlite3_log(SQLITE_ERROR, "load icu so failed");
     return SQLITE_ERROR;
   }
+  MUTEX_LOGIC( sqlite3_mutex *pMainMtx; )
+  MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
+  sqlite3_mutex_enter(pMainMtx);
   tokenModulePtr = (sqlite3Fts3IcuTokenizerModule_ptr)dlsym(g_library, "sqlite3Fts3IcuTokenizerModule");
   icuInitPtr = (sqlite3IcuInit_ptr)dlsym(g_library, "sqlite3IcuInit");
+  sqlite3_mutex_leave(pMainMtx);
   if( tokenModulePtr==NULL || icuInitPtr==NULL ){
     sqlite3_log(SQLITE_ERROR, "load icu init function failed");
     return SQLITE_ERROR;
@@ -184583,7 +184746,17 @@ SQLITE_PRIVATE int sqlite3IcuInitInner(sqlite3 *db)
   if( !icuEnable ){
     return SQLITE_OK;
   }
-  return icuInitPtr(db);
+  MUTEX_LOGIC( sqlite3_mutex *pMainMtx; )
+  MUTEX_LOGIC( pMainMtx = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN); )
+  sqlite3_mutex_enter(pMainMtx);
+  if ( icuInitPtr == NULL ) {
+    sqlite3_log(SQLITE_ERROR, "icu not load");
+    sqlite3_mutex_leave(pMainMtx);
+    return SQLITE_OK;
+  }
+  int rc = icuInitPtr(db);
+  sqlite3_mutex_leave(pMainMtx);
+  return rc;
 }
 /************** Continuing where we left off in main.c ***********************/
 #endif
@@ -188450,10 +188623,12 @@ opendb_out:
     db->eOpenState = SQLITE_STATE_SICK;
   }
 #ifdef SQLITE_ENABLE_DROPTABLE_CALLBACK
-  db->isDropTable = 0;
-  db->mDropTableName = NULL;
-  db->mDropSchemaName = NULL;
-  db->xDropTableHandle = NULL;
+  if( db!=0 ){
+    db->isDropTable = 0;
+    db->mDropTableName = NULL;
+    db->mDropSchemaName = NULL;
+    db->xDropTableHandle = NULL;
+  }
 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
 #ifdef SQLITE_ENABLE_BINLOG
   sqlite3BinlogReset(db);
@@ -208898,6 +209073,39 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
   return rc;
 }
 
+/*
+** Expression node pExpr is an MSR phrase. This function restarts pExpr
+** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned
+** if successful, or an SQLite error code otherwise.
+*/
+int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){
+  int rc = SQLITE_OK;
+  if( pExpr->bEof==0 ){
+    i64 iDocid = pExpr->iDocid;
+    fts3EvalRestart(pCsr, pExpr, &rc);
+    while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){
+      fts3EvalNextRow(pCsr, pExpr, &rc);
+      if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB;
+    }
+  }
+  return rc;
+}
+
+/*
+** If expression pExpr is a phrase expression that uses an MSR query,
+** restart it as a regular, non-incremental query. Return SQLITE_OK
+** if successful, or an SQLite error code otherwise.
+*/
+static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
+  TermOffsetCtx *p = (TermOffsetCtx*)ctx;
+  int rc = SQLITE_OK;
+  if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){
+    rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr);
+    pExpr->pPhrase->bIncr = 0;
+  }
+  return rc;
+}
+
 /*
 ** Implementation of offsets() function.
 */
@@ -208934,6 +209142,12 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
   sCtx.iDocid = pCsr->iPrevId;
   sCtx.pCsr = pCsr;
 
+  /* If a query restart will be required, do it here, rather than later of
+  ** after pointers to poslist buffers that may be invalidated by a restart
+  ** have been saved.  */
+  rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx);
+  if( rc!=SQLITE_OK ) goto offsets_out;
+
   /* Loop through the table columns, appending offset information to
   ** string-buffer res for each column.
   */
@@ -243644,7 +243858,7 @@ static void fts5DataRelease(Fts5Data *pData){
 static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
   Fts5Data *pRet = fts5DataRead(p, iRowid);
   if( pRet ){
-    if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
+    if( pRet->szLeaf<4 || pRet->szLeaf>pRet->nn ){
       p->rc = FTS5_CORRUPT;
       fts5DataRelease(pRet);
       pRet = 0;
@@ -250163,9 +250377,13 @@ static void fts5IndexIntegrityCheckSegment(
         p->rc = FTS5_CORRUPT;
       }else{
         iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
-        res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
-        if( res==0 ) res = nTerm - nIdxTerm;
-        if( res<0 ) p->rc = FTS5_CORRUPT;
+        if( iOff+nTerm>pLeaf->szLeaf ){
+          p->rc = FTS5_CORRUPT;
+        }else{
+          res = fts5Memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
+          if( res==0 ) res = nTerm - nIdxTerm;
+          if( res<0 ) p->rc = FTS5_CORRUPT;
+        }
       }
 
       fts5IntegrityCheckPgidx(p, pLeaf);
@@ -259128,6 +259346,21 @@ SQLITE_API int sqlite3_stmt_init(
 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
 /************************** End of sqlite3.c ******************************/
 
+#if SQLITE_OS_UNIX
+#include <unistd.h>
+#include <sys/syscall.h>
+static inline int OsGetTid(void)
+{
+#if defined(__linux__)
+    return (int)syscall(__NR_gettid);
+#elif defined(__APPLE__)
+    return (int)syscall(SYS_thread_selfid);
+#else
+    return 0;
+#endif
+}
+#endif
+
 #ifdef SQLITE_CKSUMVFS_STATIC
 extern sqlite3_file *cksmvfsGetOrigFile(sqlite3_file *file);
 #else
@@ -261762,7 +261995,12 @@ typedef struct MetaDwrHdr {
   u32 pageSz;
   u32 pageCnt;
   u64 dbFileInode;
-  u32 reserved[12];
+  u64 walShmFileIno;
+  u64 updateTimeStamp;
+  int updateTid;
+  int updatePid;
+  u32 walFrameCksum[2];
+  u32 reserved[4];
   u32 checkSum;
   u8 *zones;
   Pgno *pages;
@@ -262117,6 +262355,88 @@ static inline u64 CaculateMetaDwrWriteOffset(int pageSz, u32 idx, u8 zone) {
   return META_DWR_HEADER_PAGE_SIZE + pageSz * (idx * 2 + zone);
 }
 
+static void MetaDwrDumpInfo(Pager *pPager)
+{
+  MetaDwrHdr *hdr = pPager->metaHdr;
+  if (hdr != NULL) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr dbSize %u mxFrameInWal %u dbIno %lu walShmIno %lu",
+      hdr->dbSize, hdr->mxFrameInWal, hdr->dbFileInode, hdr->walShmFileIno);
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr updatestamp %ld updatetid %d updatepid %d",
+      hdr->updateTimeStamp, hdr->updateTid, hdr->updatePid);
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr ckSum0 %u ckSum1 %u", hdr->walFrameCksum[0], hdr->walFrameCksum[1]);
+  }
+#ifndef SQLITE_OMIT_WAL
+  if (!pagerUseWal(pPager)) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr ignore dump wal info");
+    return;
+  }
+  WalIndexHdr *pWalHdr = &pPager->pWal->hdr;
+  sqlite3_log(SQLITE_WARNING_DUMP, "wal ckSum0 %u ckSum1 %u maxFram %u nPage %u", pWalHdr->aFrameCksum[0],
+    pWalHdr->aFrameCksum[1], pWalHdr->mxFrame, pWalHdr->nPage);
+#if SQLITE_OS_UNIX
+  if (hdr == NULL || !hdr->checkFileId) {
+    return;
+  }
+  unixFile *fd = Sqlite3GetUnixFile(pPager->fd, hdr->checkFileId);
+  if (fd == NULL || fd->pInode == NULL) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr dump invalid db fd");
+    return;
+  }
+  if (fd->pShm == NULL || fd->pShm->pShmNode == NULL || fd->pShm->pShmNode->hShm < 0) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr dump invalid shm fd");
+    return;
+  }
+  struct stat sStat;
+  if (osFstat(fd->pShm->pShmNode->hShm, &sStat)) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr dump shm stat go wrong %d", errno);
+    return;
+  }
+  sqlite3_log(SQLITE_WARNING_DUMP, "MetaDwr shm ino %lld", (i64)sStat.st_ino);
+#endif
+#endif
+}
+
+static void MetaDwrCommitUpdate(Pager *pPager)
+{
+#if SQLITE_OS_UNIX
+  MetaDwrHdr *hdr = pPager->metaHdr;
+  if (hdr == NULL) {
+    return;
+  }
+  hdr->updateTid = OsGetTid();
+  hdr->updatePid = osGetpid();
+  struct timeval time;
+  gettimeofday(&time, NULL);
+  hdr->updateTimeStamp = time.tv_sec * 1000 + time.tv_usec / 1000;
+#ifndef SQLITE_OMIT_WAL
+  if (pagerUseWal(pPager)) {
+    WalIndexHdr *pWalHdr = &pPager->pWal->hdr;
+    if (pWalHdr->isInit) {
+      hdr->walFrameCksum[0] = pWalHdr->aFrameCksum[0];
+      hdr->walFrameCksum[1] = pWalHdr->aFrameCksum[1];
+    }
+  }
+#endif
+#if !defined(NDEBUG)
+  if (!hdr->checkFileId) {
+    return;
+  }
+  unixFile *fd = Sqlite3GetUnixFile(pPager->fd, hdr->checkFileId);
+  if (fd == NULL || fd->pShm == NULL || fd->pShm->pShmNode == NULL || fd->pShm->pShmNode->hShm < 0) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "update meta header invalid shm fd");
+    return;
+  }
+  struct stat sStat;
+  if (osFstat(fd->pShm->pShmNode->hShm, &sStat)) {
+    hdr->walShmFileIno = 0;
+    sqlite3_log(SQLITE_WARNING_DUMP, "update meta header stat go wrong %d", errno);
+    return;
+  }
+  hdr->walShmFileIno = sStat.st_ino;
+#endif
+#endif
+}
+
 static void MetaDwrUpdateHeaderDbInfo(BtShared *pBt) {
   MetaDwrHdr *hdr = pBt->pPager->metaHdr;
   // 28 offset: dbSize, freelist pageNo, freelist pages count, schema cookie
@@ -262272,7 +262592,10 @@ static int MetaDwrRestoreAllPages(Btree *pBt, const ScanPages *metaPages, MetaDw
       return rc;
     }
   }
-  hdr->pageCnt = metaPages->pageCnt;
+  hdr->pageCnt = i;
+  if (metaPages->pageCnt > META_DWR_MAX_PAGES) {
+    sqlite3_log(SQLITE_WARNING_DUMP, "Meta dwr restore pages %u out of range", metaPages->pageCnt);
+  }
   MetaDwrUpdateHeaderDbInfo(pBt->pBt);
   return rc;
 }
@@ -262337,11 +262660,12 @@ static int MetaDwrOpenFile(Pager *pPager, u8 openCreate) {
   if (pPager->metaMapPage == NULL) {
     sqlite3_int64 sz = META_DWR_HEADER_PAGE_SIZE;
     sqlite3OsFileControlHint(metaFd, SQLITE_FCNTL_CHUNK_SIZE, &sz);
-    sqlite3OsFileControlHint(metaFd, SQLITE_FCNTL_SIZE_HINT, &sz);
-    void *page = osMmap(0, META_DWR_HEADER_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
-                        ((unixFile *)metaFd)->h, 0);
-    if (page != MAP_FAILED) {
-      pPager->metaMapPage = page;
+    if (sqlite3OsFileControl(metaFd, SQLITE_FCNTL_SIZE_HINT, &sz) == SQLITE_OK) {
+      void *page = osMmap(0, META_DWR_HEADER_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+                          ((unixFile *)metaFd)->h, 0);
+      if (page != MAP_FAILED) {
+        pPager->metaMapPage = page;
+      }
     }
   }
 #endif /* SQLITE_OS_UNIX */
@@ -262718,7 +263042,7 @@ CHK_RESTORE_OUT:
 static int MetaDwrOpenAndCheck(Btree *pBt)
 {
   Pager *pPager = pBt->pBt->pPager;
-  if (pPager->memDb || pPager->readOnly || !IsConnectionValidForCheck(pPager)) {
+  if (pPager->memDb || pPager->readOnly || !IsOnlyOneConnection(pPager)) {
     return SQLITE_OK;
   }
 #ifdef SQLITE_HAS_CODEC
@@ -262763,7 +263087,7 @@ DWR_OPEN_OUT:
 static void MetaDwrDisable(Btree *pBt)
 {
   Pager *pPager = pBt->pBt->pPager;
-  if (pPager->metaFd == NULL || pPager->memDb || pPager->readOnly || !IsConnectionValidForCheck(pPager)) {
+  if (pPager->metaFd == NULL || pPager->memDb || pPager->readOnly || !IsOnlyOneConnection(pPager)) {
     return;
   }
 #ifdef SQLITE_HAS_CODEC
@@ -262789,19 +263113,6 @@ static void MetaDwrDisable(Btree *pBt)
 #endif /* SQLITE_META_DWR */
 
 #if SQLITE_OS_UNIX
-#include <unistd.h>
-#include <sys/syscall.h>
-static inline int OsGetTid(void)
-{
-#if defined(__linux__)
-    return (int)syscall(__NR_gettid);
-#elif defined(__APPLE__)
-    return (int)syscall(SYS_thread_selfid);
-#else
-    return 0;
-#endif
-}
-
 static u8 IsOnlyOneConnection(Pager *pPager)
 {
 #if SQLITE_OS_UNIX
@@ -262931,8 +263242,13 @@ static inline const char *FlockToName(int l_type)
 
 static int DumpProcessLocks(int fd, struct flock *lock, const char *lockName, char *dumpBuf, int bufLen)
 {
+#ifdef F_OFD_GETLK
+  int lkType = F_OFD_GETLK;
+#else
+  int lkType = F_GETLK;
+#endif
   dumpBuf[0] = '\0';
-  if (osFcntl(fd, F_GETLK, lock) != SQLITE_OK) {
+  if (osFcntl(fd, lkType, lock) != SQLITE_OK) {
     sqlite3_log(SQLITE_ERROR, "[SQLite]Get wal file lock ofs %u failed, errno: %d", lock->l_start, errno);
     return 0;
   }
@@ -263106,6 +263422,35 @@ SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db)
   return SQLITE_OK;
 }
 
+static void sqlite3FreeMonitorTablesConfig(sqlite3 *db, MonitorTablesConfig *config)
+{
+  if (config == NULL) return;
+
+  if (config->tables != NULL) {
+    for (int i = 0; i < config->tableCount; i++) {
+      MonitorTableCol *tc = &config->tables[i];
+      if (tc->tableName != NULL) {
+        sqlite3DbFree(db, (void *)tc->tableName);
+        tc->tableName = NULL;
+      }
+      if (tc->cols != NULL) {
+        for (int j = 0; j < tc->colCount; j++) {
+          if (tc->cols[j] != NULL) {
+            sqlite3DbFree(db, (void *)tc->cols[j]);
+            tc->cols[j] = NULL;
+          }
+        }
+        sqlite3DbFree(db, tc->cols);
+        tc->cols = NULL;
+      }
+    }
+    sqlite3DbFree(db, config->tables);
+    config->tables = NULL;
+  }
+  sqlite3DbFree(db, config);
+  config = NULL;
+}
+
 SQLITE_PRIVATE int sqlite3SetMonitorConfig(sqlite3 *db, MonitorTablesConfig *src)
 {
   if (src == NULL) {
@@ -263127,7 +263472,10 @@ SQLITE_PRIVATE int sqlite3SetMonitorConfig(sqlite3 *db, MonitorTablesConfig *src
         int len = strlen(srcTable->tableName);
         dstTable->tableName = sqlite3DbMallocZero(db, len + 1);
         if (dstTable->tableName) {
-          memcpy((void *)dstTable->tableName, (void *)srcTable->tableName, len);
+          memcpy((void *)dstTable->tableName, (void *)srcTable->tableName, len + 1);
+        } else {
+          sqlite3FreeMonitorTablesConfig(db, dst);
+          return SQLITE_ERROR;
         }
       }
 
@@ -263137,17 +263485,19 @@ SQLITE_PRIVATE int sqlite3SetMonitorConfig(sqlite3 *db, MonitorTablesConfig *src
           if (dstTable->tableName) {
             sqlite3DbFree(db, (void *)dstTable->tableName);
           }
-          sqlite3DbFree(db, dst->tables);
-          sqlite3DbFree(db, dst);
+          sqlite3FreeMonitorTablesConfig(db, dst);
           return SQLITE_ERROR;
         }
 
         for (int j = 0; j < srcTable->colCount; j++) {
-          if (srcTable->cols[j] != NULL) {
+          if (srcTable->cols != NULL && srcTable->cols[j] != NULL) {
             int len = strlen(srcTable->cols[j]);
             dstTable->cols[j] = sqlite3DbMallocZero(db, len + 1);
             if (dstTable->cols[j]) {
-              memcpy((void *)dstTable->cols[j], (void *)srcTable->cols[j], len);
+              memcpy((void *)dstTable->cols[j], (void *)srcTable->cols[j], len + 1);
+            } else {
+              sqlite3FreeMonitorTablesConfig(db, dst);
+              return SQLITE_ERROR;
             }
           }
           dstTable->colCount++;
@@ -263160,30 +263510,6 @@ SQLITE_PRIVATE int sqlite3SetMonitorConfig(sqlite3 *db, MonitorTablesConfig *src
   return SQLITE_OK;
 }
 
-static void sqlite3FreeMonitorTablesConfig(sqlite3 *db, MonitorTablesConfig *config)
-{
-  if (config == NULL) return;
-
-  if (config->tables != NULL) {
-    for (int i = 0; i < config->tableCount; i++) {
-      MonitorTableCol *tc = &config->tables[i];
-      if (tc->tableName != NULL) {
-        sqlite3DbFree(db, (void *)tc->tableName);
-      }
-      if (tc->cols != NULL) {
-        for (int j = 0; j < tc->colCount; j++) {
-          if (tc->cols[j] != NULL) {
-            sqlite3DbFree(db, (void *)tc->cols[j]);
-          }
-        }
-        sqlite3DbFree(db, tc->cols);
-      }
-    }
-    sqlite3DbFree(db, config->tables);
-  }
-  sqlite3DbFree(db, config);
-}
-
 SQLITE_PRIVATE int sqlite3SetBinLogConfig(sqlite3 *db, Sqlite3BinlogConfig *bConfig)
 {
   if (db == NULL) {
@@ -263245,6 +263571,7 @@ SQLITE_PRIVATE void sqlite3BinlogFreeOldColumns(sqlite3 *db, BinlogSearchResult
 {
   if (oldCounts<=0) {
     sqlite3DbFree(db, res->nameAndValues);
+    sqlite3DbFree(db, res->colTypes);
   }
   for (int i=0; i<oldCounts*2; i++) {
     if (res->nameAndValues != NULL && res->nameAndValues[i] != NULL) {
@@ -263260,13 +263587,18 @@ SQLITE_PRIVATE int sqlite3BinlogGetSearchFieldDataForSearch(sqlite3 *db, Table *
   if (res->nameAndValues == NULL) {
       return SQLITE_ERROR;
   }
+  res->colTypes = (int *)sqlite3DbMallocZero(db, pTab->nCol * sizeof(int));
+  if (res->colTypes == NULL) {
+      sqlite3DbFree(db, res->nameAndValues);
+      return SQLITE_ERROR;
+  }
   for (int i=0; i<pTab->nCol; i++) {
     char *fieldName = sqlite3_mprintf("%s", pTab->aCol[i].zCnName);
     if (fieldName == NULL) {
       sqlite3BinlogFreeOldColumns(db, res, i-1);
       return SQLITE_NOMEM;
     }
-    char *fieldValue = sqlite3BinlogGetNthColForSearch(db, pTab, pRow, i);
+    char *fieldValue = sqlite3BinlogGetNthColForSearch(db, pTab, pRow, i, &res->colTypes[i]);
     if (fieldValue == NULL) {
       sqlite3DbFree(db, fieldName);
       sqlite3BinlogFreeOldColumns(db, res, i-1);
@@ -263292,18 +263624,23 @@ SQLITE_PRIVATE int sqlite3BinlogGetSearchFieldDataNoRowidForSearch(
   if (res->nameAndValues == NULL) {
       return SQLITE_ERROR;
   }
+  res->colTypes = (int *)sqlite3DbMallocZero(db, pTab->nCol * sizeof(int));
+  if (res->colTypes == NULL) {
+      sqlite3DbFree(db, res->nameAndValues);
+      return SQLITE_ERROR;
+  }
   /* Without rowid table fields are rearranged with primary keys at front */
   for (int i=0; i<pIdx->nKeyCol; i++) {
       i16 iCol = pIdx->aiColumn[i];
       res->nameAndValues[i*2] = sqlite3_mprintf("%s", pTab->aCol[iCol].zCnName);
-      res->nameAndValues[i*2 + 1] = sqlite3BinlogGetNthColForSearch(db, pTab, pRow, iCol);
+      res->nameAndValues[i*2 + 1] = sqlite3BinlogGetNthColForSearch(db, pTab, pRow, iCol, &res->colTypes[iCol]);
   }
 
   int idx = pIdx->nKeyCol;
   for (int k=0; k<pTab->nCol; k++) {
     if ( (pTab->aCol[k].colFlags & COLFLAG_PRIMKEY) == 0 ) {
         res->nameAndValues[idx*2] = sqlite3_mprintf("%s", pTab->aCol[k].zCnName);
-        res->nameAndValues[idx*2 + 1] = sqlite3BinlogGetNthColForSearch(db, pTab, pRow, k);
+        res->nameAndValues[idx*2 + 1] = sqlite3BinlogGetNthColForSearch(db, pTab, pRow, k, &res->colTypes[k]);
         idx++;
     }
   }
@@ -263621,7 +263958,7 @@ no_mem:
 }
 
 /* Return an insert sql based on the binlog row data, the returned value needs free by caller */
-SQLITE_PRIVATE char *sqlite3BinlogRowInsert(sqlite3 *db, Table *pTab, const BinlogRow *pRow){
+SQLITE_PRIVATE char *sqlite3BinlogRowInsertUpdate(sqlite3 *db, Table *pTab, const BinlogRow *pRow){
   char *fieldNames = NULL;
   char *fieldValues = NULL;
 
@@ -263654,24 +263991,6 @@ SQLITE_PRIVATE char *sqlite3BinlogRowInsert(sqlite3 *db, Table *pTab, const Binl
   return result;
 }
 
-/* Return an update sql for a regular table. Without rowid table has no update operation */
-SQLITE_PRIVATE char *sqlite3BinlogRowUpdate(sqlite3 *db, Table *pTab, const BinlogRow *pRow){
-  char *fieldNames = NULL;
-  char *fieldValues = NULL;
-  if (sqlite3BinlogGetFieldData(db, pTab, pRow, &fieldNames, &fieldValues) != SQLITE_OK) {
-    return NULL;
-  }
-  char *result = sqlite3_mprintf(
-    "insert into \"%s\" (%s, rowid) values (%s, %lld) on conflict(rowid) do update set (%s) = (%s);",
-    pTab->zName, fieldNames,
-    fieldValues, pRow->rowid,
-    fieldNames, fieldValues
-  );
-  sqlite3DbFree(db, fieldNames);
-  sqlite3DbFree(db, fieldValues);
-  return result;
-}
-
 /* Return the sql statement corresponds to a binlog row data, NULL is returned if error occurs */
 SQLITE_PRIVATE char *sqlite3GetBinlogRowStmt(sqlite3 *db, const BinlogRow *pRow, Table *pTab){
   assert( db!=NULL );
@@ -263682,9 +264001,8 @@ SQLITE_PRIVATE char *sqlite3GetBinlogRowStmt(sqlite3 *db, const BinlogRow *pRow,
     case SQLITE_DELETE:
       return HasRowid(pTab) ? sqlite3BinlogRowDelete(pTab, pRow->rowid) : sqlite3BinlogRowDeleteNoRowid(db, pTab, pRow);
     case SQLITE_INSERT:
-      return sqlite3BinlogRowInsert(db, pTab, pRow);
     case SQLITE_UPDATE:
-      return sqlite3BinlogRowUpdate(db, pTab, pRow);
+      return sqlite3BinlogRowInsertUpdate(db, pTab, pRow);
     default:
       return NULL;
   }
@@ -263730,49 +264048,85 @@ SQLITE_PRIVATE Table *sqlite3BinlogFindTable(BtCursor *pC){
   return pTab;
 }
 
-SQLITE_PRIVATE void CheckAndNotify(sqlite3 *db, char *tableName){
+SQLITE_PRIVATE void NotifyDataChange(Vdbe *p, char *tableName){
+  sqlite3 *db = p->db;
+  const char *zFile = sqlite3_db_filename(db, 0);
+  if (zFile == NULL || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
+    sqlite3_log(SQLITE_ERROR, "binlog db has no file path");
+  }
+  db->xBinlogHandle.xChangeCallback(zFile, tableName);
+}
+
+SQLITE_PRIVATE int CheckNeedNotify(Vdbe *p, char *tableName){
+  sqlite3 *db = p->db;
   if (tableName == NULL) {
-      return;
+      return 0;
   }
-  if (db->xBinlogHandle.mode != ROW_FOR_SEARCH) {
-    return;
+  char **changedCols = p->changedCols;
+  int changedColsCount = p->changedColsCount;
+  if (changedCols == NULL && changedColsCount > 0) {
+    sqlite3_log(SQLITE_WARNING, "CheckAndNotify: cahngedCiks ss NULL but count > 0");
+    return 0;
+  }
+  if (db->xBinlogHandle.mode != ROW_FOR_SEARCH || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
+    return 0;
   }
   if (db->xBinlogHandle.monitorConfig==NULL) {
-      if (db->xBinlogHandle.jsonParseCallback != NULL) {
-        const char *zFile = sqlite3_db_filename(db, 0);
-        if (zFile == NULL || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
-          sqlite3_log(SQLITE_ERROR, "binlog db has no file path");
-          return;
-        }
-        MonitorTablesConfig *monitorConfig = db->xBinlogHandle.jsonParseCallback(zFile);
-        if (monitorConfig == NULL) {
-          sqlite3_log(SQLITE_ERROR, "jsonPaarseMonitorConfig get null");
-          return;
-        }
-        int rc = sqlite3SetMonitorConfig(db, monitorConfig);
-        if (rc != SQLITE_OK) {
-          sqlite3_log(SQLITE_ERROR, "setmonitorConfig fail in jsonPaarseMonitorConfig");
-          return;
-        }
+    if (db->xBinlogHandle.jsonParseCallback != NULL) {
+      const char *zFile = sqlite3_db_filename(db, 0);
+      if (zFile == NULL) {
+        sqlite3_log(SQLITE_ERROR, "binlog db has no file path");
+        return 0;
+      }
+      MonitorTablesConfig *monitorConfig = db->xBinlogHandle.jsonParseCallback(zFile);
+      if (monitorConfig == NULL) {
+        return 0;
       }
+      int rc = sqlite3SetMonitorConfig(db, monitorConfig);
+      if (db->xBinlogHandle.freeJsonParseCallback != NULL) {
+        db->xBinlogHandle.freeJsonParseCallback(monitorConfig);
+      }
+      if (rc != SQLITE_OK) {
+        sqlite3_log(SQLITE_ERROR, "setmonitorConfig fail in jsonPaarseMonitorConfig");
+        return 0;
+      }
+    }
   }
   if (db->xBinlogHandle.monitorConfig == NULL) {
-      return;
+      return 0;
   }
   MonitorTablesConfig *config = db->xBinlogHandle.monitorConfig;
+  const char *zFile = sqlite3_db_filename(db, 0);
+  if (zFile == NULL) {
+    sqlite3_log(SQLITE_ERROR, "binlog db has no file path");
+    return 0;
+  }
   for (int i = 0; i < config->tableCount; i++) {
-    if (strcmp(tableName, config->tables[i].tableName) == 0) {
-        if (db->xBinlogHandle.xChangeCallback == NULL) {
-            return;
-        }
-        const char *zFile = sqlite3_db_filename(db, 0);
-        if (zFile == NULL || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
-          sqlite3_log(SQLITE_ERROR, "binlog db has no file path");
-          return;
+    if (db->xBinlogHandle.xChangeCallback == NULL) {
+      return 0;
+    }
+    if (p->isInsertOrDelete) {
+      return 1;
+    }
+    int isFound = 0;
+    MonitorTableCol tableConfig = config->tables[i];
+    for (int j = 0; j < changedColsCount; j++) {
+      for (int k = 0; k < tableConfig.colCount; k++) {
+        if (strcmp(changedCols[j], tableConfig.cols[k]) == 0) {
+          isFound = 1;
+          break;
         }
-        db->xBinlogHandle.xChangeCallback(zFile, tableName);
+      }
+      if (isFound) {
+          break;
+      }
+    }
+    if(!isFound){
+      return 0;
     }
+    return 1;
   }
+  return 0;
 }
 
 SQLITE_PRIVATE int sqlite3BinlogWriteTable(Vdbe *p, Table *pTable){
@@ -263823,12 +264177,28 @@ SQLITE_PRIVATE int sqlite3BinlogDecodeRowBuffer(char *buffer, u32 nBuffer, u8 *o
   return SQLITE_OK;
 }
 
-SQLITE_PRIVATE int sqlite3BinlogGetRowBuffer(const BinlogRow *pRow, char **pOutBuffer, u64 *nOutBuffer){
+SQLITE_PRIVATE int sqlite3BinlogGetRowBuffer(const BinlogRow *pRow, u8 isNeedNotify, char **pOutBuffer, u64 *nOutBuffer){
   assert( pRow!=NULL );
   assert( pOutBuffer!=NULL );
   assert( nOutBuffer!=NULL );
 
-  u8 op = pRow->op == SQLITE_DELETE ? SQLITE_DELETE : SQLITE_INSERT;
+  u8 op = pRow->op;
+  if (op != SQLITE_DELETE && op != SQLITE_UPDATE) {
+    op = SQLITE_INSERT;
+  }
+  if (isNeedNotify) {
+      switch (pRow->op) {
+        case SQLITE_DELETE:
+          op = SQLITE_SEARCHABLE_DELETE;
+          break;
+        case SQLITE_INSERT:
+          op = SQLITE_SEARCHABLE_INSERT;
+          break;
+        default:
+          op = SQLITE_SEARCHABLE_UPDATE;
+          break;
+      }
+  }
   sqlite3_int64 rowid = pRow->rowid;
   sqlite3_uint64 nData = pRow->nData;
   int nZero = pRow->nZero;
@@ -263882,7 +264252,7 @@ error_when_memcpy_binlog_row:
   return SQLITE_ERROR;
 }
 
-SQLITE_PRIVATE int sqlite3BinlogWriteRowData(Vdbe *p, const BinlogRow *pRow){
+SQLITE_PRIVATE int sqlite3BinlogWriteRowData(Vdbe *p, const BinlogRow *pRow, u8 isNeedNotify){
   assert( p!=NULL && pRow!=NULL );
   if ((pRow->nData == 0 || pRow->pData == NULL) && pRow->op != SQLITE_DELETE) {
     sqlite3_log(SQLITE_ERROR, "binlog row data empty, %llu", pRow->nData);
@@ -263891,7 +264261,7 @@ SQLITE_PRIVATE int sqlite3BinlogWriteRowData(Vdbe *p, const BinlogRow *pRow){
 
   u64 nRowBuffer = 0;
   char *pRowBuffer = NULL;
-  int rc = sqlite3BinlogGetRowBuffer(pRow, &pRowBuffer, &nRowBuffer);
+  int rc = sqlite3BinlogGetRowBuffer(pRow, isNeedNotify, &pRowBuffer, &nRowBuffer);
   if (rc != SQLITE_OK) {
     return rc;
   }
@@ -263905,7 +264275,8 @@ SQLITE_PRIVATE int sqlite3BinlogWriteRowData(Vdbe *p, const BinlogRow *pRow){
 ** This is the function called in sqlite3VdbeExec to write a row-based binlog statement.
 ** If an error occured, then the row data is cleared and the state will be logged in statement mode
 **/
-SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor, const BinlogRow *pRow){
+SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor, const BinlogRow *pRow)
+{
   assert( p!=NULL && pCursor!=NULL && pRow!=NULL );
   assert( p->db!=NULL );
   assert( sqlite3IsRowBasedBinlog(p) );
@@ -263962,8 +264333,15 @@ SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor, const
     dataContainer->pTable = pTab;
   }
 
-  if (sqlite3BinlogWriteRowData(p, pRow) == SQLITE_OK) {
-    CheckAndNotify(db, pTab->zName);
+  if (db->xBinlogHandle.mode == ROW_FOR_SEARCH && (pRow->op == SQLITE_INSERT || pRow->op == SQLITE_DELETE)) {
+      p->isInsertOrDelete = 1;
+  }
+  u8 isNeedNotify = CheckNeedNotify(p, pTab->zName);
+  FreeChangedCols(p);
+  if (sqlite3BinlogWriteRowData(p, pRow, isNeedNotify) == SQLITE_OK) {
+    if (isNeedNotify) {
+        NotifyDataChange(p, pTab->zName);
+    }
     return;
   }
 
@@ -264103,10 +264481,11 @@ SQLITE_PRIVATE char *sqlite3BinlogGetNthCol(sqlite3 *db, const Table *pTab, cons
 }
 
 /* Get the Nth column value for search - returns raw value without SQL quotes */
-SQLITE_PRIVATE char *sqlite3BinlogGetNthColForSearch(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol) {
+SQLITE_PRIVATE char *sqlite3BinlogGetNthColForSearch(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol, int *pColType) {
   assert( db != NULL && pTab != NULL && pRow != NULL );
   assert( iCol >= 0 );
   if (HasRowid(pTab) && pTab->iPKey == iCol) {
+      if (pColType) *pColType = SQLITE_INTEGER;
       return sqlite3_mprintf("%lld", pRow->rowid);
   }
 
@@ -264130,6 +264509,7 @@ SQLITE_PRIVATE char *sqlite3BinlogGetNthColForSearch(sqlite3 *db, const Table *p
     return NULL;
   }
   int type = sqlite3_value_type(tempVal);
+  if (pColType) *pColType = type;
   const unsigned char *temp = sqlite3_value_text(tempVal);
 
   char *res = NULL;
@@ -264157,7 +264537,8 @@ SQLITE_PRIVATE int sqlite3IsSkipWriteBinlog(Vdbe *p) {
     || !isBinlogEnabled
     || p->db->nVdbeExec > 1
     || (p->readOnly && p->stmtType != STMT_TYPE_BEGIN_TRANSACTION && p->stmtType != STMT_TYPE_SAVEPOINT &&
-      p->stmtType != STMT_TYPE_COMMIT_TRANSACTION && p->stmtType != STMT_TYPE_ROLLBACK_TRANSACTION);
+      p->stmtType != STMT_TYPE_COMMIT_TRANSACTION && p->stmtType != STMT_TYPE_ROLLBACK_TRANSACTION)
+    || (p->db->xBinlogHandle.mode == READ_FOR_SEARCH);
 }
 
 SQLITE_PRIVATE void sqlite3BinlogWrite(Vdbe *p)
@@ -264265,6 +264646,7 @@ SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db)
     sqlite3_log(SQLITE_ERROR, "binlog reset parameter is null");
     return;
   }
+  sqlite3_log(SQLITE_OK, "binlog reset");
   db->xBinlogHandle.flags = 0;
   (void)memset_s(db->xBinlogHandle.xTid, SQLITE_UUID_BLOB_LENGTH, 0, SQLITE_UUID_BLOB_LENGTH);
   db->xBinlogHandle.xErrorCallback = NULL;
@@ -264290,7 +264672,8 @@ SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db)
 SQLITE_PRIVATE int sqlite3BinlogResetHwm(sqlite3 *db)
 {
   if (db == NULL || db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi == NULL) {
-    sqlite3_log(SQLITE_ERROR, "binlog reset hwm is null");
+    sqlite3_log(SQLITE_ERROR, "binlog reset hwm is null %d %d", db == NULL,
+      db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi == NULL);
     return SQLITE_ERROR;
   }
   int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi(db->xBinlogHandle.binlogConn));
@@ -264360,6 +264743,17 @@ SQLITE_PRIVATE char *sqlite3BinlogGetRowSql(sqlite3 *db, Table* pTable, char *bu
 
   BinlogRow pRow;
   pRow.op = op;
+  switch (pRow.op) {
+    case SQLITE_SEARCHABLE_DELETE:
+    pRow.op = SQLITE_DELETE;
+      break;
+    case SQLITE_SEARCHABLE_INSERT:
+      pRow.op = SQLITE_INSERT;
+      break;
+    case SQLITE_SEARCHABLE_UPDATE:
+      pRow.op = SQLITE_UPDATE;
+      break;
+  }
   pRow.nData = nData;
   pRow.nZero = nZero;
   pRow.pData = pData;
@@ -264449,7 +264843,13 @@ SQLITE_PRIVATE int checkIfMonitorTable(sqlite3 *db, char *tableName)
   if (config == NULL) {
       return 1;
   }
+  if (tableName == NULL) {
+    return 0;
+  }
   for (int i = 0; i < config->tableCount; i++) {
+    if (config->tables == NULL || config->tables[i].tableName == NULL) {
+      continue;
+    }
     if (strcmp(tableName, config->tables[i].tableName) == 0) {
       return 1;
     }
@@ -264481,15 +264881,20 @@ SQLITE_PRIVATE int sqlite3BinlogGetSearchData(sqlite3 *db, Sqlite3BinlogStmt *bS
         rc = SQLITE_ERROR;
         sqlite3_log(SQLITE_WARNING, "search binlog find no table, rc=%d", rc);
       } else {
+        db->xBinlogHandle.pTable = *pOutTable;
         rc = SQLITE_ROW;
       }
       return rc;
     }
     case BINLOG_EVENT_TYPE_ROW_FULL_DATA: {
       if (pOutTable == NULL || *pOutTable == NULL) {
-        rc = SQLITE_ERROR;
-        sqlite3_log(SQLITE_ERROR, "binlog row has no table");
-        return rc;
+        if (db->xBinlogHandle.pTable != NULL) {
+            *pOutTable = db->xBinlogHandle.pTable;
+        } else {
+          rc = SQLITE_ERROR;
+          sqlite3_log(SQLITE_ERROR, "binlog row has no table");
+          return rc;
+        }
       }
       if (!checkIfMonitorTable(db, (*pOutTable)->zName)) {
           return SQLITE_ROW;
@@ -264510,6 +264915,19 @@ SQLITE_PRIVATE int sqlite3BinlogGetSearchData(sqlite3 *db, Sqlite3BinlogStmt *bS
 
       BinlogRow pRow;
       pRow.op = op;
+      switch (pRow.op) {
+        case SQLITE_SEARCHABLE_DELETE:
+          pRow.op = SQLITE_DELETE;
+            break;
+        case SQLITE_SEARCHABLE_INSERT:
+          pRow.op = SQLITE_INSERT;
+          break;
+        case SQLITE_SEARCHABLE_UPDATE:
+          pRow.op = SQLITE_UPDATE;
+          break;
+        default:
+          return SQLITE_ROW;
+      }
       pRow.nData = nData;
       pRow.nZero = nZero;
       pRow.pData = pData;
@@ -264688,6 +265106,11 @@ SQLITE_PRIVATE void BinlogSearchResultFree(sqlite3 *srcDb, BinlogSearchResult *r
         sqlite3DbFree(srcDb, row->nameAndValues);
         row->nameAndValues = NULL;
     }
+
+    if (row->colTypes != NULL) {
+        sqlite3DbFree(srcDb, row->colTypes);
+        row->colTypes = NULL;
+    }
 }
 
 /**
@@ -264708,7 +265131,7 @@ SQLITE_PRIVATE void BinlogSearchResultSetDestroy(sqlite3 *srcDb, BinlogSearchRes
 SQLITE_PRIVATE int sqlite3BinlogSearchDataGet(sqlite3 *srcDb, sqlite3 *destDb, BinlogSearchResultSet **rs)
 {
   if ((srcDb->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
-    sqlite3_log(SQLITE_ERROR, "binlog replay srcDb not enable binlog");
+    sqlite3_log(SQLITE_ERROR, "binlog replay srcDb not enable binlog %llu", srcDb->xBinlogHandle.flags);
     return SQLITE_ERROR;
   }
   const char *srcFile = sqlite3_db_filename(srcDb, 0);
@@ -264822,12 +265245,13 @@ struct sqlite3_api_routines_extra {
 #else
   void *dymmyRekeyV3Func;
 #endif
-  int (*is_support_binlog)(void);
+  int (*is_support_binlog)(const char*);
   int (*replay_binlog)(sqlite3*, sqlite3*);
 #ifdef SQLITE_ENABLE_BINLOG
   int (*set_monitor_config)(sqlite3*, MonitorTablesConfig*);
   int (*set_xChange_callback)(sqlite3*, void (*xChangeCallback)(const char *dbPath, char *tableName));
   int (*set_json_parse_callback)(sqlite3*, MonitorTablesConfig*(*jsonParseCallback)(const char *dbPath));
+  int (*free_json_parse_callback)(sqlite3*, int(*freeJsonParseCallback)(MonitorTablesConfig *config));
   int (*get_search_data_binlog)(sqlite3*, sqlite3*, BinlogSearchResultSet**);
   int (*free_search_data_binlog)(sqlite3*, BinlogSearchResultSet**);
   int (*set_search_hwm_binlog)(sqlite3*, BinlogSearchHwmT*, BinlogHwmSetModeE);
@@ -264841,6 +265265,8 @@ struct sqlite3_api_routines_extra {
   void *dymmyFunc5;
   void *dymmyFunc6;
   void *dymmyFunc7;
+  void *dymmyFunc8;
+  void *dymmyFunc9;
 #endif 
 #ifdef SQLITE_ENABLE_PAGE_COMPRESS
   int (*compressdb_backup)(sqlite3*, const char*);
@@ -264872,6 +265298,7 @@ static const sqlite3_api_routines_extra sqlite3ExtraApis = {
   sqlite3_set_monitor_config_binlog,
   sqlite3_set_xChange_callback_binlog,
   sqlite3_set_json_parse_callback_binlog,
+  sqlite3_free_json_parse_callback_binlog,
   sqlite3_get_search_data_binlog,
   sqlite3_free_search_data_binlog,
   sqlite3_set_search_hwm_binlog,
@@ -264888,6 +265315,7 @@ static const sqlite3_api_routines_extra sqlite3ExtraApis = {
   0,
   0,
   0,
+  0,
 #endif/* SQLITE_ENABLE_BINLOG */
 #ifdef SQLITE_ENABLE_PAGE_COMPRESS
   sqlite3_compressdb_backup,
-- 
2.34.1