From 6c2dadc333863b92b7425e48e520babbb124f0ab Mon Sep 17 00:00:00 2001
From: wddhw <wangxiangdong13@huawei.com>
Date: Thu, 7 May 2026 10:59:12 +0800
Subject: [PATCH] Support-Binlog-Search

---
 src/sqlite3.c        | 900 +++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 876 insertions(+), 24 deletions(-)

diff --git a/src/sqlite3.c b/src/sqlite3.c
index 9b029d8..e2709cb 100644
--- a/src/sqlite3.c
+++ b/src/sqlite3.c
@@ -5367,6 +5367,60 @@ SQLITE_API int sqlite3_is_support_binlog(void);
 
 SQLITE_API int sqlite3_replay_binlog(sqlite3 *srcDb, sqlite3 *destDb);
 
+typedef struct BinlogSearchResult {
+  int op;                // one of the SQLITE_INSERT, SQLITE_UPDATE, SQLITE_DELETE
+  sqlite3_int64 rowid;   // rowid of changed data, -1 for without rowid table
+  sqlite3_uint64 nCol;  // size of pData
+  const char *tableName;
+  int fileIndex;
+  sqlite3_uint64 readPos;
+  char **nameAndValues; // pointer to the row data in record format
+} BinlogSearchResult;
+
+typedef struct BinlogSearchResultSet{
+    int capacity;
+    int row_count;
+    BinlogSearchResult *results;
+} BinlogSearchResultSet;
+
+typedef struct BinlogSearchHwm {
+    int readFileIndex;
+    sqlite3_uint64 readPos;
+} BinlogSearchHwmT;
+
+typedef enum BinlogHwmSetMode {
+    BINLOG_PERSIST_MODE = 0,
+    BINLOG_CACHE_MODE = 1,
+    BINLOG_MODE_MAX,
+} BinlogHwmSetModeE;
+
+typedef struct MonitorTableCol {
+  const char *tableName;    // tableName
+  char **cols;        // colsArray
+  int colCount;       // colsCount
+} MonitorTableCol;
+
+typedef struct MonitorTablesConfig {
+  MonitorTableCol *tables;  // tableCols
+  int tableCount;           // tableCount
+} MonitorTablesConfig;
+
+SQLITE_API int sqlite3_set_monitor_config_binlog(sqlite3 *srcDb, MonitorTablesConfig *monitorConfig);
+
+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, 
+  MonitorTablesConfig*(*jsonParseCallback)(const char *dbPath));
+// get data from binlog file
+SQLITE_API int sqlite3_get_search_data_binlog(sqlite3 *srcDb, sqlite3 *destDb, BinlogSearchResultSet **rs);
+// free result data
+SQLITE_API int sqlite3_free_search_data_binlog(sqlite3 *srcDb, BinlogSearchResultSet **rs);
+// set search hwm of binlog by different mode
+SQLITE_API int sqlite3_set_search_hwm_binlog(sqlite3 *srcDb, BinlogSearchHwmT *hwm, BinlogHwmSetModeE setMode);
+// reset search hwm of binlog to writehwm
+SQLITE_API int sqlite3_reset_search_hwm_binlog(sqlite3 *srcDb);
+
 typedef enum BinlogFileCleanMode {
   BINLOG_FILE_CLEAN_ALL_MODE = 0,
   BINLOG_FILE_CLEAN_READ_MODE = 1,
@@ -17649,6 +17703,7 @@ typedef struct CodecParameter {
  
 typedef enum {
   ROW = 0,
+  ROW_FOR_SEARCH = 2
 } Sqlite3BinlogMode;
  
 typedef enum {
@@ -17716,14 +17771,23 @@ typedef struct BinlogEvent {
 typedef struct BinlogReadResult {
   u32 eventNum;
   BinlogEventT **sqlEvent;
+  BinlogSearchHwmT waterMark;
 } BinlogReadResultT;
- 
+
+typedef enum BinlogReadMode {
+    BINLOG_REPLAY_MODE = 0,
+    BINLOG_SEARCH_MODE = 1,
+    BINLOG_READ_MODE_MAX,
+} BinlogReadModeE;
+
 typedef struct BinlogInstanceT BinlogInstanceT;
 typedef void (*BinlogFreeReadResult)(BinlogInstanceT *instance, BinlogReadResultT *readRes);
 typedef BinlogErrno (*BinlogOpen)(const BinlogConfigT *, BinlogInstanceT **);
 typedef BinlogErrno (*BinlogClose)(BinlogInstanceT *instance);
 typedef BinlogErrno (*BinlogWrite)(BinlogInstanceT *instance, const BinlogWriteDataT *data);
-typedef BinlogErrno (*BinlogRead)(BinlogInstanceT *instance, BinlogReadResultT **readRes);
+typedef BinlogErrno (*BinlogRead)(BinlogInstanceT *instance, BinlogReadModeE readType, BinlogReadResultT **readRes);
+typedef BinlogErrno (*BinlogSetSearchWaterMark)(BinlogInstanceT *binlogIns, BinlogSearchHwmT *waterMark, BinlogHwmSetModeE setMode);
+typedef BinlogErrno (*BinlogResetSearchHwm)(BinlogInstanceT *binlogIns);
 typedef BinlogErrno (*BinlogFileClean)(BinlogInstanceT *instance, BinlogFileCleanModeE cleanMode);
 typedef BinlogErrno (*BinlogLockRead)(BinlogInstanceT *instance);
 typedef BinlogErrno (*BinlogUnlockRead)(BinlogInstanceT *instance);
@@ -17742,6 +17806,9 @@ typedef struct Sqlite3BinlogConfig {
   void *callbackCtx;
 } Sqlite3BinlogConfig;
 
+void (*xChangeCallback)(const char *dbPath, char *tableName);
+MonitorTablesConfig*(*jsonParseCallback)(const char *dbPath);
+
 typedef struct BinlogRow {
   int op;                // one of the SQLITE_INSERT, SQLITE_UPDATE, SQLITE_DELETE
   sqlite3_int64 rowid;   // rowid of changed data, -1 for without rowid table
@@ -17766,6 +17833,8 @@ typedef struct BinlogApi {
   BinlogWrite binlogWriteApi;
   BinlogRead binlogReadApi;
   BinlogFreeReadResult binlogFreeReadResultApi;
+  BinlogSetSearchWaterMark binlogSetSearchWaterMark;
+  BinlogResetSearchHwm binlogResetSearchHwmApi;
   BinlogFileClean binlogFileCleanApi;
   BinlogLockRead binlogLockReadApi;
   BinlogUnlockRead binlogUnlockReadApi;
@@ -17776,8 +17845,11 @@ typedef struct Sqlite3BinlogHandle {
   void *callbackCtx;
   u64 flags;
   u8 xTid[SQLITE_UUID_BLOB_LENGTH];
+  MonitorTablesConfig *monitorConfig;
   void (*xErrorCallback)(void *pCtx, int errNo, char *errMsg, const char *dbPath);
   void (*xLogFullCallback)(void *pCtx, u16 currentCount, const char *dbPath);
+  void (*xChangeCallback)(const char *dbPath, char *tabelName);
+  MonitorTablesConfig* (*jsonParseCallback)(const char *dbPath);
   BinlogInstanceT *binlogConn;
   BinlogApi binlogApi;
   u8 isSkipTrigger;
@@ -17815,7 +17887,7 @@ typedef struct Sqlite3BinlogApiInfo {
   const char *funcN;
 } Sqlite3BinlogApiInfo;
  
-SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, Sqlite3BinlogStmt *bStmt);
+SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, BinlogReadModeE readMode, Sqlite3BinlogStmt *bStmt);
 SQLITE_PRIVATE int sqlite3BinlogStmtStep(sqlite3 *db, Sqlite3BinlogStmt *bStmt, char **sql, Table **pOutTable);
 SQLITE_PRIVATE void sqlite3BinlogStmtFinalize(sqlite3 *db, Sqlite3BinlogStmt *bStmt);
  
@@ -17823,11 +17895,17 @@ SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db);
 SQLITE_PRIVATE int sqlite3SetBinLogConfig(sqlite3 *db, Sqlite3BinlogConfig *bConfig);
 SQLITE_PRIVATE int sqlite3TransferBinlogErrno(BinlogErrno err);
  
+SQLITE_PRIVATE int sqlite3SetMonitorConfig(sqlite3 *db, MonitorTablesConfig *src);
 SQLITE_PRIVATE void sqlite3BinlogWrite(Vdbe *p);
 SQLITE_PRIVATE int sqlite3BinlogClose(sqlite3 *db);
 SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db);
 SQLITE_PRIVATE int sqlite3BinlogReplay(sqlite3 *srcDb, sqlite3 *destDb);
 SQLITE_PRIVATE int sqlite3BinlogClean(sqlite3 *db, BinlogFileCleanModeE mode);
+SQLITE_PRIVATE int sqlite3BinlogSearchDataGet(sqlite3 *srcDb, sqlite3 *destDb, BinlogSearchResultSet **rs);
+SQLITE_PRIVATE void BinlogSearchResultSetDestroy(sqlite3 *srcDb, BinlogSearchResultSet **rs);
+SQLITE_PRIVATE int BinlogSearchResultExpand(sqlite3 *srcDb, BinlogSearchResultSet **rs);
+SQLITE_PRIVATE int sqlite3BinlogResetHwm(sqlite3 *db);
+SQLITE_PRIVATE int sqlite3BinlogSetHwm(sqlite3 *db, BinlogSearchHwmT *waterMark, BinlogHwmSetModeE setMode);
 SQLITE_PRIVATE void sqlite3BinlogErrorCallback(sqlite3 *db, int errNo, char *errMsg);
 SQLITE_PRIVATE BinlogEventTypeE sqlite3TransferLogEventType(StmtType stmtType);
 SQLITE_PRIVATE int sqlite3IsSkipWriteBinlog(Vdbe *p);
@@ -17836,6 +17914,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);
 /************** Binlog typedef in sqlite3 END ************************************/
 #endif
 
@@ -92389,6 +92468,137 @@ SQLITE_API int sqlite3_is_support_binlog(void)
   return SQLITE_ERROR;
 }
 
+SQLITE_API int sqlite3_set_search_hwm_binlog(sqlite3 *srcDb, BinlogSearchHwmT *hwm, BinlogHwmSetModeE setMode)
+{
+  if (srcDb == NULL || hwm == NULL) {
+    sqlite3_log(SQLITE_ERROR, "set binlog search hwm is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "get search parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  int rc = sqlite3BinlogSetHwm(srcDb, hwm, setMode);
+  if (rc != SQLITE_OK && rc != SQLITE_CORRUPT) {
+    sqlite3_log(rc, "get binlog err:%d", rc);
+  }
+  return rc;
+}
+
+SQLITE_API int sqlite3_reset_search_hwm_binlog(sqlite3 *srcDb)
+{
+  if (srcDb == NULL) {
+    sqlite3_log(SQLITE_ERROR, "free binlog reset search hwm is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "get search parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  int rc = sqlite3BinlogResetHwm(srcDb);
+  if (rc != SQLITE_OK && rc != SQLITE_CORRUPT) {
+    sqlite3_log(rc, "get binlog err:%d", rc);
+  }
+  return rc;
+}
+
+SQLITE_API int sqlite3_set_monitor_config_binlog(sqlite3 *srcDb, MonitorTablesConfig *monitorConfig)
+{
+  if (srcDb == NULL) {
+    sqlite3_log(SQLITE_ERROR, "set monitor config binlog parameter is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "set monitor config binlog parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  if (((srcDb->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0)||
+      (srcDb->xBinlogHandle.mode!=ROW_FOR_SEARCH)) {
+    sqlite3_log(SQLITE_ERROR, "set monitor config srcDb not enable binlog");
+    return SQLITE_ERROR;
+  }
+  int rc = sqlite3SetMonitorConfig(srcDb, monitorConfig);
+  if (rc != SQLITE_OK) {
+    sqlite3_log(rc, "get monitor config with not expect way");
+  }
+  return rc;
+}
+
+SQLITE_API int sqlite3_set_xChange_callback_binlog(sqlite3 *srcDb, 
+  void (*xChangeCallback)(const char *dbPath, char *tableName))
+{
+  if (srcDb == NULL || xChangeCallback == NULL) {
+    sqlite3_log(SQLITE_ERROR, "set change callback binlog parameter is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "set change callback binlog parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  if (srcDb->xBinlogHandle.xChangeCallback != NULL) {
+    sqlite3_log(SQLITE_WARNING, "set change callback binlog xBinlogHandle is not valid");
+    return SQLITE_ERROR;
+  }
+  srcDb->xBinlogHandle.xChangeCallback = xChangeCallback;
+  return SQLITE_OK;
+}
+
+SQLITE_API int sqlite3_set_json_parse_callback_binlog(sqlite3 *srcDb,
+  MonitorTablesConfig*(*jsonParseCallback)(const char *dbPath))
+{
+  if (srcDb == NULL) {
+    sqlite3_log(SQLITE_ERROR, "set json parse binlog parameter is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "set json parse binlog parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  if (srcDb->xBinlogHandle.jsonParseCallback != NULL) {
+      sqlite3_log(SQLITE_WARNING, "set json parse  callback binlog jsonParseCallback is alreadySet");
+      return SQLITE_ERROR;
+  }
+  srcDb->xBinlogHandle.jsonParseCallback = jsonParseCallback;
+  return SQLITE_OK;
+}
+
+SQLITE_API int sqlite3_free_search_data_binlog(sqlite3 *srcDb, BinlogSearchResultSet **rs)
+{
+  if (srcDb == NULL || rs == NULL || *rs == NULL) {
+    sqlite3_log(SQLITE_ERROR, "free binlog search data is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb)) {
+    sqlite3_log(SQLITE_WARNING, "get search parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  BinlogSearchResultSetDestroy(srcDb, rs);
+  return SQLITE_OK;
+}
+
+SQLITE_API int sqlite3_get_search_data_binlog(sqlite3 *srcDb, sqlite3 *destDb, BinlogSearchResultSet **rs)
+{
+  if (srcDb == NULL || destDb == NULL) {
+    sqlite3_log(SQLITE_ERROR, "get binlog parameter is null");
+    return SQLITE_ERROR;
+  }
+  if (!sqlite3SafetyCheckOk(srcDb) ||  !sqlite3SafetyCheckOk(destDb)) {
+    sqlite3_log(SQLITE_WARNING, "get binlog parameter is not valid db");
+    return SQLITE_MISUSE_BKPT;
+  }
+  sqlite3_mutex_enter(srcDb->mutex);
+  int rc = sqlite3BinlogSearchDataGet(srcDb, destDb, rs);
+  if (rc != SQLITE_OK && rc != SQLITE_CORRUPT && rc != SQLITE_DONE) {
+    sqlite3_log(rc, "get binlog err:%d", rc);
+  }
+  if (rc == SQLITE_CORRUPT) {
+    rc = SQLITE_ERROR;
+    sqlite3_log(SQLITE_ERROR, "get binlog err with not expect way");
+  }
+  sqlite3_mutex_leave(srcDb->mutex);
+  return rc;
+}
+
 SQLITE_API int sqlite3_replay_binlog(sqlite3 *srcDb, sqlite3 *destDb)
 {
   if (srcDb == NULL || destDb == NULL) {
@@ -100264,23 +100474,38 @@ case OP_Delete: {
     && sqlite3IsRowBasedBinlog(p)
     && sqlite3TransferLogEventType(p->stmtType) == BINLOG_EVENT_TYPE_DML ){
     if (pC->isTable) {
-      BinlogRow rowData = {SQLITE_DELETE, sqlite3BtreeIntegerKey(pC->uc.pCursor), 0, 0, NULL};
-      sqlite3StoreBinlogRowData(p, pC->uc.pCursor, &rowData);
-    } else {
+      BtCursor *pBtCur = pC->uc.pCursor;
+      if (sqlite3BtreeCursorIsValidNN(pBtCur)) {
+        u32 nPayload = sqlite3BtreePayloadSize(pBtCur);
+          if (nPayload > 0) {
+              char *pBuf = sqlite3DbMallocRaw(db, nPayload);
+              if (pBuf) {
+                // get data to be deleted
+                sqlite3BtreePayload(pBtCur, 0, nPayload, pBuf);
+                BinlogRow rowData = {SQLITE_DELETE, sqlite3BtreeIntegerKey(pC->uc.pCursor), nPayload, 0, pBuf};
+                sqlite3StoreBinlogRowData(p, pC->uc.pCursor, &rowData);
+                sqlite3DbFree(db, pBuf);
+              }
+          }
+      }else {
+          BinlogRow rowData = {SQLITE_DELETE, sqlite3BtreeIntegerKey(pC->uc.pCursor), 0, 0, NULL};
+          sqlite3StoreBinlogRowData(p, pC->uc.pCursor, &rowData);
+      }
+    }else{
       Table *pBinlogTab = sqlite3BinlogFindTable(pC->uc.pCursor);
       if (pBinlogTab != NULL && !HasRowid(pBinlogTab)) {
-        /* If pC is not a table, then this is a delete operation in a without rowid table
-        ** pC may have no row data or invalid row data when deletion has a subquery
-        ** so we need to get row data from op_notfound, which is stored in p->pDataTmp
-        */
-        int isUseTmpData = pOp->p4type == P4_TABLE && p->nDataTmp != 0 && p->pDataTmp != NULL;
-        sqlite3_uint64 nData = isUseTmpData ? p->nDataTmp : pC->szRow;
-        const char *pData = isUseTmpData ? p->pDataTmp : (const char *)pC->aRow;
-        BinlogRow rowData = {SQLITE_DELETE, -1, nData, 0, pData};
-        sqlite3StoreBinlogRowData(p, pC->uc.pCursor, &rowData);
-      }
+      /* If pC is not a table, then this is a delete operation in a without rowid table
+      ** pC may have no row data or invalid row data when deletion has a subquery
+      ** so we need to get row data from op_notfound, which is stored in p->pDataTmp
+      */
+      int isUseTmpData = pOp->p4type == P4_TABLE && p->nDataTmp != 0 && p->pDataTmp != NULL;
+      sqlite3_uint64 nData = isUseTmpData ? p->nDataTmp : pC->szRow;
+      const char *pData = isUseTmpData ? p->pDataTmp : (const char *)pC->aRow;
+      BinlogRow rowData = {SQLITE_DELETE, -1, nData, 0, pData};
+      sqlite3StoreBinlogRowData(p, pC->uc.pCursor, &rowData);
     }
   }
+}
 #endif
 
   /* If the update-hook or pre-update-hook will be invoked, set zDb to
@@ -138126,6 +138351,20 @@ typedef int (*sqlite3_loadext_entry)(
 #define sqlite3_is_support_binlog      sqlite3_api->is_support_binlog
 /* replay binlog from src db to the dest db */
 #define sqlite3_replay_binlog          sqlite3_api->replay_binlog
+/* set binlog monitor tables and columns */
+#define sqlite3_set_monitor_config_binlog         sqlite3_api->set_monitor_config
+/* set binlog change callback when monitos table change */
+#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
+/* get search data from binlog */
+#define sqlite3_get_search_data_binlog       sqlite3_api->get_search_data
+/* free search data from binlog */
+#define sqlite3_free_search_data_binlog       sqlite3_api->free_search_data
+/* set search hwm of binlog */
+#define sqlite3_set_search_hwm_binlog       sqlite3_api->set_search_hwm
+/* reset search hwm of binlog to write hwm*/
+#define sqlite3_reset_search_hwm_binlog       sqlite3_api->reset_search_hwm
 /* clean the binlog of the db */
 #define sqlite3_clean_binlog           sqlite3_api->clean_binlog
 #endif /* SQLITE_ENABLE_BINLOG */
@@ -258831,6 +259070,8 @@ SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db)
     {(void **)&db->xBinlogHandle.binlogApi.binlogWriteApi, "BinlogWrite"},
     {(void **)&db->xBinlogHandle.binlogApi.binlogReadApi, "BinlogRead"},
     {(void **)&db->xBinlogHandle.binlogApi.binlogFreeReadResultApi, "BinlogFreeReadResult"},
+    {(void **)&db->xBinlogHandle.binlogApi.binlogSetSearchWaterMark, "BinlogSetSearchWaterMark"},
+    {(void **)&db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi, "BinlogResetSearchHwm"},
     {(void **)&db->xBinlogHandle.binlogApi.binlogFileCleanApi, "BinlogFileClean"},
     {(void **)&db->xBinlogHandle.binlogApi.binlogLockReadApi, "BinlogLockRead"},
     {(void **)&db->xBinlogHandle.binlogApi.binlogUnlockReadApi, "BinlogUnlockRead"},
@@ -258845,7 +259086,85 @@ SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db)
   }
   return SQLITE_OK;
 }
- 
+
+SQLITE_PRIVATE int sqlite3SetMonitorConfig(sqlite3 *db, MonitorTablesConfig *src)
+{
+  if (src == NULL) {
+    return SQLITE_ERROR;
+  }
+  MonitorTablesConfig *dst = (MonitorTablesConfig *)sqlite3DbMallocZero(db, sizeof(MonitorTablesConfig));
+  if (src->tableCount > 0) {
+    dst->tables = sqlite3DbMallocZero(db, src->tableCount * sizeof(MonitorTableCol));
+    if (dst->tables == NULL) {
+      sqlite3DbFree(db, dst);
+      return SQLITE_ERROR;
+    }
+
+    for (int i = 0; i < src->tableCount; i++) {
+      MonitorTableCol *srcTable = &src->tables[i];
+      MonitorTableCol *dstTable = &dst->tables[i];
+
+      if (srcTable->tableName != NULL) {
+        int len = strlen(srcTable->tableName);
+        dstTable->tableName = sqlite3DbMallocZero(db, len + 1);
+        if (dstTable->tableName) {
+          memcpy((void *)dstTable->tableName, (void *)srcTable->tableName, len);
+        }
+      }
+
+      if (srcTable->colCount > 0 && srcTable->cols != NULL) {
+        dstTable->cols = sqlite3DbMallocZero(db, srcTable->colCount * sizeof(char*));
+        if (dstTable->cols == NULL) {
+          if (dstTable->tableName) {
+            sqlite3DbFree(db, (void *)dstTable->tableName);
+          }
+          sqlite3DbFree(db, dst->tables);
+          sqlite3DbFree(db, dst);
+          return SQLITE_ERROR;
+        }
+
+        for (int j = 0; j < srcTable->colCount; j++) {
+          if (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);
+            }
+          }
+          dstTable->colCount++;
+        }
+      }
+      dst->tableCount++;
+    }
+  }
+  db->xBinlogHandle.monitorConfig = dst;
+  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) {
@@ -258902,7 +259221,144 @@ SQLITE_PRIVATE int sqlite3SetBinLogConfig(sqlite3 *db, Sqlite3BinlogConfig *bCon
   db->xBinlogHandle.isSkipTrigger = 0;
   return SQLITE_OK;
 }
- 
+
+SQLITE_PRIVATE void sqlite3BinlogFreeOldColumns(sqlite3 *db, BinlogSearchResult *res, int oldCounts)
+{
+  if (oldCounts<=0) {
+    sqlite3DbFree(db, res->nameAndValues);
+  }
+  for (int i=0; i<oldCounts*2; i++) {
+    if (res->nameAndValues != NULL && res->nameAndValues[i] != NULL) {
+      sqlite3DbFree(db, res->nameAndValues[i]);
+    }
+    sqlite3DbFree(db, res->nameAndValues);
+  }
+}
+
+/* Get field name and values for rowid tables in search mode - uses raw values without SQL quotes */
+SQLITE_PRIVATE int sqlite3BinlogGetSearchFieldDataForSearch(sqlite3 *db, Table *pTab, const BinlogRow *pRow, BinlogSearchResult *res){
+  res->nameAndValues = (char **)sqlite3DbMallocZero(db, 2 * pTab->nCol * sizeof(const char *));
+  if (res->nameAndValues == NULL) {
+      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);
+    if (fieldValue == NULL) {
+      sqlite3DbFree(db, fieldName);
+      sqlite3BinlogFreeOldColumns(db, res, i-1);
+      return SQLITE_NOMEM;
+    }
+    res->nameAndValues[i * 2] = fieldName;
+    res->nameAndValues[i * 2 + 1] = fieldValue;
+  }
+  res->nCol = pTab->nCol;
+  res->tableName = pTab->zName;
+  return SQLITE_OK;
+}
+
+/* Get the field name and field values for a without rowid table, fixed version for search */
+SQLITE_PRIVATE int sqlite3BinlogGetSearchFieldDataNoRowidForSearch(
+  sqlite3 *db,                  /* dB handle */
+  Table *pTab,                  /* Table to which the row belongs */
+  const BinlogRow *pRow,        /* Row data to be written into binlog */
+  BinlogSearchResult *res              /* field values to be returned */
+){
+  Index *pIdx = sqlite3PrimaryKeyIndex(pTab);
+  res->nameAndValues = (char **)sqlite3DbMallocZero(db, 2 * (pIdx->nKeyCol + pTab->nCol ) * sizeof(const char *));
+  if (res->nameAndValues == NULL) {
+      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);
+  }
+
+  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);
+        idx++;
+    }
+  }
+  res->nCol = pTab->nCol;
+  return SQLITE_OK;
+}
+
+/* Return an insert sql based on the binlog row data, the returned value needs free by caller */
+SQLITE_PRIVATE int sqlite3BinlogRowInsertGet(sqlite3 *db, Table *pTab, const BinlogRow *pRow, BinlogSearchResult *res){
+
+  int rc = SQLITE_OK;
+  if (HasRowid(pTab)) {
+    rc = sqlite3BinlogGetSearchFieldDataForSearch(db, pTab, pRow, res);
+  } else {
+    rc = sqlite3BinlogGetSearchFieldDataNoRowidForSearch(db, pTab, pRow, res);
+  }
+
+  if (rc != SQLITE_OK) {
+    sqlite3_log(rc, "binlog get field data err:%d", rc);
+    return rc;
+  }
+  res->op = SQLITE_INSERT;
+  res->tableName = pTab->zName;
+  return SQLITE_OK;
+}
+
+/* Return search result for UPDATE operation - fills BinlogSearchResult */
+SQLITE_PRIVATE int sqlite3BinlogRowUpdateGet(sqlite3 *db, Table *pTab, const BinlogRow *pRow, BinlogSearchResult *res)
+{
+    int rc = SQLITE_OK;
+    if (HasRowid(pTab)) {
+        rc = sqlite3BinlogGetSearchFieldDataForSearch(db, pTab, pRow, res);
+    } else {
+        rc = sqlite3BinlogGetSearchFieldDataNoRowidForSearch(db, pTab, pRow, res);
+    }
+
+    if (rc != SQLITE_OK) {
+        sqlite3_log(rc, "binlog update get field data err:%d", rc);
+        return rc;
+    }
+    res->rowid = pRow->rowid;
+    res->op = SQLITE_UPDATE;
+    res->tableName = pTab->zName;
+    return SQLITE_OK;
+}
+
+/* Return search result for DELETE operation (rowid table) - fills BinlogSearchResult */
+SQLITE_PRIVATE int sqlite3BinlogRowDeleteGet(sqlite3 *db, Table *pTab, const BinlogRow *pRow, BinlogSearchResult *res)
+{
+    int rc = sqlite3BinlogGetSearchFieldDataForSearch(db, pTab, pRow, res);
+    if (rc != SQLITE_OK) {
+        sqlite3_log(rc, "binlog delete get field data err:%d", rc);
+        return rc;
+    }
+    res->rowid = pRow->rowid;
+    res->op = SQLITE_DELETE;
+    res->tableName = pTab->zName;
+    return SQLITE_OK;
+}
+
+/* Return search result for DELETE operation (no rowid table) - fills BinlogSearchResult */
+SQLITE_PRIVATE int sqlite3BinlogRowDeleteNoRowidGet(sqlite3 *db, Table *pTab, const BinlogRow *pRow, BinlogSearchResult *res)
+{
+    int rc = sqlite3BinlogGetSearchFieldDataNoRowidForSearch(db, pTab, pRow, res);
+    if (rc != SQLITE_OK) {
+        sqlite3_log(rc, "binlog delete no rowid get field data err:%d", rc);
+        return rc;
+    }
+    res->rowid = pRow->rowid;
+    res->op = SQLITE_DELETE;
+    res->tableName = pTab->zName;
+    return SQLITE_OK;
+}
+
 SQLITE_PRIVATE int sqlite3GenerateUuid(u8 *aBlobOut)
  {
   if (aBlobOut == NULL) {
@@ -259215,6 +259671,25 @@ SQLITE_PRIVATE char *sqlite3GetBinlogRowStmt(sqlite3 *db, const BinlogRow *pRow,
   }
 }
 
+/* Return the search result fills BinlogSearchResult for SEARCH mode - returns int status */
+SQLITE_PRIVATE int sqlite3GetBinlogRowColsSearch(sqlite3 *db, const BinlogRow *pRow, Table *pTab, BinlogSearchResult *res)
+{
+  assert( db!=NULL );
+  assert( pRow!=NULL );
+  assert( pTab!=NULL );
+
+  switch (pRow->op) {
+    case SQLITE_DELETE:
+      return HasRowid(pTab) ? sqlite3BinlogRowDeleteGet(db, pTab, pRow, res) : sqlite3BinlogRowDeleteNoRowidGet(db, pTab, pRow, res);
+    case SQLITE_INSERT:
+      return sqlite3BinlogRowInsertGet(db, pTab, pRow, res);
+    case SQLITE_UPDATE:
+      return sqlite3BinlogRowUpdateGet(db, pTab, pRow, res);
+    default:
+      return SQLITE_ERROR;
+  }
+}
+
 /* Find the table that is pointed by pC, NULL is returned if a valid table can not be found */
 SQLITE_PRIVATE Table *sqlite3BinlogFindTable(BtCursor *pC){
   if (pC==NULL || pC->pBtree == NULL || pC->pBtree->pBt == NULL || pC->pBtree->pBt->pSchema == NULL) {
@@ -259236,6 +259711,51 @@ SQLITE_PRIVATE Table *sqlite3BinlogFindTable(BtCursor *pC){
   return pTab;
 }
 
+SQLITE_PRIVATE void CheckAndNotify(sqlite3 *db, char *tableName){
+  if (tableName == NULL) {
+      return;
+  }
+  if (db->xBinlogHandle.mode != ROW_FOR_SEARCH) {
+    return;
+  }
+  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.monitorConfig == NULL) {
+      return;
+  }
+  MonitorTablesConfig *config = db->xBinlogHandle.monitorConfig;
+  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;
+        }
+        db->xBinlogHandle.xChangeCallback(zFile, tableName);
+    }
+  }
+}
+
 SQLITE_PRIVATE int sqlite3BinlogWriteTable(Vdbe *p, Table *pTable){
   assert( p!=NULL );
   assert( pTable!=NULL );
@@ -259424,6 +259944,7 @@ SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor, const
   }
 
   if (sqlite3BinlogWriteRowData(p, pRow) == SQLITE_OK) {
+    CheckAndNotify(db, pTab->zName);
     return;
   }
 
@@ -259440,7 +259961,7 @@ SQLITE_PRIVATE int sqlite3IsRowBasedBinlog(Vdbe *p) {
   return p 
     && p->db
     && (p->db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE)
-    && (p->db->xBinlogHandle.mode == ROW);
+    && ((p->db->xBinlogHandle.mode == ROW) || (p->db->xBinlogHandle.mode == ROW_FOR_SEARCH));
 }
 
 /* Get i-th column value from a row formated data. i starts from 0 */
@@ -259562,6 +260083,51 @@ SQLITE_PRIVATE char *sqlite3BinlogGetNthCol(sqlite3 *db, const Table *pTab, cons
   return res;
 }
 
+/* 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) {
+  assert( db != NULL && pTab != NULL && pRow != NULL );
+  assert( iCol >= 0 );
+  if (HasRowid(pTab) && pTab->iPKey == iCol) {
+      return sqlite3_mprintf("%lld", pRow->rowid);
+  }
+
+  sqlite3_value *tempVal = NULL;
+  sqlite3_uint64 nData = pRow->nData + (sqlite3_uint64)pRow->nZero;
+  char *pData = sqlite3MallocZero(nData);
+  if (pData == NULL) {
+    sqlite3_log(SQLITE_ERROR, "binlog failed to allocate memory");
+    return NULL;
+  }
+  errno_t errNo = memcpy_s(pData, nData, pRow->pData, pRow->nData);
+  if (errNo != EOK) {
+    sqlite3_log(SQLITE_ERROR, "binlog failed to copy data, rc:%d, errno:%d", errNo, errno);
+    sqlite3_free(pData);
+    return NULL;
+  }
+  sqlite3BinlogStat4Column(db, pData, nData, iCol, &tempVal);
+  if (tempVal == NULL) {
+    sqlite3_log(SQLITE_ERROR, "decode data failed");
+    sqlite3_free(pData);
+    return NULL;
+  }
+  int type = sqlite3_value_type(tempVal);
+  const unsigned char *temp = sqlite3_value_text(tempVal);
+
+  char *res = NULL;
+  if (type == SQLITE_BLOB) {
+    res = sqlite3BinlogGetHexFromBlobOrStr(db, tempVal);
+  } else if (type == SQLITE_TEXT) {
+    res = sqlite3_mprintf("%s", temp);
+  } else if (type == SQLITE_NULL) {
+    res = sqlite3_mprintf("NULL");
+  } else {
+    res = sqlite3_mprintf("%s", temp);
+  }
+  sqlite3ValueFree(tempVal);
+  sqlite3_free(pData);
+  return res;
+}
+
 /* Check if binlog is enabled and the stmtType needs being logged */
 SQLITE_PRIVATE int sqlite3IsSkipWriteBinlog(Vdbe *p) {
   int isBinlogEnabled = (p != NULL) && (p->db != NULL) && 
@@ -259669,6 +260235,7 @@ SQLITE_PRIVATE int sqlite3BinlogClose(sqlite3 *db)
       return rc;
     }
   }
+  sqlite3FreeMonitorTablesConfig(db, db->xBinlogHandle.monitorConfig);
   sqlite3BinlogReset(db);
   return SQLITE_OK;
 }
@@ -259688,6 +260255,8 @@ SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db)
   db->xBinlogHandle.binlogApi.binlogReadApi = NULL;
   db->xBinlogHandle.binlogApi.binlogFileCleanApi = NULL;
   db->xBinlogHandle.binlogApi.binlogFreeReadResultApi = NULL;
+  db->xBinlogHandle.binlogApi.binlogSetSearchWaterMark = NULL;
+  db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi = NULL;
   db->xBinlogHandle.binlogApi.binlogLockReadApi = NULL;
   db->xBinlogHandle.binlogApi.binlogUnlockReadApi = NULL;
   db->xBinlogHandle.callbackCtx = NULL;
@@ -259698,8 +260267,41 @@ SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db)
   }
   db->xBinlogHandle.isSkipTrigger = 0;
 }
