SQLite

Check-in [0f152416be]
Login

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

Overview
Comment:Further tweaks to work with zipvfs.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | ota-update-no-pager_ota_mode
Files: files | file ages | folders
SHA1: 0f152416be792457c52417aeb531ac860d12a5bd
User & Date: dan 2015-02-10 20:00:38.125
Context
2015-02-11
16:25
Ensure that an error is reported if an attempt is made to update a wal mode database via ota. (check-in: 6fc5d4d26a user: dan tags: ota-update-no-pager_ota_mode)
2015-02-10
20:00
Further tweaks to work with zipvfs. (check-in: 0f152416be user: dan tags: ota-update-no-pager_ota_mode)
17:08
Add documentation and test cases for sqlite3ota_create_vfs(). Also code to detect errors in zipvfs/ota setup. (check-in: e729668168 user: dan tags: ota-update-no-pager_ota_mode)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/ota/sqlite3ota.c.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#define OTA_STATE_IDX         3
#define OTA_STATE_ROW         4
#define OTA_STATE_PROGRESS    5
#define OTA_STATE_CKPT        6
#define OTA_STATE_COOKIE      7

#define OTA_STAGE_OAL         1
#define OTA_STAGE_COPY        2
#define OTA_STAGE_CKPT        3
#define OTA_STAGE_DONE        4


#define OTA_CREATE_STATE "CREATE TABLE IF NOT EXISTS ota.ota_state"        \
                             "(k INTEGER PRIMARY KEY, v)"








<







71
72
73
74
75
76
77

78
79
80
81
82
83
84
#define OTA_STATE_IDX         3
#define OTA_STATE_ROW         4
#define OTA_STATE_PROGRESS    5
#define OTA_STATE_CKPT        6
#define OTA_STATE_COOKIE      7

#define OTA_STAGE_OAL         1

#define OTA_STAGE_CKPT        3
#define OTA_STAGE_DONE        4


#define OTA_CREATE_STATE "CREATE TABLE IF NOT EXISTS ota.ota_state"        \
                             "(k INTEGER PRIMARY KEY, v)"

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192


193
194
195
196
197
198
199
  char *zErrmsg;                  /* Error message if rc!=SQLITE_OK */
  int nStep;                      /* Rows processed for current object */
  int nProgress;                  /* Rows processed for all objects */
  OtaObjIter objiter;             /* Iterator for skipping through tbl/idx */
  sqlite3_ckpt *pCkpt;            /* Incr-checkpoint handle */
  ota_file *pTargetFd;            /* File handle open on target db */
  const char *zVfsName;           /* Name of automatically created ota vfs */
  unsigned int iCookie;
};

struct ota_vfs {
  sqlite3_vfs base;             /* ota VFS shim methods */
  sqlite3_vfs *pRealVfs;        /* Underlying VFS */
  sqlite3_mutex *mutex;
  const char *zOtaWal;
};

struct ota_file {
  sqlite3_file base;              /* sqlite3_file methods */
  sqlite3_file *pReal;            /* Underlying file handle */
  ota_vfs *pOtaVfs;               /* Pointer to the ota_vfs object */
  sqlite3ota *pOta;               /* Pointer to ota object (ota target only) */



  int nShm;                       /* Number of entries in apShm[] array */
  char **apShm;                   /* Array of mmap'd *-shm regions */
  const char *zWal;               /* Wal filename for this db file */
  char *zDel;                     /* Delete this when closing file */
};








<














>
>







170
171
172
173
174
175
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
  char *zErrmsg;                  /* Error message if rc!=SQLITE_OK */
  int nStep;                      /* Rows processed for current object */
  int nProgress;                  /* Rows processed for all objects */
  OtaObjIter objiter;             /* Iterator for skipping through tbl/idx */
  sqlite3_ckpt *pCkpt;            /* Incr-checkpoint handle */
  ota_file *pTargetFd;            /* File handle open on target db */
  const char *zVfsName;           /* Name of automatically created ota vfs */

};

struct ota_vfs {
  sqlite3_vfs base;             /* ota VFS shim methods */
  sqlite3_vfs *pRealVfs;        /* Underlying VFS */
  sqlite3_mutex *mutex;
  const char *zOtaWal;
};

