SQLite

Check-in [96443ecb69]
Login

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

Overview
Comment:Add the sqlite3ota_create_vfs() and sqlite3ota_destroy_vfs() functions.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | ota-update-no-pager_ota_mode
Files: files | file ages | folders
SHA1: 96443ecb6909141aa621a16e628455857d036482
User & Date: dan 2015-02-09 20:07:35.066
Context
2015-02-10
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)
2015-02-09
20:07
Add the sqlite3ota_create_vfs() and sqlite3ota_destroy_vfs() functions. (check-in: 96443ecb69 user: dan tags: ota-update-no-pager_ota_mode)
2015-02-07
20:20
Add comments to explain the role of the ota vfs. (check-in: 7bb633639d user: dan tags: ota-update-no-pager_ota_mode)
Changes
Unified Diff Ignore Whitespace Patch
Changes to ext/ota/ota1.test.
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  set rc
}

# Same as [step_ota], except using a URI to open the target db.
#
proc step_ota_uri {target ota} {
  while 1 {
    sqlite3ota ota file:$target?xyz=123 $ota
    set rc [ota step]
    ota close
    if {$rc != "SQLITE_OK"} break
  }
  set rc
}








|







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  set rc
}

# Same as [step_ota], except using a URI to open the target db.
#
proc step_ota_uri {target ota} {
  while 1 {
    sqlite3ota ota file:$target?xyz=&abc=123 $ota
    set rc [ota step]
    ota close
    if {$rc != "SQLITE_OK"} break
  }
  set rc
}

Changes to ext/ota/sqlite3ota.c.
81
82
83
84
85
86
87


88
89
90
91
92
93
94


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

typedef struct OtaState OtaState;
typedef struct OtaObjIter OtaObjIter;



/*
** A structure to store values read from the ota_state table in memory.
*/
struct OtaState {
  int eStage;
  char *zTbl;







>
>







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96


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

typedef struct OtaState OtaState;
typedef struct OtaObjIter OtaObjIter;
typedef struct ota_vfs ota_vfs;
typedef struct ota_file ota_file;

/*
** A structure to store values read from the ota_state table in memory.
*/
struct OtaState {
  int eStage;
  char *zTbl;
151
152
153
154
155
156
157

158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173

174
175




















176
177
178
179
180
181
182
*/
#define OTA_PK_NOTABLE        0
#define OTA_PK_NONE           1
#define OTA_PK_IPK            2
#define OTA_PK_EXTERNAL       3
#define OTA_PK_WITHOUT_ROWID  4
#define OTA_PK_VTAB           5


/*
** OTA handle.
*/
struct sqlite3ota {
  int eStage;                     /* Value of OTA_STATE_STAGE field */
  sqlite3 *db;                    /* "main" -> target db, "ota" -> ota db */
  char *zTarget;                  /* Path to target db */
  char *zOta;                     /* Path to ota db */
  int rc;                         /* Value returned by last ota_step() call */
  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 */
  sqlite3_vfs *pVfs;              /* Special ota VFS object */

  unsigned int iCookie;
};





















static void otaCreateVfs(sqlite3ota*, const char*);
static void otaDeleteVfs(sqlite3ota*);

/*
** Prepare the SQL statement in buffer zSql against database handle db.
** If successful, set *ppStmt to point to the new statement and return







>















|
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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
200
201
202
203
204
205
206
*/
#define OTA_PK_NOTABLE        0
#define OTA_PK_NONE           1
#define OTA_PK_IPK            2
#define OTA_PK_EXTERNAL       3
#define OTA_PK_WITHOUT_ROWID  4
#define OTA_PK_VTAB           5


/*
** OTA handle.
*/
struct sqlite3ota {
  int eStage;                     /* Value of OTA_STATE_STAGE field */
  sqlite3 *db;                    /* "main" -> target db, "ota" -> ota db */
  char *zTarget;                  /* Path to target db */
  char *zOta;                     /* Path to ota db */
  int rc;                         /* Value returned by last ota_step() call */
  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 */
};


static void otaCreateVfs(sqlite3ota*, const char*);
static void otaDeleteVfs(sqlite3ota*);

/*
** Prepare the SQL statement in buffer zSql against database handle db.
** If successful, set *ppStmt to point to the new statement and return
417
418
419
420
421
422
423














424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
      p->rc = sqlite3_exec(p->db, zSql, 0, 0, &p->zErrmsg);
      sqlite3_free(zSql);
    }
  }
  va_end(ap);
  return p->rc;
}















/*
** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
** there is room for at least nCol elements. If an OOM occurs, store an
** error code in the OTA handle passed as the first argument.
*/
static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
  int nByte = (2*sizeof(char*) + sizeof(int) + 2*sizeof(unsigned char)) * nCol;
  char **azNew;

