• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1From d9bf27e15f4d3ebe73de27fa608c06c5049d34e3 Mon Sep 17 00:00:00 2001
2From: Liu Hongyang <liuhongyang4@huawei.com>
3Date: Fri, 15 Aug 2025 14:28:12 +0800
4Subject: [PATCH 06/12] Support-Binlog
5
6---
7 src/sqlite3.c | 1522 ++++++++++++++++++++++++++++++++++++++++++++++++-
8 1 file changed, 1513 insertions(+), 9 deletions(-)
9
10diff --git a/src/sqlite3.c b/src/sqlite3.c
11index b433dfb..4105866 100644
12--- a/src/sqlite3.c
13+++ b/src/sqlite3.c
14@@ -2938,7 +2938,9 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
15 #define SQLITE_DBCONFIG_SET_SHAREDBLOCK  2004
16 #define SQLITE_DBCONFIG_USE_SHAREDBLOCK  2005
17 #endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */
18-
19+#ifdef SQLITE_ENABLE_BINLOG
20+#define SQLITE_DBCONFIG_ENABLE_BINLOG    2006 /* Sqlite3BinlogConfig */
21+#endif
22 /*
23 ** CAPI3REF: Set the Last Insert Rowid value.
24 ** METHOD: sqlite3
25@@ -5351,6 +5353,19 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
26 SQLITE_API int sqlite3_set_droptable_handle(sqlite3*, void (*xFunc)(sqlite3*,const char*,const char*));
27 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
28
29+#ifdef SQLITE_ENABLE_BINLOG
30+SQLITE_API int sqlite3_is_support_binlog(void);
31+
32+SQLITE_API int sqlite3_replay_binlog(sqlite3 *srcDb, sqlite3 *destDb);
33+
34+typedef enum BinlogFileCleanMode {
35+  BINLOG_FILE_CLEAN_ALL_MODE = 0,
36+  BINLOG_FILE_CLEAN_READ_MODE = 1,
37+  BINLOG_FILE_CLEAN_MODE_MAX,
38+} BinlogFileCleanModeE;
39+
40+SQLITE_API int sqlite3_clean_binlog(sqlite3 *db, BinlogFileCleanModeE mode);
41+#endif
42 /*
43 ** CAPI3REF: Number of columns in a result set
44 ** METHOD: sqlite3_stmt
45@@ -17617,6 +17632,201 @@ typedef struct CodecParameter {
46 } CodecParameter;
47 #endif /* defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) */
48
49+#ifdef SQLITE_ENABLE_BINLOG
50+/************** Begin of the header file of binlog ************************************/
51+#define SQLITE_UUID_BLOB_LENGTH 16
52+
53+typedef enum {
54+  ROW = 0,
55+} Sqlite3BinlogMode;
56+
57+typedef enum {
58+  GMERR_OK = 0,
59+  GMERR_BASE = 1000000,
60+  GMERR_LOCK_NOT_AVAILABLE = GMERR_BASE + 12002,
61+  GMERR_BINLOG_READ_FINISH = GMERR_BASE + 29200,
62+} BinlogErrnoE;
63+
64+typedef int BinlogErrno;
65+typedef void (*BinlogOnErrorFuncT)(void *pCtx, BinlogErrno errNo, char *errMsg, const char *dbPath);
66+typedef void (*BinlogOnLogFullFuncT)(void *pCtx, u16 currentCount, const char *dbPath);
67+typedef int (*BinlogOnErrnoTransFuncT)(BinlogErrno errNo);
68+
69+typedef struct BinlogConfig {
70+  Sqlite3BinlogMode logMode;
71+  u16 fullCallbackThreshold;
72+  u32 maxFileSize;
73+  char *filePath;
74+  BinlogOnErrorFuncT onError;
75+  BinlogOnLogFullFuncT onLogFull;
76+  void *callbackCtx;
77+  u32 readHwmdelay;
78+  BinlogOnErrnoTransFuncT onErrnoTrans;
79+} BinlogConfigT;
80+
81+typedef enum BinlogEventType {
82+  BINLOG_EVENT_TYPE_UNDEFINED = 0,
83+  BINLOG_EVENT_TYPE_DDL = 64,
84+  BINLOG_EVENT_TYPE_PRAGMA = 65,
85+  BINLOG_EVENT_TYPE_DML = 66,
86+  BINLOG_EVENT_TYPE_ROLLBACK = 67,
87+  BINLOG_EVENT_TYPE_ROW_START = 69,
88+  BINLOG_EVENT_TYPE_ROW_TABLE = 70,
89+  BINLOG_EVENT_TYPE_ROW_FULL_DATA = 71,
90+  BINLOG_EVENT_TYPE_ROW_COMMIT = 72,
91+  BINLOG_EVENT_TYPE_ROW_ROLLBACK = 73,
92+  BINLOG_EVENT_TYPE_BUTT,
93+} BinlogEventTypeE;
94+
95+typedef u8 BinlogXidT[SQLITE_UUID_BLOB_LENGTH];
96+
97+typedef struct BinlogWriteData {
98+  BinlogEventTypeE type;
99+  BinlogXidT xid;
100+  char *data;
101+  u32 dataLength;
102+  int isFinishTrx;
103+} BinlogWriteDataT;
104+
105+typedef struct BinlogEventHead {
106+  u32 checksum;
107+  u64 timestamp;
108+  u8 eventType;
109+  u32 eventLength;
110+  BinlogXidT xid;
111+  u64 nextEventPos;
112+} BinlogEventHeadT;
113+
114+typedef struct BinlogEvent {
115+  BinlogEventHeadT head;
116+  u8 *body;
117+} BinlogEventT;
118+
119+typedef struct BinlogReadResult {
120+  u32 eventNum;
121+  BinlogEventT **sqlEvent;
122+} BinlogReadResultT;
123+
124+typedef struct BinlogInstanceT BinlogInstanceT;
125+typedef void (*BinlogFreeReadResult)(BinlogInstanceT *instance, BinlogReadResultT *readRes);
126+typedef BinlogErrno (*BinlogOpen)(const BinlogConfigT *, BinlogInstanceT **);
127+typedef BinlogErrno (*BinlogClose)(BinlogInstanceT *instance);
128+typedef BinlogErrno (*BinlogWrite)(BinlogInstanceT *instance, const BinlogWriteDataT *data);
129+typedef BinlogErrno (*BinlogRead)(BinlogInstanceT *instance, BinlogReadResultT **readRes);
130+typedef BinlogErrno (*BinlogFileClean)(BinlogInstanceT *instance, BinlogFileCleanModeE cleanMode);
131+typedef BinlogErrno (*BinlogLockRead)(BinlogInstanceT *instance);
132+typedef BinlogErrno (*BinlogUnlockRead)(BinlogInstanceT *instance);
133+/************** End of the header file of binlog ************************************/
134+
135+/************** Binlog typedef in sqlite3 BEGIN ************************************/
136+#define BINLOG_FLAG_ENABLE         0x00000001
137+#define BINLOG_FLAG_UPDATE_TID     0x00000002
138+
139+typedef struct Sqlite3BinlogConfig {
140+  Sqlite3BinlogMode mode;
141+  u16 fullCallbackThreshold;
142+  u32 maxFileSize;
143+  void (*xErrorCallback)(void *pCtx, int errNo, char *errMsg, const char *dbPath);
144+  void (*xLogFullCallback)(void *pCtx, u16 currentCount, const char *dbPath);
145+  void *callbackCtx;
146+} Sqlite3BinlogConfig;
147+
148+typedef struct BinlogRow {
149+  int op;                // one of the SQLITE_INSERT, SQLITE_UPDATE, SQLITE_DELETE
150+  sqlite3_int64 rowid;   // rowid of changed data, -1 for without rowid table
151+  sqlite3_uint64 nData;  // size of pData
152+  const char *pData;     // pointer to the row data in record format
153+} BinlogRow;
154+
155+/* stores the affected rows by one DML statement*/
156+typedef struct BinlogDMLData {
157+  Table *pTable;           // pointer to the table being modified
158+  char *pSavePointName;    // savepoint name for all the rows modified by current statment
159+  int isSavePointReleased; // if the savepoint is released, used when free BinlogDMLData
160+} BinlogDMLData;
161+
162+typedef void* BinlogLib;
163+
164+typedef struct BinlogApi {
165+  BinlogLib binlogLib;
166+  BinlogOpen binlogOpenApi;
167+  BinlogClose binlogCloseApi;
168+  BinlogWrite binlogWriteApi;
169+  BinlogRead binlogReadApi;
170+  BinlogFreeReadResult binlogFreeReadResultApi;
171+  BinlogFileClean binlogFileCleanApi;
172+  BinlogLockRead binlogLockReadApi;
173+  BinlogUnlockRead binlogUnlockReadApi;
174+} BinlogApi;
175+
176+typedef struct Sqlite3BinlogHandle {
177+  Sqlite3BinlogMode mode;
178+  void *callbackCtx;
179+  u64 flags;
180+  u8 xTid[SQLITE_UUID_BLOB_LENGTH];
181+  void (*xErrorCallback)(void *pCtx, int errNo, char *errMsg, const char *dbPath);
182+  void (*xLogFullCallback)(void *pCtx, u16 currentCount, const char *dbPath);
183+  BinlogInstanceT *binlogConn;
184+  BinlogApi binlogApi;
185+  u8 isSkipTrigger;
186+} Sqlite3BinlogHandle;
187+
188+typedef enum {
189+  STMT_TYPE_DML = 0,
190+  STMT_TYPE_CREATE_TABLE,
191+  STMT_TYPE_CREATE_INDEX,
192+  STMT_TYPE_CREATE_TRIGGER,
193+  STMT_TYPE_CREATE_VIEW,
194+  STMT_TYPE_DROP_TABLE,
195+  STMT_TYPE_DROP_INDEX,
196+  STMT_TYPE_DROP_TRIGGER,
197+  STMT_TYPE_DROP_VIEW,
198+  STMT_TYPE_ALTER_ADD_COL,
199+  STMT_TYPE_ALTER_RENAME_COL,
200+  STMT_TYPE_ALTER_RENAME_TABLE,
201+  STMT_TYPE_ALTER_DROP_COL,
202+  STMT_TYPE_BEGIN_TRANSACTION,
203+  STMT_TYPE_COMMIT_TRANSACTION,
204+  STMT_TYPE_ROLLBACK_TRANSACTION,
205+  STMT_TYPE_PRAGMA,
206+  STMT_TYPE_SAVEPOINT,
207+} StmtType;
208+
209+typedef struct Sqlite3BinlogStmt {
210+  BinlogReadResultT *cursor;
211+  u32 curIdx;
212+} Sqlite3BinlogStmt;
213+
214+typedef struct Sqlite3BinlogApiInfo {
215+  void **funcP;
216+  const char *funcN;
217+} Sqlite3BinlogApiInfo;
218+
219+SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, Sqlite3BinlogStmt *bStmt);
220+SQLITE_PRIVATE int sqlite3BinlogStmtStep(sqlite3 *db, Sqlite3BinlogStmt *bStmt, char **sql, Table **pOutTable);
221+SQLITE_PRIVATE void sqlite3BinlogStmtFinalize(sqlite3 *db, Sqlite3BinlogStmt *bStmt);
222+
223+SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db);
224+SQLITE_PRIVATE int sqlite3SetBinLogConfig(sqlite3 *db, Sqlite3BinlogConfig *bConfig);
225+SQLITE_PRIVATE int sqlite3TransferBinlogErrno(BinlogErrno err);
226+
227+SQLITE_PRIVATE void sqlite3BinlogWrite(Vdbe *p);
228+SQLITE_PRIVATE int sqlite3BinlogClose(sqlite3 *db);
229+SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db);
230+SQLITE_PRIVATE int sqlite3BinlogReplay(sqlite3 *srcDb, sqlite3 *destDb);
231+SQLITE_PRIVATE int sqlite3BinlogClean(sqlite3 *db, BinlogFileCleanModeE mode);
232+SQLITE_PRIVATE void sqlite3BinlogErrorCallback(sqlite3 *db, int errNo, char *errMsg);
233+SQLITE_PRIVATE BinlogEventTypeE sqlite3TransferLogEventType(StmtType stmtType);
234+SQLITE_PRIVATE int sqlite3IsSkipWriteBinlog(Vdbe *p);
235+SQLITE_PRIVATE int sqlite3IsRowBasedBinlog(Vdbe *p);
236+SQLITE_PRIVATE Table *sqlite3BinlogFindTable(BtCursor *pC);
237+SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor, sqlite3_uint64 nData,
238+  const char *pData, int op, sqlite3_int64 rowid);
239+SQLITE_PRIVATE void sqlite3FreeBinlogRowData(Vdbe *p);
240+SQLITE_PRIVATE char *sqlite3BinlogGetNthCol(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol);
241+/************** Binlog typedef in sqlite3 END ************************************/
242+#endif
243+
244 /*
245 ** Each database connection is an instance of the following structure.
246 */
247@@ -17770,6 +17980,9 @@ struct sqlite3 {
248 #if defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED)
249   CodecParameter codecParm;
250 #endif /* defined(SQLITE_HAS_CODEC) && defined(SQLITE_CODEC_ATTACH_CHANGED) */
251+#ifdef SQLITE_ENABLE_BINLOG
252+  Sqlite3BinlogHandle xBinlogHandle;
253+#endif
254 };
255
256 /*
257@@ -23666,6 +23879,14 @@ struct Vdbe {
258   int startPos;
259   int addedRows;
260 #endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */
261+#ifdef SQLITE_ENABLE_BINLOG
262+/* begin of binlog related field */
263+  StmtType stmtType;              /* Type of binlog statement */
264+  BinlogDMLData *pBinlogDMLData;  /* Data for binlog row-based DML statement */
265+  const char *pDataTmp;           /* Temporary binlog data for op_notfound */
266+  sqlite3_uint64 nDataTmp;        /* Size of pDataTmp */
267+/* end of binlog related field */
268+#endif
269 };
270
271 /*
272@@ -88435,7 +88656,11 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
273         rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
274       }
275     }
276-
277+#ifdef SQLITE_ENABLE_BINLOG
278+    if ( rc==SQLITE_OK ){
279+      sqlite3BinlogWrite(p);
280+    }
281+#endif
282     /* Do the commit only if all databases successfully complete phase 1.
283     ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
284     ** IO error while deleting or truncating a journal file. It is unlikely,
285@@ -88583,6 +88808,9 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
286     ** transaction is already guaranteed, but some stray 'cold' journals
287     ** may be lying around. Returning an error code won't help matters.
288     */
289+#ifdef SQLITE_ENABLE_BINLOG
290+    sqlite3BinlogWrite(p);
291+#endif
292     disable_simulated_io_errors();
293     sqlite3BeginBenignMalloc();
294     for(i=0; i<db->nDb; i++){
295@@ -88868,6 +89096,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
296     }else if( eStatementOp==0 ){
297       if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
298         eStatementOp = SAVEPOINT_RELEASE;
299+#ifdef SQLITE_ENABLE_BINLOG
300+        sqlite3BinlogWrite(p);
301+#endif
302       }else if( p->errorAction==OE_Abort ){
303         eStatementOp = SAVEPOINT_ROLLBACK;
304       }else{
305@@ -89090,6 +89321,9 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
306       fclose(out);
307     }
308   }
309+#endif
310+#ifdef SQLITE_ENABLE_BINLOG
311+  sqlite3FreeBinlogRowData(p);
312 #endif
313   return p->rc & db->errMask;
314 }
315@@ -89194,6 +89428,9 @@ static void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
316     sqlite3DbFree(db, p->aScan);
317   }
318 #endif
319+#ifdef SQLITE_ENABLE_BINLOG
320+  sqlite3FreeBinlogRowData(p);
321+#endif
322 }
323
324 /*
325@@ -91886,6 +92123,44 @@ SQLITE_API int sqlite3_set_droptable_handle(sqlite3 *db, void (*xFunc)(sqlite3*,
326 }
327 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
328
329+#ifdef SQLITE_ENABLE_BINLOG
330+SQLITE_API int sqlite3_is_support_binlog(void)
331+{
332+  return SQLITE_ERROR;
333+}
334+
335+SQLITE_API int sqlite3_replay_binlog(sqlite3 *srcDb, sqlite3 *destDb)
336+{
337+  if (srcDb == NULL || destDb == NULL) {
338+    sqlite3_log(SQLITE_ERROR, "replay binlog parameter is null");
339+    return SQLITE_ERROR;
340+  }
341+  int rc = SQLITE_OK;
342+  sqlite3_mutex_enter(srcDb->mutex);
343+  rc = sqlite3BinlogReplay(srcDb, destDb);
344+  if (rc != SQLITE_OK) {
345+    sqlite3_log(rc, "replay binlog err:%d", rc);
346+  }
347+  sqlite3_mutex_leave(srcDb->mutex);
348+  return rc;
349+}
350+
351+SQLITE_API int sqlite3_clean_binlog(sqlite3 *db, BinlogFileCleanModeE mode)
352+{
353+  if (db == NULL) {
354+    sqlite3_log(SQLITE_ERROR, "clean binlog parameter is null");
355+    return SQLITE_ERROR;
356+  }
357+  int rc = SQLITE_OK;
358+  sqlite3_mutex_enter(db->mutex);
359+  rc = sqlite3BinlogClean(db, mode);
360+  if (rc != SQLITE_OK) {
361+    sqlite3_log(rc, "clean binlog err:%d", rc);
362+  }
363+  sqlite3_mutex_leave(db->mutex);
364+  return rc;
365+}
366+#endif
367 /*
368 ** This is the top-level implementation of sqlite3_step().  Call
369 ** sqlite3Step() to do most of the work.  If a schema error occurs,
370@@ -99189,6 +99464,12 @@ case OP_Found: {        /* jump, in3, ncycle */
371   pC->nullRow = 1-alreadyExists;
372   pC->deferredMoveto = 0;
373   pC->cacheStatus = CACHE_STALE;
374+#ifdef SQLITE_ENABLE_BINLOG
375+  if ( pOp->opcode==OP_NotFound && r.nField==0 && !sqlite3IsSkipWriteBinlog(p)){
376+    p->nDataTmp = r.aMem->n;
377+    p->pDataTmp = r.aMem->z;
378+  }
379+#endif
380   if( pOp->opcode==OP_Found ){
381     VdbeBranchTaken(alreadyExists!=0,2);
382     if( alreadyExists ) goto jump_to_p2;
383@@ -99584,6 +99865,15 @@ case OP_Insert: {
384   }
385   x.pKey = 0;
386   assert( BTREE_PREFORMAT==OPFLAG_PREFORMAT );
387+#ifdef SQLITE_ENABLE_BINLOG
388+  if ( pOp->p4.pTab
389+    && !sqlite3IsSkipWriteBinlog(p)
390+    && sqlite3IsRowBasedBinlog(p)
391+    && sqlite3TransferLogEventType(p->stmtType) == BINLOG_EVENT_TYPE_DML ) {
392+    sqlite3StoreBinlogRowData(p, pC->uc.pCursor, x.nData, pData->z,
393+      (pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT, x.nKey);
394+  }
395+#endif
396   rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
397       (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
398       seekResult
399@@ -99699,6 +99989,27 @@ case OP_Delete: {
400     assert( CORRUPT_DB || pC->movetoTarget==iKey );
401   }
402 #endif
403+#ifdef SQLITE_ENABLE_BINLOG
404+  if( !sqlite3IsSkipWriteBinlog(p)
405+    && sqlite3IsRowBasedBinlog(p)
406+    && sqlite3TransferLogEventType(p->stmtType) == BINLOG_EVENT_TYPE_DML ){
407+    if (pC->isTable) {
408+      sqlite3StoreBinlogRowData(p, pC->uc.pCursor, 0, NULL, SQLITE_DELETE, sqlite3BtreeIntegerKey(pC->uc.pCursor));
409+    } else {
410+      Table *pBinlogTab = sqlite3BinlogFindTable(pC->uc.pCursor);
411+      if (pBinlogTab != NULL && !HasRowid(pBinlogTab)) {
412+        /* If pC is not a table, then this is a delete operation in a without rowid table
413+        ** pC may have no row data or invalid row data when deletion has a subquery
414+        ** so we need to get row data from op_notfound, which is stored in p->pDataTmp
415+        */
416+        int isUseTmpData = pOp->p4type == P4_TABLE && p->nDataTmp != 0 && p->pDataTmp != NULL;
417+        sqlite3_uint64 nData = isUseTmpData ? p->nDataTmp : pC->szRow;
418+        const char *pData = isUseTmpData ? p->pDataTmp : (const char *)pC->aRow;
419+        sqlite3StoreBinlogRowData(p, pC->uc.pCursor, nData, pData, SQLITE_DELETE, -1);
420+      }
421+    }
422+  }
423+#endif
424
425   /* If the update-hook or pre-update-hook will be invoked, set zDb to
426   ** the name of the db to pass as to it. Also set local pTab to a copy
427@@ -100322,6 +100633,14 @@ case OP_IdxInsert: {        /* in2 */
428   x.pKey = pIn2->z;
429   x.aMem = aMem + pOp->p3;
430   x.nMem = (u16)pOp->p4.i;
431+#ifdef SQLITE_ENABLE_BINLOG
432+  if( (pOp->p5 & OPFLAG_NCHANGE)
433+    && !sqlite3IsSkipWriteBinlog(p)
434+    && sqlite3IsRowBasedBinlog(p)
435+    && sqlite3TransferLogEventType(p->stmtType) == BINLOG_EVENT_TYPE_DML ){
436+    sqlite3StoreBinlogRowData(p, pC->uc.pCursor, x.nKey, pIn2->z, SQLITE_INSERT, -1);
437+  }
438+#endif
439   rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
440        (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
441       ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
442@@ -116953,7 +117272,11 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
443
444   renameReloadSchema(pParse, iDb, INITFLAG_AlterRename);
445   renameTestSchema(pParse, zDb, iDb==1, "after rename", 0);
446-
447+#ifdef SQLITE_ENABLE_BINLOG
448+  if( v ){
449+    v->stmtType = STMT_TYPE_ALTER_RENAME_TABLE;
450+  }
451+#endif
452 exit_rename_table:
453   sqlite3SrcListDelete(db, pSrc);
454   sqlite3DbFree(db, zName);
455@@ -117135,6 +117458,9 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
456         zTab, zDb
457       );
458     }
459+#ifdef SQLITE_ENABLE_BINLOG
460+    v->stmtType = STMT_TYPE_ALTER_ADD_COL;
461+#endif
462   }
463 }
464
465@@ -117346,7 +117672,13 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(
466   /* Drop and reload the database schema. */
467   renameReloadSchema(pParse, iSchema, INITFLAG_AlterRename);
468   renameTestSchema(pParse, zDb, iSchema==1, "after rename", 1);
469-
470+#ifdef SQLITE_ENABLE_BINLOG
471+  Vdbe *v = NULL;
472+  v = sqlite3GetVdbe(pParse);
473+  if( v ){
474+    v->stmtType = STMT_TYPE_ALTER_RENAME_COL;
475+  }
476+#endif
477  exit_rename_column:
478   sqlite3SrcListDelete(db, pSrc);
479   sqlite3DbFree(db, zOld);
480@@ -118957,7 +119289,13 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T
481     sqlite3VdbeAddOp2(v, OP_Next, iCur, addr+1); VdbeCoverage(v);
482     sqlite3VdbeJumpHere(v, addr);
483   }
484-
485+#ifdef SQLITE_ENABLE_BINLOG
486+  Vdbe *v = NULL;
487+  v = sqlite3GetVdbe(pParse);
488+  if( v ){
489+    v->stmtType = STMT_TYPE_ALTER_DROP_COL;
490+  }
491+#endif
492 exit_drop_column:
493   sqlite3DbFree(db, zCol);
494   sqlite3SrcListDelete(db, pSrc);
495@@ -123295,6 +123633,9 @@ SQLITE_PRIVATE void sqlite3StartTable(
496     sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
497     sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
498     sqlite3VdbeAddOp0(v, OP_Close);
499+#ifdef SQLITE_ENABLE_BINLOG
500+    v->stmtType = STMT_TYPE_CREATE_TABLE;
501+#endif
502   }
503
504   /* Normal (non-error) return. */
505@@ -124963,7 +125304,13 @@ SQLITE_PRIVATE void sqlite3CreateView(
506
507   /* Use sqlite3EndTable() to add the view to the schema table */
508   sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
509-
510+#ifdef SQLITE_ENABLE_BINLOG
511+  Vdbe *v = NULL;
512+  v = sqlite3GetVdbe(pParse);
513+  if( v ){
514+    v->stmtType = STMT_TYPE_CREATE_VIEW;
515+  }
516+#endif
517 create_view_fail:
518   sqlite3SelectDelete(db, pSelect);
519   if( IN_RENAME_OBJECT ){
520@@ -125486,6 +125833,9 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
521       sqlite3FkDropTable(pParse, pName, pTab);
522     }
523     sqlite3CodeDropTable(pParse, pTab, iDb, isView);
524+#ifdef SQLITE_ENABLE_BINLOG
525+    v->stmtType = (isView > 0) ? STMT_TYPE_DROP_VIEW : STMT_TYPE_DROP_TABLE;
526+#endif
527   }
528
529 exit_drop_table:
530@@ -126344,6 +126694,9 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
531         /* A named index with an explicit CREATE INDEX statement */
532         zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
533             onError==OE_None ? "" : " UNIQUE", n, pName->z);
534+#ifdef SQLITE_ENABLE_BINLOG
535+        v->stmtType = STMT_TYPE_CREATE_INDEX;
536+#endif
537       }else{
538         /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
539         /* zStmt = sqlite3MPrintf(""); */
540@@ -126546,6 +126899,9 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
541     sqlite3ChangeCookie(pParse, iDb);
542     destroyRootPage(pParse, pIndex->tnum, iDb);
543     sqlite3VdbeAddOp4(v, OP_DropIndex, iDb, 0, 0, pIndex->zName, 0);
544+#ifdef SQLITE_ENABLE_BINLOG
545+    v->stmtType = STMT_TYPE_DROP_INDEX;
546+#endif
547   }
548
549 exit_drop_index:
550@@ -127070,6 +127426,9 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
551     }
552   }
553   sqlite3VdbeAddOp0(v, OP_AutoCommit);
554+#ifdef SQLITE_ENABLE_BINLOG
555+  v->stmtType = STMT_TYPE_BEGIN_TRANSACTION;
556+#endif
557 }
558
559 /*
560@@ -127092,6 +127451,9 @@ SQLITE_PRIVATE void sqlite3EndTransaction(Parse *pParse, int eType){
561   v = sqlite3GetVdbe(pParse);
562   if( v ){
563     sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, isRollback);
564+#ifdef SQLITE_ENABLE_BINLOG
565+    v->stmtType = (isRollback > 0) ? STMT_TYPE_ROLLBACK_TRANSACTION : STMT_TYPE_COMMIT_TRANSACTION;
566+#endif
567   }
568 }
569
570@@ -127112,6 +127474,9 @@ SQLITE_PRIVATE void sqlite3Savepoint(Parse *pParse, int op, Token *pName){
571       return;
572     }
573     sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC);
574+#ifdef SQLITE_ENABLE_BINLOG
575+    v->stmtType = STMT_TYPE_SAVEPOINT;
576+#endif
577   }
578 }
579
580@@ -137480,6 +137845,13 @@ typedef int (*sqlite3_loadext_entry)(
581 /* handle after drop table done */
582 #define sqlite3_set_droptable_handle   sqlite3_api->set_droptable_handle
583 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
584+#ifdef SQLITE_ENABLE_BINLOG
585+#define sqlite3_is_support_binlog      sqlite3_api->is_support_binlog
586+/* replay binlog from src db to the dest db */
587+#define sqlite3_replay_binlog          sqlite3_api->replay_binlog
588+/* clean the binlog of the db */
589+#define sqlite3_clean_binlog           sqlite3_api->clean_binlog
590+#endif /* SQLITE_ENABLE_BINLOG */
591 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
592
593 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
594@@ -139533,6 +139905,9 @@ SQLITE_PRIVATE void sqlite3Pragma(
595   if( v==0 ) return;
596   sqlite3VdbeRunOnlyOnce(v);
597   pParse->nMem = 2;
598+#ifdef SQLITE_ENABLE_BINLOG
599+  v->stmtType = STMT_TYPE_PRAGMA;
600+#endif
601
602   /* Interpret the [schema.] part of the pragma statement. iDb is the
603   ** index of the database this pragma is being applied to in db.aDb[]. */
604@@ -152286,7 +152661,13 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
605   if (tr_tm == TK_INSTEAD){
606     tr_tm = TK_BEFORE;
607   }
608-
609+#ifdef SQLITE_ENABLE_BINLOG
610+  Vdbe *v = NULL;
611+  v = sqlite3GetVdbe(pParse);
612+  if( v ){
613+    v->stmtType = STMT_TYPE_CREATE_TRIGGER;
614+  }
615+#endif
616   /* Build the Trigger object */
617   pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger));
618   if( pTrigger==0 ) goto trigger_cleanup;
619@@ -152681,6 +153062,13 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr)
620     goto drop_trigger_cleanup;
621   }
622   sqlite3DropTriggerPtr(pParse, pTrigger);
623+#ifdef SQLITE_ENABLE_BINLOG
624+  Vdbe *v = NULL;
625+  v = sqlite3GetVdbe(pParse);
626+  if (v) {
627+    v->stmtType = STMT_TYPE_DROP_TRIGGER;
628+  }
629+#endif
630
631 drop_trigger_cleanup:
632   sqlite3SrcListDelete(db, pName);
633@@ -153427,6 +153815,15 @@ SQLITE_PRIVATE void sqlite3CodeRowTrigger(
634   assert( tr_tm==TRIGGER_BEFORE || tr_tm==TRIGGER_AFTER );
635   assert( (op==TK_UPDATE)==(pChanges!=0) );
636
637+#ifdef SQLITE_ENABLE_BINLOG
638+  /* If this db is a replica for a row-based binlog, the trigger operations
639+  ** will be recorded by the row statements. Therefore, we need to skip the
640+  ** trigger operations on replica database.
641+  */
642+  if (pParse->db->xBinlogHandle.isSkipTrigger) {
643+    return;
644+  }
645+#endif
646   for(p=pTrigger; p; p=p->pNext){
647
648     /* Sanity checking:  The schema for the trigger and for the table are
649@@ -180656,6 +181053,13 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
650       break;
651     }
652 #endif /* SQLITE_SHARED_BLOCK_OPTIMIZATION */
653+#ifdef SQLITE_ENABLE_BINLOG
654+    case SQLITE_DBCONFIG_ENABLE_BINLOG: {
655+      Sqlite3BinlogConfig *pBinlogConfig = va_arg(ap, Sqlite3BinlogConfig*);
656+      rc = sqlite3SetBinLogConfig(db, pBinlogConfig);
657+      break;
658+    }
659+#endif
660     default: {
661       static const struct {
662         int op;      /* The opcode */
663@@ -181083,6 +181487,9 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
664   sqlite3CollapseDatabaseArray(db);
665   assert( db->nDb<=2 );
666   assert( db->aDb==db->aDbStatic );
667+#ifdef SQLITE_ENABLE_BINLOG
668+  (void)sqlite3BinlogClose(db);
669+#endif
670
671   /* Tell the code in notify.c that the connection no longer holds any
672   ** locks and does not require any further unlock-notify callbacks.
673@@ -183308,6 +183715,9 @@ opendb_out:
674   db->mDropSchemaName = NULL;
675   db->xDropTableHandle = NULL;
676 #endif /* SQLITE_ENABLE_DROPTABLE_CALLBACK */
677+#ifdef SQLITE_ENABLE_BINLOG
678+  sqlite3BinlogReset(db);
679+#endif
680   *ppDb = db;
681 #ifdef SQLITE_ENABLE_SQLLOG
682   if( sqlite3GlobalConfig.xSqllog ){
683@@ -256913,6 +257323,1088 @@ static void walLogCheckpointInfo(Wal *pWal, sqlite3 *db, sqlite3_int64 startTime
684 }
685 #endif
686
687+#ifdef SQLITE_ENABLE_BINLOG
688+/************** Begin of binlog implement ************************************/
689+SQLITE_PRIVATE int sqlite3BinlogInitApi(sqlite3 *db)
690+{
691+  if (db == NULL || db->xBinlogHandle.binlogApi.binlogLib == NULL) {
692+    sqlite3_log(SQLITE_ERROR, "binlog init api parameter is null");
693+    return SQLITE_ERROR;
694+  }
695+  Sqlite3BinlogApiInfo apiInfo[] = {
696+    {(void **)&db->xBinlogHandle.binlogApi.binlogOpenApi, "BinlogOpen"},
697+    {(void **)&db->xBinlogHandle.binlogApi.binlogCloseApi, "BinlogClose"},
698+    {(void **)&db->xBinlogHandle.binlogApi.binlogWriteApi, "BinlogWrite"},
699+    {(void **)&db->xBinlogHandle.binlogApi.binlogReadApi, "BinlogRead"},
700+    {(void **)&db->xBinlogHandle.binlogApi.binlogFreeReadResultApi, "BinlogFreeReadResult"},
701+    {(void **)&db->xBinlogHandle.binlogApi.binlogFileCleanApi, "BinlogFileClean"},
702+    {(void **)&db->xBinlogHandle.binlogApi.binlogLockReadApi, "BinlogLockRead"},
703+    {(void **)&db->xBinlogHandle.binlogApi.binlogUnlockReadApi, "BinlogUnlockRead"},
704+  };
705+  int apiCount = sizeof(apiInfo) / sizeof(apiInfo[0]);
706+  for (int i = 0; i < apiCount; i++) {
707+    *apiInfo[i].funcP = sqlite3OsDlSym(db->pVfs, db->xBinlogHandle.binlogApi.binlogLib, apiInfo[i].funcN);
708+    if (*apiInfo[i].funcP == NULL) {
709+      sqlite3_log(SQLITE_ERROR, "dlsym binlog err: %s", apiInfo[i].funcN);
710+      return SQLITE_ERROR;
711+    }
712+  }
713+  return SQLITE_OK;
714+}
715+
716+SQLITE_PRIVATE int sqlite3SetBinLogConfig(sqlite3 *db, Sqlite3BinlogConfig *bConfig)
717+{
718+  if (db == NULL) {
719+    sqlite3_log(SQLITE_ERROR, "set binlog config parameter is null");
720+    return SQLITE_ERROR;
721+  }
722+  if (bConfig == NULL) {
723+    return sqlite3BinlogClose(db);
724+  }
725+
726+  const char *zFile = sqlite3_db_filename(db, 0);
727+  if (zFile == NULL || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE)) {
728+    sqlite3_log(SQLITE_ERROR, "set binlog config zFile is null or binlog is already enabled");
729+    return SQLITE_ERROR;
730+  }
731+
732+  void *handle = sqlite3OsDlOpen(db->pVfs, "libarkdata_db_core.z.so");
733+  if (handle == NULL) {
734+      sqlite3_log(SQLITE_ERROR, "dlopen binlog err:%d", errno);
735+      return SQLITE_ERROR;
736+  }
737+  db->xBinlogHandle.binlogApi.binlogLib = handle;
738+  int rc = sqlite3BinlogInitApi(db);
739+  if (rc != SQLITE_OK) {
740+      sqlite3_log(rc, "set binlog config init api err:%d", rc);
741+      sqlite3BinlogReset(db);
742+      return rc;
743+  }
744+
745+  BinlogConfigT conf;
746+  conf.logMode = bConfig->mode;
747+  conf.fullCallbackThreshold = bConfig->fullCallbackThreshold;
748+  conf.maxFileSize = bConfig->maxFileSize;
749+  conf.filePath = (char *)zFile;
750+  conf.onError = bConfig->xErrorCallback;
751+  conf.onLogFull = bConfig->xLogFullCallback;
752+  conf.onErrnoTrans = sqlite3TransferBinlogErrno;
753+  conf.callbackCtx = bConfig->callbackCtx;
754+
755+  BinlogInstanceT *inst = NULL;
756+  rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogOpenApi(&conf, &inst));
757+  if (rc != SQLITE_OK) {
758+    sqlite3_log(SQLITE_ERROR, "binlog open err:%d %d", rc, errno);
759+    sqlite3BinlogReset(db);
760+    return SQLITE_ERROR;
761+  }
762+
763+  db->xBinlogHandle.mode = bConfig->mode;
764+  db->xBinlogHandle.flags |= (BINLOG_FLAG_ENABLE | BINLOG_FLAG_UPDATE_TID);
765+  db->xBinlogHandle.xErrorCallback = bConfig->xErrorCallback;
766+  db->xBinlogHandle.xLogFullCallback = bConfig->xLogFullCallback;
767+  db->xBinlogHandle.callbackCtx = bConfig->callbackCtx;
768+  db->xBinlogHandle.binlogConn = inst;
769+  db->xBinlogHandle.isSkipTrigger = 0;
770+  return SQLITE_OK;
771+}
772+
773+SQLITE_PRIVATE int sqlite3GenerateUuid(u8 *aBlobOut)
774+ {
775+  if (aBlobOut == NULL) {
776+    return SQLITE_ERROR;
777+  }
778+  u8 aBlob[SQLITE_UUID_BLOB_LENGTH];
779+  sqlite3_randomness(SQLITE_UUID_BLOB_LENGTH, aBlob);
780+  aBlob[6] = (aBlob[6] & 0x0f) + 0x40;
781+  aBlob[8] = (aBlob[8] & 0x3f) + 0x80;
782+
783+  errno_t errNo = EOK;
784+  errNo = memcpy_s(aBlobOut, SQLITE_UUID_BLOB_LENGTH, aBlob, SQLITE_UUID_BLOB_LENGTH);
785+  if (errNo != EOK) {
786+    return SQLITE_ERROR;
787+  }
788+  return SQLITE_OK;
789+}
790+
791+SQLITE_PRIVATE BinlogEventTypeE sqlite3TransferLogEventType(StmtType stmtType)
792+{
793+  switch(stmtType)  {
794+    case STMT_TYPE_CREATE_TABLE:
795+    case STMT_TYPE_CREATE_INDEX:
796+    case STMT_TYPE_CREATE_TRIGGER:
797+    case STMT_TYPE_CREATE_VIEW:
798+    case STMT_TYPE_DROP_TABLE:
799+    case STMT_TYPE_DROP_INDEX:
800+    case STMT_TYPE_DROP_TRIGGER:
801+    case STMT_TYPE_DROP_VIEW:
802+    case STMT_TYPE_ALTER_ADD_COL:
803+    case STMT_TYPE_ALTER_RENAME_COL:
804+    case STMT_TYPE_ALTER_RENAME_TABLE:
805+    case STMT_TYPE_ALTER_DROP_COL:
806+      return BINLOG_EVENT_TYPE_DDL;
807+    case STMT_TYPE_PRAGMA:
808+      return BINLOG_EVENT_TYPE_PRAGMA;
809+    case STMT_TYPE_ROLLBACK_TRANSACTION:
810+      return BINLOG_EVENT_TYPE_ROLLBACK;
811+    default:
812+      return BINLOG_EVENT_TYPE_DML;
813+  }
814+}
815+
816+/* Write an sql into binlog with given type and isFinish flag */
817+SQLITE_PRIVATE int sqlite3DirectWriteBinlog(Vdbe *p, BinlogEventTypeE type, char *zSql, u32 nSql, int isFinishTrx)
818+{
819+  BinlogWriteDataT logData;
820+  errno_t errNo = memcpy_s(&logData.xid, SQLITE_UUID_BLOB_LENGTH, p->db->xBinlogHandle.xTid, SQLITE_UUID_BLOB_LENGTH);
821+  if (errNo != EOK) {
822+    sqlite3_log(SQLITE_WARNING, "binlog memcpy xid failed");
823+    return SQLITE_ERROR;
824+  }
825+
826+  logData.type = type;
827+  logData.data = zSql;
828+  logData.dataLength = nSql;
829+  logData.isFinishTrx = isFinishTrx;
830+  sqlite3 *db = p->db;
831+  int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogWriteApi(db->xBinlogHandle.binlogConn,
832+    &logData));
833+  if (rc != SQLITE_OK) {
834+    sqlite3_log(SQLITE_WARNING, "binlog write err:%d", rc);
835+    return SQLITE_ERROR;
836+  }
837+  return SQLITE_OK;
838+}
839+
840+/* Free the pBinlogDMLData saved on the Vdbe pointer */
841+SQLITE_PRIVATE void sqlite3FreeBinlogRowData(Vdbe *p) {
842+  if (p == NULL || p->db == NULL || p->pBinlogDMLData == NULL) {
843+      return;
844+  }
845+  int isReleased = p->pBinlogDMLData->isSavePointReleased;
846+  if (!isReleased && p->db->autoCommit > 0 && p->pBinlogDMLData->pSavePointName != NULL) {
847+    u32 len = strlen(p->pBinlogDMLData->pSavePointName) + 1;
848+    sqlite3DirectWriteBinlog(p, BINLOG_EVENT_TYPE_ROW_ROLLBACK, p->pBinlogDMLData->pSavePointName, len, 0);
849+    sqlite3DirectWriteBinlog(p, BINLOG_EVENT_TYPE_ROW_COMMIT, p->pBinlogDMLData->pSavePointName, len, 1);
850+    p->db->xBinlogHandle.flags |= BINLOG_FLAG_UPDATE_TID;
851+  } else if (!isReleased && p->pBinlogDMLData->pSavePointName != NULL) {
852+    /* If not in auto commit mode and savepoint not released,
853+    ** it means this statement failed to execute within a transaction.
854+    ** In this case, we need to rollback to the point before this statement is executed.
855+    ** If the rollback statement failed to write into binlog, then there may be an memory leak
856+    */
857+    u32 len = strlen(p->pBinlogDMLData->pSavePointName) + 1;
858+    sqlite3DirectWriteBinlog(p, BINLOG_EVENT_TYPE_ROW_ROLLBACK, p->pBinlogDMLData->pSavePointName, len, 0);
859+  }
860+
861+  if (p->pBinlogDMLData->pSavePointName != NULL) {
862+    sqlite3DbFree(p->db, p->pBinlogDMLData->pSavePointName);
863+    p->pBinlogDMLData->pSavePointName = NULL;
864+  }
865+
866+  sqlite3DbFree(p->db, p->pBinlogDMLData);
867+  p->pBinlogDMLData = NULL;
868+}
869+
870+/* Check and create a new binlog xid if needed */
871+SQLITE_PRIVATE int sqlite3UpdateBinlogXidIfNeeded(sqlite3 *db){
872+  assert( db!=NULL );
873+  if ((db->xBinlogHandle.flags & BINLOG_FLAG_UPDATE_TID) != 0) {
874+    if (sqlite3GenerateUuid(db->xBinlogHandle.xTid) != SQLITE_OK) {
875+      sqlite3_log(SQLITE_ERROR, "binlog generate uuid failed");
876+      return SQLITE_ERROR;
877+    }
878+    db->xBinlogHandle.flags &= ~BINLOG_FLAG_UPDATE_TID;
879+  }
880+  return SQLITE_OK;
881+}
882+
883+/* Return a delete sql for a regular table, the returned value needs free by caller */
884+SQLITE_PRIVATE char *sqlite3BinlogRowDelete(Table *pTab, sqlite3_int64 rowid){
885+  return sqlite3_mprintf("delete from \"%s\" where rowid=%lld;", pTab->zName, rowid);
886+}
887+
888+/* Return a delete sql for a without rowid table, the returned value needs free by caller */
889+SQLITE_PRIVATE char *sqlite3BinlogRowDeleteNoRowid(sqlite3 *db, Table *pTab, const BinlogRow *pRow){
890+  char *whereClause = NULL;;
891+  Index *pIdx = sqlite3PrimaryKeyIndex(pTab);
892+
893+  for (int i=0; i<pIdx->nKeyCol; i++) {
894+    i16 iCol = pIdx->aiColumn[i];
895+    Column *pCol = &(pTab->aCol[iCol]);
896+    if (whereClause == NULL) {
897+      char *colValue = sqlite3BinlogGetNthCol(db, pTab, pRow, i);
898+      if (colValue == NULL) {
899+        return NULL;
900+      }
901+      whereClause = sqlite3_mprintf("%s=%s", pCol->zCnName, colValue);
902+      sqlite3DbFree(db, colValue);
903+    } else {
904+      char *colValue = sqlite3BinlogGetNthCol(db, pTab, pRow, i);
905+      if (colValue == NULL) {
906+        sqlite3DbFree(db, whereClause);
907+        return NULL;
908+      }
909+      char *temp = sqlite3_mprintf("%s and %s=%s", whereClause, pCol->zCnName, colValue);
910+      sqlite3DbFree(db, whereClause);
911+      sqlite3DbFree(db, colValue);
912+      whereClause = temp;
913+    }
914+    if (whereClause == NULL) {
915+      return NULL;
916+    }
917+  }
918+
919+  char *result = sqlite3_mprintf("delete from \"%s\" where %s;", pTab->zName, whereClause);
920+  sqlite3DbFree(db, whereClause);
921+  return result;
922+}
923+
924+/* Get the field name and field values for a without rowid table, in two comma seperated strings */
925+SQLITE_PRIVATE int sqlite3BinlogGetFieldDataNoRowid(
926+  sqlite3 *db,                  /* dB handle */
927+  Table *pTab,                  /* Table to which the row belongs */
928+  const BinlogRow *pRow,        /* Row data to be written into binlog */
929+  char **outNames,              /* field names to be returned */
930+  char **outValues              /* field values to be returned */
931+){
932+  char *fieldNames = NULL;
933+  char *fieldValues = NULL;
934+  Index *pIdx = sqlite3PrimaryKeyIndex(pTab);
935+  /* Without rowid table fields are rearranged with primary keys at front */
936+  for (int i=0; i<pIdx->nKeyCol; i++) {
937+    i16 iCol = pIdx->aiColumn[i];
938+    if (i==0) {
939+      fieldNames = sqlite3_mprintf("%s", pTab->aCol[iCol].zCnName);
940+    } else {
941+      char *temp = sqlite3_mprintf("%s, %s", fieldNames, pTab->aCol[iCol].zCnName);
942+      sqlite3DbFree(db, fieldNames);
943+      fieldNames = temp;
944+    }
945+    if (fieldNames==NULL) goto no_rowid_no_mem;
946+  }
947+
948+  for (int k=0; k<pTab->nCol; k++) {
949+    if ( (pTab->aCol[k].colFlags & COLFLAG_PRIMKEY) == 0 ) {
950+        char *temp = sqlite3_mprintf("%s, %s", fieldNames, pTab->aCol[k].zCnName);
951+        sqlite3DbFree(db, fieldNames);
952+        fieldNames = temp;
953+    }
954+    if (k==0) {
955+      fieldValues = sqlite3BinlogGetNthCol(db, pTab, pRow, k);
956+    } else {
957+      char *newVal = sqlite3BinlogGetNthCol(db, pTab, pRow, k);
958+      if (newVal==NULL) {
959+        goto no_rowid_no_mem;
960+      }
961+      char *temp = sqlite3_mprintf("%s, %s", fieldValues, newVal);
962+      sqlite3DbFree(db, newVal);
963+      sqlite3DbFree(db, fieldValues);
964+      fieldValues = temp;
965+    }
966+    if (fieldValues == NULL || fieldValues == NULL) {
967+      goto no_rowid_no_mem;
968+    }
969+  }
970+  *outNames = fieldNames;
971+  *outValues = fieldValues;
972+  return SQLITE_OK;
973+
974+no_rowid_no_mem:
975+  sqlite3DbFree(db, fieldNames);
976+  sqlite3DbFree(db, fieldValues);
977+  return SQLITE_NOMEM;
978+}
979+
980+/* Get the field name and field values for a regular rowid table, in two comma seperated strings */
981+SQLITE_PRIVATE int sqlite3BinlogGetFieldData(sqlite3 *db, Table *pTab, const BinlogRow *pRow, char **outNames, char **outValues){
982+  char *fieldNames = NULL;
983+  char *fieldValues = NULL;
984+  for (int i=0; i<pTab->nCol; i++) {
985+    if (i==0) {
986+      fieldNames = sqlite3_mprintf("%s", pTab->aCol[i].zCnName);
987+      fieldValues = sqlite3BinlogGetNthCol(db, pTab, pRow, i);
988+    } else {
989+      char *temp = sqlite3_mprintf("%s, %s", fieldNames, pTab->aCol[i].zCnName);
990+      sqlite3DbFree(db, fieldNames);
991+      fieldNames = temp;
992+
993+      char *newVal = sqlite3BinlogGetNthCol(db, pTab, pRow, i);
994+      if (newVal==NULL) goto no_mem;
995+      temp = sqlite3_mprintf("%s, %s", fieldValues, newVal);
996+      sqlite3DbFree(db, newVal);
997+      sqlite3DbFree(db, fieldValues);
998+      fieldValues = temp;
999+
1000+    }
1001+    if (fieldValues == NULL || fieldValues == NULL) {
1002+      goto no_mem;
1003+    }
1004+  }
1005+  *outNames = fieldNames;
1006+  *outValues = fieldValues;
1007+  return SQLITE_OK;
1008+
1009+no_mem:
1010+    sqlite3DbFree(db, fieldNames);
1011+    sqlite3DbFree(db, fieldValues);
1012+  return SQLITE_NOMEM;
1013+}
1014+
1015+/* Return an insert sql based on the binlog row data, the returned value needs free by caller */
1016+SQLITE_PRIVATE char *sqlite3BinlogRowInsert(sqlite3 *db, Table *pTab, const BinlogRow *pRow){
1017+  char *fieldNames = NULL;
1018+  char *fieldValues = NULL;
1019+
1020+  int rc = SQLITE_OK;
1021+  if (HasRowid(pTab)) {
1022+    rc = sqlite3BinlogGetFieldData(db, pTab, pRow, &fieldNames, &fieldValues);
1023+  } else {
1024+    rc = sqlite3BinlogGetFieldDataNoRowid(db, pTab, pRow, &fieldNames, &fieldValues);
1025+  }
1026+
1027+  if (rc != SQLITE_OK) {
1028+    sqlite3_log(rc, "binlog get field data err:%d", rc);
1029+    return NULL;
1030+  }
1031+  char *result = NULL;
1032+  if (HasRowid(pTab)) {
1033+    result = sqlite3_mprintf(
1034+      "insert into \"%s\" (%s, rowid) values (%s, %lld) on conflict do update set (%s) = (%s);",
1035+      pTab->zName, fieldNames, fieldValues, pRow->rowid, fieldNames, fieldValues
1036+    );
1037+  } else {
1038+    result = sqlite3_mprintf(
1039+      "insert into \"%s\" (%s) values (%s) on conflict do update set (%s) = (%s);",
1040+      pTab->zName, fieldNames, fieldValues, fieldNames, fieldValues
1041+    );
1042+  }
1043+
1044+  sqlite3DbFree(db, fieldNames);
1045+  sqlite3DbFree(db, fieldValues);
1046+  return result;
1047+}
1048+
1049+/* Return an update sql for a regular table. Without rowid table has no update operation */
1050+SQLITE_PRIVATE char *sqlite3BinlogRowUpdate(sqlite3 *db, Table *pTab, const BinlogRow *pRow){
1051+  char *fieldNames = NULL;
1052+  char *fieldValues = NULL;
1053+  if (sqlite3BinlogGetFieldData(db, pTab, pRow, &fieldNames, &fieldValues) != SQLITE_OK) {
1054+    return NULL;
1055+  }
1056+  char *result = sqlite3_mprintf(
1057+    "insert into \"%s\" (%s, rowid) values (%s, %lld) on conflict(rowid) do update set (%s) = (%s);",
1058+    pTab->zName, fieldNames,
1059+    fieldValues, pRow->rowid,
1060+    fieldNames, fieldValues
1061+  );
1062+  sqlite3DbFree(db, fieldNames);
1063+  sqlite3DbFree(db, fieldValues);
1064+  return result;
1065+}
1066+
1067+/* Return the sql statement corresponds to a binlog row data, NULL is returned if error occurs */
1068+SQLITE_PRIVATE char *sqlite3GetBinlogRowStmt(sqlite3 *db, const BinlogRow *pRow, Table *pTab){
1069+  assert( db!=NULL );
1070+  assert( pRow!=NULL );
1071+  assert( pTab!=NULL );
1072+
1073+  switch (pRow->op) {
1074+    case SQLITE_DELETE:
1075+      return HasRowid(pTab) ? sqlite3BinlogRowDelete(pTab, pRow->rowid) : sqlite3BinlogRowDeleteNoRowid(db, pTab, pRow);
1076+    case SQLITE_INSERT:
1077+      return sqlite3BinlogRowInsert(db, pTab, pRow);
1078+    case SQLITE_UPDATE:
1079+      return sqlite3BinlogRowUpdate(db, pTab, pRow);
1080+    default:
1081+      return NULL;
1082+  }
1083+}
1084+
1085+/* Find the table that is pointed by pC, NULL is returned if a valid table can not be found */
1086+SQLITE_PRIVATE Table *sqlite3BinlogFindTable(BtCursor *pC){
1087+  if (pC==NULL || pC->pBtree == NULL || pC->pBtree->pBt == NULL || pC->pBtree->pBt->pSchema == NULL) {
1088+    return NULL;
1089+  }
1090+  Schema *pSchema = (Schema *)pC->pBtree->pBt->pSchema;
1091+  Table *pTab = NULL;
1092+  HashElem *k;
1093+  for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
1094+    Table *tempTab = (Table*)sqliteHashData(k);
1095+    if (tempTab->tnum==pC->pgnoRoot) {
1096+      pTab = tempTab;
1097+      break;
1098+    }
1099+  }
1100+  if (pTab == NULL || sqlite3_stricmp(pTab->zName, LEGACY_SCHEMA_TABLE) == 0) {
1101+    return NULL;
1102+  }
1103+  return pTab;
1104+}
1105+
1106+SQLITE_PRIVATE int sqlite3BinlogWriteTable(Vdbe *p, Table *pTable){
1107+  assert( p!=NULL );
1108+  assert( pTable!=NULL );
1109+
1110+  char *tableName = pTable->zName;
1111+  u32 len = strlen(tableName) + 1;
1112+  return sqlite3DirectWriteBinlog(p, BINLOG_EVENT_TYPE_ROW_TABLE, tableName, len, 0);
1113+}
1114+
1115+SQLITE_PRIVATE int sqlite3BinlogDecodeRowBuffer(char *buffer, u32 nBuffer, u8 *op,
1116+  sqlite3_int64 *rowid, sqlite3_uint64 *nData, char **pData)
1117+{
1118+  if (nBuffer < sizeof(u8) + sizeof(sqlite3_int64) + sizeof(sqlite3_uint64)) {
1119+    sqlite3_log(SQLITE_WARNING, "Failed to decode binlog row, %u", nBuffer);
1120+    return SQLITE_ERROR;
1121+  }
1122+
1123+  u64 offset = 0;
1124+  *op = *((u8*)(buffer + offset));
1125+  offset += sizeof(u8);
1126+
1127+  errno_t errNo = memcpy_s(rowid, sizeof(sqlite3_int64), buffer + offset, sizeof(sqlite3_int64));
1128+  if (errNo != EOK) {
1129+    sqlite3_log(SQLITE_WARNING, "Failed to decode binlog row rowid");
1130+    return SQLITE_ERROR;
1131+  }
1132+  offset += sizeof(sqlite3_int64);
1133+
1134+  errNo = memcpy_s(nData, sizeof(sqlite3_uint64), buffer + offset, sizeof(sqlite3_uint64));
1135+  if (errNo != EOK) {
1136+    sqlite3_log(SQLITE_WARNING, "Failed to decode binlog row nData");
1137+    return SQLITE_ERROR;
1138+  }
1139+  offset += sizeof(sqlite3_uint64);
1140+
1141+  *pData = buffer + offset;
1142+  return SQLITE_OK;
1143+}
1144+
1145+SQLITE_PRIVATE int sqlite3BinlogGetRowBuffer(sqlite3_uint64 nData, const char *pData, u8 op,
1146+  sqlite3_int64 rowid, char **pOutBuffer, u64 *nOutBuffer){
1147+  assert( pOutBuffer!=NULL );
1148+  assert( nOutBuffer!=NULL );
1149+
1150+  u64 nRowBuffer = sizeof(op) + sizeof(rowid) + sizeof(nData) + nData;
1151+  char *pRowBuffer = sqlite3MallocZero(nRowBuffer);
1152+  if (pRowBuffer == NULL) {
1153+    sqlite3_log(SQLITE_ERROR, "Failed to malloc binlog row, %lu", nRowBuffer);
1154+    return SQLITE_ERROR;
1155+  }
1156+
1157+  errno_t errNo = memcpy_s(pRowBuffer, nRowBuffer, &op, sizeof(op));
1158+  if (errNo != EOK) {
1159+    goto error_when_memcpy_binlog_row;
1160+  }
1161+
1162+  u64 offset = sizeof(op);
1163+  errNo = memcpy_s(pRowBuffer + offset, nRowBuffer, &rowid, sizeof(rowid));
1164+  offset += sizeof(rowid);
1165+  if (errNo != EOK) {
1166+    goto error_when_memcpy_binlog_row;
1167+  }
1168+
1169+  errNo = memcpy_s(pRowBuffer + offset, nRowBuffer, &nData, sizeof(nData));
1170+  offset += sizeof(nData);
1171+  if (errNo != EOK) {
1172+    goto error_when_memcpy_binlog_row;
1173+  }
1174+
1175+  if (nData > 0) {
1176+    errNo = memcpy_s(pRowBuffer + offset, nRowBuffer, pData, nData);
1177+     if (errNo != EOK) {
1178+      goto error_when_memcpy_binlog_row;
1179+    }
1180+  }
1181+
1182+  *pOutBuffer = pRowBuffer;
1183+  *nOutBuffer = nRowBuffer;
1184+  return SQLITE_OK;
1185+
1186+error_when_memcpy_binlog_row:
1187+  sqlite3_free(pRowBuffer);
1188+  sqlite3_log(SQLITE_ERROR, "Failed to copy binlog row, %lu", nRowBuffer);
1189+  return SQLITE_ERROR;
1190+}
1191+
1192+SQLITE_PRIVATE int sqlite3BinlogWriteRowData(
1193+  Vdbe *p, sqlite3_uint64 nData, const char *pData, int op, sqlite3_int64 rowid){
1194+  if ((nData == 0 || pData == NULL) && op != SQLITE_DELETE) {
1195+    sqlite3_log(SQLITE_ERROR, "binlog row data empty, %llu", nData);
1196+    return SQLITE_ERROR;
1197+  }
1198+
1199+  u8 opData = op == SQLITE_DELETE ? SQLITE_DELETE: SQLITE_INSERT;
1200+  u64 nRowBuffer = 0;
1201+  char *pRowBuffer = NULL;
1202+  int rc = sqlite3BinlogGetRowBuffer(nData, pData, opData, rowid, &pRowBuffer, &nRowBuffer);
1203+  if (rc != SQLITE_OK) {
1204+    return rc;
1205+  }
1206+
1207+  rc = sqlite3DirectWriteBinlog(p, BINLOG_EVENT_TYPE_ROW_FULL_DATA, pRowBuffer, nRowBuffer, 0);
1208+  sqlite3_free(pRowBuffer);
1209+  return rc;
1210+}
1211+
1212+/**
1213+** This is the function called in sqlite3VdbeExec to write a row-based binlog statement.
1214+** If an error occured, then the row data is cleared and the state will be logged in statement mode
1215+**/
1216+SQLITE_PRIVATE void sqlite3StoreBinlogRowData(Vdbe *p, BtCursor *pCursor,
1217+  sqlite3_uint64 nData, const char *pData, int op, sqlite3_int64 rowid){
1218+  assert( p!=NULL && pCursor!=NULL );
1219+  assert( p->db!=NULL );
1220+  assert( sqlite3IsRowBasedBinlog(p) );
1221+  assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
1222+  sqlite3 *db = p->db;
1223+  BinlogDMLData *dataContainer = NULL;
1224+
1225+  Table *pTab = sqlite3BinlogFindTable(pCursor);
1226+  if (pTab == NULL) {
1227+    goto error_when_store_binlog;
1228+  }
1229+
1230+  if (p->pBinlogDMLData == NULL) {
1231+    /* If pBinlogDMLData is empty, we need to initialize the data by the following steps:
1232+    ** 1. Change the xid if needed
1233+    ** 2. Start a savepoint so that all the rows are in the same transaction
1234+    */
1235+    dataContainer = (BinlogDMLData *)sqlite3DbMallocZero(db, sizeof(BinlogDMLData));
1236+    if (dataContainer == NULL) {
1237+      goto error_when_store_binlog;
1238+    }
1239+
1240+    if (sqlite3UpdateBinlogXidIfNeeded(p->db) != SQLITE_OK) {
1241+      goto error_when_store_binlog;
1242+    }
1243+
1244+    char xTidStr[SQLITE_UUID_BLOB_LENGTH*2+1] = {0};
1245+    for (int i=0; i<SQLITE_UUID_BLOB_LENGTH; i++) {
1246+      u8 byte = db->xBinlogHandle.xTid[i];
1247+      sqlite3_snprintf(3, xTidStr+i*2, "%02x", byte);
1248+    }
1249+    char *savePointName = sqlite3_mprintf("%s%s", "BinlogRow", xTidStr);
1250+    if (savePointName == NULL) {
1251+      goto error_when_store_binlog;
1252+    }
1253+    dataContainer->pSavePointName = savePointName;
1254+    p->pBinlogDMLData = dataContainer;
1255+    int rc = sqlite3DirectWriteBinlog(p, BINLOG_EVENT_TYPE_ROW_START, savePointName, strlen(savePointName)+1, 0);
1256+    if (rc != SQLITE_OK) {
1257+      goto error_when_store_binlog;
1258+    }
1259+  } else {
1260+    dataContainer = p->pBinlogDMLData;
1261+  }
1262+
1263+  if (dataContainer->pTable != pTab) {
1264+    int rc = sqlite3BinlogWriteTable(p, pTab);
1265+    if (rc != SQLITE_OK) {
1266+      goto error_when_store_binlog;
1267+    }
1268+    dataContainer->pTable = pTab;
1269+  }
1270+
1271+  if (sqlite3BinlogWriteRowData(p, nData, pData, op, rowid) == SQLITE_OK) {
1272+    return;
1273+  }
1274+
1275+error_when_store_binlog:
1276+  sqlite3_log(SQLITE_WARNING, "Failed to store row-based binlog data, use statement mode instead.");
1277+  if (p->pBinlogDMLData == NULL && dataContainer != NULL) {
1278+    sqlite3DbFree(db, dataContainer);
1279+  }
1280+  sqlite3FreeBinlogRowData(p);
1281+  return;
1282+}
1283+
1284+SQLITE_PRIVATE int sqlite3IsRowBasedBinlog(Vdbe *p) {
1285+  return p
1286+    && p->db
1287+    && (p->db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE)
1288+    && (p->db->xBinlogHandle.mode == ROW);
1289+}
1290+
1291+/* Get i-th column value from a row formated data. i starts from 0 */
1292+SQLITE_PRIVATE int sqlite3BinlogStat4Column(
1293+  sqlite3 *db,                    /* Database handle */
1294+  const void *pRec,               /* Pointer to buffer containing record */
1295+  int nRec,                       /* Size of buffer pRec in bytes */
1296+  int iCol,                       /* Column to extract */
1297+  sqlite3_value **ppVal           /* OUT: Extracted value */
1298+){
1299+  u32 t = 0;                      /* a column type code */
1300+  u32 nHdr;                       /* Size of the header in the record */
1301+  u32 iHdr;                       /* Next unread header byte */
1302+  i64 iField;                     /* Next unread data byte */
1303+  u32 szField = 0;                /* Size of the current data field */
1304+  int i;                          /* Column index */
1305+  u8 *a = (u8*)pRec;              /* Typecast byte array */
1306+  Mem *pMem = *ppVal;             /* Write result into this Mem object */
1307+
1308+  iHdr = getVarint32(a, nHdr);
1309+  if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
1310+  iField = nHdr;
1311+  for(i=0; i<=iCol; i++){
1312+    iHdr += getVarint32(&a[iHdr], t);
1313+
1314+    if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT;
1315+    szField = sqlite3VdbeSerialTypeLen(t);
1316+    iField += szField;
1317+  }
1318+
1319+  if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
1320+  if( pMem==0 ){
1321+    pMem = *ppVal = sqlite3ValueNew(db);
1322+    if( pMem==0 ) return SQLITE_NOMEM_BKPT;
1323+  }
1324+  sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
1325+  pMem->enc = ENC(db);
1326+  return SQLITE_OK;
1327+}
1328+
1329+SQLITE_PRIVATE char *sqlite3BinlogGetHexFromBlobOrStr(sqlite3 *db, sqlite3_value *value) {
1330+  assert( db!=NULL && value!=NULL );
1331+  size_t strSize = (value->n) * 2 + 1;
1332+  char *zHex, *z;
1333+  z = zHex =sqlite3DbMallocZero(db, strSize);
1334+  if (z == NULL) {
1335+    return NULL;
1336+  }
1337+  for(int i=0; i<value->n; i++) {
1338+    unsigned char c = (value->z)[i];
1339+    *(z++) = hexdigits[(c>>4)&0xf];
1340+    *(z++) = hexdigits[c&0xf];
1341+  }
1342+  *z = 0;
1343+  return zHex;
1344+}
1345+
1346+SQLITE_PRIVATE char *sqlite3BinlogGetEscapedText(sqlite3 *db, const char *originalStr) {
1347+  sqlite3_str *pStr = sqlite3_str_new(db);
1348+  size_t nOriginal = strlen(originalStr);
1349+  sqlite3_str_appendchar(pStr, 1, '\'');
1350+  for (size_t i=0; i<nOriginal; i++) {
1351+    if (originalStr[i] == '\'') {
1352+      sqlite3_str_appendchar(pStr, 1, '\'');
1353+    }
1354+    sqlite3_str_appendchar(pStr, 1, originalStr[i]);
1355+  }
1356+  sqlite3_str_appendchar(pStr, 1, '\'');
1357+  return sqlite3_str_finish(pStr);
1358+}
1359+
1360+SQLITE_PRIVATE char *sqlite3BinlogGetNthCol(sqlite3 *db, const Table *pTab, const BinlogRow *pRow, int iCol) {
1361+  assert( db != NULL && pTab != NULL && pRow != NULL );
1362+  assert( iCol >= 0 );
1363+  if (HasRowid(pTab) && pTab->iPKey == iCol) {
1364+      return sqlite3_mprintf("%lld", pRow->rowid);
1365+  }
1366+
1367+  sqlite3_value *tempVal = NULL;
1368+  sqlite3BinlogStat4Column(db, pRow->pData, pRow->nData, iCol, &tempVal);
1369+  if (tempVal == NULL) {
1370+    sqlite3_log(SQLITE_ERROR, "decode data failed");
1371+    return NULL;
1372+  }
1373+  int type = sqlite3_value_type(tempVal);
1374+  const unsigned char *temp = sqlite3_value_text(tempVal);
1375+
1376+  char *res = NULL;
1377+  if (type == SQLITE_BLOB) {
1378+    char *hex = sqlite3BinlogGetHexFromBlobOrStr(db, tempVal);
1379+    if (hex != NULL) {
1380+      res = sqlite3_mprintf("x'%s'", hex);
1381+      sqlite3DbFree(db, hex);
1382+    } else {
1383+      res = sqlite3_mprintf("'%s'", temp);
1384+    }
1385+  } else if (type == SQLITE_TEXT) {
1386+    res = sqlite3BinlogGetEscapedText(db, (const char *)temp);
1387+  } else if (type == SQLITE_NULL) {
1388+    res = sqlite3_mprintf("NULL");
1389+  } else {
1390+    res = sqlite3_mprintf("%s", temp);
1391+  }
1392+  sqlite3ValueFree(tempVal);
1393+
1394+  return res;
1395+}
1396+
1397+/* Check if binlog is enabled and the stmtType needs being logged */
1398+SQLITE_PRIVATE int sqlite3IsSkipWriteBinlog(Vdbe *p) {
1399+  int isBinlogEnabled = (p != NULL) && (p->db != NULL) &&
1400+    ((p->db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) != 0) &&
1401+    p->db->xBinlogHandle.binlogApi.binlogWriteApi;
1402+
1403+  return p == NULL
1404+    || !isBinlogEnabled
1405+    || p->db->nVdbeExec > 1
1406+    || (p->readOnly && p->stmtType != STMT_TYPE_BEGIN_TRANSACTION && p->stmtType != STMT_TYPE_SAVEPOINT &&
1407+      p->stmtType != STMT_TYPE_COMMIT_TRANSACTION && p->stmtType != STMT_TYPE_ROLLBACK_TRANSACTION);
1408+}
1409+
1410+SQLITE_PRIVATE void sqlite3BinlogWrite(Vdbe *p)
1411+{
1412+  if (sqlite3IsSkipWriteBinlog(p)) {
1413+    sqlite3FreeBinlogRowData(p);
1414+    return;
1415+  }
1416+
1417+  sqlite3 *db = p->db;
1418+  BinlogWriteDataT logData;
1419+  logData.type = sqlite3TransferLogEventType(p->stmtType);
1420+
1421+  char *zSql = NULL;
1422+  if (logData.type == BINLOG_EVENT_TYPE_DML && sqlite3IsRowBasedBinlog(p) && p->pBinlogDMLData != NULL) {
1423+    // release savepoint
1424+    zSql = sqlite3_mprintf("%s", p->pBinlogDMLData->pSavePointName);
1425+    logData.type = BINLOG_EVENT_TYPE_ROW_COMMIT;
1426+    p->pBinlogDMLData->isSavePointReleased = 1;
1427+  } else {
1428+    zSql = sqlite3_expanded_sql((sqlite3_stmt *)p);
1429+  }
1430+  if (zSql == NULL) {
1431+    sqlite3_log(SQLITE_ERROR, "binlog get sql failed");
1432+    sqlite3FreeBinlogRowData(p);
1433+    return;
1434+  }
1435+
1436+  if (sqlite3UpdateBinlogXidIfNeeded(db) != SQLITE_OK) {
1437+    sqlite3FreeBinlogRowData(p);
1438+    sqlite3BinlogErrorCallback(db, SQLITE_ERROR, "generate tid failed");
1439+    sqlite3_free(zSql);
1440+    sqlite3BinlogClose(db);
1441+    return;
1442+  }
1443+  errno_t errNo = EOK;
1444+  errNo = memcpy_s(&logData.xid, SQLITE_UUID_BLOB_LENGTH, db->xBinlogHandle.xTid, SQLITE_UUID_BLOB_LENGTH);
1445+  if (errNo != EOK) {
1446+    sqlite3FreeBinlogRowData(p);
1447+    sqlite3BinlogErrorCallback(db, SQLITE_ERROR, "copy tid failed");
1448+    sqlite3_free(zSql);
1449+    sqlite3BinlogClose(db);
1450+    return;
1451+  }
1452+  logData.data = zSql;
1453+  logData.dataLength = strlen(zSql);
1454+  logData.isFinishTrx = db->autoCommit;
1455+  int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogWriteApi(db->xBinlogHandle.binlogConn,
1456+    &logData));
1457+  sqlite3_free(zSql);
1458+  if (rc != SQLITE_OK) {
1459+    sqlite3FreeBinlogRowData(p);
1460+    sqlite3_log(SQLITE_ERROR, "binlog write err:%d len:%u", rc, logData.dataLength);
1461+    sqlite3BinlogClose(db);
1462+    return;
1463+  }
1464+  if (db->autoCommit > 0) {
1465+    db->xBinlogHandle.flags |= BINLOG_FLAG_UPDATE_TID;
1466+  }
1467+  sqlite3FreeBinlogRowData(p);
1468+}
1469+
1470+SQLITE_PRIVATE int sqlite3TransferBinlogErrno(BinlogErrno err)
1471+{
1472+  switch (err) {
1473+    case GMERR_OK:
1474+      return SQLITE_OK;
1475+    case GMERR_LOCK_NOT_AVAILABLE:
1476+      return SQLITE_BUSY;
1477+    case GMERR_BINLOG_READ_FINISH:
1478+      return SQLITE_DONE;
1479+    default:
1480+      return SQLITE_ERROR;
1481+  }
1482+}
1483+
1484+SQLITE_PRIVATE int sqlite3BinlogClose(sqlite3 *db)
1485+{
1486+  if (db == NULL) {
1487+    sqlite3_log(SQLITE_ERROR, "binlog close parameter is null");
1488+    return SQLITE_ERROR;
1489+  }
1490+  if ((db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
1491+    return SQLITE_OK;
1492+  }
1493+  if (db->xBinlogHandle.binlogApi.binlogCloseApi) {
1494+    int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogCloseApi(db->xBinlogHandle.binlogConn));
1495+    if (rc != SQLITE_OK) {
1496+      sqlite3_log(rc, "binlog close err:%d", rc);
1497+      return rc;
1498+    }
1499+  }
1500+  sqlite3BinlogReset(db);
1501+  return SQLITE_OK;
1502+}
1503+
1504+SQLITE_PRIVATE void sqlite3BinlogReset(sqlite3 *db)
1505+{
1506+  if (db == NULL) {
1507+    sqlite3_log(SQLITE_ERROR, "binlog reset parameter is null");
1508+    return;
1509+  }
1510+  db->xBinlogHandle.flags = 0;
1511+  (void)memset_s(db->xBinlogHandle.xTid, SQLITE_UUID_BLOB_LENGTH, 0, SQLITE_UUID_BLOB_LENGTH);
1512+  db->xBinlogHandle.xErrorCallback = NULL;
1513+  db->xBinlogHandle.binlogApi.binlogOpenApi = NULL;
1514+  db->xBinlogHandle.binlogApi.binlogCloseApi = NULL;
1515+  db->xBinlogHandle.binlogApi.binlogWriteApi = NULL;
1516+  db->xBinlogHandle.binlogApi.binlogReadApi = NULL;
1517+  db->xBinlogHandle.binlogApi.binlogFileCleanApi = NULL;
1518+  db->xBinlogHandle.binlogApi.binlogFreeReadResultApi = NULL;
1519+  db->xBinlogHandle.binlogApi.binlogLockReadApi = NULL;
1520+  db->xBinlogHandle.binlogApi.binlogUnlockReadApi = NULL;
1521+  db->xBinlogHandle.callbackCtx = NULL;
1522+  db->xBinlogHandle.binlogConn = NULL;
1523+  if (db->xBinlogHandle.binlogApi.binlogLib) {
1524+    sqlite3OsDlClose(db->pVfs, db->xBinlogHandle.binlogApi.binlogLib);
1525+    db->xBinlogHandle.binlogApi.binlogLib = NULL;
1526+  }
1527+  db->xBinlogHandle.isSkipTrigger = 0;
1528+}
1529+
1530+SQLITE_PRIVATE int sqlite3BinlogStmtPrepare(sqlite3 *db, Sqlite3BinlogStmt *bStmt)
1531+{
1532+  if (db == NULL || db->xBinlogHandle.binlogApi.binlogReadApi == NULL || bStmt == NULL) {
1533+    sqlite3_log(SQLITE_ERROR, "binlog stmt prepare parameter is null");
1534+    return SQLITE_ERROR;
1535+  }
1536+  BinlogReadResultT *result = NULL;
1537+  int rc = sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogReadApi(db->xBinlogHandle.binlogConn,
1538+    &result));
1539+  if (rc != SQLITE_OK) {
1540+    if (rc != SQLITE_DONE) {
1541+      sqlite3_log(rc, "binlog stmt prepare err:%d", rc);
1542+    }
1543+    return rc;
1544+  }
1545+  bStmt->cursor = result;
1546+  bStmt->curIdx = 0;
1547+  return SQLITE_OK;
1548+}
1549+
1550+SQLITE_PRIVATE char *sqlite3BinlogGetRowSql(sqlite3 *db, Table* pTable, char *buffer, u32 nBuffer, int *rc)
1551+{
1552+  if (buffer == NULL) {
1553+    *rc = SQLITE_ERROR;
1554+    sqlite3_log(SQLITE_ERROR, "binlog row has no data");
1555+    return NULL;
1556+  }
1557+
1558+  u8 op = 0;
1559+  sqlite3_int64 rowid = 0;
1560+  sqlite3_uint64 nData = 0;
1561+  char *pData = NULL;
1562+  *rc = sqlite3BinlogDecodeRowBuffer(buffer, nBuffer, &op, &rowid, &nData, &pData);
1563+  if (*rc != SQLITE_OK) {
1564+    return NULL;
1565+  }
1566+
1567+  BinlogRow pRow;
1568+  pRow.op = op;
1569+  pRow.nData = nData;
1570+  pRow.pData = pData;
1571+  pRow.rowid = rowid;
1572+  return sqlite3GetBinlogRowStmt(db, &pRow, pTable);
1573+}
1574+
1575+SQLITE_PRIVATE Table *sqliteBinlogGetTable(sqlite3 *db, const char *pTableName, int *rc)
1576+{
1577+  assert( db!=NULL );
1578+  assert( pTableName!=NULL );
1579+  assert( rc!=NULL );
1580+  sqlite3_stmt *stmt = NULL;
1581+  *rc = sqlite3_prepare_v2(db, "SELECT COUNT(*) FROM sqlite_master WHERE type = 'table';", -1, &stmt, NULL);
1582+  if (*rc != SQLITE_OK) {
1583+    return NULL;
1584+  }
1585+  *rc = sqlite3_step(stmt);
1586+  Table *pTable = sqlite3FindTable(db, pTableName, NULL);
1587+  sqlite3_finalize(stmt);
1588+  return pTable;
1589+}
1590+
1591+SQLITE_PRIVATE char *sqlite3BinlogGetEventSql(sqlite3 *db, BinlogEventT *event, Table **pOutTable, int *rc)
1592+{
1593+  assert( event!=NULL );
1594+  switch (event->head.eventType) {
1595+    case BINLOG_EVENT_TYPE_ROW_START:
1596+      return sqlite3_mprintf("savepoint %s;", (char *)event->body);
1597+    case BINLOG_EVENT_TYPE_ROW_TABLE: {
1598+      char *pTableName = (char *)event->body;
1599+      *pOutTable = sqliteBinlogGetTable(db, pTableName, rc);
1600+      if (*pOutTable == NULL) {
1601+        *rc = SQLITE_ERROR;
1602+        sqlite3_log(SQLITE_WARNING, "binlog find no table, rc=%d", *rc);
1603+      } else {
1604+        *rc = SQLITE_OK;
1605+      }
1606+      return NULL;
1607+    }
1608+    case BINLOG_EVENT_TYPE_ROW_FULL_DATA:
1609+      if (pOutTable == NULL || *pOutTable == NULL) {
1610+        *rc = SQLITE_ERROR;
1611+        sqlite3_log(SQLITE_ERROR, "binlog row has no table");
1612+        return NULL;
1613+      }
1614+      return sqlite3BinlogGetRowSql(db, *pOutTable, (char *)event->body, event->head.eventLength, rc);
1615+    case BINLOG_EVENT_TYPE_ROW_COMMIT:
1616+      return sqlite3_mprintf("release %s;", (char *)event->body);
1617+    case BINLOG_EVENT_TYPE_ROW_ROLLBACK:
1618+      return sqlite3_mprintf("rollback to %s;", (char *)event->body);
1619+    default:
1620+      return sqlite3_mprintf("%s", (char *)event->body);
1621+  }
1622+}
1623+
1624+SQLITE_PRIVATE int sqlite3BinlogStmtStep(sqlite3 *db, Sqlite3BinlogStmt *bStmt, char **sql, Table **pOutTable)
1625+{
1626+  if (bStmt == NULL || bStmt->cursor == NULL || sql == NULL) {
1627+    sqlite3_log(SQLITE_ERROR, "binlog stmt step parameter is null");
1628+    return SQLITE_ERROR;
1629+  }
1630+  if (bStmt->curIdx >= bStmt->cursor->eventNum) {
1631+    return SQLITE_DONE;
1632+  }
1633+  BinlogEventT *event = bStmt->cursor->sqlEvent[bStmt->curIdx];
1634+  if (event == NULL) {
1635+    sqlite3_log(SQLITE_ERROR, "binlog stmt step event is null");
1636+    return SQLITE_ERROR;
1637+  }
1638+  int rc = SQLITE_OK;
1639+  *sql = sqlite3BinlogGetEventSql(db, event, pOutTable, &rc);
1640+  if (rc != SQLITE_OK){
1641+    return rc;
1642+  }
1643+  bStmt->curIdx++;
1644+  return SQLITE_ROW;
1645+}
1646+
1647+SQLITE_PRIVATE void sqlite3BinlogStmtFinalize(sqlite3 *db, Sqlite3BinlogStmt *bStmt)
1648+{
1649+  if (db == NULL || bStmt == NULL || bStmt->cursor == NULL ||
1650+    db->xBinlogHandle.binlogApi.binlogFreeReadResultApi == NULL) {
1651+    sqlite3_log(SQLITE_WARNING, "binlog stmt finalize parameter is null");
1652+    return;
1653+  }
1654+  db->xBinlogHandle.binlogApi.binlogFreeReadResultApi(db->xBinlogHandle.binlogConn, bStmt->cursor);
1655+  bStmt->cursor = NULL;
1656+  bStmt->curIdx = 0;
1657+}
1658+
1659+SQLITE_PRIVATE int sqlite3BinlogExecuteReplaySql(sqlite3 *srcDb, sqlite3 *destDb, const char *sql)
1660+{
1661+  if (srcDb == NULL || destDb == NULL || sql == NULL) {
1662+    sqlite3_log(SQLITE_ERROR, "binlog execute replay sql parameter is null");
1663+    return SQLITE_ERROR;
1664+  }
1665+  int rc = sqlite3_exec(destDb, sql, NULL, NULL, NULL);
1666+  if (rc == SQLITE_OK || rc == SQLITE_CONSTRAINT) {
1667+    return SQLITE_OK;
1668+  }
1669+  return rc;
1670+}
1671+
1672+SQLITE_PRIVATE int sqlite3BinlogReplay(sqlite3 *srcDb, sqlite3 *destDb)
1673+{
1674+  if (srcDb == NULL || destDb == NULL) {
1675+    sqlite3_log(SQLITE_ERROR, "binlog replay parameter is null");
1676+    return SQLITE_ERROR;
1677+  }
1678+  if ((srcDb->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
1679+    sqlite3_log(SQLITE_ERROR, "binlog replay srcDb not enable binlog");
1680+    return SQLITE_ERROR;
1681+  }
1682+  const char *srcFile = sqlite3_db_filename(srcDb, 0);
1683+  const char *destFile = sqlite3_db_filename(destDb, 0);
1684+  if (srcFile == NULL || destFile == NULL || strcmp(srcFile, destFile) == 0) {
1685+    return SQLITE_ERROR;
1686+  }
1687+  int res =
1688+    sqlite3TransferBinlogErrno(srcDb->xBinlogHandle.binlogApi.binlogLockReadApi(srcDb->xBinlogHandle.binlogConn));
1689+  if (res == SQLITE_BUSY) {
1690+    return res;
1691+  }
1692+  if (res != SQLITE_OK) {
1693+    sqlite3BinlogClose(srcDb);
1694+    return res;
1695+  }
1696+  u8 oldIsSkipTrigger = destDb->xBinlogHandle.isSkipTrigger;
1697+  destDb->xBinlogHandle.isSkipTrigger = 1;
1698+  do {
1699+    Sqlite3BinlogStmt bStmt;
1700+    bStmt.curIdx = 0;
1701+    bStmt.cursor = NULL;
1702+    res = sqlite3BinlogStmtPrepare(srcDb, &bStmt);
1703+    if (res != SQLITE_OK) {
1704+      if (res == SQLITE_DONE) {
1705+        res = SQLITE_OK;
1706+      } else {
1707+        sqlite3_log(res, "read binlog failed");
1708+      }
1709+      break;
1710+    }
1711+    char *bSql = NULL;
1712+    Table *pTab = NULL;
1713+    while ((res = sqlite3BinlogStmtStep(destDb, &bStmt, &bSql, &pTab)) == SQLITE_ROW) {
1714+      if (bSql == NULL) {
1715+        continue;
1716+      }
1717+      int errCode = sqlite3BinlogExecuteReplaySql(srcDb, destDb, bSql);
1718+      sqlite3_free(bSql);
1719+      bSql = NULL;
1720+      if (errCode != SQLITE_OK) {
1721+        res = errCode;
1722+        break;
1723+      }
1724+    }
1725+    sqlite3BinlogStmtFinalize(srcDb, &bStmt);
1726+    if (res != SQLITE_DONE && res != SQLITE_OK) {
1727+      sqlite3BinlogErrorCallback(srcDb, res, "exec replay sql failed");
1728+    }
1729+    if (res != SQLITE_DONE) {
1730+      break;
1731+    }
1732+  } while (1);
1733+  destDb->xBinlogHandle.isSkipTrigger = oldIsSkipTrigger;
1734+  (void)srcDb->xBinlogHandle.binlogApi.binlogUnlockReadApi(srcDb->xBinlogHandle.binlogConn);
1735+  if (res != SQLITE_OK) {
1736+    sqlite3BinlogClose(srcDb);
1737+  }
1738+  return res;
1739+}
1740+
1741+SQLITE_PRIVATE int sqlite3BinlogClean(sqlite3 *db, BinlogFileCleanModeE mode)
1742+{
1743+  if (db == NULL || mode < BINLOG_FILE_CLEAN_ALL_MODE ||
1744+    mode >= BINLOG_FILE_CLEAN_MODE_MAX || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0 ||
1745+    db->xBinlogHandle.binlogApi.binlogFileCleanApi == NULL) {
1746+    sqlite3_log(SQLITE_ERROR, "binlog clean parameter is invalid");
1747+    return SQLITE_ERROR;
1748+  }
1749+
1750+  return sqlite3TransferBinlogErrno(db->xBinlogHandle.binlogApi.binlogFileCleanApi(db->xBinlogHandle.binlogConn, mode));
1751+}
1752+
1753+SQLITE_PRIVATE void sqlite3BinlogErrorCallback(sqlite3 *db, int errNo, char *errMsg)
1754+{
1755+  if (db == NULL || db->xBinlogHandle.xErrorCallback == NULL) {
1756+    return;
1757+  }
1758+
1759+  const char *zFile = sqlite3_db_filename(db, 0);
1760+  if (zFile == NULL || (db->xBinlogHandle.flags & BINLOG_FLAG_ENABLE) == 0) {
1761+    sqlite3_log(SQLITE_ERROR, "binlog db has no file path");
1762+    return;
1763+  }
1764+  db->xBinlogHandle.xErrorCallback(db->xBinlogHandle.callbackCtx, errNo, errMsg, zFile);
1765+}
1766+/************** End of binlog implement ************************************/
1767+#endif /* SQLITE_ENABLE_BINLOG */
1768+
1769 // export the symbols
1770 #ifdef SQLITE_EXPORT_SYMBOLS
1771 #ifndef SQLITE_CKSUMVFS_STATIC
1772@@ -256942,6 +258434,9 @@ struct sqlite3_api_routines_extra {
1773   int (*key_v2)(sqlite3*,const char*,const void*,int);
1774   int (*rekey)(sqlite3*,const void*,int);
1775   int (*rekey_v2)(sqlite3*,const char*,const void*,int);
1776+  int (*is_support_binlog)(void);
1777+  int (*replay_binlog)(sqlite3*, sqlite3*);
1778+  int (*clean_binlog)(sqlite3*, BinlogFileCleanModeE);
1779 };
1780
1781 typedef struct sqlite3_api_routines_extra sqlite3_api_routines_extra;
1782@@ -256952,13 +258447,22 @@ static const sqlite3_api_routines_extra sqlite3ExtraApis = {
1783   sqlite3_key,
1784   sqlite3_key_v2,
1785   sqlite3_rekey,
1786-  sqlite3_rekey_v2
1787+  sqlite3_rekey_v2,
1788 #else
1789   0,
1790   0,
1791   0,
1792-  0
1793+  0,
1794 #endif /* SQLITE_HAS_CODEC */
1795+#ifdef SQLITE_ENABLE_BINLOG
1796+  sqlite3_is_support_binlog,
1797+  sqlite3_replay_binlog,
1798+  sqlite3_clean_binlog,
1799+#else
1800+  0,
1801+  0,
1802+  0,
1803+#endif/* SQLITE_ENABLE_BINLOG */
1804 };
1805
1806 EXPORT_SYMBOLS const sqlite3_api_routines *sqlite3_export_symbols = &sqlite3Apis;
1807--
18082.47.0.windows.2
1809
1810