Diff
Not logged in

Differences From:

File src/xfer.c part of check-in [507ee45f25] - Fix an off-by-one bug in the network protocol handler so that it can accept a zero-length file. by drh on 2007-08-25 12:31:55. Also file src/xfer.c part of check-in [b0ad3f90bc] - Merging aku's changes into the head. by drh on 2007-08-25 19:00:33. [view]

To:

File src/xfer.c part of check-in [8c828207a2] - Give an error if an attempt is made to merge, update, or checkout against an incomplete baseline - one that contains phantoms. Update the xfer protocol to converge on a stable synchronization faster and (hopeful) not quit until the sync is complete. by drh on 2007-08-27 00:04:32. Also file src/xfer.c part of check-in [15652ff081] - Merged drh's fixes new features (xfer, timeline handling, javascript based timeline highlighting) into my branch. by aku on 2007-08-29 02:55:33. [view]

@@ -64,8 +64,15 @@
   }
   return rid;
 }
 
+/*
+** Remember that the other side of the connection already has a copy
+** of the file rid.
+*/
+static void remote_has(int rid){
+  db_multi_exec("INSERT OR IGNORE INTO onremote VALUES(%d)", rid);
+}
 
 /*
 ** The aToken[0..nToken-1] blob array is a parse of a "file" line
 ** message.  This routine finishes parsing that message and does
@@ -127,8 +134,9 @@
     blob_appendf(&pXfer->err, "%s", g.zErrMsg);
   }else{
     manifest_crosslink(rid, &content);
   }
+  remote_has(rid);
 }
 
 /*
 ** Try to send a file as a delta.  If successful, return the number
@@ -222,19 +230,44 @@
     pXfer->nFileSent++;
   }else{
     pXfer->nDeltaSent++;
   }
-  db_multi_exec("INSERT INTO onremote VALUES(%d)", rid);
+  remote_has(rid);
   blob_reset(&uuid);
 }
 
 /*
+** Send the file identified by mid and pUuid.  If that file happens
+** to be a manifest, then also send all of the associated content
+** files for that manifest.  If the file is not a manifest, then this
+** routine is the equivalent of send_file().
+*/
+static void send_manifest(Xfer *pXfer, int mid, Blob *pUuid, int srcId){
+  Stmt q2;
+  send_file(pXfer, mid, pUuid, srcId);
+  db_prepare(&q2,
+     "SELECT pid, uuid, fid FROM mlink, blob"
+     " WHERE rid=fid AND mid=%d",
+     mid
+  );
+  while( db_step(&q2)==SQLITE_ROW ){
+    int pid, fid;
+    Blob uuid;
+    pid = db_column_int(&q2, 0);
+    db_ephemeral_blob(&q2, 1, &uuid);
+    fid = db_column_int(&q2, 2);
+    send_file(pXfer, fid, &uuid, pid);
+  }
+  db_finalize(&q2);
+}
+
+/*
 ** This routine runs when either client or server is notified that
-** the other side things rid is a leaf manifest.  If we hold
+** the other side thinks rid is a leaf manifest.  If we hold
 ** children of rid, then send them over to the other side.
 */
 static void leaf_response(Xfer *pXfer, int rid){
-  Stmt q1, q2;
+  Stmt q1;
   db_prepare(&q1,
       "SELECT cid, uuid FROM plink, blob"
       " WHERE blob.rid=plink.cid"
       "   AND plink.pid=%d",
@@ -245,22 +278,9 @@
     int cid;
 
     cid = db_column_int(&q1, 0);
     db_ephemeral_blob(&q1, 1, &uuid);
-    send_file(pXfer, cid, &uuid, rid);
-    db_prepare(&q2,
-       "SELECT pid, uuid, fid FROM mlink, blob"
-       " WHERE rid=fid AND mid=%d",
-       cid
-    );
-    while( db_step(&q2)==SQLITE_ROW ){
-      int pid, fid;
-      pid = db_column_int(&q2, 0);
-      db_ephemeral_blob(&q2, 1, &uuid);
-      fid = db_column_int(&q2, 2);
-      send_file(pXfer, fid, &uuid, pid);
-    }
-    db_finalize(&q2);
+    send_manifest(pXfer, cid, &uuid, rid);
     if( blob_size(pXfer->pOut)<pXfer->mxSend ){
       leaf_response(pXfer, cid);
     }
   }
@@ -279,8 +299,35 @@
     const char *zUuid = db_column_text(&q, 0);
     blob_appendf(pXfer->pOut, "leaf %s\n", zUuid);
   }
   db_finalize(&q);