  assert( p->rc==SQLITE_OK );
  azNew = (char**)sqlite3_malloc(nByte);
  if( azNew ){
    memset(azNew, 0, nByte);
    pIter->azTblCol = azNew;
    pIter->azTblType = &azNew[nCol];
    pIter->aiSrcOrder = (int*)&pIter->azTblType[nCol];
    pIter->abTblPk = (unsigned char*)&pIter->aiSrcOrder[nCol];
    pIter->abNotNull = (unsigned char*)&pIter->abTblPk[nCol];
  }else{
    p->rc = SQLITE_NOMEM;
  }
}

static char *otaStrndup(const char *zStr, int nStr, int *pRc){
  char *zRet = 0;
  assert( *pRc==SQLITE_OK );








>
>
>
>
>
>
>
>
>
>
>
>
>
>










<
|

<





<
<







441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471

472
473

474
475
476
477
478


479
480
481
482
483
484
485
      p->rc = sqlite3_exec(p->db, zSql, 0, 0, &p->zErrmsg);
      sqlite3_free(zSql);
    }
  }
  va_end(ap);
  return p->rc;
}

static void *otaMalloc(sqlite3ota *p, int nByte){
  void *pRet = 0;
  if( p->rc==SQLITE_OK ){
    pRet = sqlite3_malloc(nByte);
    if( pRet==0 ){
      p->rc = SQLITE_NOMEM;
    }else{
      memset(pRet, 0, nByte);
    }
  }
  return pRet;
}


/*
** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
** there is room for at least nCol elements. If an OOM occurs, store an
** error code in the OTA handle passed as the first argument.
*/
static void otaAllocateIterArrays(sqlite3ota *p, OtaObjIter *pIter, int nCol){
  int nByte = (2*sizeof(char*) + sizeof(int) + 2*sizeof(unsigned char)) * nCol;
  char **azNew;


  azNew = (char**)otaMalloc(p, nByte);
  if( azNew ){

    pIter->azTblCol = azNew;
    pIter->azTblType = &azNew[nCol];
    pIter->aiSrcOrder = (int*)&pIter->azTblType[nCol];
    pIter->abTblPk = (unsigned char*)&pIter->aiSrcOrder[nCol];
    pIter->abNotNull = (unsigned char*)&pIter->abTblPk[nCol];


  }
}

static char *otaStrndup(const char *zStr, int nStr, int *pRc){
  char *zRet = 0;
  assert( *pRc==SQLITE_OK );

709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
    sqlite3_free(zSql);
    zSql = 0;
  }
  va_end(ap);
  return zSql;
}

static void *otaMalloc(sqlite3ota *p, int nByte){
  void *pRet = 0;
  if( p->rc==SQLITE_OK ){
    pRet = sqlite3_malloc(nByte);
    if( pRet==0 ){
      p->rc = SQLITE_NOMEM;
    }else{
      memset(pRet, 0, nByte);
    }
  }
  return pRet;
}

/*
** This function constructs and returns a pointer to a nul-terminated 
** string containing some SQL clause or list based on one or more of the 
** column names currently stored in the pIter->azTblCol[] array.
*/
static char *otaObjIterGetCollist(
  sqlite3ota *p,                  /* OTA object */







<
<
<
<
<
<
<
<
<
<
<
<
<







743
744
745
746
747
748
749













750
751
752
753
754
755
756
    sqlite3_free(zSql);
    zSql = 0;
  }
  va_end(ap);
  return zSql;
}














/*
** This function constructs and returns a pointer to a nul-terminated 
** string containing some SQL clause or list based on one or more of the 
** column names currently stored in the pIter->azTblCol[] array.
*/
static char *otaObjIterGetCollist(
  sqlite3ota *p,                  /* OTA object */
987
988
989
990
991
992
993
994
995
996

997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
    }
  }
  return zList;
}

static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){
  char *zRet = 0;
  if( p->rc==SQLITE_OK ){
    int nByte = nBind*2 + 1;
    zRet = sqlite3_malloc(nByte);

    if( zRet==0 ){
      p->rc = SQLITE_NOMEM;
    }else{
      int i;
      for(i=0; i<nBind; i++){
        zRet[i*2] = '?';
        zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';
      }
    }
  }
  return zRet;
}

