-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
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
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
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
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.
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
+#include <stdarg.h>
#include "sqlite3.h"
static const char zUsage[] =
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 *************
****************************************************************************/
# 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
/*
}
}
+/* 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.
**
** 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);
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);
}
/*
** 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);
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);
}
**
** 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;
if( isOrigin ){
ctx.pIn = stdin;
ctx.pOut = stdout;
+ ctx.isRemote = 1;
originSide(&ctx);
return 0;
}
ctx.zOrigin = 0;
ctx.pIn = stdin;
ctx.pOut = stdout;
+ ctx.isRemote = 1;
replicaSide(&ctx);
return 0;
}