]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Progress on the sqlite3-rsync utility. This is an incremental check-in. It
authordrh <>
Wed, 11 Sep 2024 17:02:44 +0000 (17:02 +0000)
committerdrh <>
Wed, 11 Sep 2024 17:02:44 +0000 (17:02 +0000)
does compile, but it does not work.

FossilOrigin-Name: fa06977b6db7fa745720561ec0b10570cf7e71598dc7a7c5ee650640e5bdf6f5

Makefile.in
Makefile.msc
ext/misc/sha1.c
main.mk
manifest
manifest.uuid
tool/sqlite3-rsync.c

index 5a36dca9d10e565e715904989527d26d7145ee5b..0cfae780cce6c6dbfa9696f2546e8dd23111bcd8 100644 (file)
@@ -700,8 +700,18 @@ sqldiff$(TEXE):    $(TOP)/tool/sqldiff.c sqlite3.lo sqlite3.h
 dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.lo sqlite3.h
        $(LTLINK) -o $@ $(TOP)/tool/dbhash.c sqlite3.lo $(TLIBS)
 
-sqlite3-rsync$(TEXE):  $(TOP)/tool/sqlite3-rsync.c sqlite3.lo sqlite3.h
-       $(LTLINK) -o $@ $(TOP)/tool/sqlite3-rsync.c sqlite3.lo $(TLIBS)
+RSYNC_SRC = \
+  $(TOP)/tool/sqlite3-rsync.c \
+  $(TOP)/ext/misc/sha1.c \
+  sqlite3.c
+
+RSYNC_OPT = \
+  -DSQLITE_THREADSAFE=0 \
+  -DSQLITE_OMIT_LOAD_EXTENSION \
+  -DSQLITE_OMIT_DEPRECATED
+
+sqlite3-rsync$(TEXE):  $(RSYNC_SRC)
+       $(TCC) -o $@ $(RSYNC_SRC) $(TLIBS)
 
 scrub$(TEXE):  $(TOP)/ext/misc/scrub.c sqlite3.lo
        $(LTLINK) -o $@ -I. -DSCRUB_STANDALONE \
index e60df047e6ac60153183ce3fb4afca98d714683c..461859d1f192b50386345e31def07832e311d19e 100644 (file)
@@ -1867,8 +1867,18 @@ sqldiff.exe:     $(TOP)\tool\sqldiff.c $(TOP)\ext\consio\console_io.h $(TOP)\ext\con
 dbhash.exe:    $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
        $(LTLINK) $(NO_WARN) $(TOP)\tool\dbhash.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
 
-sqlite3-rsync.exe:     $(TOP)\tool\sqlite3-rsync.c $(SQLITE3C) $(SQLITE3H) $(LIBRESOBJS)
-       $(LTLINK) $(NO_WARN) $(TOP)\tool\sqlite3-rsync.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
+RSYNC_SRC = \
+  $(TOP)\tool\sqlite3-rsync.c \
+  $(TOP)\ext\misc\sha1.c \
+  $(SQLITE3C)
+
+RSYNC_OPT = \
+  -DSQLITE_THREADSAFE=0 \
+  -DSQLITE_OMIT_LOAD_EXTENSION \
+  -DSQLITE_OMIT_DEPRECATED
+
+sqlite3-rsync.exe:     $(RSYNC_SRC) $(LIBRESOBJS)
+       $(LTLINK) $(RSYNC_OPT) $(NO_WARN) $(RSYNC_SRC) /link $(LDFLAGS) $(LTLINKOPTS) $(LIBRESOBJS)
 
 scrub.exe:     $(TOP)\ext\misc\scrub.c $(SQLITE3C) $(SQLITE3H)
        $(LTLINK) $(NO_WARN) -DSCRUB_STANDALONE=1 $(TOP)\ext\misc\scrub.c $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