/*
** The iterator currently points to a table (not index) of type 







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







1008
1009
1010
1011
1012
1013
1014

1015
1016
1017
1018


1019
1020
1021
1022

1023
1024
1025
1026
1027
1028
1029
    }
  }
  return zList;
}

static char *otaObjIterGetBindlist(sqlite3ota *p, int nBind){
  char *zRet = 0;

  int nByte = nBind*2 + 1;

  zRet = (char*)otaMalloc(p, nByte);
  if( zRet ){


    int i;
    for(i=0; i<nBind; i++){
      zRet[i*2] = '?';
      zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';

    }
  }
  return zRet;
}

/*
** The iterator currently points to a table (not index) of type 
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466








1467


1468
1469
1470
1471
1472
1473
1474
** 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->pVfs->zName);
  if( p->rc ){
    p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(p->db));
  }








  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.
**
** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database







|


|
>
>
>
>
>
>
>
>
|
>
>







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
** 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.
**
** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045



2046


2047
2048
2049
2050
2051
2052
2053
        p->nProgress = pState->nProgress;
      }
    }
    assert( p->rc!=SQLITE_OK || p->eStage!=0 );

    if( p->rc==SQLITE_OK ){
      if( p->eStage==OTA_STAGE_OAL ){
        const char *zScript =
          "PRAGMA journal_mode=off;"
          "BEGIN IMMEDIATE;"
        ;



        p->rc = sqlite3_exec(p->db, zScript, 0, 0, &p->zErrmsg);


  
        /* Point the object iterator at the first object */
        if( p->rc==SQLITE_OK ){
          p->rc = otaObjIterFirst(p, &p->objiter);
        }
  
        if( p->rc==SQLITE_OK ){







|
<
<
|
>
>
>
|
>
>







2063
2064
2065
2066
2067
2068
2069
2070


2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
        p->nProgress = pState->nProgress;
      }
    }
    assert( p->rc!=SQLITE_OK || p->eStage!=0 );

    if( p->rc==SQLITE_OK ){
      if( p->eStage==OTA_STAGE_OAL ){
        ota_vfs *pOtaVfs = p->pTargetFd->pOtaVfs;



        sqlite3_mutex_enter(pOtaVfs->mutex);
        assert( pOtaVfs->zOtaWal==0 );
        pOtaVfs->zOtaWal = p->pTargetFd->zWal;
        p->rc = sqlite3_exec(p->db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
        pOtaVfs->zOtaWal = 0;
        sqlite3_mutex_leave(pOtaVfs->mutex);
  
        /* Point the object iterator at the first object */
        if( p->rc==SQLITE_OK ){
          p->rc = otaObjIterFirst(p, &p->objiter);
        }
  
        if( p->rc==SQLITE_OK ){
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
**           non-zero (to tell SQLite that it does exist) anyway.
**
**   5. In OTA_STAGE_OAL mode, if SQLite tries to open a *-wal file 
**      associated with a target database, open the corresponding *-oal file
**      instead.
*/

typedef struct ota_file ota_file;
typedef struct ota_vfs ota_vfs;

struct ota_file {
  sqlite3_file base;              /* sqlite3_file methods */
  sqlite3_file *pReal;            /* Underlying file handle */
  ota_vfs *pOtaVfs;               /* Pointer to the ota_vfs object */

  int nShm;                       /* Number of entries in apShm[] array */
  char **apShm;                   /* Array of mmap'd *-shm regions */
  char *zFilename;                /* Filename for *-oal file only */
};

struct ota_vfs {
  sqlite3_vfs base;             /* ota VFS shim methods */
  sqlite3_vfs *pRealVfs;        /* Underlying VFS */
  sqlite3ota *pOta;
  ota_file *pTargetDb;          /* Target database file descriptor */
  const char *zTargetDb;        /* Path that pTargetDb was opened with */
};

/*
** Close an ota file.
*/
static int otaVfsClose(sqlite3_file *pFile){
  ota_file *p = (ota_file*)pFile;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc;
  int i;

  /* Free the contents of the apShm[] array. And the array itself. */
  for(i=0; i<p->nShm; i++){
    sqlite3_free(p->apShm[i]);
  }
  sqlite3_free(p->apShm);
  p->apShm = 0;
  sqlite3_free(p->zFilename);

  if( p==pOtaVfs->pTargetDb ){
    pOtaVfs->pTargetDb = 0;
    pOtaVfs->zTargetDb = 0;
  }

  rc = p->pReal->pMethods->xClose(p->pReal);
  return rc;
}


/*







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





<









|
<
<
<
<
<







2210
2211
2212
2213
2214
2215
2216





















2217
2218
2219
2220
2221

2222
2223
2224
2225
2226
2227
2228
2229
2230
2231





2232
2233
2234
2235
2236
2237
2238
**           non-zero (to tell SQLite that it does exist) anyway.
**
**   5. In OTA_STAGE_OAL mode, if SQLite tries to open a *-wal file 
**      associated with a target database, open the corresponding *-oal file
**      instead.
*/






















/*
** Close an ota file.
*/
static int otaVfsClose(sqlite3_file *pFile){
  ota_file *p = (ota_file*)pFile;

  int rc;
  int i;

  /* Free the contents of the apShm[] array. And the array itself. */
  for(i=0; i<p->nShm; i++){
    sqlite3_free(p->apShm[i]);
  }
  sqlite3_free(p->apShm);
  p->apShm = 0;
  sqlite3_free(p->zDel);






  rc = p->pReal->pMethods->xClose(p->pReal);
  return rc;
}


/*
2248
2249
2250
2251
2252
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
static int otaVfsRead(
  sqlite3_file *pFile, 
  void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  ota_file *p = (ota_file*)pFile;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
  if( rc==SQLITE_OK && p==pOtaVfs->pTargetDb && iOfst==0 ){
    unsigned char *pBuf = (unsigned char*)zBuf;
    assert( iAmt>=100 );
    pOtaVfs->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;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
  if( rc==SQLITE_OK && p==pOtaVfs->pTargetDb && iOfst==0 ){
    unsigned char *pBuf = (unsigned char*)zBuf;
    assert( iAmt>=100 );
    pOtaVfs->pOta->iCookie = otaGetU32(&pBuf[24]);
  }
  return rc;
}

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







<

|


|














<

|


|







2252
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
static int otaVfsRead(
  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.
*/
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320

2321
2322
2323
2324
2325
2326
2327
}

/*
** Lock an otaVfs-file.
*/
static int otaVfsLock(sqlite3_file *pFile, int eLock){
  ota_file *p = (ota_file*)pFile;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc = SQLITE_OK;
  int eStage = pOtaVfs->pOta->eStage;

  if( pOtaVfs->pTargetDb==p 
   && (eStage==OTA_STAGE_OAL || eStage==OTA_STAGE_CKPT) 
   && eLock==SQLITE_LOCK_EXCLUSIVE

  ){
    /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this 
    ** prevents it from checkpointing the database from sqlite3_close(). */
    rc = SQLITE_BUSY;
  }else{
    rc = p->pReal->pMethods->xLock(p->pReal, eLock);
  }







|

<

<
<
|
>







2309
2310
2311
2312
2313
2314
2315
2316
2317

2318


2319
2320
2321
2322
2323
2324
2325
2326
2327
}

/*
** Lock an otaVfs-file.
*/
static int otaVfsLock(sqlite3_file *pFile, int eLock){
  ota_file *p = (ota_file*)pFile;
  sqlite3ota *pOta = p->pOta;
  int rc = SQLITE_OK;




  if( pOta && eLock==SQLITE_LOCK_EXCLUSIVE
   && (pOta->eStage==OTA_STAGE_OAL || pOta->eStage==OTA_STAGE_CKPT) 
  ){
    /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this 
    ** prevents it from checkpointing the database from sqlite3_close(). */
    rc = SQLITE_BUSY;
  }else{
    rc = p->pReal->pMethods->xLock(p->pReal, eLock);
  }
2346
2347
2348
2349
2350
2351
2352






2353
2354
2355
2356
2357
2358
2359
}

/*
** File control method. For custom operations on an otaVfs-file.
*/
static int otaVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
  ota_file *p = (ota_file *)pFile;






  return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
}

/*
** Return the sector-size in bytes for an otaVfs-file.
*/
static int otaVfsSectorSize(sqlite3_file *pFile){







>
>
>
>
>
>







2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
}

/*
** File control method. For custom operations on an otaVfs-file.
*/
static int otaVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
  ota_file *p = (ota_file *)pFile;
  if( op==SQLITE_FCNTL_OTA ){
    sqlite3ota *pOta = (sqlite3ota*)pArg;
    pOta->pTargetFd = p;
    p->pOta = pOta;
    return SQLITE_OK;
  }
  return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
}

/*
** Return the sector-size in bytes for an otaVfs-file.
*/
static int otaVfsSectorSize(sqlite3_file *pFile){
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
}

/*
** 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;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc = SQLITE_OK;

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

  if( pOtaVfs->pTargetDb==p && pOtaVfs->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 );
    return p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
  }

  return rc;
}

static int otaVfsShmMap(
  sqlite3_file *pFile, 
  int iRegion, 
  int szRegion, 
  int isWrite, 
  void volatile **pp
){
  ota_file *p = (ota_file*)pFile;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc = SQLITE_OK;

  /* If not in OTA_STAGE_OAL, allow this call to pass through. Or, if this
  ** ota is in the OTA_STAGE_OAL state, use heap memory for *-shm space 
  ** instead of a file on disk.  */
  if( pOtaVfs->pTargetDb==p && pOtaVfs->pOta->eStage==OTA_STAGE_OAL ){
    if( iRegion<=p->nShm ){
      int nByte = (iRegion+1) * sizeof(char*);
      char **apNew = (char**)sqlite3_realloc(p->apShm, nByte);
      if( apNew==0 ){
        rc = SQLITE_NOMEM;
      }else{
        memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));







<






|







|













<





|







2376
2377
2378
2379
2380
2381
2382

2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410

2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
}

/*
** 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);
  }

  return rc;
}

static int otaVfsShmMap(
  sqlite3_file *pFile, 
  int iRegion, 
  int szRegion, 
  int isWrite, 
  void volatile **pp
){
  ota_file *p = (ota_file*)pFile;

  int rc = SQLITE_OK;

  /* If not in OTA_STAGE_OAL, allow this call to pass through. Or, if this
  ** ota is in the OTA_STAGE_OAL state, use heap memory for *-shm space 
  ** instead of a file on disk.  */
  if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
    if( iRegion<=p->nShm ){
      int nByte = (iRegion+1) * sizeof(char*);
      char **apNew = (char**)sqlite3_realloc(p->apShm, nByte);
      if( apNew==0 ){
        rc = SQLITE_NOMEM;
      }else{
        memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
static void otaVfsShmBarrier(sqlite3_file *pFile){
  ota_file *p = (ota_file *)pFile;
  p->pReal->pMethods->xShmBarrier(p->pReal);
}

static int otaVfsShmUnmap(sqlite3_file *pFile, int delFlag){
  ota_file *p = (ota_file*)pFile;
  ota_vfs *pOtaVfs = p->pOtaVfs;
  int rc = SQLITE_OK;

  if( pOtaVfs->pTargetDb==p && pOtaVfs->pOta->eStage==OTA_STAGE_OAL ){
    /* no-op */
  }else{
    rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
  }
  return rc;
}


static int otaVfsIswal(ota_vfs *pOtaVfs, const char *zPath){
  int nPath = strlen(zPath);
  int nTargetDb = strlen(pOtaVfs->zTargetDb);
  return ( nPath==(nTargetDb+4) 
        && 0==memcmp(zPath, pOtaVfs->zTargetDb, nTargetDb)
        && 0==memcmp(&zPath[nTargetDb], "-wal", 4)
  );
}


/*
** Open an ota file handle.
*/
static int otaVfsOpen(
  sqlite3_vfs *pVfs,
  const char *zName,







<


|






<
<
<
<
<
<
<
<
<
<
<







2454
2455
2456
2457
2458
2459
2460

2461
2462
2463
2464
2465
2466
2467
2468
2469











2470
2471
2472
2473
2474
2475
2476
static void otaVfsShmBarrier(sqlite3_file *pFile){
  ota_file *p = (ota_file *)pFile;
  p->pReal->pMethods->xShmBarrier(p->pReal);
}

static int otaVfsShmUnmap(sqlite3_file *pFile, int delFlag){
  ota_file *p = (ota_file*)pFile;

  int rc = SQLITE_OK;

  if( p->pOta && p->pOta->eStage==OTA_STAGE_OAL ){
    /* no-op */
  }else{
    rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
  }
  return rc;
}












/*
** Open an ota file handle.
*/
static int otaVfsOpen(
  sqlite3_vfs *pVfs,
  const char *zName,
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517















2518









2519
2520
2521
2522
2523
2524

2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
    otaVfsShmMap,                 /* xShmMap */
    otaVfsShmLock,                /* xShmLock */
    otaVfsShmBarrier,             /* xShmBarrier */
    otaVfsShmUnmap                /* xShmUnmap */
  };
  ota_vfs *pOtaVfs = (ota_vfs*)pVfs;
  sqlite3_vfs *pRealVfs = pOtaVfs->pRealVfs;
  sqlite3ota *p = pOtaVfs->pOta;
  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 && p->eStage==OTA_STAGE_OAL && otaVfsIswal(pOtaVfs, zName) ){
    char *zCopy = otaStrndup(zName, -1, &rc);
    if( zCopy ){
      int nCopy = strlen(zCopy);
      zCopy[nCopy-3] = 'o';
      zOpen = (const char*)(pFd->zFilename = zCopy);

    }
  }

  if( rc==SQLITE_OK ){
    rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, flags, pOutFlags);
  }
  if( pFd->pReal->pMethods ){
    pFile->pMethods = &otavfs_io_methods;
    if( pOtaVfs->pTargetDb==0 ){
      /* This is the target db file. */
      assert( (flags & SQLITE_OPEN_MAIN_DB) );
      assert( zOpen==zName );
      pOtaVfs->pTargetDb = pFd;
      pOtaVfs->zTargetDb = zName;
    }
  }

  return rc;
}

/*
** Delete the file located at zPath.







<







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
>








<
<
<
<
<
<
<







2495
2496
2497
2498
2499
2500
2501

2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548







2549
2550
2551
2552
2553
2554
2555
    otaVfsShmMap,                 /* xShmMap */
    otaVfsShmLock,                /* xShmLock */
    otaVfsShmBarrier,             /* xShmBarrier */
    otaVfsShmUnmap                /* xShmUnmap */
  };
  ota_vfs *pOtaVfs = (ota_vfs*)pVfs;
  sqlite3_vfs *pRealVfs = pOtaVfs->pRealVfs;

  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.  */
      int n = strlen(zName);
      const char *z = &zName[n];
      if( flags & SQLITE_OPEN_URI ){
        int odd = 0;
        while( 1 ){
          if( z[0]==0 ){
            odd = 1 - odd;
            if( odd && z[1]==0 ) break;
          }
          z++;
        }
        z += 2;
      }else{
        while( *z==0 ) z++;
      }
      z += (n + 8 + 1);
      pFd->zWal = z;
    }
    else if( (flags & SQLITE_OPEN_WAL) && zName==pOtaVfs->zOtaWal ){
      char *zCopy = otaStrndup(zName, -1, &rc);
      if( zCopy ){
        int nCopy = strlen(zCopy);
        zCopy[nCopy-3] = 'o';
        zOpen = (const char*)(pFd->zDel = zCopy);
      }
    }
  }

  if( rc==SQLITE_OK ){
    rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, flags, pOutFlags);
  }
  if( pFd->pReal->pMethods ){
    pFile->pMethods = &otavfs_io_methods;







  }

  return rc;
}

