/ Check-in [92e7bb90]
Login

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

Overview
Comment:Improve error reporting from sqlite3_file_control, SQLITE_FCNTL_REPLACE_DATABASE shouldn't copy file security/ACL metadata, bad bit mask fixed
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | apple-osx
Files: files | file ages | folders
SHA1:92e7bb907802c905f99ed4c47669f97723f63ed3
User & Date: adam 2012-05-16 22:08:18
Context
2012-05-17
14:17
Stylistic fixes: limit source code line length to 80 characters. C89-style comments only, not C++/C99 style. check-in: 4068abe0 user: drh tags: apple-osx
2012-05-16
22:08
Improve error reporting from sqlite3_file_control, SQLITE_FCNTL_REPLACE_DATABASE shouldn't copy file security/ACL metadata, bad bit mask fixed check-in: 92e7bb90 user: adam tags: apple-osx
2012-05-14
02:05
Version 3.7.12 check-in: d9348b2a user: drh tags: apple-osx
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858

2859
2860
2861
2862
2863
2864
2865
2866
2867
    assert( fd!=0 );
    if( op==SQLITE_FCNTL_FILE_POINTER ){
      *(sqlite3_file**)pArg = fd;
      rc = SQLITE_OK;
    }else if( fd->pMethods ){
      rc = sqlite3OsFileControl(fd, op, pArg);
#ifndef SQLITE_OMIT_WAL
      if( (op==SQLITE_FCNTL_LAST_ERRNO)&&(*(int *)pArg==0) ){
        sqlite3_file *pWalFd = sqlite3PagerWalFile(pPager);
        if( pWalFd&&(pWalFd->pMethods) ){
          rc = sqlite3OsFileControl(pWalFd, op, pArg);
        }
      }
#endif
    }else{
      rc = SQLITE_NOTFOUND;
    }
    sqlite3BtreeLeave(pBtree);
  }

  sqlite3_mutex_leave(db->mutex);
  return rc;   
}

/*
** Interface to the testing logic.
*/
int sqlite3_test_control(int op, ...){
  int rc = 0;







|











>

|







2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
    assert( fd!=0 );
    if( op==SQLITE_FCNTL_FILE_POINTER ){
      *(sqlite3_file**)pArg = fd;
      rc = SQLITE_OK;
    }else if( fd->pMethods ){
      rc = sqlite3OsFileControl(fd, op, pArg);
#ifndef SQLITE_OMIT_WAL
      if( (rc==SQLITE_OK)&&(op==SQLITE_FCNTL_LAST_ERRNO)&&(*(int *)pArg==0) ){
        sqlite3_file *pWalFd = sqlite3PagerWalFile(pPager);
        if( pWalFd&&(pWalFd->pMethods) ){
          rc = sqlite3OsFileControl(pWalFd, op, pArg);
        }
      }
#endif
    }else{
      rc = SQLITE_NOTFOUND;
    }
    sqlite3BtreeLeave(pBtree);
  }
  sqlite3Error(db, rc, 0);
  sqlite3_mutex_leave(db->mutex);
  return rc;
}