struct ota_file {
  sqlite3_file base;              /* sqlite3_file methods */
  sqlite3_file *pReal;            /* Underlying file handle */
  ota_vfs *pOtaVfs;               /* Pointer to the ota_vfs object */
  sqlite3ota *pOta;               /* Pointer to ota object (ota target only) */
  int openFlags;                  /* Flags this file was opened with */
  unsigned int iCookie;           /* Cookie value for main db files */

  int nShm;                       /* Number of entries in apShm[] array */
  char **apShm;                   /* Array of mmap'd *-shm regions */
  const char *zWal;               /* Wal filename for this db file */
  char *zDel;                     /* Delete this when closing file */
};

1464
1465
1466
1467
1468
1469
1470




1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484



1485
1486
1487

1488
1489
1490
1491
1492
1493

1494
1495
1496
1497
1498
1499
1500
      memcpy(pIter->zMask, zMask, pIter->nTblCol);
    }
    sqlite3_free(zWhere);
    sqlite3_free(zSet);
  }
  return p->rc;
}





/*
** Open the database handle and attach the OTA database as "ota". If an
** error occurs, leave an error code and message in the OTA handle.
*/
static void otaOpenDatabase(sqlite3ota *p){
  int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
  assert( p->rc==SQLITE_OK );
  assert( p->db==0 );

  p->rc = sqlite3_open_v2(p->zTarget, &p->db, flags, p->zVfsName);
  if( p->rc ){
    p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
  }else{



    /* Mark the database file just opened as an OTA target database. If 
    ** this call returns SQLITE_NOTFOUND, then the OTA vfs is not in use.
    ** This is an error.  */

    p->rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_OTA, (void*)p);
    if( p->rc==SQLITE_NOTFOUND ){
      p->rc = SQLITE_ERROR;
      p->zErrmsg = sqlite3_mprintf("ota vfs not found");
    }else{
      otaMPrintfExec(p, "ATTACH %Q AS ota", p->zOta);

    }
  }
}

/*
** This routine is a copy of the sqlite3FileSuffix3() routine from the core.
** It is a no-op unless SQLITE_ENABLE_8_3_NAMES is defined.







>
>
>
>














>
>
>



>
|
|
|
|
<
<
>







1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499


1500
1501
1502
1503
1504
1505
1506
1507
      memcpy(pIter->zMask, zMask, pIter->nTblCol);
    }
    sqlite3_free(zWhere);
    sqlite3_free(zSet);
  }
  return p->rc;
}

static void otaSqlTrace(void *pCtx, const char *zSql){
  /* printf("SQL: %s\n", zSql); */
}

/*
** Open the database handle and attach the OTA database as "ota". If an
** error occurs, leave an error code and message in the OTA handle.
*/
static void otaOpenDatabase(sqlite3ota *p){
  int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
  assert( p->rc==SQLITE_OK );
  assert( p->db==0 );

  p->rc = sqlite3_open_v2(p->zTarget, &p->db, flags, p->zVfsName);
  if( p->rc ){
    p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
  }else{
    otaMPrintfExec(p, "ATTACH %Q AS ota", p->zOta);
    /*   sqlite3_trace(p->db, otaSqlTrace, 0); */

    /* Mark the database file just opened as an OTA target database. If 
    ** this call returns SQLITE_NOTFOUND, then the OTA vfs is not in use.
    ** This is an error.  */
    if( p->rc==SQLITE_OK ){
      p->rc = sqlite3_file_control(p->db, "main", SQLITE_FCNTL_OTA, (void*)p);
      if( p->rc==SQLITE_NOTFOUND ){
        p->rc = SQLITE_ERROR;
        p->zErrmsg = sqlite3_mprintf("ota vfs not found");


      }
    }
  }
}

