SQLite

Check-in [e4126dcd1e]
Login

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

Overview
Comment:Allow sqlite3_rsync to work on non-WAL-mode databases, as long as the --wal-only flag is not used.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | rsync-non-wal-mode
Files: files | file ages | folders
SHA3-256: e4126dcd1eba4f040a7c07102d34692287b74b41a3437a3b9d15c4f8c9d4e6fd
User & Date: drh 2025-05-01 16:07:52.716
Context
2025-05-01
18:07
Enhance sqlite3_rsync so that, by default, it will sync non-WAL-mode database files. Add a new command-line option --wal-only that restricts the sync to WAL-mode databases only (the former default). Improve command-line option parsing so that only a single "-" is required before each option. (Leaf check-in: 4b53603fe4 user: drh tags: trunk)
16:07
Allow sqlite3_rsync to work on non-WAL-mode databases, as long as the --wal-only flag is not used. (Closed-Leaf check-in: e4126dcd1e user: drh tags: rsync-non-wal-mode)
16:02
Mistake: Started from the wrong check-out. (Leaf check-in: 9f88f73ce3 user: drh tags: mistake)
2025-04-30
14:37
Fix a harmless problem in the CLI in which SQL errors that occur during the ".schema" command are properly ignored, yes still appear in the ".log" output. Forum post 42fe6520b8 (check-in: 20abf1ec10 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to tool/sqlite3_rsync.c.
31
32
33
34
35
36
37

38
39
40
41
42
43
44
  "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"
  "   --version     Show detailed version information\n"

;

typedef unsigned char u8;

/* Context for the run */
typedef struct SQLiteRsync SQLiteRsync;
struct SQLiteRsync {







>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  "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"
  "   --version     Show detailed version information\n"
  "   --wal-only    Do not sync unless both databases are in WAL mode\n"
;

typedef unsigned char u8;

/* Context for the run */
typedef struct SQLiteRsync SQLiteRsync;
struct SQLiteRsync {
53
54
55
56
57
58
59

60
61
62
63
64
65
66
  int nWrErr;              /* Number of failed attempts to write on the pipe */
  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 isReplica;            /* True if running on the replica side */
  u8 iProtocol;            /* Protocol version number */
  u8 wrongEncoding;        /* ATTACH failed due to wrong encoding */

  sqlite3_uint64 nOut;     /* Bytes transmitted */
  sqlite3_uint64 nIn;      /* Bytes received */
  unsigned int nPage;      /* Total number of pages in the database */
  unsigned int szPage;     /* Database page size */
  unsigned int nHashSent;  /* Hashes sent (replica to origin) */
  unsigned int nPageSent;  /* Page contents sent (origin to replica) */
};







>







54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
  int nWrErr;              /* Number of failed attempts to write on the pipe */
  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 isReplica;            /* True if running on the replica side */
  u8 iProtocol;            /* Protocol version number */
  u8 wrongEncoding;        /* ATTACH failed due to wrong encoding */
  u8 bWalOnly;             /* Require WAL mode */
  sqlite3_uint64 nOut;     /* Bytes transmitted */
  sqlite3_uint64 nIn;      /* Bytes received */
  unsigned int nPage;      /* Total number of pages in the database */
  unsigned int szPage;     /* Database page size */
  unsigned int nHashSent;  /* Hashes sent (replica to origin) */
  unsigned int nPageSent;  /* Page contents sent (origin to replica) */
};
1233
1234
1235
1236
1237
1238
1239

1240
1241
1242

1243
1244
1245
1246
1247
1248
1249
      reportError(p, "cannot open origin \"%s\": %s",
                  p->zOrigin, sqlite3_errmsg(p->db));
      closeDb(p);
      return;
    }
    hashRegister(p->db);
    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 */
      writeByte(p, ORIGIN_BEGIN);







>
|
|
|
>