/*
** Delete the file located at zPath.
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
){
  ota_vfs *pOtaVfs = (ota_vfs*)pVfs;
  sqlite3_vfs *pRealVfs = pOtaVfs->pRealVfs;
  int rc;

  rc = pRealVfs->xAccess(pRealVfs, zPath, flags, pResOut);

  if( rc==SQLITE_OK 
   && flags==SQLITE_ACCESS_EXISTS 
   && pOtaVfs->pOta->eStage==OTA_STAGE_OAL 
   && otaVfsIswal(pOtaVfs, zPath) 
  ){
    if( *pResOut ){
      rc = SQLITE_CANTOPEN;
    }else{
      *pResOut = 1;
    }
  }








<
|
<
<
<







2571
2572
2573
2574
2575
2576
2577

2578



2579
2580
2581
2582
2583
2584
2585
){
  ota_vfs *pOtaVfs = (ota_vfs*)pVfs;
  sqlite3_vfs *pRealVfs = pOtaVfs->pRealVfs;
  int rc;

  rc = pRealVfs->xAccess(pRealVfs, zPath, flags, pResOut);


  if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS && pOtaVfs->zOtaWal==zPath ){



    if( *pResOut ){
      rc = SQLITE_CANTOPEN;
    }else{
      *pResOut = 1;
    }
  }

2662
2663
2664
2665
2666
2667
2668








2669
2670
2671
2672
2673
2674
2675
2676
  return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
}

static int otaVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
  return 0;
}









static void otaCreateVfs(sqlite3ota *p, const char *zParent){

  /* Template for VFS */
  static sqlite3_vfs vfs_template = {
    1,                            /* iVersion */
    0,                            /* szOsFile */
    0,                            /* mxPathname */
    0,                            /* pNext */







>
>
>
>
>
>
>
>
|







2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
  return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
}