/*
** This routine is a copy of the sqlite3FileSuffix3() routine from the core.
** It is a no-op unless SQLITE_ENABLE_8_3_NAMES is defined.
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
        "(%d, %lld) ",
        OTA_STATE_STAGE, p->eStage,
        OTA_STATE_TBL, p->objiter.zTbl, 
        OTA_STATE_IDX, p->objiter.zIdx, 
        OTA_STATE_ROW, p->nStep, 
        OTA_STATE_PROGRESS, p->nProgress,
        OTA_STATE_CKPT,
        OTA_STATE_COOKIE, (sqlite3_int64)p->iCookie
      )
  );
  assert( pInsert==0 || rc==SQLITE_OK );
  if( rc==SQLITE_OK ){
    if( p->pCkpt ){
      unsigned char *pCkptState = 0;
      int nCkptState = 0;







|







1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
        "(%d, %lld) ",
        OTA_STATE_STAGE, p->eStage,
        OTA_STATE_TBL, p->objiter.zTbl, 
        OTA_STATE_IDX, p->objiter.zIdx, 
        OTA_STATE_ROW, p->nStep, 
        OTA_STATE_PROGRESS, p->nProgress,
        OTA_STATE_CKPT,
        OTA_STATE_COOKIE, (sqlite3_int64)p->pTargetFd->iCookie
      )
  );
  assert( pInsert==0 || rc==SQLITE_OK );
  if( rc==SQLITE_OK ){
    if( p->pCkpt ){
      unsigned char *pCkptState = 0;
      int nCkptState = 0;
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
**
** If an error occurs, leave an error code and message in the ota handle
** and return NULL.
*/
static OtaState *otaLoadState(sqlite3ota *p){
  const char *zSelect = "SELECT k, v FROM ota.ota_state";
  OtaState *pRet = 0;
  sqlite3_stmt *pStmt;
  int rc;
  int rc2;

  assert( p->rc==SQLITE_OK );
  pRet = (OtaState*)sqlite3_malloc(sizeof(OtaState));
  if( pRet==0 ){
    rc = SQLITE_NOMEM;
  }else{
    memset(pRet, 0, sizeof(OtaState));
    rc = prepareAndCollectError(p->db, &pStmt, &p->zErrmsg, zSelect);
  }

  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
    switch( sqlite3_column_int(pStmt, 0) ){
      case OTA_STATE_STAGE:
        pRet->eStage = sqlite3_column_int(pStmt, 1);
        if( pRet->eStage!=OTA_STAGE_OAL
         && pRet->eStage!=OTA_STAGE_COPY
         && pRet->eStage!=OTA_STAGE_CKPT
        ){
          p->rc = SQLITE_CORRUPT;
        }
        break;

      case OTA_STATE_TBL:







|

















<







1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929

1930
1931
1932
1933
1934
1935
1936
**
** If an error occurs, leave an error code and message in the ota handle
** and return NULL.
*/
static OtaState *otaLoadState(sqlite3ota *p){
  const char *zSelect = "SELECT k, v FROM ota.ota_state";
  OtaState *pRet = 0;
  sqlite3_stmt *pStmt = 0;
  int rc;
  int rc2;

  assert( p->rc==SQLITE_OK );
  pRet = (OtaState*)sqlite3_malloc(sizeof(OtaState));
  if( pRet==0 ){
    rc = SQLITE_NOMEM;
  }else{
    memset(pRet, 0, sizeof(OtaState));
    rc = prepareAndCollectError(p->db, &pStmt, &p->zErrmsg, zSelect);
  }

  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
    switch( sqlite3_column_int(pStmt, 0) ){
      case OTA_STATE_STAGE:
        pRet->eStage = sqlite3_column_int(pStmt, 1);
        if( pRet->eStage!=OTA_STAGE_OAL

         && pRet->eStage!=OTA_STAGE_CKPT
        ){
          p->rc = SQLITE_CORRUPT;
        }
        break;

      case OTA_STATE_TBL:
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966

      case OTA_STATE_COOKIE:
        /* At this point (p->iCookie) contains the value of the change-counter
        ** cookie (the thing that gets incremented when a transaction is 
        ** committed in rollback mode) currently stored on page 1 of the 
        ** database file. */
        if( pRet->eStage==OTA_STAGE_OAL 
         && p->iCookie!=(unsigned int)sqlite3_column_int64(pStmt, 1) 
        ){
          rc = SQLITE_BUSY;
          p->zErrmsg = sqlite3_mprintf("database modified during ota update");
        }
        break;

      default:







|







1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972

      case OTA_STATE_COOKIE:
        /* At this point (p->iCookie) contains the value of the change-counter
        ** cookie (the thing that gets incremented when a transaction is 
        ** committed in rollback mode) currently stored on page 1 of the 
        ** database file. */
        if( pRet->eStage==OTA_STAGE_OAL 
         && p->pTargetFd->iCookie!=(unsigned int)sqlite3_column_int64(pStmt, 1) 
        ){
          rc = SQLITE_BUSY;
          p->zErrmsg = sqlite3_mprintf("database modified during ota update");
        }
        break;

      default:
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
  sqlite3_file *pFile, 
  void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  ota_file *p = (ota_file*)pFile;
  int rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
  if( rc==SQLITE_OK && p->pOta && iOfst==0 ){
    unsigned char *pBuf = (unsigned char*)zBuf;
    assert( iAmt>=100 );
    p->pOta->iCookie = otaGetU32(&pBuf[24]);
  }
  return rc;
}

/*
** Write data to an otaVfs-file.
*/
static int otaVfsWrite(
  sqlite3_file *pFile, 
  const void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  ota_file *p = (ota_file*)pFile;
  int rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
  if( rc==SQLITE_OK && p->pOta && iOfst==0 ){
    unsigned char *pBuf = (unsigned char*)zBuf;
    assert( iAmt>=100 );
    p->pOta->iCookie = otaGetU32(&pBuf[24]);
  }
  return rc;
}

/*
** Truncate an otaVfs-file.
*/







|
<
<
|















|
<
<
|







2259
2260
2261
2262
2263
2264
2265
2266


2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283


2284
2285
2286
2287
2288
2289
2290
2291
  sqlite3_file *pFile, 
  void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  ota_file *p = (ota_file*)pFile;
  int rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
  if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){


    p->iCookie = otaGetU32((unsigned char*)&zBuf[24]);
  }
  return rc;
}

/*
** Write data to an otaVfs-file.
*/
static int otaVfsWrite(
  sqlite3_file *pFile, 
  const void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  ota_file *p = (ota_file*)pFile;
  int rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
  if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){


    p->iCookie = otaGetU32((unsigned char*)&zBuf[24]);
  }
  return rc;
}

