From: drh <> Date: Wed, 11 Sep 2024 17:02:44 +0000 (+0000) Subject: Progress on the sqlite3-rsync utility. This is an incremental check-in. It X-Git-Tag: version-3.47.0~118^2~20 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dc3bec34a6398334ec24af7879ae648da8e96e21;p=thirdparty%2Fsqlite.git Progress on the sqlite3-rsync utility. This is an incremental check-in. It does compile, but it does not work. FossilOrigin-Name: fa06977b6db7fa745720561ec0b10570cf7e71598dc7a7c5ee650640e5bdf6f5 --- diff --git a/Makefile.in b/Makefile.in index 5a36dca9d1..0cfae780cc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 \ diff --git a/Makefile.msc b/Makefile.msc index e60df047e6..461859d1f1 100644 --- a/Makefile.msc +++ b/Makefile.msc @@ -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) diff --git a/ext/misc/sha1.c b/ext/misc/sha1.c index 9790a1d877..dbdebbff87 100644 --- a/ext/misc/sha1.c +++ b/ext/misc/sha1.c @@ -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 4c395e6e61..7414e7488e 100644 --- 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) diff --git a/manifest b/manifest index c58c5ffe3d..c99d1cd05f 100644 --- 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. diff --git a/manifest.uuid b/manifest.uuid index a90d6d4a2b..6113137f18 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -9a1a95f523a96303aad57e2422c2b51ea7e125f5490f32f7a2929d49b6c69ef8 +fa06977b6db7fa745720561ec0b10570cf7e71598dc7a7c5ee650640e5bdf6f5 diff --git a/tool/sqlite3-rsync.c b/tool/sqlite3-rsync.c index 02a59a92bd..68bfe5d668 100644 --- a/tool/sqlite3-rsync.c +++ b/tool/sqlite3-rsync.c @@ -17,6 +17,7 @@ #include #include #include +#include #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; }