static int otaVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
  return 0;
}

void sqlite3ota_destroy_vfs(const char *zName){
  sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
  if( pVfs ){
    sqlite3_vfs_unregister(pVfs);
    sqlite3_free(pVfs);
  }
}

int sqlite3ota_create_vfs(const char *zName, const char *zParent){

  /* Template for VFS */
  static sqlite3_vfs vfs_template = {
    1,                            /* iVersion */
    0,                            /* szOsFile */
    0,                            /* mxPathname */
    0,                            /* pNext */
2692
2693
2694
2695
2696
2697
2698


2699
2700

2701
2702
2703
2704
2705
2706
2707




2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729


2730


2731



2732










2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
    otaVfsGetLastError,           /* xGetLastError */
    0,                            /* xCurrentTimeInt64 (version 2) */
    0, 0, 0                       /* Unimplemented version 3 methods */
  };

  sqlite3_vfs *pParent;           /* Parent VFS */
  ota_vfs *pNew = 0;              /* Newly allocated VFS */



  assert( p->rc==SQLITE_OK );

  pParent = sqlite3_vfs_find(zParent);
  if( pParent==0 ){
    p->rc = SQLITE_ERROR;
    p->zErrmsg = sqlite3_mprintf("no such vfs: %s", zParent);
  }else{
    int nByte = sizeof(ota_vfs) + 64;
    pNew = (ota_vfs*)otaMalloc(p, nByte);




  }

  if( pNew ){
    int rnd;
    char *zName;
    memcpy(&pNew->base, &vfs_template, sizeof(sqlite3_vfs));
    pNew->base.mxPathname = pParent->mxPathname;
    pNew->base.szOsFile = sizeof(ota_file) + pParent->szOsFile;
    pNew->pOta = p;
    pNew->pRealVfs = pParent;

    /* Give the new VFS a unique name */
    sqlite3_randomness(sizeof(int), (void*)&rnd);
    pNew->base.zName = (const char*)(zName = (char*)&pNew[1]);
    sprintf(zName, "ota_vfs_%d", rnd);

    /* Register the new VFS (not as the default) */
    assert( p->rc==SQLITE_OK );
    p->rc = sqlite3_vfs_register(&pNew->base, 0);
    if( p->rc ){
      p->zErrmsg = sqlite3_mprintf("error in sqlite3_vfs_register()");
      sqlite3_free(pNew);


    }else{


      p->pVfs = &pNew->base;



    }










  }
}

