Fossil

Check-in [48b9bf35]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:More fixes to the unversioned file sync protocol.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | unversioned-files
Files: files | file ages | folders
SHA1:48b9bf351f4a9eaf53cdf175838612f80912ccdd
User & Date: drh 2016-08-09 18:59:03
Context
2016-08-09
19:41
Do not add extra \n characters to the sync protocol. check-in: 71a50a1d user: drh tags: unversioned-files
18:59
More fixes to the unversioned file sync protocol. check-in: 48b9bf35 user: drh tags: unversioned-files
17:31
Another unversioned file sync fix. check-in: a6b66652 user: drh tags: unversioned-files
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/unversioned.c.

282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

306
307
308
309
310
311
312
313
                       " ORDER BY name");
      }
      while( db_step(&q)==SQLITE_ROW ){
        fossil_print("%s\n", db_column_text(&q,0));
      }
    }else{
      db_prepare(&q,
        "SELECT hash, datetime(mtime,'unixepoch'), sz, name, content IS NULL"
        "   FROM unversioned"
        "  ORDER BY name;"
      );
      while( db_step(&q)==SQLITE_ROW ){
        const char *zHash = db_column_text(&q, 0);
        const char *zNoContent = "";
        if( zHash==0 ){
          if( !allFlag ) continue;
          zHash = "(deleted)";
        }else if( db_column_int(&q,4) ){
          zNoContent = " (no content)";
        }
        fossil_print("%12.12s %s %8d %s%s\n",
           zHash,
           db_column_text(&q,1),
           db_column_int(&q,2),

           db_column_text(&q,3),
           zNoContent
        );
      }
    }
    db_finalize(&q);
  }else if( memcmp(zCmd, "revert", nCmd)==0 ){
    fossil_fatal("not yet implemented...");







|









|


|



>
|







282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
                       " ORDER BY name");
      }
      while( db_step(&q)==SQLITE_ROW ){
        fossil_print("%s\n", db_column_text(&q,0));
      }
    }else{
      db_prepare(&q,
        "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name"
        "   FROM unversioned"
        "  ORDER BY name;"
      );
      while( db_step(&q)==SQLITE_ROW ){
        const char *zHash = db_column_text(&q, 0);
        const char *zNoContent = "";
        if( zHash==0 ){
          if( !allFlag ) continue;
          zHash = "(deleted)";
        }else if( db_column_type(&q,3)==SQLITE_NULL ){
          zNoContent = " (no content)";
        }
        fossil_print("%12.12s %s %8d %8d %s%s\n",
           zHash,
           db_column_text(&q,1),
           db_column_int(&q,2),
           db_column_int(&q,3),
           db_column_text(&q,4),
           zNoContent
        );
      }
    }
    db_finalize(&q);
  }else if( memcmp(zCmd, "revert", nCmd)==0 ){
    fossil_fatal("not yet implemented...");

Changes to src/xfer.c.

309
310
311
312
313
314
315

316
317
318
319
320
321
322
...
327
328
329
330
331
332
333

334
335
336
337
338
339
340
...
354
355
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378




379

380






381
382
383
384
385

386
387
388
389
390
391
392
...
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
....
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
....
1839
1840
1841
1842
1843
1844
1845

1846
1847
1848
1849
1850
1851
1852
....
2053
2054
2055
2056
2057
2058
2059


2060
2061
2062
2063
2064
2065
2066
static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){
  sqlite3_int64 mtime;      /* The MTIME */
  Blob *pHash;              /* The HASH value */
  int sz;                   /* The SIZE */
  int flags;                /* The FLAGS */
  Blob content;             /* The CONTENT */
  Blob hash;                /* Hash computed from CONTENT to compare with HASH */

  Stmt q;                   /* SQL statements for comparison and insert */
  int isDelete;             /* HASH is "-" indicating this is a delete operation */
  int nullContent;          /* True of CONTENT is NULL */
  int iStatus;              /* Result from unversioned_status() */

  pHash = &pXfer->aToken[3];
  if( pXfer->nToken==5
................................................................................
   || !blob_is_int(&pXfer->aToken[5], &flags)
  ){
    blob_appendf(&pXfer->err, "malformed uvfile line");
    return;
  }
  blob_init(&content, 0, 0);
  blob_init(&hash, 0, 0);

  if( sz>0 && (flags & 0x0005)==0 ){
    blob_extract(pXfer->pIn, sz, &content);
    nullContent = 0;
    sha1sum_blob(&content, &hash);
    if( blob_compare(&hash, pHash)!=0 ){
      blob_appendf(&pXfer->err, "in uvfile line, HASH does not match CONTENT");
      goto end_accept_unversioned_file;
................................................................................
  if( iStatus>=3 ) goto end_accept_unversioned_file;
  
  /* Store the content */
  isDelete = blob_eq(pHash, "-");
  if( isDelete ){
    db_prepare(&q,
      "UPDATE unversioned"
      "   SET rcvid=:rcvid, mtime=:mtime, hash=NULL, sz=0, content=NULL"
      " WHERE name=:name"
    );

  }else if( iStatus==4 ){
    db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name");
  }else{
    db_prepare(&q,
      "REPLACE INTO unversioned(name, rcvid, mtime, hash, sz, content)"
      " VALUES(:name,:rcvid,:mtime,:hash,:sz,:content)"
    );
  }
  db_bind_text(&q, ":name", blob_str(&pXfer->aToken[1]));
  db_bind_int(&q, ":rcvid", g.rcvid);
  db_bind_int64(&q, ":mtime", mtime);
  db_bind_text(&q, ":hash", blob_str(pHash));
  db_bind_int(&q, ":sz", blob_size(&content));
  if( !nullContent ){
    blob_compress(&content, &content);




    db_bind_blob(&q, ":content", &content);

  }






  db_step(&q);
  db_finalize(&q);
  db_unset("uv-hash", 0);

end_accept_unversioned_file:

  blob_reset(&content);
  blob_reset(&hash);
}

/*
** Try to send a file as a delta against its parent.
** If successful, return the number of bytes in the delta.
................................................................................
    );
  }
  if( db_step(&q1)==SQLITE_ROW ){
    sqlite3_int64 mtime = db_column_int64(&q1, 0);
    const char *zHash = db_column_text(&q1, 1);
    blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime);
    if( zHash==0 ){
      blob_append(pXfer->pOut, " 0 0 1\n", -1);
    }else if( noContent ){
      blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3));
    }else{
      Blob content;
      blob_init(&content, 0, 0);
      db_column_blob(&q1, 4, &content);
      if( db_column_int(&q1, 2) ){
................................................................................
  }

  /* When syncing unversioned files, create a TEMP table in which to store
  ** the names of files that do not need to be sent from client to server.
  */
  if( syncFlags & SYNC_UNVERSIONED ){
    db_multi_exec(
       "CREATE TEMP TABLE uv_toSend("
       "  name TEXT PRIMARY KEY,"
       "  mtimeOnly BOOLEAN"
       ") WITHOUT ROWID;"
       "INSERT INTO uv_toSend(name,mtimeOnly)"
       "  SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
    );
  }
................................................................................
      Stmt uvq;
      assert( (syncFlags & SYNC_UNVERSIONED)!=0 );
      assert( uvStatus==2 );
      db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend");
      while( db_step(&uvq)==SQLITE_ROW ){
        send_unversioned_file(&xfer, db_column_text(&uvq,0), db_column_int(&uvq,1));
        nCardSent++;

      }
      db_finalize(&uvq);
      uvDoPush = 0;
    }

    /* Append randomness to the end of the message.  This makes all
    ** messages unique so that that the login-card nonce will always
................................................................................
          );
          db_unset("uv-hash", 0);
        }
        if( iStatus<=3 ){
          db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
        }else if( iStatus==4 ){
          db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q", zName);


        }
      }else

      /*   push  SERVERCODE  PRODUCTCODE
      **
      ** Should only happen in response to a clone.  This message tells
      ** the client what product to use for the new database.







>







 







>







 







|


>




|
|

<
<
|
<
|
|
|
|
>
>
>
>
|
>
|
>
>
>
>
>
>





>







 







|







 







|







 







>







 







>
>







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
...
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373


374

375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
....
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
....
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
....
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
static void xfer_accept_unversioned_file(Xfer *pXfer, int isWriter){
  sqlite3_int64 mtime;      /* The MTIME */
  Blob *pHash;              /* The HASH value */
  int sz;                   /* The SIZE */
  int flags;                /* The FLAGS */
  Blob content;             /* The CONTENT */
  Blob hash;                /* Hash computed from CONTENT to compare with HASH */
  Blob x;                   /* Compressed content */
  Stmt q;                   /* SQL statements for comparison and insert */
  int isDelete;             /* HASH is "-" indicating this is a delete operation */
  int nullContent;          /* True of CONTENT is NULL */
  int iStatus;              /* Result from unversioned_status() */

  pHash = &pXfer->aToken[3];
  if( pXfer->nToken==5
................................................................................
   || !blob_is_int(&pXfer->aToken[5], &flags)
  ){
    blob_appendf(&pXfer->err, "malformed uvfile line");
    return;
  }
  blob_init(&content, 0, 0);
  blob_init(&hash, 0, 0);
  blob_init(&x, 0, 0);
  if( sz>0 && (flags & 0x0005)==0 ){
    blob_extract(pXfer->pIn, sz, &content);
    nullContent = 0;
    sha1sum_blob(&content, &hash);
    if( blob_compare(&hash, pHash)!=0 ){
      blob_appendf(&pXfer->err, "in uvfile line, HASH does not match CONTENT");
      goto end_accept_unversioned_file;
................................................................................
  if( iStatus>=3 ) goto end_accept_unversioned_file;
  
  /* Store the content */
  isDelete = blob_eq(pHash, "-");
  if( isDelete ){
    db_prepare(&q,
      "UPDATE unversioned"
      "   SET rcvid=:rcvid, mtime=:mtime, hash=NULL, sz=0, encoding=0, content=NULL"
      " WHERE name=:name"
    );
    db_bind_int(&q, ":rcvid", g.rcvid);
  }else if( iStatus==4 ){
    db_prepare(&q, "UPDATE unversioned SET mtime=:mtime WHERE name=:name");
  }else{
    db_prepare(&q,
      "REPLACE INTO unversioned(name, rcvid, mtime, hash, sz, encoding, content)"
      " VALUES(:name,:rcvid,:mtime,:hash,:sz,:encoding,:content)"
    );


    db_bind_int(&q, ":rcvid", g.rcvid);

    db_bind_text(&q, ":hash", blob_str(pHash));
    db_bind_int(&q, ":sz", blob_size(&content));
    if( !nullContent ){
      blob_compress(&content, &x);
      if( blob_size(&x) < 0.8*blob_size(&content) ){
        db_bind_blob(&q, ":content", &x);
        db_bind_int(&q, ":encoding", 1);
      }else{
        db_bind_blob(&q, ":content", &content);
        db_bind_int(&q, ":encoding", 0);
      }
    }else{
      db_bind_int(&q, ":encoding", 0);
    }
  }
  db_bind_text(&q, ":name", blob_str(&pXfer->aToken[1]));
  db_bind_int64(&q, ":mtime", mtime);
  db_step(&q);
  db_finalize(&q);
  db_unset("uv-hash", 0);

end_accept_unversioned_file:
  blob_reset(&x);
  blob_reset(&content);
  blob_reset(&hash);
}

/*
** Try to send a file as a delta against its parent.
** If successful, return the number of bytes in the delta.
................................................................................
    );
  }
  if( db_step(&q1)==SQLITE_ROW ){
    sqlite3_int64 mtime = db_column_int64(&q1, 0);
    const char *zHash = db_column_text(&q1, 1);
    blob_appendf(pXfer->pOut, "uvfile %s %lld", zName, mtime);
    if( zHash==0 ){
      blob_append(pXfer->pOut, " - 0 1\n", -1);
    }else if( noContent ){
      blob_appendf(pXfer->pOut, " %s %d 4\n", zHash, db_column_int(&q1,3));
    }else{
      Blob content;
      blob_init(&content, 0, 0);
      db_column_blob(&q1, 4, &content);
      if( db_column_int(&q1, 2) ){
................................................................................
  }

  /* When syncing unversioned files, create a TEMP table in which to store
  ** the names of files that do not need to be sent from client to server.
  */
  if( syncFlags & SYNC_UNVERSIONED ){
    db_multi_exec(
       "CREATE TEMP TABLE uv_tosend("
       "  name TEXT PRIMARY KEY,"
       "  mtimeOnly BOOLEAN"
       ") WITHOUT ROWID;"
       "INSERT INTO uv_toSend(name,mtimeOnly)"
       "  SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
    );
  }
................................................................................
      Stmt uvq;
      assert( (syncFlags & SYNC_UNVERSIONED)!=0 );
      assert( uvStatus==2 );
      db_prepare(&uvq, "SELECT name, mtimeOnly FROM uv_tosend");
      while( db_step(&uvq)==SQLITE_ROW ){
        send_unversioned_file(&xfer, db_column_text(&uvq,0), db_column_int(&uvq,1));
        nCardSent++;
        nArtifactSent++;
      }
      db_finalize(&uvq);
      uvDoPush = 0;
    }

    /* Append randomness to the end of the message.  This makes all
    ** messages unique so that that the login-card nonce will always
................................................................................
          );
          db_unset("uv-hash", 0);
        }
        if( iStatus<=3 ){
          db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
        }else if( iStatus==4 ){
          db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q", zName);
        }else if( iStatus==5 ){
          db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)", zName);
        }
      }else

      /*   push  SERVERCODE  PRODUCTCODE
      **
      ** Should only happen in response to a clone.  This message tells
      ** the client what product to use for the new database.