+}
+
+/*
+** Sent leaf content for every leaf that is not found in the
+** onremote table.  This is intended to send leaf content for
+** every leaf that is unknown on the remote end.
+**
+** In addition, we might send "igot" messages for a few generations of
+** parents of the unknown leaves.  This will speed the transmission
+** of new branches.
+*/
+static void send_unknown_leaf_content(Xfer *pXfer){
+  Stmt q1;
+  db_prepare(&q1,
+    "SELECT rid, uuid FROM blob WHERE rid IN"
+    "  (SELECT cid FROM plink EXCEPT SELECT pid FROM plink)"
+    "  AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)"
+  );
+  while( db_step(&q1)==SQLITE_ROW ){
+    Blob uuid;
+    int cid;
+
+    cid = db_column_int(&q1, 0);
+    db_ephemeral_blob(&q1, 1, &uuid);
+    send_manifest(pXfer, cid, &uuid, 0);
+  }
+  db_finalize(&q1);
 }
 
 /*
 ** Sen a gimme message for every phantom.
@@ -408,9 +455,11 @@
     }else
 
     /*   gimme UUID
     **
-    ** Client is requesting a file
+    ** Client is requesting a file.  If the file is a manifest,
+    ** the server can assume that the client also needs all content
+    ** files associated with that manifest.
     */
     if( blob_eq(&xfer.aToken[0], "gimme")
      && xfer.nToken==2
      && blob_is_uuid(&xfer.aToken[1])
@@ -417,16 +466,16 @@
     ){
       if( isPull ){
         int rid = rid_from_uuid(&xfer.aToken[1], 0);
         if( rid ){
-          send_file(&xfer, rid, &xfer.aToken[1], 0);
+          send_manifest(&xfer, rid, &xfer.aToken[1], 0);
         }
       }
     }else
 
     /*   igot UUID
     **
-    ** Client announces that it has a particular file
+    ** Client announces that it has a particular file.
     */
     if( xfer.nToken==2
      && blob_eq(&xfer.aToken[0], "igot")
      && blob_is_uuid(&xfer.aToken[1])
@@ -448,20 +497,24 @@
      && blob_eq(&xfer.aToken[0], "leaf")
      && blob_is_uuid(&xfer.aToken[1])
     ){
       int rid = rid_from_uuid(&xfer.aToken[1], 0);
-      if( isPull && rid ){
-        leaf_response(&xfer, rid);
-      }
-      if( isPush && !rid ){
+      if( rid ){
+        remote_has(rid);
+        if( isPull ){
+          leaf_response(&xfer, rid);
+        }
+      }else if( isPush ){
         content_put(0, blob_str(&xfer.aToken[1]), 0);
       }
     }else
 
     /*    pull  SERVERCODE  PROJECTCODE
     **    push  SERVERCODE  PROJECTCODE
     **
-    ** The client wants either send or receive
+    ** The client wants either send or receive.  The server should
+    ** verify that the project code matches and that the server code
+    ** does not match.
     */
     if( xfer.nToken==3
      && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push"))
      && blob_is_uuid(&xfer.aToken[1])
@@ -538,8 +591,9 @@
 
     /*    login  USER  NONCE  SIGNATURE
     **
     ** Check for a valid login.  This has to happen before anything else.
+    ** The client can send multiple logins.  Permissions are cumulative.
     */
     if( blob_eq(&xfer.aToken[0], "login")
      && xfer.nToken==4
     ){
@@ -559,8 +613,11 @@
     blobarray_reset(xfer.aToken, xfer.nToken);
   }
   if( isPush ){
     request_phantoms(&xfer);
+  }
+  if( isPull ){
+    send_unknown_leaf_content(&xfer);
   }
   db_end_transaction(0);
 }
 
@@ -698,17 +755,19 @@
 
       /*   file UUID SIZE \n CONTENT
       **   file UUID DELTASRC SIZE \n CONTENT
       **
-      ** Receive a file transmitted from the other side
+      ** Receive a file transmitted from the server.
       */
       if( blob_eq(&xfer.aToken[0],"file") ){
         xfer_accept_file(&xfer);
       }else
 
       /*   gimme UUID
       **
-      ** Server is requesting a file
+      ** Server is requesting a file.  If the file is a manifest, assume
+      ** that the server will also want to know all of the content files
+      ** associated with the manifest and send those too.
       */
       if( blob_eq(&xfer.aToken[0], "gimme")
        && xfer.nToken==2
        && blob_is_uuid(&xfer.aToken[1])
@@ -715,34 +774,46 @@
       ){
         nMsg++;
         if( pushFlag ){
           int rid = rid_from_uuid(&xfer.aToken[1], 0);
-          send_file(&xfer, rid, &xfer.aToken[1], 0);
+          send_manifest(&xfer, rid, &xfer.aToken[1], 0);
         }
       }else
 
       /*   igot UUID
       **
-      ** Server announces that it has a particular file
+      ** Server announces that it has a particular file.  If this is
+      ** not a file that we have and we are pulling, then create a
+      ** phantom to cause this file to be requested on the next cycle.
+      ** Always remember that the server has this file so that we do
+      ** not transmit it by accident.
       */
       if( xfer.nToken==2
        && blob_eq(&xfer.aToken[0], "igot")
        && blob_is_uuid(&xfer.aToken[1])
       ){
+        int rid = 0;
         nMsg++;
         if( pullFlag ){
           if( !db_exists("SELECT 1 FROM blob WHERE uuid='%b' AND size>=0",
                 &xfer.aToken[1]) ){
-            content_put(0, blob_str(&xfer.aToken[1]), 0);
+            rid = content_put(0, blob_str(&xfer.aToken[1]), 0);
             newPhantom = 1;
           }
         }
+        if( rid==0 ){
+          rid = rid_from_uuid(&xfer.aToken[1], 0);
+        }
+        remote_has(rid);
       }else
 
 
       /*   leaf UUID
       **
-      ** Server announces that it has a particular manifest
+      ** Server announces that it has a particular manifest.  Send
+      ** any children of this leaf that we have if we are pushing.
+      ** Make the leaf a phantom if we are pulling.  Remember that the
+      ** remote end has the specified UUID.
       */
       if( xfer.nToken==2
        && blob_eq(&xfer.aToken[0], "leaf")
        && blob_is_uuid(&xfer.aToken[1])
@@ -752,17 +823,19 @@
         if( pushFlag && rid ){
           leaf_response(&xfer, rid);
         }
         if( pullFlag && rid==0 ){
-          content_put(0, blob_str(&xfer.aToken[1]), 0);
+          rid = content_put(0, blob_str(&xfer.aToken[1]), 0);
           newPhantom = 1;
         }
+        remote_has(rid);
       }else
 
 
       /*   push  SERVERCODE  PRODUCTCODE
       **
-      ** Should only happen in response to a clone.
+      ** Should only happen in response to a clone.  This message tells
+      ** the client what product to use for the new database.
       */
       if( blob_eq(&xfer.aToken[0],"push")
        && xfer.nToken==3
        && cloneFlag