index 9790a1d87752fbe998db7bf3f3e6ddd22d2b298b..dbdebbff8773b53509fcdf7c47cb77aad2b1fc9f 100644 (file)
@@ -196,7 +196,8 @@ static void hash_step_vformat(
 ** zOut[].  zOut[] must be at least 41 bytes long. */
 static void hash_finish(
   SHA1Context *p,           /* The SHA1 context to finish and render */
-  char *zOut                /* Store hexadecimal hash here */
+  char *zOut,               /* Store hex or binary hash here */
+  int bAsBinary             /* 1 for binary hash, 0 for hex hash */
 ){
   unsigned int i;
   unsigned char finalcount[8];
@@ -251,7 +252,7 @@ static void sha1Func(
   }else{
     hash_step(&cx, sqlite3_value_text(argv[0]), nByte);
   }
-  hash_finish(&cx, zOut);
+  hash_finish(&cx, zOut, sqlite3_user_data(context)!=0);
   sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
 }
 
@@ -365,7 +366,7 @@ static void sha1QueryFunc(
     }
     sqlite3_finalize(pStmt);
   }
-  hash_finish(&cx, zOut);
+  hash_finish(&cx, zOut, 0);
   sqlite3_result_text(context, zOut, 40, SQLITE_TRANSIENT);
 }
 