static void otaDeleteVfs(sqlite3ota *p){
  if( p->pVfs ){
    sqlite3_vfs_unregister(p->pVfs);
    sqlite3_free(p->pVfs);
    p->pVfs = 0;
  }
}


/**************************************************************************/

#ifdef SQLITE_TEST 







>
>

<
>


|
<

|
|
>
>
>
>
|
|
|
|
|



<


<
<
|
|


<
|
|
<

>
>
|
>
>
|
>
>
>
|
>
>
>
>
>
>
>
>
>
>




|
<
|
|







2705
2706
2707
2708
2709
2710
2711
2712
2713
2714

2715
2716
2717
2718

2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733

2734
2735


2736
2737
2738
2739

2740
2741

2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767

2768
2769
2770
2771
2772
2773
2774
2775
2776
    otaVfsGetLastError,           /* xGetLastError */
    0,                            /* xCurrentTimeInt64 (version 2) */
    0, 0, 0                       /* Unimplemented version 3 methods */
  };

  sqlite3_vfs *pParent;           /* Parent VFS */
  ota_vfs *pNew = 0;              /* Newly allocated VFS */
  int nName;
  int rc = SQLITE_OK;


  nName = strlen(zName);
  pParent = sqlite3_vfs_find(zParent);
  if( pParent==0 ){
    rc = SQLITE_NOTFOUND;

  }else{
    int nByte = sizeof(ota_vfs) + nName + 1;
    pNew = (ota_vfs*)sqlite3_malloc(nByte);
    if( pNew==0 ){
      rc = SQLITE_NOMEM;
    }else{
      memset(pNew, 0, nByte);
    }
  }

  if( rc==SQLITE_OK ){
    char *zSpace;
    memcpy(&pNew->base, &vfs_template, sizeof(sqlite3_vfs));
    pNew->base.mxPathname = pParent->mxPathname;
    pNew->base.szOsFile = sizeof(ota_file) + pParent->szOsFile;

    pNew->pRealVfs = pParent;



    pNew->base.zName = (const char*)(zSpace = (char*)&pNew[1]);
    memcpy(zSpace, zName, nName);

    /* Register the new VFS (not as the default) */

    rc = sqlite3_vfs_register(&pNew->base, 0);
    if( rc ){

      sqlite3_free(pNew);
    }
  }

  return rc;
}

