Compare commits

..

10 Commits

Author SHA1 Message Date
stephan 11e1defc8f CLI arg handling robustness improvements.
FossilOrigin-Name: 129aca54f6b791c222b51f3eb01569e1e569269860e153b005140eb65af378b9
2024-09-14 16:30:01 +00:00
drh 3d56d59adc Add the --version option.
FossilOrigin-Name: 9961334c8007e7cb6ae55885075b74acddc4fa701b359cf67e0f3c237d7eba4a
2024-09-14 16:28:38 +00:00
drh 7154803d4c New debugging options: --logfile, --errorfile, --remote-errorfile.
If page 1 changes, always send it last.

FossilOrigin-Name: 2d8cd76691554578e987ce682cf0c42c083711dd1511a178148978182ef43ba2
2024-09-14 16:03:16 +00:00
drh df9b488799 Always show the -v traffic message even if there are multiple -v options.
FossilOrigin-Name: fc05a5b7f77cdbfcc659d49eb09569a64a172362cb90199e2861028085178f10
2024-09-14 11:38:46 +00:00
drh 15e05eab32 Add the --logfile FILE option for debugging.
FossilOrigin-Name: 30e1b92d5663e24d2f325f2bab35f81b55848ef39d15688e40b9005269626303
2024-09-14 11:23:57 +00:00
drh 9d3047bd0d Improved error messages coming out of sqlite3-rsync.
FossilOrigin-Name: 452fb6de3984c3cb10d30b51dcdb2574578ca128a0c519b2bd43df0bdd343083
2024-09-14 10:59:32 +00:00
drh 2b30518804 Make the output from sqlite3-rsync with a single -v option the same as it
is with regular rsync.  Only show the SSH command with two or more -v options,
or if there is an error in popen2().

FossilOrigin-Name: 105ec44b470318fc9ff1773027c4064343f224068c9b6e71c5618f18f7dfcc3f
2024-09-14 10:48:05 +00:00
drh 33f0a3ed13 Merge sqlite_dbpage fixes into the sqlite3-rsync branch.
FossilOrigin-Name: dff76b7a3436031ea5a61b8a44ddfa1d40ea20c983f3d34a8501cd7074db68b8
2024-09-13 23:41:16 +00:00
drh 762946b236 Bug fix in the enhanced sqlite_dbpage for when truncating two or more
ATTACH-ed databases within the same transaction.

