]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a protocol version number to the first message, and give the two sides
authordrh <>
Thu, 12 Sep 2024 15:36:34 +0000 (15:36 +0000)
committerdrh <>
Thu, 12 Sep 2024 15:36:34 +0000 (15:36 +0000)
an opportunity to negotiate a suitable protocol number, for future
compatibility.  Send the page size as a power-of-two.

FossilOrigin-Name: df0623aae1154281157409f62d6d3fb3ce41829281d53bc55868ce44b3d36883

manifest
manifest.uuid
tool/sqlite3-rsync.c

index a0d9e1b120997e70c3e32c6e18165e9cc3914f7a..aea0407ab4f60bcc5866251a957f5142b44758d8 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\ssha1()\sfunctions\sto\sthe\sCLI.\s\sFix\ssha1b()\ssuch\sthat\sit\sactually\sreturns\na\sBLOB.
-D 2024-09-12T14:43:05.090
+C Add\sa\sprotocol\sversion\snumber\sto\sthe\sfirst\smessage,\sand\sgive\sthe\stwo\ssides\nan\sopportunity\sto\snegotiate\sa\ssuitable\sprotocol\snumber,\sfor\sfuture\ncompatibility.\s\sSend\sthe\spage\ssize\sas\sa\spower-of-two.
+D 2024-09-12T15:36:34.506
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -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 d9f8803f79c66dbc213761a345e24ae22c7de14fd334150086519c611ff1a705
+F tool/sqlite3-rsync.c f3283380beddab3cf876be781fbd2d00308249f08d6e4438411e64f27e7b67bd
 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 80461e0d724963aaf2646005298f1194c5f1c4c9ae41c1085d4d137ed485bd9f
-R 40763ccf45039aac90ad4e181b463c0b
+P fe65821a3b912f061026e6fd7174be26897010e6b474e2780350cac60faebaad
+R 70c38e114e917b5cf93c46e6c1a47f24
 U drh
-Z 969215ecb0944de74c8a9b9fbf319407
+Z 9a7103765746c5b1023f192fb95a127f
 # Remove this line to create a well-formed Fossil manifest.
index 86f93a2219756fcd542e634d07d51aa1613bf532..01d7fcfa05875c40ebeb3a90ff61ecf157b041ca 100644 (file)
@@ -1 +1 @@
-fe65821a3b912f061026e6fd7174be26897010e6b474e2780350cac60faebaad
+df0623aae1154281157409f62d6d3fb3ce41829281d53bc55868ce44b3d36883
index 276b9af90fb981cf5898a40480a6ea474e129fe5..315c7198a45406ccda1562d8a9c4aa7f97240e6c 100644 (file)
 #include "sqlite3.h"
 
 static const char zUsage[] = 
-  "sqlite3-rsync ORIGIN REPLICA\n"
+  "sqlite3-rsync ORIGIN REPLICA ?OPTIONS?\n"
   "\n"
   "One of ORIGIN or REPLICA is a pathname to a database on the local\n"
   "machine and the other is of the form \"USER@HOST:PATH\" describing\n"
   "a database on a remote machine.  This utility makes REPLICA into a\n"
   "copy of ORIGIN\n"
+  "\n"
+  "OPTIONS:\n"
+  "\n"
+  "   --exe PATH    Name of the sqlite3-rsync program on the remote side\n"
+  "   --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"
 ;
 
 typedef unsigned char u8;