static void otaCreateVfs(sqlite3ota *p, const char *zParent){
  int rnd;
  char zRnd[64];

  assert( p->rc==SQLITE_OK );
  sqlite3_randomness(sizeof(int), (void*)&rnd);
  sprintf(zRnd, "ota_vfs_%d", rnd);
  p->rc = sqlite3ota_create_vfs(zRnd, zParent);
  if( p->rc==SQLITE_NOTFOUND ){
    p->zErrmsg = sqlite3_mprintf("no such vfs: %s", zParent);
  }else if( p->rc==SQLITE_OK ){
    sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd);
    assert( pVfs );
    p->zVfsName = pVfs->zName;
  }
}

static void otaDeleteVfs(sqlite3ota *p){
  if( p->zVfsName ){

    sqlite3ota_destroy_vfs(p->zVfsName);
    p->zVfsName = 0;
  }
}


/**************************************************************************/

#ifdef SQLITE_TEST 
Changes to ext/ota/sqlite3ota.h.
296
297
298
299
300
301
302
















303
304
/*
** Return the total number of key-value operations (inserts, deletes or 
** updates) that have been performed on the target database since the
** current OTA update was started.
*/
sqlite3_int64 sqlite3ota_progress(sqlite3ota *pOta);

















#endif /* _SQLITE3OTA_H */








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
/*
** Return the total number of key-value operations (inserts, deletes or 
** updates) that have been performed on the target database since the
** current OTA update was started.
*/
sqlite3_int64 sqlite3ota_progress(sqlite3ota *pOta);