FossilOrigin-Name: 6aa9c8e79b440c6419e65990d9ceba8f00a6f975455138cf2aa82b113daec825
2024-09-13 21:47:57 +00:00
drh 8550e4a9f8 Fix harmless compiler warning on Windows.
FossilOrigin-Name: 86e0219d977c493ac19d00c3ddcf560eb317d506c7cf6e4ef17e92daa91e1762
2024-09-13 16:12:54 +00:00
9 changed files with 216 additions and 135 deletions
+2 -9
View File
@@ -702,23 +702,16 @@ dbhash$(TEXE): $(TOP)/tool/dbhash.c sqlite3.lo sqlite3.h
RSYNC_SRC = \
$(TOP)/tool/sqlite3-rsync.c \
$(TOP)/ext/consio/console_io.c \
sqlite3.c
RSYNC_OPT = \
-I$(TOP)/ext/consio \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-USQLITE_THREADSAFE \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DCONSIO_SET_ERROR_STREAM
-DSQLITE_OMIT_DEPRECATED
RSYNC_DEP = \
$(TOP)/ext/consio/console_io.h \
$(TOP)/ext/consio/console_io.c
sqlite3-rsync$(TEXE): $(RSYNC_DEP) $(RSYNC_SRC)
sqlite3-rsync$(TEXE): $(RSYNC_SRC)
$(TCC) -o $@ $(RSYNC_OPT) $(RSYNC_SRC) $(TLIBS)
scrub$(TEXE): $(TOP)/ext/misc/scrub.c sqlite3.lo
+2 -5
View File
@@ -1869,18 +1869,15 @@ dbhash.exe: $(TOP)\tool\dbhash.c $(SQLITE3C) $(SQLITE3H)
RSYNC_SRC = \
$(TOP)\tool\sqlite3-rsync.c \
$(TOP)\ext\consio\console_io.c \
$(SQLITE3C)
RSYNC_OPT = \
-I$(TOP)/ext/consio \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DCONSIO_SET_ERROR_STREAM
-DSQLITE_OMIT_DEPRECATED
sqlite3-rsync.exe: $(RSYNC_SRC) $(RSYNC_DEP) $(LIBRESOBJS)
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)
+1 -1
View File
@@ -154,7 +154,7 @@ static ConsoleInfo consoleInfo = {
SAC_NoConsole /* sacSetup */
};
FILE* invalidFileStream = (FILE *)~0;
SQLITE_INTERNAL_LINKAGE FILE* invalidFileStream = (FILE *)~0;
# if CIO_WIN_WC_XLATE
static void maybeSetupAsConsole(PerStreamTags *ppst, short odir){
+1 -1
View File
@@ -120,7 +120,7 @@ SQLITE_INTERNAL_LINKAGE void SQLITE_CDECL consoleRestore( void );
** It is error to close a stream so designated, then, without
** designating another, use the corresponding {o,e}Emit(...).
*/
extern FILE *invalidFileStream;
SQLITE_INTERNAL_LINKAGE FILE *invalidFileStream;
SQLITE_INTERNAL_LINKAGE FILE *setOutputStream(FILE *pf);
# ifdef CONSIO_SET_ERROR_STREAM
SQLITE_INTERNAL_LINKAGE FILE *setErrorStream(FILE *pf);
+2 -8
View File
@@ -570,21 +570,15 @@ dbhash$(EXE): $(TOP)/tool/dbhash.c sqlite3.c sqlite3.h
RSYNC_SRC = \
$(TOP)/tool/sqlite3-rsync.c \
$(TOP)/ext/consio/console_io.c \
sqlite3.c
RSYNC_DEP = \
$(TOP)/ext/consio/console_io.h
RSYNC_OPT = \
-I$(TOP)/ext/consio \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DCONSIO_SET_ERROR_STREAM
-DSQLITE_OMIT_DEPRECATED
sqlite3-rsync$(EXE): $(RSYNC_SRC) $(RSYNC_DEP)
sqlite3-rsync$(EXE): $(RSYNC_SRC)
$(TCC) -o $@ $(RSYNC_OPT) $(RSYNC_SRC) $(TLIBS)
scrub$(EXE): $(TOP)/ext/misc/scrub.c sqlite3.o
+13 -13
View File
@@ -1,11 +1,11 @@
C Fix\sthe\shanging\sproblem.
D 2024-09-13T16:15:07.770
C CLI\sarg\shandling\srobustness\simprovements.
D 2024-09-14T16:30:01.739
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F Makefile.in 53f65164355c90793e6df88c5770ac04d02c1690e8a175e7cf5e703ca497cc4c
F Makefile.in 31368ad3e1800bb5f311adede543ee456ca7d2595403c7f131797ae65a7d415c
F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
F Makefile.msc 8732e98919b1b2b072fb52d46b5d200741b210b9f46bc17c355f9dcfc4844b4a
F Makefile.msc 62ace0005c53b52f189c20c1d6d8fa4dbd2a37c90d9c1362b60f4fb4c841fa15
F README.md c3c0f19532ce28f6297a71870f3c7b424729f0e6d9ab889616d3587dd2332159
F VERSION 0db40f92c04378404eb45bff93e9e42c148c7e54fd3da99469ed21e22411f5a6
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@ -53,8 +53,8 @@ F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
F ext/async/README.txt e12275968f6fde133a80e04387d0e839b0c51f91
F ext/async/sqlite3async.c 6f247666b495c477628dd19364d279c78ea48cd90c72d9f9b98ad1aff3294f94
F ext/async/sqlite3async.h 46b47c79357b97ad85d20d2795942c0020dc20c532114a49808287f04aa5309a
F ext/consio/console_io.c 5f8d6b832faa9db9f7442d6e02cf7e62409282c31fe68efa67da312db88c4fab x
F ext/consio/console_io.h 6212909529c4ab3deeeee29db2d0b2b9cd5c881cc9dd51dede079273ce91db3d
F ext/consio/console_io.c d2b74afae8d301de2e8447b1045fcd33eb59df13bf581d906d99c74fe5d2b13f x
F ext/consio/console_io.h b5ebe34aa15b357621ebbea3d3f2e2b24750d4280b5802516409e23947fd9ee5
F ext/expert/README.md b321c2762bb93c18ea102d5a5f7753a4b8bac646cb392b3b437f633caf2020c3
F ext/expert/expert.c d548d603a4cc9e61f446cc179c120c6713511c413f82a4a32b1e1e69d3f086a4
F ext/expert/expert1.test 661f873fd451127edf822ef0d520088faa319135f6a15bd10be6801ac284ac9b
@@ -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 27b50c79c995968403b7b2b5726069abb5c99d429c9949302049fed8a769671e
F main.mk 8b9c0252aef57b5b2a10f34b8b46e89f9ed06bdccef1df98673a12f34e9b3e79
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -714,7 +714,7 @@ F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d49
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c b224d3db0f28c4a5f1407c50107a0a8133bd244ff3c7f6f8cedeb896a8cf1b64
F src/date.c 89ce1ff20512a7fa5070ba6e7dd5c171148ca7d580955795bf97c79c2456144a
F src/dbpage.c 3c437630c2933b9eefca915d191f8dea9da135195593bb17f553be58ffcd3634
F src/dbpage.c 12e49515d67d4a59625d71f9aa42499556cfdc2e4f1ea49086e674a7f47f46e5
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
F src/delete.c 444c4d1eaac40103461e3b6f0881846dd3aafc1cec1dd169d3482fa331667da7
F src/expr.c 6d5f2c38fe3ec06a7eac599dac822788b36064124e20112a844e9cd5156cb239
@@ -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 780f48e6de46308546e1e4382f36ce85a4ab2085b75daa9c660e6d74fb1ea14c
F tool/sqlite3-rsync.c 00c9b4483851ca0337dff4b544ee39dffa8cdbe9dc55cd51d391590fd1f03763
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 b4eff1fd6eb5f373f0b4ca2709fcab735aed426cdfd8a88ed3f08fd10afcd534
R cd42a019762d0f5cd5c8b6435be44826
U drh
Z ce55e767fb46264cb3e1a3b4cf6e579f
P 9961334c8007e7cb6ae55885075b74acddc4fa701b359cf67e0f3c237d7eba4a
R 88be3363ce7bdb6c2bdb93ef027d46e9
U stephan
Z 5b00ba151c4dd893eaaa646fc9f450ae
# Remove this line to create a well-formed Fossil manifest.
+1 -1
View File
@@ -1 +1 @@
d57903f7fdb5abbda70f049bb0b4e229deefc39b8a4a4c48203d4c4e7be3782c
129aca54f6b791c222b51f3eb01569e1e569269860e153b005140eb65af378b9
+4 -1
View File
@@ -360,7 +360,7 @@ static int dbpageUpdate(
iDb = 0;
}else{
const char *zSchema = (const char*)sqlite3_value_text(argv[4]);
iDb = zSchema ? sqlite3FindDbName(pTab->db, zSchema) : -1;
iDb = sqlite3FindDbName(pTab->db, zSchema);
if( iDb<0 ){
zErr = "no such schema";
goto update_fail;
@@ -377,8 +377,11 @@ static int dbpageUpdate(
){
if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert ){
if( iDb>=pTab->nTrunc ){
testcase( pTab->aTrunc!=0 );
pTab->aTrunc = sqlite3_realloc(pTab->aTrunc, (iDb+1)*sizeof(Pgno));
if( pTab->aTrunc ){
int j;
for(j=pTab->nTrunc; j<iDb; j++) pTab->aTrunc[j] = 0;
pTab->nTrunc = iDb+1;
}else{
return SQLITE_NOMEM;
+190 -96
View File
@@ -20,7 +20,7 @@
#include <stdarg.h>
#include "sqlite3.h"
static const char zUsage[] =
static const char zUsage[] =
"sqlite3-rsync ORIGIN REPLICA ?OPTIONS?\n"
"\n"
"One of ORIGIN or REPLICA is a pathname to a database on the local\n"
@@ -34,6 +34,7 @@ static const char zUsage[] =
" --help Show this help screen\n"
" --ssh PATH Name of the SSH program used to reach the remote side\n"
" -v Verbose. Multiple v's for increasing output\n"
" --version Show detailed version information\n"
;
typedef unsigned char u8;
@@ -43,8 +44,10 @@ typedef struct SQLiteRsync SQLiteRsync;
struct SQLiteRsync {
const char *zOrigin; /* Name of the origin */
const char *zReplica; /* Name of the replica */
const char *zErrFile; /* Append error messages to this file */
FILE *pOut; /* Transmit to the other side */
FILE *pIn; /* Receive from the other side */
FILE *pLog; /* Duplicate output here if not NULL */
sqlite3 *db; /* Database connection */
int nErr; /* Number of errors encountered */
u8 eVerbose; /* Bigger for more output. 0 means none. */
@@ -82,34 +85,6 @@ struct SQLiteRsync {
#define REPLICA_READY 0x65 /* Read to receive page content */
#define REPLICA_MSG 0x66 /* Informational message */
#include "console_io.h"
/* From here onward, fgets() is redirected to the console_io library. */
# define fgets(b,n,f) fGetsUtf8(b,n,f)
/*
* Define macros for emitting output text in various ways:
* sputz(s, z) => emit 0-terminated string z to given stream s
* sputf(s, f, ...) => emit varargs per format f to given stream s
* oputz(z) => emit 0-terminated string z to default stream
* oputf(f, ...) => emit varargs per format f to default stream
* eputz(z) => emit 0-terminated string z to error stream
* eputf(f, ...) => emit varargs per format f to error stream
* oputb(b, n) => emit char buffer b[0..n-1] to default stream
*
* Note that the default stream is whatever has been last set via:
* setOutputStream(FILE *pf)
* This is normally the stream that CLI normal output goes to.
* For the stand-alone CLI, it is stdout with no .output redirect.
*
* The ?putz(z) forms are for unformatted strings.
*/
# define sputz(s,z) fPutsUtf8(z,s)
# define sputf fPrintfUtf8
# define oputz(z) oPutsUtf8(z)
# define oputf oPrintfUtf8
# define eputz(z) ePutsUtf8(z)
# define eputf ePrintfUtf8
# define oputb(buf,na) oPutbUtf8(buf,na)
/*# define fflush(s) fFlushBuffer(s)*/
/****************************************************************************
** Beginning of the popen2() implementation copied from Fossil *************
@@ -121,7 +96,7 @@ struct SQLiteRsync {
** Print a fatal error and quit.
*/
static void win32_fatal_error(const char *zMsg){
eputf("%s\n", zMsg);
fprintf(stderr, "%s", zMsg);
exit(1);
}
extern int _open_osfhandle(intptr_t,int);
@@ -299,7 +274,7 @@ static int popen2(
close(0);
fd = dup(pout[0]);
if( fd!=0 ) {
eputz("popen2() failed to open file descriptor 0\n");
fprintf(stderr,"popen2() failed to open file descriptor 0");
exit(1);
}
close(pout[0]);
@@ -307,7 +282,7 @@ static int popen2(
close(1);
fd = dup(pin[1]);
if( fd!=1 ){
eputz("popen() failed to open file descriptor 1\n");
fprintf(stderr,"popen() failed to open file descriptor 1");
exit(1);
}
close(pin[0]);
@@ -840,6 +815,24 @@ const char *file_tail(const char *z){
return zTail;
}
/*
** Append error message text to the error file, if an error file is
** specified. In any case, increment the error count.
*/
static void logError(SQLiteRsync *p, const char *zFormat, ...){
if( p->zErrFile ){
FILE *pErr = fopen(p->zErrFile, "a");
if( pErr ){
va_list ap;
va_start(ap, zFormat);
vfprintf(pErr, zFormat, ap);
va_end(ap);
fclose(pErr);
}
}
p->nErr++;
}
/* Read a single big-endian 32-bit unsigned integer from the input
** stream. Return 0 on success and 1 if there are any errors.
@@ -868,8 +861,9 @@ static int writeUint32(SQLiteRsync *p, unsigned int x){
buf[1] = x & 0xff;
x >>= 8;
buf[0] = x;
if( p->pLog ) fwrite(buf, sizeof(buf), 1, p->pLog);
if( fwrite(buf, sizeof(buf), 1, p->pOut)!=1 ){
p->nErr++;
logError(p, "failed to write 32-bit integer 0x%x", x);
return 1;
}
p->nOut += 4;
@@ -887,6 +881,7 @@ int readByte(SQLiteRsync *p){
/* Write a single byte into the wire.
*/
void writeByte(SQLiteRsync *p, int c){
if( p->pLog ) fputc(c, p->pLog);
fputc(c, p->pOut);
p->nOut++;
}
@@ -896,7 +891,7 @@ void writeByte(SQLiteRsync *p, int c){
int readPow2(SQLiteRsync *p){
int x = readByte(p);
if( x>=32 ){
p->nErr++;
logError(p, "read invalid page size %d\n", x);
return 0;
}
return 1<<x;
@@ -907,7 +902,7 @@ int readPow2(SQLiteRsync *p){
void writePow2(SQLiteRsync *p, int c){
int n;
if( c<0 || (c&(c-1))!=0 ){
p->nErr++;
logError(p, "trying to read invalid page size %d\n", c);
}
for(n=0; c>1; n++){ c /= 2; }
writeByte(p, n);
@@ -919,17 +914,18 @@ void readBytes(SQLiteRsync *p, int nByte, void *pData){
if( fread(pData, 1, nByte, p->pIn)==nByte ){
p->nIn += nByte;
}else{
p->nErr++;
logError(p, "failed to read %d bytes", nByte);
}
}
/* Write an array of bytes onto the wire.
*/
void writeBytes(SQLiteRsync *p, int nByte, const void *pData){
if( p->pLog ) fwrite(pData, 1, nByte, p->pLog);
if( fwrite(pData, 1, nByte, p->pOut)==nByte ){
p->nOut += nByte;
}else{
p->nErr++;
logError(p, "failed to write %d bytes", nByte);
}
}
@@ -956,15 +952,15 @@ static void reportError(SQLiteRsync *p, const char *zFormat, ...){
writeBytes(p, nMsg, zMsg);
fflush(p->pOut);
}else{
eputf("%s\n", zMsg);
fprintf(stderr, "%s\n", zMsg);
}
logError(p, "%s\n", zMsg);
sqlite3_free(zMsg);
p->nErr++;
}
/* Send an informational message.
**
** If this happens on the remote side, we send back a *_MSG
** If this happens on the remote side, we send back a *_MSG
** message. On the local side, the message goes to stdout.
*/
static void infoMsg(SQLiteRsync *p, const char *zFormat, ...){
@@ -985,7 +981,7 @@ static void infoMsg(SQLiteRsync *p, const char *zFormat, ...){
writeBytes(p, nMsg, zMsg);
fflush(p->pOut);
}else{
oputf("%s\n", zMsg);
printf("%s\n", zMsg);
}
sqlite3_free(zMsg);
}
@@ -998,22 +994,22 @@ static void readAndDisplayMessage(SQLiteRsync *p, int c){
const char *zPrefix;
if( c==ORIGIN_ERROR || c==REPLICA_ERROR ){
zPrefix = "ERROR: ";
p->nErr++;
}else{
zPrefix = "";
}
readUint32(p, &n);
if( n==0 ){
eputz("ERROR: unknown (possibly out-of-memory)\n");
fprintf(stderr,"ERROR: unknown (possibly out-of-memory)\n");
}else{
zMsg = sqlite3_malloc64( n+1 );
if( zMsg==0 ){
eputz("ERROR: out-of-memory\n");
fprintf(stderr, "ERROR: out-of-memory\n");
return;
}
memset(zMsg, 0, n+1);
readBytes(p, n, zMsg);
eputf("%s%s\n", zPrefix, zMsg);
fprintf(stderr,"%s%s\n", zPrefix, zMsg);
if( zPrefix[0] ) logError(p, "%s%s\n", zPrefix, zMsg);
sqlite3_free(zMsg);
}
}
@@ -1212,8 +1208,8 @@ static void originSide(SQLiteRsync *p){
/* Open the ORIGIN database. */
rc = sqlite3_open_v2(p->zOrigin, &p->db, SQLITE_OPEN_READWRITE, 0);
if( rc ){
reportError(p, "unable to open origin database file \"%s\": %s",
sqlite3_errmsg(p->db));
reportError(p, "cannot open origin \"%s\": %s",
p->zOrigin, sqlite3_errmsg(p->db));
closeDb(p);
return;
}
@@ -1225,7 +1221,7 @@ static void originSide(SQLiteRsync *p){
}
runSqlReturnUInt(p, &nPage, "PRAGMA page_count");
runSqlReturnUInt(p, &szPg, "PRAGMA page_size");
if( p->nErr==0 ){
/* Send the ORIGIN_BEGIN message */
writeByte(p, ORIGIN_BEGIN);
@@ -1238,7 +1234,7 @@ static void originSide(SQLiteRsync *p){
p->iProtocol = PROTOCOL_VERSION;
}
}
/* Respond to message from the replica */
while( p->nErr==0 && (c = readByte(p))!=EOF && c!=REPLICA_END ){
switch( c ){
@@ -1283,32 +1279,56 @@ static void originSide(SQLiteRsync *p){
}
case REPLICA_READY: {
sqlite3_stmt *pStmt;
int needPageOne = 0;
sqlite3_finalize(pCkHash);
pCkHash = 0;
if( iPage+1<p->nPage ){
runSql(p, "WITH RECURSIVE c(n) AS"
" (VALUES(%d) UNION ALL SELECT n+1 FROM c WHERE n<%d)"
" INSERT INTO badHash SELECT n FROM c",
iPage+1, p->nPage);
}
pStmt = prepareStmt(p,
"SELECT pgno, data"
" FROM badHash JOIN sqlite_dbpage('main') USING(pgno) "
"UNION ALL "
"SELECT pgno, data"
" FROM sqlite_dbpage('main')"
" WHERE pgno>%d",
iPage);
" FROM badHash JOIN sqlite_dbpage('main') USING(pgno)");
if( pStmt==0 ) break;
while( sqlite3_step(pStmt)==SQLITE_ROW && p->nErr==0 ){
unsigned int pgno = (unsigned int)sqlite3_column_int64(pStmt,0);
const void *pContent = sqlite3_column_blob(pStmt, 1);
writeByte(p, ORIGIN_PAGE);
writeUint32(p, (unsigned int)sqlite3_column_int64(pStmt, 0));
writeBytes(p, szPg, pContent);
p->nPageSent++;
if( pgno==1 ){
needPageOne = 1;
}else{
writeByte(p, ORIGIN_PAGE);
writeUint32(p, (unsigned int)sqlite3_column_int64(pStmt, 0));
writeBytes(p, szPg, pContent);
p->nPageSent++;
}
}
sqlite3_finalize(pStmt);
if( needPageOne ){
pStmt = prepareStmt(p,
"SELECT data"
" FROM sqlite_dbpage('main')"
" WHERE pgno=1"
);
if( pStmt==0 ) break;
while( sqlite3_step(pStmt)==SQLITE_ROW && p->nErr==0 ){
const void *pContent = sqlite3_column_blob(pStmt, 0);
writeByte(p, ORIGIN_PAGE);
writeUint32(p, 1);
writeBytes(p, szPg, pContent);
p->nPageSent++;
}
sqlite3_finalize(pStmt);
}
writeByte(p, ORIGIN_TXN);
writeUint32(p, nPage);
writeByte(p, ORIGIN_END);
goto origin_end;
}
default: {
reportError(p, "Origin side received unknown message: 0x%02x", c);
reportError(p, "Unknown message 0x%02x %lld bytes into conversation",
c, p->nIn);
break;
}
}
@@ -1396,7 +1416,7 @@ static void replicaSide(SQLiteRsync *p){
p->szPage = szOPage;
rc = sqlite3_open(p->zReplica, &p->db);
if( rc ){
reportError(p, "cannot open replica database \"%s\": %s",
reportError(p, "cannot open replica \"%s\": %s",
p->zReplica, sqlite3_errmsg(p->db));
closeDb(p);
break;
@@ -1458,6 +1478,7 @@ static void replicaSide(SQLiteRsync *p){
sqlite3_sql(pIns), sqlite3_errmsg(p->db));
}
sqlite3_reset(pIns);
p->nPage = nOPage;
runSql(p, "COMMIT");
}
break;
@@ -1469,7 +1490,7 @@ static void replicaSide(SQLiteRsync *p){
if( p->nErr ) break;
if( pIns==0 ){
pIns = prepareStmt(p,
"INSERT INTO sqlite_dbpage(pgno,data,schema) VALUES(?1,?2,'main')"
"INSERT INTO sqlite_dbpage(pgno,data) VALUES(?1,?2)"
);
if( pIns==0 ) break;
}
@@ -1480,14 +1501,15 @@ static void replicaSide(SQLiteRsync *p){
sqlite3_bind_blob(pIns, 2, buf, szOPage, SQLITE_STATIC);
rc = sqlite3_step(pIns);
if( rc!=SQLITE_DONE ){
reportError(p, "SQL statement [%s] failed: %s",
sqlite3_sql(pIns), sqlite3_errmsg(p->db));
reportError(p, "SQL statement [%s] failed (pgno=%u): %s",
sqlite3_sql(pIns), pgno, sqlite3_errmsg(p->db));
}
sqlite3_reset(pIns);
break;
}
default: {
reportError(p, "Replica side received unknown message: 0x%02x", c);
reportError(p, "Unknown message 0x%02x %lld bytes into conversation",
c, p->nIn);
break;
}
}
@@ -1518,13 +1540,24 @@ static int numVs(const char *z){
static const char *cmdline_option_value(int argc, const char * const*argv,
int i){
if( i==argc ){
eputf("%s: Error: missing argument to %s\n",
argv[0], argv[argc-1]);
fprintf(stderr,"%s: Error: missing argument to %s\n",
argv[0], argv[argc-1]);
exit(1);
}
return argv[i];
}
/*
** Return the current time in milliseconds since the Julian epoch.
*/
sqlite3_int64 currentTime(void){
sqlite3_int64 now = 0;
sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
if( pVfs && pVfs->iVersion>=2 && pVfs->xCurrentTimeInt64!=0 ){
pVfs->xCurrentTimeInt64(pVfs, &now);
}
return now;
}
/*
** Parse command-line arguments. Dispatch subroutines to do the
@@ -1564,11 +1597,13 @@ int main(int argc, char const * const *argv){
const char *zSsh = "ssh";
const char *zExe = "sqlite3-rsync";
char *zCmd = 0;
sqlite3_int64 tmStart;
sqlite3_int64 tmEnd;
sqlite3_int64 tmElapse;
const char *zRemoteErrFile = 0;
#define cli_opt_val cmdline_option_value(argc, argv, ++i)
memset(&ctx, 0, sizeof(ctx));
setOutputStream(stdout);
setErrorStream(stderr);
for(i=1; i<argc; i++){
const char *z = argv[i];
if( strcmp(z,"--origin")==0 ){
@@ -1591,10 +1626,39 @@ int main(int argc, char const * const *argv){
zExe = cli_opt_val;
continue;
}
if( strcmp(z, "--logfile")==0 ){
/* DEBUG OPTION: --logfile FILENAME
** Cause all local output traffic to be duplicated in FILENAME */
const char *zLog = cli_opt_val;
if( ctx.pLog ) fclose(ctx.pLog);
ctx.pLog = fopen(zLog, "wb");
if( ctx.pLog==0 ){
fprintf(stderr, "cannot open \"%s\" for writing\n", argv[i]);
return 1;
}
continue;
}
if( strcmp(z, "--errorfile")==0 ){
/* DEBUG OPTION: --errorfile FILENAME
** Error messages on the local side are written into FILENAME */
ctx.zErrFile = cli_opt_val;
continue;
}
if( strcmp(z, "--remote-errorfile")==0 ){
/* DEBUG OPTION: --remote-errorfile FILENAME
** Error messages on the remote side are written into FILENAME on
** the remote side. */
zRemoteErrFile = cli_opt_val;
continue;
}
if( strcmp(z, "-help")==0 || strcmp(z, "--help")==0
|| strcmp(z, "-?")==0
){
oputf("%s", zUsage);
printf("%s", zUsage);
return 0;
}
if( strcmp(z, "--version")==0 ){
printf("%s\n", sqlite3_sourceid());
return 0;
}
if( z[0]=='-' ){
@@ -1613,10 +1677,11 @@ int main(int argc, char const * const *argv){
for(k=0; k<argc; k++){
append_escaped_arg(pStr, argv[k], i!=k);
}
oputf("%s\n", sqlite3_str_value(pStr));
printf("%s\n", sqlite3_str_value(pStr));
return 0;
}
eputf("unknown option: \"%s\". Use --help for more detail.\n", z);
fprintf(stderr,
"unknown option: \"%s\". Use --help for more detail.\n", z);
return 1;
}
if( ctx.zOrigin==0 ){
@@ -1624,20 +1689,20 @@ int main(int argc, char const * const *argv){
}else if( ctx.zReplica==0 ){
ctx.zReplica = z;
}else{
eputf("Unknown argument: \"%s\"\n", z);
fprintf(stderr, "Unknown argument: \"%s\"\n", z);
return 1;
}
}
if( ctx.zOrigin==0 ){
eputz("missing ORIGIN database filename\n");
fprintf(stderr, "missing ORIGIN database filename\n");
return 1;
}
if( ctx.zReplica==0 ){
eputz("missing REPLICA database filename\n");
fprintf(stderr, "missing REPLICA database filename\n");
return 1;
}
if( isOrigin && isReplica ){
eputz("bad option combination\n");
fprintf(stderr, "bad option combination\n");
return 1;
}
if( isOrigin ){
@@ -1655,13 +1720,14 @@ int main(int argc, char const * const *argv){
return 0;
}
if( ctx.zReplica==0 ){
eputz("missing REPLICA database filename\n");
fprintf(stderr, "missing REPLICA database filename\n");
return 1;
}
tmStart = currentTime();
zDiv = strchr(ctx.zOrigin,':');
if( zDiv ){
if( strchr(ctx.zReplica,':')!=0 ){
eputz(
fprintf(stderr,
"At least one of ORIGIN and REPLICA must be a local database\n"
"You provided two remote databases.\n");
return 1;
@@ -1678,12 +1744,16 @@ int main(int argc, char const * const *argv){
append_escaped_arg(pStr, "--commcheck", 0);
if( ctx.eVerbose==0 ) ctx.eVerbose = 1;
}
if( zRemoteErrFile ){
append_escaped_arg(pStr, "--errorfile", 0);
append_escaped_arg(pStr, zRemoteErrFile, 1);
}
append_escaped_arg(pStr, zDiv, 1);
append_escaped_arg(pStr, file_tail(ctx.zReplica), 1);
zCmd = sqlite3_str_finish(pStr);
if( ctx.eVerbose ) oputf("%s\n", zCmd);
if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
eputf("Could not start auxiliary process: %s\n", zCmd);
fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
return 1;
}
replicaSide(&ctx);
@@ -1700,12 +1770,16 @@ int main(int argc, char const * const *argv){
append_escaped_arg(pStr, "--commcheck", 0);
if( ctx.eVerbose==0 ) ctx.eVerbose = 1;
}
if( zRemoteErrFile ){
append_escaped_arg(pStr, "--errorfile", 0);
append_escaped_arg(pStr, zRemoteErrFile, 1);
}
append_escaped_arg(pStr, file_tail(ctx.zOrigin), 1);
append_escaped_arg(pStr, zDiv, 1);
zCmd = sqlite3_str_finish(pStr);
if( ctx.eVerbose ) oputf("%s\n", zCmd);
if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
eputf("Could not start auxiliary process: %s\n", zCmd);
fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
return 1;
}
originSide(&ctx);
@@ -1717,31 +1791,51 @@ int main(int argc, char const * const *argv){
if( ctx.bCommCheck ){
append_escaped_arg(pStr, "--commcheck", 0);
}
if( zRemoteErrFile ){
append_escaped_arg(pStr, "--errorfile", 0);
append_escaped_arg(pStr, zRemoteErrFile, 1);
}
append_escaped_arg(pStr, ctx.zOrigin, 1);
append_escaped_arg(pStr, ctx.zReplica, 1);
zCmd = sqlite3_str_finish(pStr);
if( ctx.eVerbose ) oputf("%s\n", zCmd);
if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
eputf("Could not start auxiliary process: %s\n", zCmd);
fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
return 1;
}
originSide(&ctx);
}
if( ctx.pLog ) fclose(ctx.pLog);
tmEnd = currentTime();
tmElapse = tmEnd - tmStart; /* Elapse time in milliseconds */
if( ctx.nErr ){
printf("Databases where not synced due to errors\n");
}
if( ctx.eVerbose==1 ){
printf("Network traffic is %.1f%% of database size\n",
(100.0*(double)(ctx.nIn+ctx.nOut))/(ctx.szPage*(double)ctx.nPage));
}
if( ctx.eVerbose>=2 ){
if( ctx.nErr ) oputf("%d errors, ", ctx.nErr);
oputf("%lld bytes sent, %lld bytes received\n", ctx.nOut, ctx.nIn);
if( ctx.eVerbose>=2 ){
oputf("Database is %u pages of %u bytes each.\n",
ctx.nPage, ctx.szPage);
oputf("Sent %u hashes, %u page contents\n",
ctx.nHashSent, ctx.nPageSent);
if( ctx.eVerbose>=1 ){
char *zMsg;
sqlite3_int64 szTotal = (sqlite3_int64)ctx.nPage*(sqlite3_int64)ctx.szPage;
sqlite3_int64 nIO = ctx.nOut +ctx.nIn;
zMsg = sqlite3_mprintf("sent %,lld bytes, received %,lld bytes",
ctx.nOut, ctx.nIn);
printf("%s", zMsg);
sqlite3_free(zMsg);
if( tmElapse>0 ){
zMsg = sqlite3_mprintf(", %,.2f bytes/sec",
1000.0*(double)nIO/(double)tmElapse);
printf("%s\n", zMsg);
sqlite3_free(zMsg);
}else{
printf("\n");
}
if( ctx.nErr==0 ){
if( nIO<=szTotal && nIO>0 ){
zMsg = sqlite3_mprintf("total size %,lld speedup is %.2f",
szTotal, (double)szTotal/(double)nIO);
}else{
zMsg = sqlite3_mprintf("total size %,lld", szTotal);
}
printf("%s\n", zMsg);
sqlite3_free(zMsg);
}
}
sqlite3_free(zCmd);