/*
** Truncate an otaVfs-file.
*/
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
** Shared-memory methods are all pass-thrus.
*/
static int otaVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
  ota_file *p = (ota_file*)pFile;
  int rc = SQLITE_OK;

#ifdef SQLITE_AMALGAMATION
    assert( WAL_WRITE_CKPT==1 );
#endif

  if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
    /* Magic number 1 is the WAL_WRITE_CKPT lock. Preventing SQLite from
    ** taking this lock also prevents any checkpoints from occurring. 
    ** todo: really, it's not clear why this might occur, as 
    ** wal_autocheckpoint ought to be turned off.  */
    if( ofst==1 && n==1 ) rc = SQLITE_BUSY;
  }else{
    assert( p->nShm==0 );
    rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);







|



|







2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
** Shared-memory methods are all pass-thrus.
*/
static int otaVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
  ota_file *p = (ota_file*)pFile;
  int rc = SQLITE_OK;

#ifdef SQLITE_AMALGAMATION
    assert( WAL_CKPT_LOCK==1 );
#endif

  if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
    /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
    ** taking this lock also prevents any checkpoints from occurring. 
    ** todo: really, it's not clear why this might occur, as 
    ** wal_autocheckpoint ought to be turned off.  */
    if( ofst==1 && n==1 ) rc = SQLITE_BUSY;
  }else{
    assert( p->nShm==0 );
    rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
2448
2449
2450
2451
2452
2453
2454

2455
2456
2457
2458
2459
2460
2461
    }

    if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
      char *pNew = (char*)sqlite3_malloc(szRegion);
      if( pNew==0 ){
        rc = SQLITE_NOMEM;
      }else{

        p->apShm[iRegion] = pNew;
      }
    }

    if( rc==SQLITE_OK ){
      *pp = p->apShm[iRegion];
    }else{







>







2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
    }

    if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
      char *pNew = (char*)sqlite3_malloc(szRegion);
      if( pNew==0 ){
        rc = SQLITE_NOMEM;
      }else{
        memset(pNew, 0, szRegion);
        p->apShm[iRegion] = pNew;
      }
    }

    if( rc==SQLITE_OK ){
      *pp = p->apShm[iRegion];
    }else{
2523
2524
2525
2526
2527
2528
2529

2530
2531
2532
2533
2534
2535
2536
  ota_file *pFd = (ota_file *)pFile;
  int rc = SQLITE_OK;
  const char *zOpen = zName;

  memset(pFd, 0, sizeof(ota_file));
  pFd->pReal = (sqlite3_file*)&pFd[1];
  pFd->pOtaVfs = pOtaVfs;

  if( zName ){
    if( flags & SQLITE_OPEN_MAIN_DB ){
      /* A main database has just been opened. The following block sets
      ** (pFd->zWal) to point to a buffer owned by SQLite that contains
      ** the name of the *-wal file this db connection will use. SQLite
      ** happens to pass a pointer to this buffer when using xAccess()
      ** or xOpen() to operate on the *-wal file.  */







>







2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
  ota_file *pFd = (ota_file *)pFile;
  int rc = SQLITE_OK;
  const char *zOpen = zName;

  memset(pFd, 0, sizeof(ota_file));
  pFd->pReal = (sqlite3_file*)&pFd[1];
  pFd->pOtaVfs = pOtaVfs;
  pFd->openFlags = flags;
  if( zName ){
    if( flags & SQLITE_OPEN_MAIN_DB ){
      /* A main database has just been opened. The following block sets
      ** (pFd->zWal) to point to a buffer owned by SQLite that contains
      ** the name of the *-wal file this db connection will use. SQLite
      ** happens to pass a pointer to this buffer when using xAccess()
      ** or xOpen() to operate on the *-wal file.  */
Changes to main.mk.
215
216
217
218
219
220
221



222
223
224
225
226
227
228
SRC += \
  $(TOP)/ext/rtree/sqlite3rtree.h \
  $(TOP)/ext/rtree/rtree.h \
  $(TOP)/ext/rtree/rtree.c
SRC += \
  $(TOP)/ext/userauth/userauth.c \
  $(TOP)/ext/userauth/sqlite3userauth.h




# Generated source code files
#
SRC += \
  keywordhash.h \
  opcodes.c \
  opcodes.h \







>
>
>







215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
SRC += \
  $(TOP)/ext/rtree/sqlite3rtree.h \
  $(TOP)/ext/rtree/rtree.h \
  $(TOP)/ext/rtree/rtree.c
SRC += \
  $(TOP)/ext/userauth/userauth.c \
  $(TOP)/ext/userauth/sqlite3userauth.h
SRC += \
  $(TOP)/ext/ota/sqlite3ota.c \
  $(TOP)/ext/ota/sqlite3ota.h

# Generated source code files
#
SRC += \
  keywordhash.h \
  opcodes.c \
  opcodes.h \
Changes to tool/mksqlite3c.tcl.
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
   os_win.h
   os.h
   pager.h
   parse.h
   pcache.h
   pragma.h
   rtree.h

   sqlite3ext.h
   sqlite3.h
   sqliteicu.h
   sqliteInt.h
   sqliteLimit.h
   vdbe.h
   vdbeInt.h
   wal.h
   whereInt.h







>

|







108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
   os_win.h
   os.h
   pager.h
   parse.h
   pcache.h
   pragma.h
   rtree.h
   sqlite3.h
   sqlite3ext.h
   sqlite3ota.h
   sqliteicu.h
   sqliteInt.h
   sqliteLimit.h
   vdbe.h
   vdbeInt.h
   wal.h
   whereInt.h
330
331
332
333
334
335
336

337
338
339
340
341
   fts3_snippet.c
   fts3_unicode.c
   fts3_unicode2.c

   rtree.c
   icu.c
   fts3_icu.c

} {
  copy_file tsrc/$file
}

close $out







>





331
332
333
334
335
336
337
338
339
340
341
342
343
   fts3_snippet.c
   fts3_unicode.c
   fts3_unicode2.c

   rtree.c
   icu.c
   fts3_icu.c
   sqlite3ota.c
} {
  copy_file tsrc/$file
}

close $out