1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
      reportError(p, "cannot open origin \"%s\": %s",
                  p->zOrigin, sqlite3_errmsg(p->db));
      closeDb(p);
      return;
    }
    hashRegister(p->db);
    runSql(p, "BEGIN");
    if( p->bWalOnly ){
      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 */
      writeByte(p, ORIGIN_BEGIN);
1459
1460
1461
1462
1463
1464
1465

1466
1467
1468
1469

1470
1471
1472
1473
1474
1475
1476
        }
        if( nRPage==0 ){
          runSql(p, "PRAGMA replica.page_size=%u", szOPage);
          runSql(p, "PRAGMA replica.journal_mode=WAL");
          runSql(p, "SELECT * FROM replica.sqlite_schema");
        }
        runSql(p, "BEGIN IMMEDIATE");

        runSqlReturnText(p, buf, "PRAGMA replica.journal_mode");
        if( strcmp(buf, "wal")!=0 ){
          reportError(p, "replica is not in WAL mode");
          break;

        }
        runSqlReturnUInt(p, &nRPage, "PRAGMA replica.page_count");
        runSqlReturnUInt(p, &szRPage, "PRAGMA replica.page_size");
        if( szRPage!=szOPage ){
          reportError(p, "page size mismatch; origin is %d bytes and "
                         "replica is %d bytes", szOPage, szRPage);
          break;







>
|
|
|
|
>







1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
        }
        if( nRPage==0 ){
          runSql(p, "PRAGMA replica.page_size=%u", szOPage);
          runSql(p, "PRAGMA replica.journal_mode=WAL");
          runSql(p, "SELECT * FROM replica.sqlite_schema");
        }
        runSql(p, "BEGIN IMMEDIATE");
        if( p->bWalOnly ){
          runSqlReturnText(p, buf, "PRAGMA replica.journal_mode");
          if( strcmp(buf, "wal")!=0 ){
            reportError(p, "replica is not in WAL mode");
            break;
          }
        }
        runSqlReturnUInt(p, &nRPage, "PRAGMA replica.page_count");
        runSqlReturnUInt(p, &szRPage, "PRAGMA replica.page_size");
        if( szRPage!=szOPage ){
          reportError(p, "page size mismatch; origin is %d bytes and "
                         "replica is %d bytes", szOPage, szRPage);
          break;
1665
1666
1667
1668
1669
1670
1671

1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715




1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
  sqlite3_int64 tmElapse;
  const char *zRemoteErrFile = 0;

#define cli_opt_val cmdline_option_value(argc, argv, ++i)
  memset(&ctx, 0, sizeof(ctx));
  for(i=1; i<argc; i++){
    const char *z = argv[i];

    if( strcmp(z,"--origin")==0 ){
      isOrigin = 1;
      continue;
    }
    if( strcmp(z,"--replica")==0 ){
      isReplica = 1;
      continue;
    }
    if( numVs(z) ){
      ctx.eVerbose += numVs(z);
      continue;
    }
    if( strcmp(z, "--ssh")==0 ){
      zSsh = cli_opt_val;
      continue;
    }
    if( strcmp(z, "--exe")==0 ){
      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
    ){
      printf("%s", zUsage);
      return 0;
    }
    if( strcmp(z, "--version")==0 ){
      printf("%s\n", sqlite3_sourceid());
      return 0;
    }
    if( z[0]=='-' ){
      if( strcmp(z,"--commcheck")==0 ){  /* DEBUG ONLY */
        /* Run a communication check with the remote side.  Do not attempt
        ** to exchange any database connection */
        ctx.bCommCheck = 1;
        continue;
      }
      if( strcmp(z,"--arg-escape-check")==0 ){  /* DEBUG ONLY */
        /* Test the append_escaped_arg() routine by using it to render a
        ** copy of the input command-line, assuming all arguments except
        ** this one are filenames. */
        sqlite3_str *pStr = sqlite3_str_new(0);
        int k;
        for(k=0; k<argc; k++){
          append_escaped_arg(pStr, argv[k], i!=k);







>
|



|







|



|



|











|





|





>
>
>
>







|




|





|







1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
  sqlite3_int64 tmElapse;
  const char *zRemoteErrFile = 0;

#define cli_opt_val cmdline_option_value(argc, argv, ++i)
  memset(&ctx, 0, sizeof(ctx));
  for(i=1; i<argc; i++){
    const char *z = argv[i];
    if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++;
    if( strcmp(z,"-origin")==0 ){
      isOrigin = 1;
      continue;
    }
    if( strcmp(z,"-replica")==0 ){
      isReplica = 1;
      continue;
    }
    if( numVs(z) ){
      ctx.eVerbose += numVs(z);
      continue;
    }
    if( strcmp(z, "-ssh")==0 ){
      zSsh = cli_opt_val;
      continue;
    }
    if( strcmp(z, "-exe")==0 ){
      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, "-wal-only")==0 ){
      ctx.bWalOnly = 1;
      continue;
    }
    if( strcmp(z, "-help")==0 || strcmp(z, "--help")==0
     || strcmp(z, "-?")==0
    ){
      printf("%s", zUsage);
      return 0;
    }
    if( strcmp(z, "-version")==0 ){
      printf("%s\n", sqlite3_sourceid());
      return 0;
    }
    if( z[0]=='-' ){
      if( strcmp(z,"-commcheck")==0 ){  /* DEBUG ONLY */
        /* Run a communication check with the remote side.  Do not attempt
        ** to exchange any database connection */
        ctx.bCommCheck = 1;
        continue;
      }
      if( strcmp(z,"-arg-escape-check")==0 ){  /* DEBUG ONLY */
        /* Test the append_escaped_arg() routine by using it to render a
        ** copy of the input command-line, assuming all arguments except
        ** this one are filenames. */
        sqlite3_str *pStr = sqlite3_str_new(0);
        int k;
        for(k=0; k<argc; k++){
          append_escaped_arg(pStr, argv[k], i!=k);
1815
1816
1817
1818
1819
1820
1821



1822
1823
1824
1825
1826
1827
1828
      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>=2 ) printf("%s\n", zCmd);
    if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
      fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
      return 1;







>
>
>







1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
      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);
    }
    if( ctx.bWalOnly ){
      append_escaped_arg(pStr, "--wal-only", 0);
    }
    append_escaped_arg(pStr, zDiv, 1);
    append_escaped_arg(pStr, file_tail(ctx.zReplica), 1);
    zCmd = sqlite3_str_finish(pStr);
    if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
    if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
      fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
      return 1;