@@ -43,6 +50,7 @@ struct SQLiteRsync {
   u8 eVerbose;             /* Bigger for more output.  0 means none. */
   u8 bCommCheck;           /* True to debug the communication protocol */
   u8 isRemote;             /* On the remote side of a connection */
+  u8 iProtocol;            /* Protocol version number */
   sqlite3_uint64 nOut;     /* Bytes transmitted */
   sqlite3_uint64 nIn;      /* Bytes received */
   unsigned int nPage;      /* Total number of pages in the database */
@@ -51,6 +59,11 @@ struct SQLiteRsync {
   unsigned int nPageSent;  /* Page contents sent (origin to replica) */
 };
 
+/* The version number of the protocol.  Sent in the *_BEGIN message
+** to verify that both sides speak the same dialect.
+*/
+#define PROTOCOL_VERSION  1
+
 
 /* Magic numbers to identify particular messages sent over the wire.
 */
@@ -553,6 +566,28 @@ void writeByte(SQLiteRsync *p, int c){
   p->nOut++;
 }
 
+/* Read a power of two encoded as a single byte.
+*/
+int readPow2(SQLiteRsync *p){
+  int x = readByte(p);
+  if( x>=32 ){
+    p->nErr++;
+    return 0;
+  }
+  return 1<<x;
+}
+
+/* Write a power-of-two value onto the wire as a single byte.
+*/
+void writePow2(SQLiteRsync *p, int c){
+  int n;
+  if( c<0 || (c&(c-1))!=0 ){
+    p->nErr++;
+  }
+  for(n=0; c>1; n++){ c /= 2; }
+  writeByte(p, n);
+}
+
 /* Read an array of bytes from the wire.
 */
 void readBytes(SQLiteRsync *p, int nByte, void *pData){
@@ -838,16 +873,30 @@ static void originSide(SQLiteRsync *p){
   if( p->nErr==0 ){
     /* Send the ORIGIN_BEGIN message */
     writeByte(p, ORIGIN_BEGIN);
+    writeByte(p, PROTOCOL_VERSION);
+    writePow2(p, szPg);
     writeUint32(p, nPage);
-    writeUint32(p, szPg);
     fflush(p->pOut);
     p->nPage = nPage;
     p->szPage = szPg;
+    p->iProtocol = PROTOCOL_VERSION;
   }
 
   /* Respond to message from the replica */
   while( p->nErr==0 && (c = readByte(p))!=EOF && c!=REPLICA_END ){
     switch( c ){
+      case REPLICA_BEGIN: {
+        /* This message is only sent if the replica received an origin-protocol
+        ** that is larger than what it knows about.  The replica sends back
+        ** a counter-proposal of an earlier protocol which the origin can
+        ** accept by resending a new ORIGIN_BEGIN. */
+        p->iProtocol = readByte(p);
+        writeByte(p, ORIGIN_BEGIN);
+        writeByte(p, p->iProtocol);
+        writePow2(p, p->szPage);
+        writeUint32(p, p->nPage);
+        break;
+      }
       case REPLICA_ERROR: {
         readAndDisplayError(p);
         break;
@@ -916,11 +965,13 @@ origin_end:
 ** Run the replica-side protocol.  The protocol is passive in the sense
 ** that it only response to message from the origin side.
 **
-**    ORIGIN_BEGIN  nPage szPage
+**    ORIGIN_BEGIN  idProtocol szPage nPage
 **
-**         The origin is reporting the number of pages and the size of each
-**         pages.  This procedure checks compatibility, and if everything is
-**         ok, it sends hash for all its extant pages.
+**         The origin is reporting the protocol version number, the size of
+**         each page in the origin database (sent as a single-byte power-of-2),
+**         and the number of pages in the origin database.
+**         This procedure checks compatibility, and if everything is ok,
+**         it starts sending hashes of pages already present back to the origin.
 **
 **    ORIGIN_ERROR  size text
 **
@@ -970,9 +1021,19 @@ static void replicaSide(SQLiteRsync *p){
         sqlite3_stmt *pStmt = 0;
 
         closeDb(p);
+        p->iProtocol = readByte(p);
+        szOPage = readPow2(p);
         readUint32(p, &nOPage);
-        readUint32(p, &szOPage);
         if( p->nErr ) break;
+        if( p->iProtocol>PROTOCOL_VERSION ){
+          /* If the protocol version on the origin side is larger, send back
+          ** a REPLICA_BEGIN message with the protocol version number of the
+          ** replica side.  This gives the origin an opportunity to resend
+          ** a new ORIGIN_BEGIN with a reduced protocol version. */
+          writeByte(p, REPLICA_BEGIN);
+          writeByte(p, PROTOCOL_VERSION);
+          break;
+        }
         p->nPage = nOPage;
         p->szPage = szOPage;
         rc = sqlite3_open(p->zReplica, &p->db);
@@ -1003,6 +1064,8 @@ static void replicaSide(SQLiteRsync *p){
                          "replica is %d bytes", szOPage, szRPage);
           break;
         }
+        
+
         pStmt = prepareStmt(p,
                    "SELECT sha1b(data) FROM sqlite_dbpage"
                    " WHERE pgno<=min(%d,%d)"