- 
-SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, Sqlite3BinlogStmt *bStmt)
+
+SQLITE_PRIVATE int sqlite3BinlogResetHwm(sqlite3 *db)
+{
+  if (db == NULL || db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi == NULL) {
+    sqlite3_log(SQLITE_ERROR, "binlog reset hwm is null");
+    return SQLITE_ERROR;
+  }
+  int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogResetSearchHwmApi(db->xBinlogHandle.binlogConn));
+  if (rc != SQLITE_OK) {
+    if (rc != SQLITE_DONE) {
+      sqlite3_log(rc, "binlog reset hwm err:%d", rc);
+    }
+    return rc;
+  }
+  return SQLITE_OK;
+}
+
+SQLITE_PRIVATE int sqlite3BinlogSetHwm(sqlite3 *db, BinlogSearchHwmT *waterMark, BinlogHwmSetModeE setMode)
+{
+  if (db == NULL || db->xBinlogHandle.binlogApi.binlogSetSearchWaterMark == NULL) {
+    sqlite3_log(SQLITE_ERROR, "binlog set hwm parameter is null");
+    return SQLITE_ERROR;
+  }
+  int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogSetSearchWaterMark(db->xBinlogHandle.binlogConn,
+    waterMark, setMode));
+  if (rc != SQLITE_OK) {
+    if (rc != SQLITE_DONE) {
+      sqlite3_log(rc, "binlog set hwm err:%d", rc);
+    }
+    return rc;
+  }
+  return SQLITE_OK;
+}
+
+SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, BinlogReadModeE readMode, Sqlite3BinlogStmt *bStmt)
 {
   if (db == NULL || db->xBinlogHandle.binlogApi.binlogReadApi == NULL || bStmt == NULL) {
     sqlite3_log(SQLITE_ERROR, "binlog stmt prepare parameter is null");
@@ -259707,7 +260309,7 @@ SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, Sqlite3BinlogStmt *bStm
   }
   BinlogReadResultT *result = NULL;
   int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogReadApi(db->xBinlogHandle.binlogConn,
-    &result));
+    readMode, &result));
   if (rc != SQLITE_OK) {
     if (rc != SQLITE_DONE) {
       sqlite3_log(rc, "binlog stmt prepare err:%d", rc);
@@ -259822,6 +260424,101 @@ SQLITE_PRIVATE int sqlite3BinlogStmtStep(sqlite3 *db, Sqlite3BinlogStmt *bStmt,
   return SQLITE_ROW;
 }
  
+SQLITE_PRIVATE int checkIfMonitorTable(sqlite3 *db, char *tableName)
+{
+  MonitorTablesConfig *config = db->xBinlogHandle.monitorConfig;
+  if (config == NULL) {
+      return 1;
+  }
+  for (int i = 0; i < config->tableCount; i++) {
+    if (strcmp(tableName, config->tables[i].tableName) == 0) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+SQLITE_PRIVATE int sqlite3BinlogGetSearchData(sqlite3 *db, Sqlite3BinlogStmt *bStmt, BinlogSearchResultSet **rs, Table **pOutTable)
+{
+  if (bStmt == NULL || bStmt->cursor == NULL) {
+    sqlite3_log(SQLITE_ERROR, "binlog stmt step parameter is null");
+    return SQLITE_ERROR;
+  }
+  if (bStmt->curIdx >= bStmt->cursor->eventNum) {
+    return SQLITE_DONE;
+  }
+  BinlogEventT *event = bStmt->cursor->sqlEvent[bStmt->curIdx];
+  if (event == NULL) {
+    sqlite3_log(SQLITE_ERROR, "binlog stmt step event is null");
+    return SQLITE_ERROR;
+  }
+
+  int rc = SQLITE_OK;
+  switch (event->head.eventType) {
+    case BINLOG_EVENT_TYPE_ROW_TABLE: {
+      char *pTableName = (char *)event->body;
+      *pOutTable = sqliteBinlogGetTable(db, pTableName, &rc);
+      if (*pOutTable == NULL) {
+        rc = SQLITE_ERROR;
+        sqlite3_log(SQLITE_WARNING, "search binlog find no table, rc=%d", rc);
+      } else {
+        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 (!checkIfMonitorTable(db, (*pOutTable)->zName)) {
+          return SQLITE_ROW;
+      }
+      // Use the new search function that fills BinlogSearchResult
+      char *buffer = (char *)event->body;
+      u32 nBuffer = event->head.eventLength;
+
+      u8 op = 0;
+      sqlite3_int64 rowid = 0;
+      sqlite3_uint64 nData = 0;
+      int nZero = 0;
+      char *pData = NULL;
+      rc = sqlite3BinlogDecodeRowBuffer(buffer, nBuffer, &op, &rowid, &nData, &nZero, &pData);
+      if (rc != SQLITE_OK) {
+        return rc;
+      }
+
+      BinlogRow pRow;
+      pRow.op = op;
+      pRow.nData = nData;
+      pRow.nZero = nZero;
+      pRow.pData = pData;
+      pRow.rowid = rowid;
+      int row_count = (*rs)->row_count;
+      if (row_count >= (*rs)->capacity) {
+        rc = BinlogSearchResultExpand(db, rs);
+        if (rc != SQLITE_OK) {
+          sqlite3_log(rc, "binlog expand resSet for from capicity err:%d", (*rs)->capacity, rc);
+          return rc;
+        }
+      }
+      rc = sqlite3GetBinlogRowColsSearch(db, &pRow, *pOutTable, &(*rs)->results[row_count]);
+      if (rc != SQLITE_OK) {
+        sqlite3_log(rc, "binlog get row cols for search err:%d", rc);
+        return rc;
+      }
+      
+      (*rs)->results[row_count].fileIndex = bStmt->cursor->waterMark.readFileIndex;
+      (*rs)->results[row_count].readPos = bStmt->cursor->waterMark.readPos;
+      (*rs)->row_count++;
+      return SQLITE_ROW;
+    }
+    default:
+      return SQLITE_ROW;
+  }
+}
+
 SQLITE_PRIVATE void sqlite3BinlogStmtFinalize(sqlite3 *db, Sqlite3BinlogStmt *bStmt)
 {
   if (db == NULL || bStmt == NULL || bStmt->cursor == NULL ||
@@ -259877,7 +260574,7 @@ SQLITE_PRIVATE int sqlite3BinlogReplay(sqlite3 *srcDb, sqlite3 *destDb)
     Sqlite3BinlogStmt bStmt;
     bStmt.curIdx = 0;
     bStmt.cursor = NULL;
-    res = sqlite3BinlogStmtPrepare(srcDb, &bStmt);
+    res = sqlite3BinlogStmtPrepare(srcDb, BINLOG_REPLAY_MODE, &bStmt);
     if (res != SQLITE_OK) {
       if (res == SQLITE_DONE) {
         res = SQLITE_OK;
@@ -259918,7 +260615,131 @@ SQLITE_PRIVATE int sqlite3BinlogReplay(sqlite3 *srcDb, sqlite3 *destDb)
   }
   return res;
 }
- 
+
+SQLITE_PRIVATE int BinlogSearchResultSetCreate(sqlite3 *srcDb, int capacity, BinlogSearchResultSet **rs)
+{
+    if (capacity <= 0) {
+        capacity = 100;  // default capacity
+    }
+
+    BinlogSearchResultSet *rsInner = (BinlogSearchResultSet *)sqlite3DbMallocZero(srcDb, sizeof(BinlogSearchResultSet));
+    if (rsInner == NULL) {
+        return SQLITE_ERROR;
+    }
+    rsInner->capacity = capacity;
+    rsInner->row_count = 0;
+    rsInner->results = (BinlogSearchResult *)sqlite3DbMallocZero(srcDb, capacity * sizeof(BinlogSearchResult));
+    if (rsInner->results == NULL) {
+        sqlite3DbFree(srcDb, rsInner);
+        return SQLITE_ERROR;
+    }
+    *rs = rsInner;
+    return SQLITE_OK;
+}
+
+SQLITE_PRIVATE int BinlogSearchResultExpand(sqlite3 *srcDb, BinlogSearchResultSet **rs)
+{
+    if (rs == NULL || *rs == NULL) {
+        return SQLITE_ERROR;
+    }
+    int newCapacity = (*rs)->capacity * 2;
+    BinlogSearchResult *newResults = (BinlogSearchResult *)sqlite3DbMallocZero(srcDb, newCapacity * sizeof(BinlogSearchResult));
+    if (newResults == NULL) {
+        sqlite3_log(SQLITE_ERROR, " binlog resSet expand failed");
+        return SQLITE_ERROR;
+    }
+    for (int i = 0; i < (*rs)->row_count; i++) {
+      newResults[i] = (*rs)->results[i];
+    }
+    sqlite3DbFree(srcDb, (*rs)->results);
+    (*rs)->results = newResults;
+    (*rs)->capacity = newCapacity;
+    return SQLITE_OK;
+}
+
+SQLITE_PRIVATE void BinlogSearchResultFree(sqlite3 *srcDb, BinlogSearchResult *row)
+{
+    if (row == NULL) return;
+
+    if (row->nameAndValues != NULL) {
+        // release array
+        for (sqlite3_uint64 i = 0; i < 2 * row->nCol; i++) {
+            sqlite3DbFree(srcDb, row->nameAndValues[i]); // release row content
+        }
+        sqlite3DbFree(srcDb, row->nameAndValues);
+        row->nameAndValues = NULL;
+    }
+}
+
+/**
+ * @brief destroy all results set
+ */
+SQLITE_PRIVATE void BinlogSearchResultSetDestroy(sqlite3 *srcDb, BinlogSearchResultSet **rs)
+{
+    if (rs == NULL || *rs == NULL) return;
+
+    for (int i = 0; i < (*rs)->row_count; i++) {
+        BinlogSearchResultFree(srcDb, &(*rs)->results[i]);
+    }
+    sqlite3DbFree(srcDb, (*rs)->results);
+    sqlite3DbFree(srcDb, (*rs));
+    *rs = NULL;
+}
+
+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");
+    return SQLITE_ERROR;
+  }
+  const char *srcFile = sqlite3_db_filename(srcDb, 0);
+  if (srcFile == NULL) {
+    return SQLITE_ERROR;
+  }
+  uint16_t capacity = 100;
+  int res = BinlogSearchResultSetCreate(srcDb, capacity, rs);
+  if (res != SQLITE_OK) {
+    return res;
+  }
+  destDb->xBinlogHandle.isSkipTrigger = 1;
+  int replayTrxCount = 0;
+  do {
+    Sqlite3BinlogStmt bStmt;
+    bStmt.curIdx = 0;
+    bStmt.cursor = NULL;
+    res = sqlite3BinlogStmtPrepare(srcDb, BINLOG_SEARCH_MODE, &bStmt);
+    if (res != SQLITE_OK) {
+      if (res != SQLITE_DONE) {
+        sqlite3_log(res, "read binlog failed");
+      }
+      break;
+    }
+    Table *pTab = NULL;
+    while ((res = sqlite3BinlogGetSearchData(srcDb, &bStmt, rs, &pTab)) == SQLITE_ROW) {
+      bStmt.curIdx++;
+    }
+    sqlite3BinlogStmtFinalize(srcDb, &bStmt);
+    if (res != SQLITE_DONE && res != SQLITE_OK) {
+      sqlite3BinlogErrorCallback(srcDb, res, "exec replay sql failed");
+      break;
+    }
+    if (res == SQLITE_DONE && (*rs)->row_count >= 100) {
+      res = SQLITE_OK;
+      break;
+    }
+    replayTrxCount++;
+    // more than 100 then return
+    if ((*rs)->row_count < 100 && res == SQLITE_DONE) {
+      res = SQLITE_OK;
+      continue;
+    }
+  } while (1);
+  if ((*rs)->row_count == 0) {
+    BinlogSearchResultSetDestroy(srcDb, rs);
+  }
+  return res;
+}
+
 SQLITE_PRIVATE int sqlite3BinlogClean(sqlite3 *db, BinlogFileCleanModeE mode)
 {
   if (db == NULL || mode < BINLOG_FILE_CLEAN_ALL_MODE ||
@@ -259984,7 +260805,24 @@ struct sqlite3_api_routines_extra {
 #endif
   int (*is_support_binlog)(void);
   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 (*get_search_data_binlog)(sqlite3*, sqlite3*, BinlogSearchResultSet**);
+  int (*free_search_data_binlog)(sqlite3*, BinlogSearchResultSet**);
+  int (*set_search_hwm_binlog)(sqlite3*, BinlogSearchHwmT*, BinlogHwmSetModeE);
+  int (*reset_search_hwm_binlog)(sqlite3*);
   int (*clean_binlog)(sqlite3*, BinlogFileCleanModeE);
+#else
+  void *dymmyFunc;
+  void *dymmyFunc2;
+  void *dymmyFunc3;
+  void *dymmyFunc4;
+  void *dymmyFunc5;
+  void *dymmyFunc6;
+  void *dymmyFunc7;
+#endif 
 #ifdef SQLITE_ENABLE_PAGE_COMPRESS
   int (*compressdb_backup)(sqlite3*, const char*);
 #else
@@ -260012,11 +260850,25 @@ static const sqlite3_api_routines_extra sqlite3ExtraApis = {
 #ifdef SQLITE_ENABLE_BINLOG
   sqlite3_is_support_binlog,
   sqlite3_replay_binlog,
+  sqlite3_set_monitor_config_binlog,
+  sqlite3_set_xChange_callback_binlog,
+  sqlite3_set_json_parse_callback_binlog,
+  sqlite3_get_search_data_binlog,
+  sqlite3_free_search_data_binlog,
+  sqlite3_set_search_hwm_binlog,
+  sqlite3_reset_search_hwm_binlog,
   sqlite3_clean_binlog,
 #else
   0,
   0,
   0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
+  0,
 #endif/* SQLITE_ENABLE_BINLOG */
 #ifdef SQLITE_ENABLE_PAGE_COMPRESS
   sqlite3_compressdb_backup,
-- 
2.34.1