/*
** Interface to the testing logic.
*/
int sqlite3_test_control(int op, ...){
  int rc = 0;

Changes to src/os_unix.c.

3967
3968
3969
3970
3971
3972
3973









































3974
3975
3976
3977
3978
3979
3980
....
4068
4069
4070
4071
4072
4073
4074

4075
4076

4077
4078

4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
....
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
....
5089
5090
5091
5092
5093
5094
5095



5096
5097
5098
5099
5100
5101
5102
....
5163
5164
5165
5166
5167
5168
5169

5170
5171
5172
5173
5174
5175
5176
5177
....
5182
5183
5184
5185
5186
5187
5188

5189
5190
5191
5192
5193
5194
5195
....
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
....
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264




5265
5266
5267
5268


5269

5270
5271



5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
#endif
static int isProxyLockingMode(unixFile *);

#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
static int unixTruncateDatabase(unixFile *, int);

static int unixInvalidateSupportFiles(unixFile *, int);










































static int unixReplaceDatabase(unixFile *pFile, sqlite3 *srcdb) {
  sqlite3_file *id = (sqlite3_file *)pFile;
  Btree *pSrcBtree = NULL;
  sqlite3_file *src_file = NULL;
  unixFile *pSrcFile = NULL;
  char srcWalPath[MAXPATHLEN+5];
................................................................................
   */
  strlcpy(srcWalPath, pSrcFile->zPath, MAXPATHLEN+5);
  strlcat(srcWalPath, "-wal", MAXPATHLEN+5);
  srcWalFD = open(srcWalPath, O_RDONLY);
  if( !(srcWalFD<0) ){
    char dstWalPath[MAXPATHLEN+5];
    int dstWalFD = -1;

    strlcpy(dstWalPath, pFile->zPath, MAXPATHLEN+5);
    strlcat(dstWalPath, "-wal", MAXPATHLEN+5);

    dstWalFD = open(dstWalPath, O_RDWR|O_CREAT, SQLITE_DEFAULT_FILE_PERMISSIONS);
    if( !(dstWalFD<0) ){

      s = copyfile_state_alloc();
      lseek(srcWalFD, 0, SEEK_SET);
      lseek(dstWalFD, 0, SEEK_SET);
      if( fcopyfile(srcWalFD, dstWalFD, s, COPYFILE_ALL) ){
        int err=errno;
        switch(err) {
          case ENOMEM:
            rc = SQLITE_NOMEM;
            break;
          default:
            storeLastErrno(pFile, err);
................................................................................
    pread(pSrcFile->h, &srcChange, 4, 24);
    pread(pFile->h, &dstChange, 4, 24);
    
    /* copy the actual database */
    s = copyfile_state_alloc();
    lseek(pSrcFile->h, 0, SEEK_SET);
    lseek(pFile->h, 0, SEEK_SET);
    if( fcopyfile(pSrcFile->h, pFile->h, s, COPYFILE_ALL) ){
      int err=errno;
      switch(err) {
        case ENOMEM:
          rc = SQLITE_NOMEM;
          break;
        default:
          storeLastErrno(pFile, err);
................................................................................
static int unixUnsafeTruncateDatabase(unixFile *pFile){
  // this is nasty & bad.  destruction with prejudice.  we'll lose all the file locks in this process, however.
  // sqlite3_file_control works properly.  But if it fails, this works approximately
  char journalPath[MAXPATHLEN];
  char walPath[MAXPATHLEN];
  int rc = SQLITE_OK;
  



  strlcpy(journalPath, pFile->zPath, MAXPATHLEN);
  strlcat(journalPath, "-journal", MAXPATHLEN);
  strlcpy(walPath, pFile->zPath, MAXPATHLEN);
  strlcat(walPath, "-wal", MAXPATHLEN);
  int fd1 = pFile->h;
  int result = 0;
  result = ftruncate(fd1, 0ll);
................................................................................
  sqlite3_file *id = (sqlite3_file *)pFile;
  int rc = SQLITE_OK;
  void *pLock = NULL;
  int flags = 0;
  int corruptFileLock = 0;
  int isCorrupt = 0;
  int force = (bFlags & SQLITE_TRUNCATE_FORCE);

  
#if SQLITE_ENABLE_DATA_PROTECTION
  flags |= pFile->protFlags;
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
  if( isProxyLockingMode(pFile) ){
    flags |= SQLITE_OPEN_AUTOPROXY;
  }
................................................................................
    if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){
      isCorrupt = 1;
      rc = sqlite3demo_superlock_corrupt(id, SQLITE_LOCK_EXCLUSIVE, &corruptFileLock);
    }
    if( rc && !force ){
      return rc;
    }

  }
  if( (bFlags&SQLITE_TRUNCATE_INITIALIZE_HEADER_MASK)!=0 ){
    /* initialize a new database in TMPDIR and copy the contents over */
    const char *tDir = unixTempFileDir();
    int tLen = sizeof(char) * (strlen(tDir) + 11);
    char *tDbPath = (char *)malloc(tLen);
    int tFd = -1;
................................................................................
    
    strlcpy(tDbPath, tDir, tLen);
    strlcat(tDbPath, "tmpdbXXXXX", tLen);
    tFd = mkstemp(tDbPath);
    if( tFd==-1 ){
      storeLastErrno(pFile, errno);
      rc = SQLITE_IOERR;
      if( force ){
        /* attempt the truncation, even if we can't seed the database in a temp directory */
        rc = pFile->pMethod->xTruncate(id, ((pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS) != 0) ? 1L : 0L);
      }
    }else{
      sqlite3 *tDb = NULL;
      copyfile_state_t s;
      int trc = sqlite3_open_v2(tDbPath, &tDb, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_AUTOPROXY, NULL);
      char *errmsg = NULL;
      const char *sql = "";
      if( !trc && (bFlags&SQLITE_TRUNCATE_PAGESIZE_MASK) ){
................................................................................
        /* merge the wal into the db */
        sqlite3_file_control(tDb, NULL, SQLITE_FCNTL_PERSIST_WAL, &off);
        sqlite3_close(tDb);
      }
      s = copyfile_state_alloc();
      lseek(tFd, 0, SEEK_SET);
      lseek(pFile->h, 0, SEEK_SET);
      if( fcopyfile(tFd, pFile->h, s, COPYFILE_ALL) ){
        int err=errno;
        switch(err) {
          case ENOMEM:
            rc = SQLITE_NOMEM;
            break;
          default:
            storeLastErrno(pFile, err);
            rc = SQLITE_IOERR;
        }
      }
      copyfile_state_free(s);
      fsync(pFile->h);
      close(tFd);
      unlink(tDbPath);




    }
    free(tDbPath);
  } else {
    rc = pFile->pMethod->xTruncate(id, ((pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS) != 0) ? 1L : 0L);


  }

  if( rc==SQLITE_OK || force ){
    unixInvalidateSupportFiles(pFile, 0);



  }
  pFile->pMethod->xSync(id, SQLITE_SYNC_FULL);


  if( isCorrupt ){
    sqlite3demo_superunlock_corrupt(id, corruptFileLock);
  }else if( pLock ){
    sqlite3demo_superunlock(pLock);
  }else{
    assert(force);
  }
  
  if( rc!=SQLITE_OK && force){
    unixUnsafeTruncateDatabase(pFile);
  }
  
  return rc;
}

/*
 ** Lock locations for shared-memory locks used by WAL mode.







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







 







>


>
|
<
>



|







 







|







 







>
>
>







 







>
|







 







>







 







|
<
<
<







 







|



|



|






>
>
>
>




>
>
|
>

|
>
>
>












|
|







3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
....
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120

4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
....
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159
....
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
....
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
....
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
....
5244
5245
5246
5247
5248
5249
5250
5251



5252
5253
5254
5255
5256
5257
5258
....
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
#endif
static int isProxyLockingMode(unixFile *);

#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
static int unixTruncateDatabase(unixFile *, int);

static int unixInvalidateSupportFiles(unixFile *, int);

static int findCreateFileMode(const char *, int, mode_t*, uid_t *,gid_t *);

/* opens a read/write connection to a file zName inheriting the appropriate
 ** user/perms from the database file if running as root.  Returns the file 
 ** descriptor by reference */
static int unixOpenChildFile(const char *zName, int openFlags, int dbOpenFlags, int protFlags, int *pFd) {
  int fd = -1;
  mode_t openMode;              /* Permissions to create file with */
  uid_t uid;                    /* Userid for the file */
  gid_t gid;                    /* Groupid for the file */
  int rc;
  
  assert(pFd!=NULL);
  rc = findCreateFileMode(zName, dbOpenFlags, &openMode, &uid, &gid);
  if( rc!=SQLITE_OK ){
    return rc;
  }
  fd = robust_open(zName, openFlags, openMode);
  OSTRACE(("OPENX   %-3d %s 0%o\n", fd, zName, openFlags));
  if( fd<0 ){
    rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
    return rc;
  }
  /* if we're opening the wal or journal and running as root, set the journal uid/gid */
  if( dbOpenFlags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
    uid_t euid = geteuid();
    if( euid==0 && (euid!=uid || getegid()!=gid) ){
      if( fchown(fd, uid, gid) ){
        rc = SQLITE_CANTOPEN_BKPT;
      }
    }
  }
  if( rc==SQLITE_OK ){
    *pFd = fd;
  } else {
    *pFd = -1;
    close(fd);
  }
  return rc;
}

static int unixReplaceDatabase(unixFile *pFile, sqlite3 *srcdb) {
  sqlite3_file *id = (sqlite3_file *)pFile;
  Btree *pSrcBtree = NULL;
  sqlite3_file *src_file = NULL;
  unixFile *pSrcFile = NULL;
  char srcWalPath[MAXPATHLEN+5];
................................................................................
   */
  strlcpy(srcWalPath, pSrcFile->zPath, MAXPATHLEN+5);
  strlcat(srcWalPath, "-wal", MAXPATHLEN+5);
  srcWalFD = open(srcWalPath, O_RDONLY);
  if( !(srcWalFD<0) ){
    char dstWalPath[MAXPATHLEN+5];
    int dstWalFD = -1;
    int protFlags = 0;
    strlcpy(dstWalPath, pFile->zPath, MAXPATHLEN+5);
    strlcat(dstWalPath, "-wal", MAXPATHLEN+5);

    rc = unixOpenChildFile(dstWalPath, O_RDWR|O_CREAT, SQLITE_OPEN_WAL, protFlags, &dstWalFD);

    if( rc==SQLITE_OK ){
      s = copyfile_state_alloc();
      lseek(srcWalFD, 0, SEEK_SET);
      lseek(dstWalFD, 0, SEEK_SET);
      if( fcopyfile(srcWalFD, dstWalFD, s, COPYFILE_DATA) ){
        int err=errno;
        switch(err) {
          case ENOMEM:
            rc = SQLITE_NOMEM;
            break;
          default:
            storeLastErrno(pFile, err);
................................................................................
    pread(pSrcFile->h, &srcChange, 4, 24);
    pread(pFile->h, &dstChange, 4, 24);
    
    /* copy the actual database */
    s = copyfile_state_alloc();
    lseek(pSrcFile->h, 0, SEEK_SET);
    lseek(pFile->h, 0, SEEK_SET);
    if( fcopyfile(pSrcFile->h, pFile->h, s, COPYFILE_DATA) ){
      int err=errno;
      switch(err) {
        case ENOMEM:
          rc = SQLITE_NOMEM;
          break;
        default:
          storeLastErrno(pFile, err);
................................................................................
static int unixUnsafeTruncateDatabase(unixFile *pFile){
  // this is nasty & bad.  destruction with prejudice.  we'll lose all the file locks in this process, however.
  // sqlite3_file_control works properly.  But if it fails, this works approximately
  char journalPath[MAXPATHLEN];
  char walPath[MAXPATHLEN];
  int rc = SQLITE_OK;
  
#ifdef DEBUG
  fprintf(stderr, "Force truncating database %s\n", pFile->zPath);
#endif
  strlcpy(journalPath, pFile->zPath, MAXPATHLEN);
  strlcat(journalPath, "-journal", MAXPATHLEN);
  strlcpy(walPath, pFile->zPath, MAXPATHLEN);
  strlcat(walPath, "-wal", MAXPATHLEN);
  int fd1 = pFile->h;
  int result = 0;
  result = ftruncate(fd1, 0ll);
................................................................................
  sqlite3_file *id = (sqlite3_file *)pFile;
  int rc = SQLITE_OK;
  void *pLock = NULL;
  int flags = 0;
  int corruptFileLock = 0;
  int isCorrupt = 0;
  int force = (bFlags & SQLITE_TRUNCATE_FORCE);
  int safeFailed = 0;

#if SQLITE_ENABLE_DATA_PROTECTION
  flags |= pFile->protFlags;
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
  if( isProxyLockingMode(pFile) ){
    flags |= SQLITE_OPEN_AUTOPROXY;
  }
................................................................................
    if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){
      isCorrupt = 1;
      rc = sqlite3demo_superlock_corrupt(id, SQLITE_LOCK_EXCLUSIVE, &corruptFileLock);
    }
    if( rc && !force ){
      return rc;
    }
    rc = SQLITE_OK; /* Ignore the locking failure if force is true */
  }
  if( (bFlags&SQLITE_TRUNCATE_INITIALIZE_HEADER_MASK)!=0 ){
    /* initialize a new database in TMPDIR and copy the contents over */
    const char *tDir = unixTempFileDir();
    int tLen = sizeof(char) * (strlen(tDir) + 11);
    char *tDbPath = (char *)malloc(tLen);
    int tFd = -1;
................................................................................
    
    strlcpy(tDbPath, tDir, tLen);
    strlcat(tDbPath, "tmpdbXXXXX", tLen);
    tFd = mkstemp(tDbPath);
    if( tFd==-1 ){
      storeLastErrno(pFile, errno);
      rc = SQLITE_IOERR;
      safeFailed = 1;



    }else{
      sqlite3 *tDb = NULL;
      copyfile_state_t s;
      int trc = sqlite3_open_v2(tDbPath, &tDb, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_AUTOPROXY, NULL);
      char *errmsg = NULL;
      const char *sql = "";
      if( !trc && (bFlags&SQLITE_TRUNCATE_PAGESIZE_MASK) ){
................................................................................
        /* merge the wal into the db */
        sqlite3_file_control(tDb, NULL, SQLITE_FCNTL_PERSIST_WAL, &off);
        sqlite3_close(tDb);
      }
      s = copyfile_state_alloc();
      lseek(tFd, 0, SEEK_SET);
      lseek(pFile->h, 0, SEEK_SET);
      if( fcopyfile(tFd, pFile->h, s, COPYFILE_DATA) ){
        int err=errno;
        switch(err) {
          case ENOMEM:
            trc = SQLITE_NOMEM;
            break;
          default:
            storeLastErrno(pFile, err);
            trc = SQLITE_IOERR;
        }
      }
      copyfile_state_free(s);
      fsync(pFile->h);
      close(tFd);
      unlink(tDbPath);
      if( trc!=SQLITE_OK ){
        safeFailed = 1;
        rc = trc;
      }
    }
    free(tDbPath);
  } else {
    rc = pFile->pMethod->xTruncate(id, ((pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS) != 0) ? 1L : 0L);
    if( rc ){
      safeFailed = 1;
    }
  }
  if( rc==SQLITE_OK || force ){
    rc = unixInvalidateSupportFiles(pFile, 0);
    if( rc ){
      safeFailed = 1;
    }
  }
  pFile->pMethod->xSync(id, SQLITE_SYNC_FULL);


  if( isCorrupt ){
    sqlite3demo_superunlock_corrupt(id, corruptFileLock);
  }else if( pLock ){
    sqlite3demo_superunlock(pLock);
  }else{
    assert(force);
  }
  
  if( force && safeFailed){
    rc = unixUnsafeTruncateDatabase(pFile);
  }
  
  return rc;
}

/*
 ** Lock locations for shared-memory locks used by WAL mode.

Changes to src/sqlite3_private.h.

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
** behavior is applied and the database file is truncated to zero bytes, a rollback 
** journal (if present) is unlinked, a WAL journal (if present) is truncated to zero 
** bytes and the first few bytes of the -shm file is scrambled to trigger existing
** connections to rebuild the index from the database file contents.
*/
#define SQLITE_FCNTL_TRUNCATE_DATABASE      101
#define SQLITE_TRUNCATE_DATABASE            SQLITE_FCNTL_TRUNCATE_DATABASE
#define SQLITE_TRUNCATE_INITIALIZE_HEADER_MASK    (0x63<<0)
#define SQLITE_TRUNCATE_JOURNALMODE_WAL           (0x1<<0)
#define SQLITE_TRUNCATE_AUTOVACUUM_MASK           (0x3<<2)
#define SQLITE_TRUNCATE_AUTOVACUUM_OFF            (0x1<<2)
#define SQLITE_TRUNCATE_AUTOVACUUM_FULL           (0x2<<2)
#define SQLITE_TRUNCATE_AUTOVACUUM_INCREMENTAL    (0x3<<2)
#define SQLITE_TRUNCATE_PAGESIZE_MASK             (0x7<<4)
#define SQLITE_TRUNCATE_PAGESIZE_1024             (0x1<<4)







|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
** behavior is applied and the database file is truncated to zero bytes, a rollback 
** journal (if present) is unlinked, a WAL journal (if present) is truncated to zero 
** bytes and the first few bytes of the -shm file is scrambled to trigger existing
** connections to rebuild the index from the database file contents.
*/
#define SQLITE_FCNTL_TRUNCATE_DATABASE      101
#define SQLITE_TRUNCATE_DATABASE            SQLITE_FCNTL_TRUNCATE_DATABASE
#define SQLITE_TRUNCATE_INITIALIZE_HEADER_MASK    (0x7F<<0)
#define SQLITE_TRUNCATE_JOURNALMODE_WAL           (0x1<<0)
#define SQLITE_TRUNCATE_AUTOVACUUM_MASK           (0x3<<2)
#define SQLITE_TRUNCATE_AUTOVACUUM_OFF            (0x1<<2)
#define SQLITE_TRUNCATE_AUTOVACUUM_FULL           (0x2<<2)
#define SQLITE_TRUNCATE_AUTOVACUUM_INCREMENTAL    (0x3<<2)
#define SQLITE_TRUNCATE_PAGESIZE_MASK             (0x7<<4)
#define SQLITE_TRUNCATE_PAGESIZE_1024             (0x1<<4)