@@ -379,11 +380,17 @@ int sqlite3_sha_init(
   const sqlite3_api_routines *pApi
 ){
   int rc = SQLITE_OK;
+  static int one = 1;
   SQLITE_EXTENSION_INIT2(pApi);
   (void)pzErrMsg;  /* Unused parameter */
   rc = sqlite3_create_function(db, "sha1", 1, 
                        SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
-                               0, sha1Func, 0, 0);
+                                0, sha1Func, 0, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3_create_function(db, "sha1b", 1, 
+                       SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
+                          (void*)&one, sha1Func, 0, 0);
+  }
   if( rc==SQLITE_OK ){
     rc = sqlite3_create_function(db, "sha1_query", 1, 
                                  SQLITE_UTF8|SQLITE_DIRECTONLY, 0,
diff --git a/main.mk b/main.mk
index 4c395e6e616bc0948c57a4acc28515e85459fffc..7414e7488e6907fa04ae851e2f3957a650b807ef 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -568,9 +568,18 @@ dbhash$(EXE):      $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h
        $(TCCX) -o dbhash$(EXE) -DSQLITE_THREADSAFE=0 \
                $(TOP)/tool/dbhash.c sqlite3.c $(TLIBS) $(THREADLIB)
 
-sqlite3-rsync$(EXE):   $(TOP)/tool/sqlite3-rsync.c sqlite3.o
-       $(TCCX) -o sqlite3-rsync$(EXE) -DSQLITE_THREADSAFE=0 \
-               $(TOP)/tool/sqlite3-rsync.c sqlite3.o $(TLIBS) $(THREADLIB)
+RSYNC_SRC = \
+  $(TOP)/tool/sqlite3-rsync.c \
+  $(TOP)/ext/misc/sha1.c \
+  sqlite3.c
+
+RSYNC_OPT = \
+  -DSQLITE_THREADSAFE=0 \
+  -DSQLITE_OMIT_LOAD_EXTENSION \
+  -DSQLITE_OMIT_DEPRECATED
+
+sqlite3-rsync$(EXE):   $(RSYNC_SRC)
+       $(TCC) -o $@ $(RSYNC_OPT) $(RSYNC_SRC) $(TLIBS)
 
 scrub$(EXE):   $(TOP)/ext/misc/scrub.c sqlite3.o
        $(TCC) -I. -DSCRUB_STANDALONE -o scrub$(EXE) $(TOP)/ext/misc/scrub.c sqlite3.o $(THREADLIB)
index c58c5ffe3db7bac764f7eb0e1bd286b807e5acd9..c99d1cd05f4a6b970319ae4b2733646d7df0336b 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,11 +1,11 @@
-C Improved\sSSH\sinfrastructure.\s\sThe\sfoundation\sis\snow\sin\splace\sto\sbegin\sworking\non\sthe\sactual\ssync\sprotocol.\s\sStill\sexperimental.\s\sStill\sa\swork\sin\sprogress.
-D 2024-09-10T22:14:18.799
+C Progress\son\sthe\ssqlite3-rsync\sutility.\s\sThis\sis\san\sincremental\scheck-in.\s\sIt\ndoes\scompile,\sbut\sit\sdoes\snot\swork.
+D 2024-09-11T17:02:44.010
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in c9a51ee844a471c950881748f21699fdbf42ef540bf5e78d269f99003f510256
+F Makefile.in 54e80b016b0e58db383c0f08d340ef795b8b709ffb6f53a51a8ba7bf0c5e288f
 F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
-F Makefile.msc 4ecdd8ec6bb3264cc2f6c4b154cf9ddd2647e4c6fcb2a294c9725a1483cb2862
+F Makefile.msc a86e0f3fe5f807daa82d44b5056e3dbc311e569bd4748646a776a994124ec58b
 F README.md c3c0f19532ce28f6297a71870f3c7b424729f0e6d9ab889616d3587dd2332159
 F VERSION 0db40f92c04378404eb45bff93e9e42c148c7e54fd3da99469ed21e22411f5a6
 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -420,7 +420,7 @@ F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6
 F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
 F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
 F ext/misc/series.c a6089b5e8e3002bd1e5d9877cee6aead0b9a6426e406c09a399817db9e9ae823
-F ext/misc/sha1.c 4011aef176616872b2a0d5bccf0ecfb1f7ce3fe5c3d107f3a8e949d8e1e3f08d
+F ext/misc/sha1.c dfd26eb3437a88fe7349d1fe080b761549c456ae17cb11242441bf66031942bf
 F ext/misc/shathree.c 1821d90a0040c9accdbe3e3527d378d30569475d758aa70f6848924c0b430e8c
 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
 F ext/misc/spellfix.c c0aa7b80d6df45f7da59d912b38752bcac1af53a5766966160e6c5cdd397dbea
@@ -687,7 +687,7 @@ F ext/wasm/wasmfs.make 8a4955882aaa0783b3f60a9484a1f0f3d8b6f775c0fcd17c082f31966
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
 F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
 F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
-F main.mk 936f535d99e70cf7c1f51c485a3fad7c7c858abad34d41ce100729befc2b2afe
+F main.mk 9ffe4a14bdb4a0b856217a5465ca6b1ef4bef66a3c45e2da3fdb6d9bfc8d583e
 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
 F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
 F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -2174,7 +2174,7 @@ F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
 F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d40618 x
 F tool/split-sqlite3c.tcl 5aa60643afca558bc732b1444ae81a522326f91e1dc5665b369c54f09e20de60
 F tool/sqldiff.c 847fc8fcfddf5ce4797b7394cad6372f2f5dc17d8186e2ef8fb44d50fae4f44a
-F tool/sqlite3-rsync.c d9fd25997c34d9a63e7afdd99b467aaa69440e3ce4d4f85cf47da3e182f4c7e9
+F tool/sqlite3-rsync.c eb75a24e3a47fe7b5a4d5cbd2eadd4f4b6b6d6038ec7c94eb98a879ccd1bf8c5
 F tool/sqlite3_analyzer.c.in 8da2b08f56eeac331a715036cf707cc20f879f231362be0c22efd682e2b89b4f
 F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
 F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
@@ -2213,8 +2213,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 397b2d37b7a6619b0c1eee201065585d03496f94786b21540f613e4716d56612
-R 721e909ab77c2cd7295ba63619aaa44c
+P 9a1a95f523a96303aad57e2422c2b51ea7e125f5490f32f7a2929d49b6c69ef8
+R f1d49deccd29ff432816ec80f04a6271
 U drh
-Z 2e6a3a2b31db87fda5c83fa80a99e1a6
+Z f5a21c2123f254d15ff8790517b1651d
 # Remove this line to create a well-formed Fossil manifest.
index a90d6d4a2bbe741e880d5a456cdd1c57e31f1c1c..6113137f180d5fc9a41ba7b6084ba85ac92b88b5 100644 (file)
@@ -1 +1 @@
-9a1a95f523a96303aad57e2422c2b51ea7e125f5490f32f7a2929d49b6c69ef8
+fa06977b6db7fa745720561ec0b10570cf7e71598dc7a7c5ee650640e5bdf6f5
index 02a59a92bd2dc0bf207ded2f39ec61773374bd19..68bfe5d6686351e8ec58e3664d0bcbb00b630ca3 100644 (file)
@@ -17,6 +17,7 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <string.h>
+#include <stdarg.h>
 #include "sqlite3.h"
 
 static const char zUsage[] = 
@@ -37,10 +38,29 @@ struct SQLiteRsync {
   FILE *pIn;               /* Receive from the other side */
   sqlite3_uint64 nOut;     /* Bytes transmitted */
   sqlite3_uint64 nIn;      /* Bytes received */
+  sqlite3 *db;             /* Database connection */
+  int nErr;                /* Number of errors encountered */
   int eVerbose;            /* Bigger for more output.  0 means none. */
   int bCommCheck;          /* True to debug the communication protocol */
+  int isRemote;            /* On the remote side of a connection */
 };
 
+
+/* Magic numbers to identify particular messages sent over the wire.
+*/
+#define ORIGIN_BEGIN    0x41     /* Initial message */
+#define ORIGIN_END      0x42     /* Time to quit */
+#define ORIGIN_ERROR    0x43     /* Error message from the remote */
+#define ORIGIN_PAGE     0x44     /* New page data */
+#define ORIGIN_TXN      0x45     /* Transaction commit */
+
+#define REPLICA_BEGIN   0x61     /* Welcome message */
+#define REPLICA_ERROR   0x62     /* Error.  Report and quit. */
+#define REPLICA_END     0x63     /* Replica wants to stop */
+#define REPLICA_HASH    0x64     /* One or more pages hashes to report */
+#define REPLICA_READY   0x65     /* Read to receive page content */
+
+
 /****************************************************************************
 ** Beginning of the popen2() implementation copied from Fossil  *************
 ****************************************************************************/
@@ -87,6 +107,12 @@ static void win32_fatal_error(const char *zMsg){
 # define PTR_TO_INT(X)  ((int)(X))
 #endif
 
+/* Register SQL functions provided by ext/misc/sha1.c */
+extern int sqlite3_sha_init(
+  sqlite3 *db,
+  char **pzErrMsg,
+  const sqlite3_api_routines *pApi
+);
 
 #ifdef _WIN32
 /*
@@ -471,6 +497,238 @@ static void echoOneLine(SQLiteRsync *p){
   }
 }
 
+/* Read a single big-endian 32-bit unsigned integer from the input
+** stream.  Return 0 on success and 1 if there are any errors.
+*/
+static int readUint32(SQLiteRsync *p, unsigned int *pU){
+  unsigned char buf[4];
+  if( fread(buf, sizeof(buf), 1, p->pIn)==1 ){
+    *pU = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
+    return 0;
+  }else{
+    p->nErr++;
+    return 1;
+  }
+}
+
+/* Write a single big-endian 32-bit unsigned integer to the output stream.
+** Return 0 on success and 1 if there are any errors.
+*/
+static int writeUint32(SQLiteRsync *p, unsigned int x){
+  unsigned char buf[4];
+  buf[3] = x & 0xff;
+  x >>= 8;
+  buf[2] = x & 0xff;
+  x >>= 8;
+  buf[1] = x & 0xff;
+  x >>= 8;
+  buf[0] = x;
+  if( fwrite(buf, sizeof(buf), 1, p->pOut)!=1 ){
+    p->nErr++;
+    return 1;
+  }
+  return 0;
+}
+
+/* Report an error.
+**
+** If this happens on the remote side, we send back a REMOTE_ERROR
+** message.  On the local side, the error message goes to stderr.
+*/
+static void reportError(SQLiteRsync *p, const char *zFormat, ...){
+  va_list ap;
+  char *zMsg;
+  unsigned int nMsg;
+  va_start(ap, zFormat);
+  zMsg = sqlite3_vmprintf(zFormat, ap);
+  va_end(ap);
+  nMsg = zMsg ? (unsigned int)strlen(zMsg) : 0;
+  if( p->isRemote ){
+    if( p->zReplica ){
+      putc(REPLICA_ERROR, p->pOut);
+    }else{
+      putc(ORIGIN_ERROR, p->pOut);
+    }
+    writeUint32(p, nMsg);
+    fwrite(zMsg, nMsg, 1, p->pOut);
+    fflush(p->pOut);
+  }else{
+    fprintf(stderr, "%s\n", zMsg);
+  }
+  sqlite3_free(zMsg);
+  p->nErr++;
+}
+
+/* Receive and report an error message coming from the other side.
+*/
+static void readAndDisplayError(SQLiteRsync *p){
+  unsigned int n = 0;
+  char *zMsg;
+  (void)readUint32(p, &n);
+  if( n==0 ){
+    fprintf(stderr,"ERROR: unknown (possibly out-of-memory)\n");
+  }else{
+    zMsg = sqlite3_malloc64( n+1 );
+    if( zMsg==0 ){
+      fprintf(stderr, "ERROR: out-of-memory\n");
+      return;
+    }
+    memset(zMsg, 0, n+1);
+    fread(zMsg, 1, n, p->pIn);
+    fprintf(stderr,"ERROR: %s\n", zMsg);
+    sqlite3_free(zMsg);
+  }
+  p->nErr++;
+}
+
+/* Construct a new prepared statement.  Report an error and return NULL
+** if anything goes wrong.
+*/
+static sqlite3_stmt *prepareStmtVA(
+  SQLiteRsync *p,
+  char *zFormat,
+  va_list ap
+){
+  sqlite3_stmt *pStmt = 0;
+  char *zSql;
+  char *zToFree = 0;
+  int rc;
+
+  if( strchr(zFormat,'%') ){
+    zSql = sqlite3_vmprintf(zFormat, ap);
+    if( zSql==0 ){
+      reportError(p, "out-of-memory");
+      return 0;
+    }else{
+      zToFree = zSql;
+    }
+  }else{
+    zSql = zFormat;
+  }
+  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+  if( rc || pStmt==0 ){
+    reportError(p, "unable to prepare SQL [%s]: %s", zSql,
+                sqlite3_errmsg(p->db));
+    sqlite3_finalize(pStmt);
+    pStmt = 0;
+  }
+  if( zToFree ) sqlite3_free(zToFree);
+  return pStmt;
+}
+static sqlite3_stmt *prepareStmt(
+  SQLiteRsync *p,
+  char *zFormat,
+  ...
+){
+  sqlite3_stmt *pStmt;
+  va_list ap;
+  va_start(ap, zFormat);
+  pStmt = prepareStmtVA(p, zFormat, ap);
+  va_end(ap);
+  return pStmt;
+}
+
+/* Run a single SQL statement
+*/
+static void runSql(SQLiteRsync *p, char *zSql, ...){
+  sqlite3_stmt *pStmt;
+  va_list ap;
+
+  va_start(ap, zSql);
+  pStmt = prepareStmtVA(p, zSql, ap);
+  va_end(ap);
+  if( pStmt ){
+    int rc = sqlite3_step(pStmt);
+    if( rc!=SQLITE_OK && rc!=SQLITE_DONE ){
+      reportError(p, "SQL statement [%s] failed: %s", zSql,
+                  sqlite3_errmsg(p->db));
+    }
+    sqlite3_finalize(pStmt);
+  }
+}
+
+/* Run an SQL statement that returns a single unsigned 32-bit integer result
+*/
+static int runSqlReturnUInt(
+  SQLiteRsync *p,
+  unsigned int *pRes,
+  char *zSql,
+  ...
+){
+  sqlite3_stmt *pStmt;
+  int res = 0;
+  va_list ap;
+
+  va_start(ap, zSql);
+  pStmt = prepareStmtVA(p, zSql, ap);
+  va_end(ap);
+  if( pStmt==0 ){
+    res = 1;
+  }else{
+    int rc = sqlite3_step(pStmt);
+    if( rc==SQLITE_ROW ){
+      *pRes = (unsigned int)(sqlite3_column_int64(pStmt, 0)&0xffffffff);
+    }else{
+      reportError(p, "SQL statement [%s] failed: %s", zSql,
+                  sqlite3_errmsg(p->db));
+      res = 1;
+    }
+    sqlite3_finalize(pStmt);
+  }
+  return res;
+}
+
+/* Run an SQL statement that returns a single TEXT value that is no more
+** than 99 bytes in length.
+*/
+static int runSqlReturnText(
+  SQLiteRsync *p,
+  char *pRes,
+  char *zSql,
+  ...
+){
+  sqlite3_stmt *pStmt;
+  int res = 0;
+  va_list ap;
+
+  va_start(ap, zSql);
+  pStmt = prepareStmtVA(p, zSql, ap);
+  va_end(ap);
+  pRes[0] = 0;
+  if( pStmt==0 ){
+    res = 1;
+  }else{
+    int rc = sqlite3_step(pStmt);
+    if( rc==SQLITE_ROW ){
+      const unsigned char *a = sqlite3_column_text(pStmt, 0);
+      int n;
+      if( a==0 ){
+        pRes[0] = 0;
+      }else{
+        n = sqlite3_column_bytes(pStmt, 0);
+        if( n>99 ) n = 99;
+        memcpy(pRes, a, n);
+        pRes[n] = 0;
+      }
+    }else{
+      reportError(p, "SQL statement [%s] failed: %s", zSql,
+                  sqlite3_errmsg(p->db));
+      res = 1;
+    }
+    sqlite3_finalize(pStmt);
+  }
+  return res;
+}
+
+/* Close the database connection associated with p
+*/
+static void closeDb(SQLiteRsync *p){
+  if( p->db ){
+    sqlite3_close(p->db);
+    p->db = 0;
+  }
+}
+
 /*
 ** Run the origin-side protocol.
 **
@@ -484,6 +742,12 @@ static void echoOneLine(SQLiteRsync *p){
 **    7.  Send origin-end message
 */
 static void originSide(SQLiteRsync *p){
+  int rc = 0;
+  int c = 0;
+  unsigned int nPage = 0;
+  unsigned int szPg = 0;
+  char buf[100];
+
   if( p->bCommCheck ){
     fprintf(p->pOut, "sqlite3-rsync origin-begin %s\n", p->zOrigin);
     fflush(p->pOut);
@@ -493,6 +757,59 @@ static void originSide(SQLiteRsync *p){
     echoOneLine(p);
     return;
   }
+
+  /* Open the ORIGIN database. */
+  rc = sqlite3_open_v2(p->zOrigin, &p->db, SQLITE_OPEN_READONLY, 0);
+  if( rc ){
+    reportError(p, "unable to open origin database file \"%s\": %s",
+                sqlite3_errmsg(p->db));
+    closeDb(p);
+    return;
+  }
+  sqlite3_sha_init(p->db, 0, 0);
+  runSql(p, "BEGIN");
+  runSqlReturnText(p, buf, "PRAGMA journal_mode");
+  if( sqlite3_stricmp(buf,"wal")!=0 ){
+    reportError(p, "Origin database is not in WAL mode");
+  }
+  runSqlReturnUInt(p, &nPage, "PRAGMA page_count");
+  runSqlReturnUInt(p, &szPg, "PRAGMA page_size");
+
+  if( p->nErr==0 ){
+    /* Send the ORIGIN_BEGIN message */
+    fputc(ORIGIN_BEGIN, p->pOut);
+    writeUint32(p, nPage);
+    writeUint32(p, szPg);
+    fflush(p->pOut);
+  }
+
+  /* Respond to message from the replica */
+  while( p->nErr==0 && (c = fgetc(p->pIn))!=EOF ){
+    switch( c ){
+      case REPLICA_ERROR: {
+        readAndDisplayError(p);
+        break;
+      }
+      case REPLICA_BEGIN: {
+        break;
+      }
+      case REPLICA_END: {
+        break;
+      }
+      case REPLICA_HASH: {
+        break;
+      }
+      case REPLICA_READY: {
+        break;
+      }
+      default: {
+        reportError(p, "Origin side received unknown message: 0x%02x", c);
+        break;
+      }
+    }
+  }
+
+  closeDb(p);
 }
 
 /*
@@ -508,6 +825,8 @@ static void originSide(SQLiteRsync *p){
 **    7.  COMMIT
 */
 static void replicaSide(SQLiteRsync *p){
+  int c;
+  char buf[100];
   if( p->bCommCheck ){
     echoOneLine(p);
     fprintf(p->pOut, "replica-begin %s\n", p->zReplica);
@@ -517,6 +836,74 @@ static void replicaSide(SQLiteRsync *p){
     fflush(p->pOut);
     return;
   }
+
+  /* Respond to message from the origin.  The origin will initiate the
+  ** the conversation with an ORIGIN_BEGIN message.
+  */
+  while( p->nErr==0 && (c = fgetc(p->pIn))!=EOF ){
+    switch( c ){
+      case ORIGIN_ERROR: {
+        readAndDisplayError(p);
+        break;
+      }
+      case ORIGIN_BEGIN: {
+        unsigned int nOPage = 0, szOPage = 0;
+        unsigned int nRPage = 0, szRPage = 0;
+        int rc = 0;
+        sqlite3_stmt *pStmt = 0;
+
+        closeDb(p);
+        readUint32(p, &nOPage);
+        readUint32(p, &szOPage);
+        if( p->nErr ) break;
+        rc = sqlite3_open(p->zReplica, &p->db);
+        if( rc ){
+          reportError(p, "cannot open replica database \"%s\": %s",
+                      p->zReplica, sqlite3_errmsg(p->db));
+          closeDb(p);
+          break;
+        }
+        sqlite3_sha_init(p->db, 0, 0);
+        if( runSqlReturnUInt(p, &nRPage, "PRAGMA page_count") ){
+          break;
+        }
+        if( nRPage==0 ){
+          runSql(p, "PRAGMA page_size=%u", szOPage);
+          runSql(p, "PRAGMA journal_mode=WAL");
+        }
+        runSql(p, "BEGIN IMMEDIATE");
+        runSqlReturnText(p, buf, "PRAGMA journal_mode");
+        if( strcmp(buf, "wal")!=0 ){
+          reportError(p, "replica is not in WAL mode");
+          break;
+        }
+        runSqlReturnUInt(p, &nRPage, "PRAGMA page_count");
+        runSqlReturnUInt(p, &szRPage, "PRAGMA page_size");
+        if( szRPage!=szOPage ){
+          reportError(p, "page size mismatch; origin is %d bytes and "
+                         "replica is %d bytes", szOPage, szRPage);
+          break;
+        }
+        pStmt = prepareStmt(p,
+                   "SELECT pgno, sha1(data) FROM sqlite_dbpage"
+                   " WHERE pgno<=min(%d,%d)", nRPage, nOPage);
+        sqlite3_finalize(pStmt);
+        break;
+      }
+      case ORIGIN_END: {
+        break;
+      }
+      case ORIGIN_PAGE: {
+        break;
+      }
+      default: {
+        reportError(p, "Replica side received unknown message: 0x%02x", c);
+        break;
+      }
+    }
+  }
+
+  closeDb(p);
 }
 
 
@@ -544,7 +931,7 @@ static void replicaSide(SQLiteRsync *p){
 **
 ** If (3) is seen, call originSide() on stdin and stdout.
 **
-** If (4) is seen, call replicaSide() on stdin and stdout.
+q** If (4) is seen, call replicaSide() on stdin and stdout.
 */
 int main(int argc, char **argv){
   int isOrigin = 0;
@@ -635,6 +1022,7 @@ int main(int argc, char **argv){
   if( isOrigin ){
     ctx.pIn = stdin;
     ctx.pOut = stdout;
+    ctx.isRemote = 1;
     originSide(&ctx);
     return 0;
   }
@@ -643,6 +1031,7 @@ int main(int argc, char **argv){
     ctx.zOrigin = 0;
     ctx.pIn = stdin;
     ctx.pOut = stdout;
+    ctx.isRemote = 1;
     replicaSide(&ctx);
     return 0;
   }