/*
** Create an OTA VFS named zName. Use existing VFS zParent to interact
** with the file-system.
*/
int sqlite3ota_create_vfs(const char *zName, const char *zParent);

/*
** Deregister and destroy an OTA vfs previously created by 
** sqlite3ota_create_vfs().
**
** VFS objects are not reference counted. If a VFS object is destroyed
** before all database handles that use it have been closed, the results 
** are undefined.
*/
void sqlite3ota_destroy_vfs(const char *zName);

#endif /* _SQLITE3OTA_H */

Changes to src/sqlite.h.in.
966
967
968
969
970
971
972

973
974
975
976
977
978
979
#define SQLITE_FCNTL_MMAP_SIZE              18
#define SQLITE_FCNTL_TRACE                  19
#define SQLITE_FCNTL_HAS_MOVED              20
#define SQLITE_FCNTL_SYNC                   21
#define SQLITE_FCNTL_COMMIT_PHASETWO        22
#define SQLITE_FCNTL_WIN32_SET_HANDLE       23
#define SQLITE_FCNTL_ZIPVFS_PAGER           24


/*
** CAPI3REF: Mutex Handle
**
** The mutex module within SQLite defines [sqlite3_mutex] to be an
** abstract type for a mutex object.  The SQLite core never looks
** at the internal representation of an [sqlite3_mutex].  It only







>







966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
#define SQLITE_FCNTL_MMAP_SIZE              18
#define SQLITE_FCNTL_TRACE                  19
#define SQLITE_FCNTL_HAS_MOVED              20
#define SQLITE_FCNTL_SYNC                   21
#define SQLITE_FCNTL_COMMIT_PHASETWO        22
#define SQLITE_FCNTL_WIN32_SET_HANDLE       23
#define SQLITE_FCNTL_ZIPVFS_PAGER           24
#define SQLITE_FCNTL_OTA                    25

/*
** CAPI3REF: Mutex Handle
**
** The mutex module within SQLite defines [sqlite3_mutex] to be an
** abstract type for a mutex object.  The SQLite core never looks
** at the internal representation of an [sqlite3_mutex].  It only