/ Check-in [d51c086e]
Login

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

Overview
Comment:Merge in latest changes, autologging options, read only file system wal support, test config conditionalization, WAL frame write prebuffering
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | apple-osx
Files: files | file ages | folders
SHA1: d51c086e5c006821e2ab932f229649a729d914b2
User & Date: adam 2012-04-02 23:35:45
Context
2012-04-02
23:42
bad assert check-in: 0c0150f2 user: adam tags: apple-osx
23:35
Merge in latest changes, autologging options, read only file system wal support, test config conditionalization, WAL frame write prebuffering check-in: d51c086e user: adam tags: apple-osx
2012-03-31
02:46
Merge all the latest trunk changes into the apple-osx branch. check-in: 18ec60ca user: drh tags: apple-osx
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/main.c.

1846
1847
1848
1849
1850
1851
1852

1853
1854
1855
1856
1857
1858
1859
1860
1861


























1862
1863
1864
1865
1866
1867
1868
....
2086
2087
2088
2089
2090
2091
2092



















































2093
2094
2095
2096
2097
2098
2099
....
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357


2358
2359
2360





2361
2362
2363
2364




2365
2366
2367
2368
2369
2370
2371
....
3104
3105
3106
3107
3108
3109
3110



3111
3112
3113
3114
      newLimit = aHardLimit[limitId];  /* IMP: R-51463-25634 */
    }
    db->aLimit[limitId] = newLimit;
  }
  return oldLimit;                     /* IMP: R-53341-35419 */
}
#if defined(SQLITE_ENABLE_AUTO_PROFILE)

void _sqlite_auto_profile(void *aux, const char *sql, u64 ns);
void _sqlite_auto_trace(void *aux, const char *sql);
void _sqlite_auto_profile(void *aux, const char *sql, u64 ns) {
#pragma unused(aux)
	fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000);
}
void _sqlite_auto_trace(void *aux, const char *sql) {
	fprintf(stderr, "TraceSQL(%p): %s\n", aux, sql);
}


























#endif

/*
** This function is used to parse both URIs and non-URI filenames passed by the
** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database
** URIs specified as part of ATTACH statements.
**
................................................................................
    zFile = 0;
  }
  *pFlags = flags;
  *pzFile = zFile;
  return rc;
}





















































/*
** This routine does the work of opening a database on behalf of
** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"  
** is UTF-8 encoded.
*/
static int openDatabase(
................................................................................
  assert( db!=0 || rc==SQLITE_NOMEM );
  if( rc==SQLITE_NOMEM ){
    sqlite3_close(db);
    db = 0;
  }else if( rc!=SQLITE_OK ){
    db->magic = SQLITE_MAGIC_SICK;
  }
#if defined(SQLITE_ENABLE_AUTO_PROFILE)
  if( db && !rc ){
    char *envprofile = getenv("SQLITE_AUTO_PROFILE");
    char *envtrace = getenv("SQLITE_AUTO_TRACE");


    
    if( envprofile!=NULL ){
      sqlite3_profile(db, _sqlite_auto_profile, db);





    }
    if( envtrace!=NULL ){
      sqlite3_trace(db, _sqlite_auto_trace, db);
    }




  }
#endif
  *ppDb = db;
#ifdef SQLITE_ENABLE_SQLRR
  SRRecOpen(db, zFilename, flags);
#endif
  return sqlite3ApiExit(0, rc);
................................................................................
  if( sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY, NULL) == SQLITE_OK ){
    LockstatePID lockstate = {pid, -1};
    sqlite3_file_control(db, NULL, SQLITE_FCNTL_LOCKSTATE_PID, &lockstate);
    sqlite3_close(db);
    int state = lockstate.state;
    return state;
  }



  return SQLITE_LOCKSTATE_ERROR;
}

#endif /* SQLITE_ENABLE_APPLE_SPI */







>









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







 







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







 







|

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

<
<
|
>
>
>
>







 







>
>
>




1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
....
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
....
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438


2439
2440
2441
2442
2443
2444


2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
....
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
      newLimit = aHardLimit[limitId];  /* IMP: R-51463-25634 */
    }
    db->aLimit[limitId] = newLimit;
  }
  return oldLimit;                     /* IMP: R-53341-35419 */
}
#if defined(SQLITE_ENABLE_AUTO_PROFILE)
/* stderr logging */
void _sqlite_auto_profile(void *aux, const char *sql, u64 ns);
void _sqlite_auto_trace(void *aux, const char *sql);
void _sqlite_auto_profile(void *aux, const char *sql, u64 ns) {
#pragma unused(aux)
	fprintf(stderr, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000);
}
void _sqlite_auto_trace(void *aux, const char *sql) {
	fprintf(stderr, "TraceSQL(%p): %s\n", aux, sql);
}

/* syslog logging */
#include <asl.h>
static aslclient autolog_client = NULL;
static void _close_asl_log() {
  if( NULL!=autolog_client ){
    asl_close(autolog_client);
    autolog_client = NULL;
  }
}
static void _open_asl_log() {
  if( NULL==autolog_client ){
    autolog_client = asl_open("SQLite", NULL, 0);
    atexit(_close_asl_log);
  }
}

void _sqlite_auto_profile_syslog(void *aux, const char *sql, u64 ns);
void _sqlite_auto_trace_syslog(void *aux, const char *sql);
void _sqlite_auto_profile_syslog(void *aux, const char *sql, u64 ns) {
#pragma unused(aux)
	asl_log(autolog_client, NULL, ASL_LEVEL_NOTICE, "Query: %s\n Execution Time: %llu ms\n", sql, ns / 1000000);
}
void _sqlite_auto_trace_syslog(void *aux, const char *sql) {
	asl_log(autolog_client, NULL, ASL_LEVEL_NOTICE, "TraceSQL(%p): %s\n", aux, sql);
}
#endif

/*
** This function is used to parse both URIs and non-URI filenames passed by the
** user to API functions sqlite3_open() or sqlite3_open_v2(), and for database
** URIs specified as part of ATTACH statements.
**
................................................................................
    zFile = 0;
  }
  *pFlags = flags;
  *pzFile = zFile;
  return rc;
}

#if defined(SQLITE_ENABLE_AUTO_PROFILE)
#define SQLITE_AUTOLOGGING_STDERR 1
#define SQLITE_AUTOLOGGING_SYSLOG 2
static void enableAutoLogging(
  sqlite3 *db
){
  char *envprofile = getenv("SQLITE_AUTO_PROFILE");
  
  if( envprofile!=NULL ){
    int where = 0;
    if( !strncasecmp("1", envprofile, 1) ){
      if( isatty(STDERR_FILENO) ){
        where = SQLITE_AUTOLOGGING_STDERR;
      }else{
        where = SQLITE_AUTOLOGGING_SYSLOG;
      }
    } else if( !strncasecmp("stderr", envprofile, 6) ){
      where = SQLITE_AUTOLOGGING_STDERR;
    } else if( !strncasecmp("syslog", envprofile, 6) ){
      where = SQLITE_AUTOLOGGING_SYSLOG;
    }
    if( where==SQLITE_AUTOLOGGING_STDERR ){
      sqlite3_profile(db, _sqlite_auto_profile, db);
    }else if( where==SQLITE_AUTOLOGGING_SYSLOG ){
      _open_asl_log();
      sqlite3_profile(db, _sqlite_auto_profile_syslog, db);
    }
  }
  char *envtrace = getenv("SQLITE_AUTO_TRACE");
  if( envtrace!=NULL ){
    int where = 0;
    if( !strncasecmp("1", envtrace, 1) ){
      if( isatty(STDERR_FILENO) ){
        where = SQLITE_AUTOLOGGING_STDERR;
      }else{
        where = SQLITE_AUTOLOGGING_SYSLOG;
      }
    } else if( !strncasecmp("stderr", envtrace, 6) ){
      where = SQLITE_AUTOLOGGING_STDERR;
    } else if( !strncasecmp("syslog", envtrace, 6) ){
      where = SQLITE_AUTOLOGGING_SYSLOG;
    }
    if( where==SQLITE_AUTOLOGGING_STDERR ){
      sqlite3_trace(db, _sqlite_auto_trace, db);
    }else if( where==SQLITE_AUTOLOGGING_SYSLOG ){
      _open_asl_log();
      sqlite3_trace(db, _sqlite_auto_trace_syslog, db);
    }
  }
}
#endif

/*
** This routine does the work of opening a database on behalf of
** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"  
** is UTF-8 encoded.
*/
static int openDatabase(
................................................................................
  assert( db!=0 || rc==SQLITE_NOMEM );
  if( rc==SQLITE_NOMEM ){
    sqlite3_close(db);
    db = 0;
  }else if( rc!=SQLITE_OK ){
    db->magic = SQLITE_MAGIC_SICK;
  }
#if defined(__APPLE__) && ENABLE_FORCE_WAL
  if( db && !rc ){
    if ((0 == access("/var/db/enableForceWAL", R_OK))) {
#ifdef SQLITE_DEBUG
      fprintf(stderr, "SQLite WAL journal_mode ENABLED by default.\n");
#endif
      


      sqlite3_exec(db, "pragma journal_mode=wal", NULL, NULL, NULL);
#ifdef SQLITE_DEBUG
//    } else {
//      fprintf(stderr, "SQLite WAL journal_mode NOT ENABLED by default.\n");
#endif
    }


  }
#endif
#if defined(SQLITE_ENABLE_AUTO_PROFILE)
  if( db && !rc ){
    enableAutoLogging(db);
  }
#endif
  *ppDb = db;
#ifdef SQLITE_ENABLE_SQLRR
  SRRecOpen(db, zFilename, flags);
#endif
  return sqlite3ApiExit(0, rc);
................................................................................
  if( sqlite3_open_v2(path, &db, SQLITE_OPEN_READONLY, NULL) == SQLITE_OK ){
    LockstatePID lockstate = {pid, -1};
    sqlite3_file_control(db, NULL, SQLITE_FCNTL_LOCKSTATE_PID, &lockstate);
    sqlite3_close(db);
    int state = lockstate.state;
    return state;
  }
  if( NULL!=db ){ 
    sqlite3_close(db); /* need to close even if open returns an error */
  }
  return SQLITE_LOCKSTATE_ERROR;
}

#endif /* SQLITE_ENABLE_APPLE_SPI */

Changes to src/os_unix.c.

2000
2001
2002
2003
2004
2005
2006

2007



2008
2009
2010
2011
2012
2013
2014
....
2235
2236
2237
2238
2239
2240
2241

















2242


2243
2244
2245
2246
2247
2248
2249
....
3921
3922
3923
3924
3925
3926
3927

3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
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
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
4035
4036
4037
4038
4039
4040
4041
4042
....
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
4346
4347
4348
4349
4350
....
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
....
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
....
5135
5136
5137
5138
5139
5140
5141
5142







5143
5144
5145
5146
5147
5148
5149
....
5150
5151
5152
5153
5154
5155
5156




















































































































































































































































































5157
5158
5159
5160
5161
5162
5163
....
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
....
5509
5510
5511
5512
5513
5514
5515


5516
5517
5518
5519
5520
5521
5522
....
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
6104
6105
6106
6107
6108
6109
6110
6111
6112
6113
6114
6115
6116
6117
6118
6119
        }
        lock.l_type = F_RDLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = SHARED_FIRST;
        lock.l_len = divSize;
        if( unixFileLock(pFile, &lock, 10)==(-1) ){
          tErrno = errno;

          rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);



          if( IS_LOCK_ERROR(rc) ){
            pFile->lastErrno = tErrno;
          }
          goto end_unlock;
        }
        lock.l_type = F_UNLCK;
        lock.l_whence = SEEK_SET;
................................................................................
  return SQLITE_OK;
}

/*
** Close the file.
*/
static int nolockClose(sqlite3_file *id) {

















  return closeUnixFile(id);


}

/******************* End of the no-op lock implementation *********************
******************************************************************************/

/******************************************************************************
************************* Begin dot-file Locking ******************************
................................................................................
      }
#endif
    }
  }

  return SQLITE_OK;
}


#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
#include "sqlite3_private.h"
#include <copyfile.h>
static int getDbPathForUnixFile(unixFile *pFile, char *dbPath);
#endif
static int isProxyLockingMode(unixFile *);

#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
static int unixTruncateDatabase(unixFile *pFile, int bFlags) {
  sqlite3_file *id = (sqlite3_file *)pFile;
  int rc = SQLITE_OK;
  void *pLock = NULL;
  int flags = 0;
  int corruptFileLock = 0;
  int isCorrupt = 0;
    
#if SQLITE_ENABLE_DATA_PROTECTION
  flags |= pFile->protFlags;
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
  if( isProxyLockingMode(pFile) ){
    flags |= SQLITE_OPEN_AUTOPROXY;
  }
#endif
  
  rc = sqlite3demo_superlock(pFile->zPath, 0, flags, 0, 0, &pLock);
  if( rc ){
    if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){
      isCorrupt = 1;
      rc = sqlite3demo_superlock_corrupt(id, SQLITE_LOCK_EXCLUSIVE, &corruptFileLock);
    }
    if( rc ){
      return rc;
    }
  }
  rc = pFile->pMethod->xTruncate(id, ((pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS) != 0) ? 1L : 0L);
  if( rc==SQLITE_OK ){
    unixInvalidateSupportFiles(pFile, 0);
  }
  pFile->pMethod->xSync(id, SQLITE_SYNC_FULL);


  if( isCorrupt ){
    sqlite3demo_superunlock_corrupt(id, corruptFileLock);
  }else{
    sqlite3demo_superunlock(pLock);
  }
  return rc;
}

static int unixInvalidateSupportFiles(unixFile *pFile, int skipWAL) {
  char jPath[MAXPATHLEN+9];
  int zLen = strlcpy(jPath, pFile->zPath, MAXPATHLEN+9);
  if( zLen<MAXPATHLEN ){
    size_t jLen;
    const char extensions[3][9] = { "-wal", "-journal", "-shm" };
    int j = (skipWAL ? 1 : 0);
    for( ; j<3; j++ ){
      
      /* Check to see if the shm file is already opened for this pFile */
      if( j==2 ){
        unixEnterMutex(); /* Because pFile->pInode is shared across threads */
        unixShmNode *pShmNode = pFile->pInode->pShmNode;
        if( pShmNode && !pShmNode->isReadonly ){
          struct stat sStat;
          sqlite3_mutex_enter(pShmNode->mutex);
          
          if( pShmNode->h>=0 && !osFstat(pShmNode->h, &sStat) ){
            unsigned long size = (sStat.st_size<4) ? sStat.st_size : 4;
            if( size>0 ){
              bzero(pShmNode->apRegion[0], size);
              sqlite3_mutex_leave(pShmNode->mutex);
              unixLeaveMutex();
              continue;
            }
          }
          sqlite3_mutex_leave(pShmNode->mutex);
        }
        unixLeaveMutex();
      }
      jLen = strlcpy(&jPath[zLen], extensions[j], 9);
      if( jLen < 9 ){
        int jflags = (j<2) ? O_TRUNC : O_RDWR;
        int jfd = open(jPath, jflags);
        if( jfd==(-1) ){
          if( errno!=ENOENT ){
            perror(jPath);
          }
        } else {
          if( j==2 ){
            struct stat sStat;
            if( !osFstat(jfd, &sStat) ){
              unsigned long size = (sStat.st_size<4) ? sStat.st_size : 4;
              if( size>0 ){
                uint32_t zero = 0;
                pwrite(jfd, &zero, (size_t)size, 0);
              }
            }
          }
          fsync(jfd);
          close(jfd);
        }
      }
    }
  }
  return SQLITE_OK;
}

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];
................................................................................
    fprintf(stderr, "%s lock held by %d\n", zType, (int)lk.l_pid);
#endif
    return 1;
  } 
  return 0;
}

/*
** This test only works for lock testing on unix/posix VFS.
** Adapted from tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
*/
static int unixLockstatePid(unixFile *pFile, pid_t pid, int *pLockstate){
  int hDb;        /* File descriptor for the open database file */
  int hShm = -1;  /* File descriptor for WAL shared-memory file */
  ssize_t got;    /* Bytes read from header */
  int isWal;                 /* True if in WAL mode */
  int nLock = 0;             /* Number of locks held */
  unsigned char aHdr[100];   /* Database header */
  
  assert(pLockstate);
  
  /* make sure we are dealing with a database file */
  hDb = pFile->h;
  if( hDb<0 ){
    *pLockstate = SQLITE_LOCKSTATE_ERROR;
    return SQLITE_ERROR;
  }
  assert( (strlen(SQLITE_FILE_HEADER)+1)==SQLITE_FILE_HEADER_LEN );
  got = pread(hDb, aHdr, 100, 0);
  if( got<0 ){
    *pLockstate = SQLITE_LOCKSTATE_ERROR;
    return SQLITE_ERROR;
  }
  if( got!=100 || memcmp(aHdr, SQLITE_FILE_HEADER, SQLITE_FILE_HEADER_LEN)!=0 ){
    *pLockstate = SQLITE_LOCKSTATE_NOTADB;
    return SQLITE_NOTADB;
  }
  
  /* First check for an exclusive lock */
  nLock += unixIsLocked(pid, hDb, F_RDLCK, SHARED_FIRST, SHARED_SIZE, "EXCLUSIVE");
  isWal = aHdr[18]==2;
  if( nLock==0 && isWal==0 ){
    /* Rollback mode */
    nLock += unixIsLocked(pid, hDb, F_WRLCK, PENDING_BYTE, SHARED_SIZE+2, "PENDING|RESERVED|SHARED");
  }
  if( nLock==0 && isWal!=0 ){
    /* lookup the file descriptor for the shared memory file if we have it open in this process */
    unixEnterMutex(); /* Because pFile->pInode is shared across threads */
    unixShmNode *pShmNode = pFile->pInode->pShmNode;
    if( pShmNode ){
      sqlite3_mutex_enter(pShmNode->mutex);
      
      hShm = pShmNode->h;
      if( hShm >= 0){
        if( unixIsLocked(pid, hShm, F_RDLCK, SHM_RECOVER, 1, "WAL-RECOVERY") ||
           unixIsLocked(pid, hShm, F_RDLCK, SHM_WRITE, 1, "WAL-WRITE") ){
          nLock = 1;
        }
      }
      
      sqlite3_mutex_leave(pShmNode->mutex);
    } 
    
    if( hShm<0 ){
      /* the shared memory file isn't open in this process space, open our own FD */
      char zShm[MAXPATHLEN];
      
      /* WAL mode */
      strlcpy(zShm, pFile->zPath, MAXPATHLEN);
      strlcat(zShm, "-shm", MAXPATHLEN);
      hShm = open(zShm, O_RDONLY, 0);
      if( hShm<0 ){
        *pLockstate = SQLITE_LOCKSTATE_OFF;
        unixLeaveMutex();
        return SQLITE_OK;
      }
      if( unixIsLocked(pid, hShm, F_RDLCK, SHM_RECOVER, 1, "WAL-RECOVERY") ||
         unixIsLocked(pid, hShm, F_RDLCK, SHM_WRITE, 1, "WAL-WRITE") ){
        nLock = 1;
      }
      close(hShm);
    }
    unixLeaveMutex();
  }
  if( nLock>0 ){
    *pLockstate = SQLITE_LOCKSTATE_ON;
  } else {
    *pLockstate = SQLITE_LOCKSTATE_OFF;
  }
  return SQLITE_OK;
}

#endif /* (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__) */


/*
** If *pArg is inititially negative then this is a query.  Set *pArg to
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
................................................................................
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
  unixFile *pFile = (unixFile*)id;
  switch( op ){
    case SQLITE_FCNTL_LOCKSTATE: {
      *(int*)pArg = pFile->eFileLock;
      return SQLITE_OK;
    }
    case SQLITE_LAST_ERRNO: {
      *(int*)pArg = pFile->lastErrno;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_CHUNK_SIZE: {
      pFile->szChunk = *(int *)pArg;
      return SQLITE_OK;
    }
................................................................................
    */
    case SQLITE_FCNTL_DB_UNCHANGED: {
      ((unixFile*)id)->dbUpdate = 0;
      return SQLITE_OK;
    }
#endif
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
    case SQLITE_SET_LOCKPROXYFILE:
    case SQLITE_GET_LOCKPROXYFILE: {
      return proxyFileControl(id,op,pArg);
    }
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
    case SQLITE_FCNTL_TRUNCATE_DATABASE: {
      return unixTruncateDatabase(pFile, (pArg ? (*(int *)pArg) : 0));
    }
................................................................................

  /* If pShmNode->nRef has reached 0, then close the underlying
  ** shared-memory file, too */
  unixEnterMutex();
  assert( pShmNode->nRef>0 );
  pShmNode->nRef--;
  if( pShmNode->nRef==0 ){
    if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename);







    unixShmPurge(pDbFd);
  }
  unixLeaveMutex();

  return SQLITE_OK;
}

................................................................................

#else
# define unixShmMap     0
# define unixShmLock    0
# define unixShmBarrier 0
# define unixShmUnmap   0
#endif /* #ifndef SQLITE_OMIT_WAL */





















































































































































































































































































/*
** Here ends the implementation of all sqlite3_file methods.
**
********************** End sqlite3_file Methods *******************************
******************************************************************************/

................................................................................
  unixLock,                 /* xLock method */
  unixUnlock,               /* xUnlock method */
  unixCheckReservedLock     /* xCheckReservedLock method */
)
IOMETHODS(
  nolockIoFinder,           /* Finder function name */
  nolockIoMethods,          /* sqlite3_io_methods object name */
  1,                        /* shared memory is disabled */
  nolockClose,              /* xClose method */
  nolockLock,               /* xLock method */
  nolockUnlock,             /* xUnlock method */
  nolockCheckReservedLock   /* xCheckReservedLock method */
)
IOMETHODS(
  dotlockIoFinder,          /* Finder function name */
................................................................................
#endif
  }

  if( pLockingStyle == &posixIoMethods
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
    || pLockingStyle == &nfsIoMethods
#endif


  ){
    unixEnterMutex();
    rc = findInodeInfo(pNew, &pNew->pInode);
    if( rc!=SQLITE_OK ){
      /* If an error occured in findInodeInfo(), close the file descriptor
      ** immediately, before releasing the mutex. findInodeInfo() may fail
      ** in two scenarios:
................................................................................
    int useProxy = 0;

    /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means 
    ** never use proxy, NULL means use proxy for non-local files only.  */
    if( envforce!=NULL ){
      useProxy = atoi(envforce)>0;
    }else{
      if( statfs(zPath, &fsInfo) == -1 ){
        /* In theory, the close(fd) call is sub-optimal. If the file opened
        ** with fd is a database file, and there are other connections open
        ** on that file that are currently holding advisory locks on it,
        ** then the call to close() will cancel those locks. In practice,
        ** we're assuming that statfs() doesn't fail very often. At least
        ** not while other file descriptors opened by the same process on
        ** the same file are working.  */
        p->lastErrno = errno;
        robust_close(p, fd, __LINE__);
        rc = SQLITE_IOERR_ACCESS;
        goto open_finished;
      }
      useProxy = !(fsInfo.f_flags&MNT_LOCAL);
    }
    if( useProxy ){
      rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
      if( rc==SQLITE_OK ){
        /* cache the pMethod in case the transform fails */
        const struct sqlite3_io_methods *pMethod = pFile->pMethods;







>

>
>
>







 







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







 







>









|
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|







 







|
|







 







|
>
>
>
>
>
>
>







 







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







 







|







 







>
>







 







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







2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
....
2239
2240
2241
2242
2243
2244
2245
2246
2247
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
....
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961






3962


































3963
























































3964
3965
3966
3967
3968
3969
3970
....
4181
4182
4183
4184
4185
4186
4187




4188















































































4189
4190
4191
4192
4193
4194
4195
....
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
....
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
....
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
....
5002
5003
5004
5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
5038
5039
5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
5098
5099
5100
5101
5102
5103
5104
5105
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
5121
5122
5123
5124
5125
5126
5127
5128
5129
5130
5131
5132
5133
5134
5135
5136
5137
5138
5139
5140
5141
5142
5143
5144
5145
5146
5147
5148
5149
5150
5151
5152
5153
5154
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
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
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
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
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
....
5363
5364
5365
5366
5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
....
5637
5638
5639
5640
5641
5642
5643
5644
5645
5646
5647
5648
5649
5650
5651
5652
....
6223
6224
6225
6226
6227
6228
6229













6230
6231
6232
6233
6234
6235
6236
        }
        lock.l_type = F_RDLCK;
        lock.l_whence = SEEK_SET;
        lock.l_start = SHARED_FIRST;
        lock.l_len = divSize;
        if( unixFileLock(pFile, &lock, 10)==(-1) ){
          tErrno = errno;
#if OSLOCKING_CHECK_BUSY_IOERR
          rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
#else
          rc = SQLITE_IOERR_UNLOCK;
#endif
          if( IS_LOCK_ERROR(rc) ){
            pFile->lastErrno = tErrno;
          }
          goto end_unlock;
        }
        lock.l_type = F_UNLCK;
        lock.l_whence = SEEK_SET;
................................................................................
  return SQLITE_OK;
}

/*
** Close the file.
*/
static int nolockClose(sqlite3_file *id) {
  int rc = SQLITE_OK;
  unixFile *pFile = (unixFile *)id;
  unixEnterMutex();
  
  /* unixFile.pInode is always valid here. Otherwise, a different close
   ** routine (e.g. nolockClose()) would be called instead.
   */
  assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 );
  if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){
    /* If there are outstanding locks, do not actually close the file just
     ** yet because that would clear those locks.  Instead, add the file
     ** descriptor to pInode->pUnused list.  It will be automatically closed 
     ** when the last lock is cleared.
     */
    setPendingFd(pFile);
  }
  releaseInodeInfo(pFile);
  rc = closeUnixFile(id);
  unixLeaveMutex();
  return rc;
}

/******************* End of the no-op lock implementation *********************
******************************************************************************/

/******************************************************************************
************************* Begin dot-file Locking ******************************
................................................................................
      }
#endif
    }
  }

  return SQLITE_OK;
}


#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
#include "sqlite3_private.h"
#include <copyfile.h>
static int getDbPathForUnixFile(unixFile *pFile, char *dbPath);
#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];
................................................................................
    fprintf(stderr, "%s lock held by %d\n", zType, (int)lk.l_pid);
#endif
    return 1;
  } 
  return 0;
}





static int unixLockstatePid(unixFile *, pid_t, int *);
















































































#endif /* (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__) */


/*
** If *pArg is inititially negative then this is a query.  Set *pArg to
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
................................................................................
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
  unixFile *pFile = (unixFile*)id;
  switch( op ){
    case SQLITE_FCNTL_LOCKSTATE: {
      *(int*)pArg = pFile->eFileLock;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_LAST_ERRNO: {
      *(int*)pArg = pFile->lastErrno;
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_CHUNK_SIZE: {
      pFile->szChunk = *(int *)pArg;
      return SQLITE_OK;
    }
................................................................................
    */
    case SQLITE_FCNTL_DB_UNCHANGED: {
      ((unixFile*)id)->dbUpdate = 0;
      return SQLITE_OK;
    }
#endif
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
    case SQLITE_FCNTL_SET_LOCKPROXYFILE:
    case SQLITE_FCNTL_GET_LOCKPROXYFILE: {
      return proxyFileControl(id,op,pArg);
    }
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
    case SQLITE_FCNTL_TRUNCATE_DATABASE: {
      return unixTruncateDatabase(pFile, (pArg ? (*(int *)pArg) : 0));
    }
................................................................................

  /* If pShmNode->nRef has reached 0, then close the underlying
  ** shared-memory file, too */
  unixEnterMutex();
  assert( pShmNode->nRef>0 );
  pShmNode->nRef--;
  if( pShmNode->nRef==0 ){
    if( deleteFlag && pShmNode->h>=0 ) {
      if (deleteFlag == 1) { 
        unlink(pShmNode->zFilename);
      } else if (deleteFlag == 2) {
        //ftruncate(pShmNode->h, 32 * 1024);
      }
    }
    
    unixShmPurge(pDbFd);
  }
  unixLeaveMutex();

  return SQLITE_OK;
}

................................................................................

#else
# define unixShmMap     0
# define unixShmLock    0
# define unixShmBarrier 0
# define unixShmUnmap   0
#endif /* #ifndef SQLITE_OMIT_WAL */

#if (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__)
static const char *unixTempFileDir(void);

static int unixInvalidateSupportFiles(unixFile *pFile, int skipWAL) {
  char jPath[MAXPATHLEN+9];
  int zLen = strlcpy(jPath, pFile->zPath, MAXPATHLEN+9);
  if( zLen<MAXPATHLEN ){
    size_t jLen;
    const char extensions[3][9] = { "-wal", "-journal", "-shm" };
    int j = (skipWAL ? 1 : 0);
    for( ; j<3; j++ ){
      
      /* Check to see if the shm file is already opened for this pFile */
      if( j==2 ){
        unixEnterMutex(); /* Because pFile->pInode is shared across threads */
        unixShmNode *pShmNode = pFile->pInode->pShmNode;
        if( pShmNode && !pShmNode->isReadonly ){
          struct stat sStat;
          sqlite3_mutex_enter(pShmNode->mutex);
          
          if( pShmNode->h>=0 && !osFstat(pShmNode->h, &sStat) ){
            unsigned long size = (sStat.st_size<4) ? sStat.st_size : 4;
            if( size>0 ){
              bzero(pShmNode->apRegion[0], size);
              sqlite3_mutex_leave(pShmNode->mutex);
              unixLeaveMutex();
              continue;
            }
          }
          sqlite3_mutex_leave(pShmNode->mutex);
        }
        unixLeaveMutex();
      }
      jLen = strlcpy(&jPath[zLen], extensions[j], 9);
      if( jLen < 9 ){
        int jflags = (j<2) ? O_TRUNC : O_RDWR;
        int jfd = open(jPath, jflags);
        if( jfd==(-1) ){
          if( errno!=ENOENT ){
            perror(jPath);
          }
        } else {
          if( j==2 ){
            struct stat sStat;
            if( !osFstat(jfd, &sStat) ){
              unsigned long size = (sStat.st_size<4) ? sStat.st_size : 4;
              if( size>0 ){
                uint32_t zero = 0;
                pwrite(jfd, &zero, (size_t)size, 0);
              }
            }
          }
          fsync(jfd);
          close(jfd);
        }
      }
    }
  }
  return SQLITE_OK;
}

static int unixTruncateDatabase(unixFile *pFile, int bFlags) {
  sqlite3_file *id = (sqlite3_file *)pFile;
  int rc = SQLITE_OK;
  void *pLock = NULL;
  int flags = 0;
  int corruptFileLock = 0;
  int isCorrupt = 0;
    
#if SQLITE_ENABLE_DATA_PROTECTION
  flags |= pFile->protFlags;
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
  if( isProxyLockingMode(pFile) ){
    flags |= SQLITE_OPEN_AUTOPROXY;
  }
#endif
  
  rc = sqlite3demo_superlock(pFile->zPath, 0, flags, 0, 0, &pLock);
  if( rc ){
    if( rc==SQLITE_CORRUPT || rc==SQLITE_NOTADB ){
      isCorrupt = 1;
      rc = sqlite3demo_superlock_corrupt(id, SQLITE_LOCK_EXCLUSIVE, &corruptFileLock);
    }
    if( rc ){
      return rc;
    }
  }
  if( bFlags!=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 ){
      pFile->lastErrno=errno;
      rc = SQLITE_IOERR;
    }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) ){
        const char pagesize_sql[4][22] = { "pragma page_size=1024", "pragma page_size=2048", "pragma page_size=4096", "pragma page_size=8192" };
        int iPagesize = (((bFlags&SQLITE_TRUNCATE_PAGESIZE_MASK) >> 4) - 1);
        assert( iPagesize>=0 && iPagesize<=4 );
        sql = pagesize_sql[iPagesize];
        trc = sqlite3_exec(tDb, sql, 0, 0, &errmsg);
      }
      if( !trc ){
        const char autovacuum_sql[3][21] = { "pragma auto_vacuum=0", "pragma auto_vacuum=1", "pragma auto_vacuum=2" };
        int iAutovacuum = 2; /* default to incremental */
        if( (bFlags&SQLITE_TRUNCATE_AUTOVACUUM_MASK) ){
          iAutovacuum = (((bFlags&SQLITE_TRUNCATE_AUTOVACUUM_MASK) >> 2) - 1);
        }
        assert( iAutovacuum>=0 && iAutovacuum<=2 );
        sql = autovacuum_sql[iAutovacuum];
        trc = sqlite3_exec(tDb, sql, 0, 0, &errmsg);
      }
      if( !trc && (bFlags&SQLITE_TRUNCATE_JOURNALMODE_WAL) ){
        sql = "pragma journal_mode=wal";
        trc = sqlite3_exec(tDb, sql, 0, 0, &errmsg);
      }
      if( trc ){
        if( !tDb ){
          fprintf(stderr, "failed to open temp database '%s' to reset truncated database %s with flags %x: %d\n", tDbPath, pFile->zPath, bFlags, trc);
        }else{
          fprintf(stderr, "failed to set '%s' on truncated database %s, %d: %s\n", sql, pFile->zPath, trc, errmsg);
        }
      }
      if( tDb ){
        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:
            pFile->lastErrno = 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 ){
    unixInvalidateSupportFiles(pFile, 0);
  }
  pFile->pMethod->xSync(id, SQLITE_SYNC_FULL);


  if( isCorrupt ){
    sqlite3demo_superunlock_corrupt(id, corruptFileLock);
  }else{
    sqlite3demo_superunlock(pLock);
  }
  return rc;
}

/*
 ** Lock locations for shared-memory locks used by WAL mode.
 */
#ifndef SHM_BASE
# define SHM_BASE          120
# define SHM_WRITE         SHM_BASE
# define SHM_CHECKPOINT    (SHM_BASE+1)
# define SHM_RECOVER       (SHM_BASE+2)
# define SHM_READ_FIRST    (SHM_BASE+3)
# define SHM_READ_SIZE     5
#endif /* SHM_BASE */

/*
** This test only works for lock testing on unix/posix VFS.
** Adapted from tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
*/
static int unixLockstatePid(unixFile *pFile, pid_t pid, int *pLockstate){
  int hDb;        /* File descriptor for the open database file */
  int hShm = -1;  /* File descriptor for WAL shared-memory file */
  ssize_t got;    /* Bytes read from header */
  int isWal;                 /* True if in WAL mode */
  int nLock = 0;             /* Number of locks held */
  unsigned char aHdr[100];   /* Database header */
  
  assert(pLockstate);
  
  /* make sure we are dealing with a database file */
  hDb = pFile->h;
  if( hDb<0 ){
    *pLockstate = SQLITE_LOCKSTATE_ERROR;
    return SQLITE_ERROR;
  }
  assert( (strlen(SQLITE_FILE_HEADER)+1)==SQLITE_FILE_HEADER_LEN );
  got = pread(hDb, aHdr, 100, 0);
  if( got<0 ){
    *pLockstate = SQLITE_LOCKSTATE_ERROR;
    return SQLITE_ERROR;
  }
  if( got!=100 || memcmp(aHdr, SQLITE_FILE_HEADER, SQLITE_FILE_HEADER_LEN)!=0 ){
    *pLockstate = SQLITE_LOCKSTATE_NOTADB;
    return SQLITE_NOTADB;
  }
  
  /* First check for an exclusive lock */
  nLock += unixIsLocked(pid, hDb, F_RDLCK, SHARED_FIRST, SHARED_SIZE, "EXCLUSIVE");
  isWal = aHdr[18]==2;
  if( nLock==0 && isWal==0 ){
    /* Rollback mode */
    nLock += unixIsLocked(pid, hDb, F_WRLCK, PENDING_BYTE, SHARED_SIZE+2, "PENDING|RESERVED|SHARED");
  }
  if( nLock==0 && isWal!=0 ){
    /* lookup the file descriptor for the shared memory file if we have it open in this process */
    unixEnterMutex(); /* Because pFile->pInode is shared across threads */
    unixShmNode *pShmNode = pFile->pInode->pShmNode;
    if( pShmNode ){
      sqlite3_mutex_enter(pShmNode->mutex);
      
      hShm = pShmNode->h;
      if( hShm >= 0){
        if( unixIsLocked(pid, hShm, F_RDLCK, SHM_RECOVER, 1, "WAL-RECOVERY") ||
           unixIsLocked(pid, hShm, F_RDLCK, SHM_WRITE, 1, "WAL-WRITE") ){
          nLock = 1;
        }
      }
      
      sqlite3_mutex_leave(pShmNode->mutex);
    } 
    
    if( hShm<0 ){
      /* the shared memory file isn't open in this process space, open our own FD */
      char zShm[MAXPATHLEN];
      
      /* WAL mode */
      strlcpy(zShm, pFile->zPath, MAXPATHLEN);
      strlcat(zShm, "-shm", MAXPATHLEN);
      hShm = open(zShm, O_RDONLY, 0);
      if( hShm<0 ){
        *pLockstate = SQLITE_LOCKSTATE_OFF;
        unixLeaveMutex();
        return SQLITE_OK;
      }
      if( unixIsLocked(pid, hShm, F_RDLCK, SHM_RECOVER, 1, "WAL-RECOVERY") ||
         unixIsLocked(pid, hShm, F_RDLCK, SHM_WRITE, 1, "WAL-WRITE") ){
        nLock = 1;
      }
      close(hShm);
    }
    unixLeaveMutex();
  }
  if( nLock>0 ){
    *pLockstate = SQLITE_LOCKSTATE_ON;
  } else {
    *pLockstate = SQLITE_LOCKSTATE_OFF;
  }
  return SQLITE_OK;
}

#endif /* (SQLITE_ENABLE_APPLE_SPI>0) && defined(__APPLE__) */



/*
** Here ends the implementation of all sqlite3_file methods.
**
********************** End sqlite3_file Methods *******************************
******************************************************************************/

................................................................................
  unixLock,                 /* xLock method */
  unixUnlock,               /* xUnlock method */
  unixCheckReservedLock     /* xCheckReservedLock method */
)
IOMETHODS(
  nolockIoFinder,           /* Finder function name */
  nolockIoMethods,          /* sqlite3_io_methods object name */
  2,                        /* shared memory is enabled */
  nolockClose,              /* xClose method */
  nolockLock,               /* xLock method */
  nolockUnlock,             /* xUnlock method */
  nolockCheckReservedLock   /* xCheckReservedLock method */
)
IOMETHODS(
  dotlockIoFinder,          /* Finder function name */
................................................................................
#endif
  }

  if( pLockingStyle == &posixIoMethods
#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE
    || pLockingStyle == &nfsIoMethods
#endif
     /* support WAL mode on read only mounted filesystem */
    || pLockingStyle == &nolockIoMethods 
  ){
    unixEnterMutex();
    rc = findInodeInfo(pNew, &pNew->pInode);
    if( rc!=SQLITE_OK ){
      /* If an error occured in findInodeInfo(), close the file descriptor
      ** immediately, before releasing the mutex. findInodeInfo() may fail
      ** in two scenarios:
................................................................................
    int useProxy = 0;

    /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means 
    ** never use proxy, NULL means use proxy for non-local files only.  */
    if( envforce!=NULL ){
      useProxy = atoi(envforce)>0;
    }else{













      useProxy = !(fsInfo.f_flags&MNT_LOCAL);
    }
    if( useProxy ){
      rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
      if( rc==SQLITE_OK ){
        /* cache the pMethod in case the transform fails */
        const struct sqlite3_io_methods *pMethod = pFile->pMethods;

Changes to src/sqlite.h.in.

829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848




849
850
851
852
853
854
855
** that the VFS encountered an error while handling the [PRAGMA] and the
** compilation of the PRAGMA fails with an error.  ^The [SQLITE_FCNTL_PRAGMA]
** file control occurs at the beginning of pragma statement analysis and so
** it is able to override built-in [PRAGMA] statements.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE               1
#define SQLITE_GET_LOCKPROXYFILE             2
#define SQLITE_SET_LOCKPROXYFILE             3
#define SQLITE_LAST_ERRNO                    4
#define SQLITE_FCNTL_SIZE_HINT               5
#define SQLITE_FCNTL_CHUNK_SIZE              6
#define SQLITE_FCNTL_FILE_POINTER            7
#define SQLITE_FCNTL_SYNC_OMITTED            8
#define SQLITE_FCNTL_WIN32_AV_RETRY          9
#define SQLITE_FCNTL_PERSIST_WAL            10
#define SQLITE_FCNTL_OVERWRITE              11
#define SQLITE_FCNTL_VFSNAME                12
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE    13
#define SQLITE_FCNTL_PRAGMA                 14





/*
** 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







|
|
|










>
>
>
>







829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
** that the VFS encountered an error while handling the [PRAGMA] and the
** compilation of the PRAGMA fails with an error.  ^The [SQLITE_FCNTL_PRAGMA]
** file control occurs at the beginning of pragma statement analysis and so
** it is able to override built-in [PRAGMA] statements.
** </ul>
*/
#define SQLITE_FCNTL_LOCKSTATE               1
#define SQLITE_FCNTL_GET_LOCKPROXYFILE       2
#define SQLITE_FCNTL_SET_LOCKPROXYFILE       3
#define SQLITE_FCNTL_LAST_ERRNO              4
#define SQLITE_FCNTL_SIZE_HINT               5
#define SQLITE_FCNTL_CHUNK_SIZE              6
#define SQLITE_FCNTL_FILE_POINTER            7
#define SQLITE_FCNTL_SYNC_OMITTED            8
#define SQLITE_FCNTL_WIN32_AV_RETRY          9
#define SQLITE_FCNTL_PERSIST_WAL            10
#define SQLITE_FCNTL_OVERWRITE              11
#define SQLITE_FCNTL_VFSNAME                12
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE    13
#define SQLITE_FCNTL_PRAGMA                 14
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO

/*
** 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

Changes to src/sqlite3_private.h.

33
34
35
36
37
38
39
40








41
42
43










44
45
46
47
48
49
50
51
52
53
** locks by re-using open file descriptors for the database file and support
** files (-shm)
*/
#define SQLITE_FCNTL_LOCKSTATE_PID          103

/*
** Pass the SQLITE_TRUNCATE_DATABASE operation code to sqlite3_file_control() 
** to truncate a database and its associated journal file to zero length.








*/
#define SQLITE_FCNTL_TRUNCATE_DATABASE      101
#define SQLITE_TRUNCATE_DATABASE            SQLITE_FCNTL_TRUNCATE_DATABASE











/*
** Pass the SQLITE_REPLACE_DATABASE operation code to sqlite3_file_control() 
** and a sqlite3 pointer to another open database file to safely copy the 
** contents of that database file into the receiving database.
*/
#define SQLITE_FCNTL_REPLACE_DATABASE       102
#define SQLITE_REPLACE_DATABASE             SQLITE_FCNTL_REPLACE_DATABASE

#endif







|
>
>
>
>
>
>
>
>



>
>
>
>
>
>
>
>
>
>










33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
** locks by re-using open file descriptors for the database file and support
** files (-shm)
*/
#define SQLITE_FCNTL_LOCKSTATE_PID          103

/*
** Pass the SQLITE_TRUNCATE_DATABASE operation code to sqlite3_file_control() 
** to truncate a database and its associated journal file to zero length.  The 
** SQLITE_TRUNCATE_* flags represent optional flags to safely initialize an
** empty database in the place of the truncated database, the flags are passed 
** into sqlite3_file_control via the fourth argument using a pointer to an integer
** configured with the ORed flags.  If the fourth argument is NULL, the default 
** 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_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)
#define SQLITE_TRUNCATE_PAGESIZE_2048             (0x2<<4)
#define SQLITE_TRUNCATE_PAGESIZE_4096             (0x3<<4)
#define SQLITE_TRUNCATE_PAGESIZE_8192             (0x4<<4)

/*
** Pass the SQLITE_REPLACE_DATABASE operation code to sqlite3_file_control() 
** and a sqlite3 pointer to another open database file to safely copy the 
** contents of that database file into the receiving database.
*/
#define SQLITE_FCNTL_REPLACE_DATABASE       102
#define SQLITE_REPLACE_DATABASE             SQLITE_FCNTL_REPLACE_DATABASE

#endif

Changes to src/test1.c.

5064
5065
5066
5067
5068
5069
5070










5071
5072
5073
5074
5075
5076
5077
  return TCL_OK;  
}

#ifdef __APPLE__
/* From sqlite3_priavet.h */
# ifndef SQLITE_TRUNCATE_DATABASE
# define SQLITE_TRUNCATE_DATABASE      101










# endif
# ifndef SQLITE_REPLACE_DATABASE
# define SQLITE_REPLACE_DATABASE       102
# endif

/*
** tclcmd:   file_control_truncate_test DB







>
>
>
>
>
>
>
>
>
>







5064
5065
5066
5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
5086
5087
  return TCL_OK;  
}

#ifdef __APPLE__
/* From sqlite3_priavet.h */
# ifndef SQLITE_TRUNCATE_DATABASE
# define SQLITE_TRUNCATE_DATABASE      101
# 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)
# define SQLITE_TRUNCATE_PAGESIZE_2048             (0x2<<4)
# define SQLITE_TRUNCATE_PAGESIZE_4096             (0x3<<4)
# define SQLITE_TRUNCATE_PAGESIZE_8192             (0x4<<4)
# endif
# ifndef SQLITE_REPLACE_DATABASE
# define SQLITE_REPLACE_DATABASE       102
# endif

/*
** tclcmd:   file_control_truncate_test DB

Changes to src/wal.c.

2648
2649
2650
2651
2652
2653
2654




2655
2656
2657
2658
2659
2660
2661
....
2691
2692
2693
2694
2695
2696
2697






2698


2699
2700
2701
2702
2703

2704





2705
2706
2707
2708

2709
2710
2711
2712
2713
2714
2715
....
2800
2801
2802
2803
2804
2805
2806







2807
2808
2809
2810
2811
2812
2813
....
2842
2843
2844
2845
2846
2847
2848



2849
2850
2851
2852
2853
2854
2855
*/
typedef struct WalWriter {
  Wal *pWal;                   /* The complete WAL information */
  sqlite3_file *pFd;           /* The WAL file to which we write */
  sqlite3_int64 iSyncPoint;    /* Fsync at this offset */
  int syncFlags;               /* Flags for the fsync */
  int szPage;                  /* Size of one page */




} WalWriter;

/*
** Write iAmt bytes of content into the WAL file beginning at iOffset.
** Do a sync when crossing the p->iSyncPoint boundary.
**
** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
................................................................................
  WalWriter *p,               /* Where to write the frame */
  PgHdr *pPage,               /* The page of the frame to be written */
  int nTruncate,              /* The commit flag.  Usually 0.  >0 for commit */
  sqlite3_int64 iOffset       /* Byte offset at which to write */
){
  int rc;                         /* Result code from subfunctions */
  void *pData;                    /* Data actually written */






  u8 aFrame[WAL_FRAME_HDRSIZE];   /* Buffer to assemble frame-header in */


#if defined(SQLITE_HAS_CODEC)
  if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
#else
  pData = pPage->pData;
#endif

  walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);





  rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
  if( rc ) return rc;
  /* Write the page data */
  rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));

  return rc;
}

/* 
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
*/
................................................................................
  w.pWal = pWal;
  w.pFd = pWal->pWalFd;
  w.iSyncPoint = 0;
  w.syncFlags = sync_flags;
  w.szPage = szPage;
  iOffset = walFrameOffset(iFrame+1, szPage);
  szFrame = szPage + WAL_FRAME_HDRSIZE;








  /* Write all frames into the log file exactly once */
  for(p=pList; p; p=p->pDirty){
    int nDbSize;   /* 0 normally.  Positive == commit flag */
    iFrame++;
    assert( iOffset==walFrameOffset(iFrame, szPage) );
    nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
................................................................................
        nExtra++;
      }
    }else{
      rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
    }
  }




  /* If this frame set completes the first transaction in the WAL and
  ** if PRAGMA journal_size_limit is set, then truncate the WAL to the
  ** journal size limit, if possible.
  */
  if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
    i64 sz = pWal->mxWalSize;
    if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){







>
>
>
>







 







>
>
>
>
>
>

>
>





>

>
>
>
>
>




>







 







>
>
>
>
>
>
>







 







>
>
>







2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
....
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
....
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
....
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
*/
typedef struct WalWriter {
  Wal *pWal;                   /* The complete WAL information */
  sqlite3_file *pFd;           /* The WAL file to which we write */
  sqlite3_int64 iSyncPoint;    /* Fsync at this offset */
  int syncFlags;               /* Flags for the fsync */
  int szPage;                  /* Size of one page */
#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)
  void *aFrameBuf;             /* Frame buffer */
  size_t szFrameBuf;           /* Size of frame buffer */
#endif
} WalWriter;

/*
** Write iAmt bytes of content into the WAL file beginning at iOffset.
** Do a sync when crossing the p->iSyncPoint boundary.
**
** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
................................................................................
  WalWriter *p,               /* Where to write the frame */
  PgHdr *pPage,               /* The page of the frame to be written */
  int nTruncate,              /* The commit flag.  Usually 0.  >0 for commit */
  sqlite3_int64 iOffset       /* Byte offset at which to write */
){
  int rc;                         /* Result code from subfunctions */
  void *pData;                    /* Data actually written */
#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)
  void *aFrame;

  assert(sizeof(p->aFrameBuf) == (p->szPage + WAL_FRAME_HDRSIZE));
  aFrame = p->aFrameBuf;
#else
  u8 aFrame[WAL_FRAME_HDRSIZE];   /* Buffer to assemble frame-header in */
#endif
  
#if defined(SQLITE_HAS_CODEC)
  if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
#else
  pData = pPage->pData;
#endif

  walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
  
#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)
  memcpy(&aFrame[WAL_FRAME_HDRSIZE], pData, p->szPage);
  rc = walWriteToLog(p, aFrame, (p->szPage + WAL_FRAME_HDRSIZE), iOffset);
#else
  rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
  if( rc ) return rc;
  /* Write the page data */
  rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
#endif
  return rc;
}

/* 
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
*/
................................................................................
  w.pWal = pWal;
  w.pFd = pWal->pWalFd;
  w.iSyncPoint = 0;
  w.syncFlags = sync_flags;
  w.szPage = szPage;
  iOffset = walFrameOffset(iFrame+1, szPage);
  szFrame = szPage + WAL_FRAME_HDRSIZE;
#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)
  w.aFrameBuf = (void *)malloc(szFrame);
  if( NULL==w.aFrameBuf ){
    return SQLITE_NOMEM;
  }
#endif


  /* Write all frames into the log file exactly once */
  for(p=pList; p; p=p->pDirty){
    int nDbSize;   /* 0 normally.  Positive == commit flag */
    iFrame++;
    assert( iOffset==walFrameOffset(iFrame, szPage) );
    nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
................................................................................
        nExtra++;
      }
    }else{
      rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
    }
  }

#if defined(SQLITE_WRITE_WALFRAME_PREBUFFERED)
  free(w.aFrameBuf);
#endif
  /* If this frame set completes the first transaction in the WAL and
  ** if PRAGMA journal_size_limit is set, then truncate the WAL to the
  ** journal size limit, if possible.
  */
  if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
    i64 sz = pWal->mxWalSize;
    if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){

Changes to test/8_3_names.test.

142
143
144
145
146
147
148


149
150
151
152
153
154
155
##########################################################################
# WAL mode.
#
ifcapable !wal {
  finish_test
  return
}


db close
forcedelete test.db
do_test 8_3_names-5.0 {
  sqlite3 db file:./test.db?8_3_names=1
  register_wholenumber_module db
  db eval {
    PRAGMA journal_mode=WAL;







>
>







142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
##########################################################################
# WAL mode.
#
ifcapable !wal {
  finish_test
  return
}
if ![wal_is_ok] { finish_test; return }

db close
forcedelete test.db
do_test 8_3_names-5.0 {
  sqlite3 db file:./test.db?8_3_names=1
  register_wholenumber_module db
  db eval {
    PRAGMA journal_mode=WAL;

Changes to test/attach4.test.

76
77
78
79
80
81
82

83
84
85
86
87
88
89
foreach {name f} $files {
  if {[permutation] == "journaltest"} {
    set mode delete
  } else {
    set mode wal
  }
  ifcapable !wal { set mode delete }

  lappend L $mode
  append S "
    PRAGMA $name.journal_mode = WAL;
    UPDATE $name.tbl SET x = '$name';
  "
}
do_execsql_test 1.5 $S $L







>







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
foreach {name f} $files {
  if {[permutation] == "journaltest"} {
    set mode delete
  } else {
    set mode wal
  }
  ifcapable !wal { set mode delete }
  if ![wal_is_ok] { set mode delete }
  lappend L $mode
  append S "
    PRAGMA $name.journal_mode = WAL;
    UPDATE $name.tbl SET x = '$name';
  "
}
do_execsql_test 1.5 $S $L

Changes to test/capi3.test.

897
898
899
900
901
902
903

904
905
906
907
908
909
910
911
912
913

914
915
916
917
918
919
920
} {0 {}}
do_test capi3-11.9.3 {
  sqlite3_get_autocommit $DB
} 1
do_test capi3-11.10 {
  sqlite3_step $STMT
} {SQLITE_ERROR}

do_test capi3-11.11 {
  sqlite3_step $STMT
} {SQLITE_ROW}
do_test capi3-11.12 {
  sqlite3_step $STMT
  sqlite3_step $STMT
} {SQLITE_DONE}
do_test capi3-11.13 {
  sqlite3_finalize $STMT
} {SQLITE_OK}

do_test capi3-11.14 {
  execsql {
    SELECT a FROM t2;
  }
} {1 2}
do_test capi3-11.14.1 {
  sqlite3_get_autocommit $DB







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







897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
} {0 {}}
do_test capi3-11.9.3 {
  sqlite3_get_autocommit $DB
} 1
do_test capi3-11.10 {
  sqlite3_step $STMT
} {SQLITE_ERROR}
ifcapable {autoreset} {
  do_test capi3-11.11 {
    sqlite3_step $STMT
  } {SQLITE_ROW}
  do_test capi3-11.12 {
    sqlite3_step $STMT
    sqlite3_step $STMT
  } {SQLITE_DONE}
  do_test capi3-11.13 {
    sqlite3_finalize $STMT
  } {SQLITE_OK}
}
do_test capi3-11.14 {
  execsql {
    SELECT a FROM t2;
  }
} {1 2}
do_test capi3-11.14.1 {
  sqlite3_get_autocommit $DB

Changes to test/capi3c.test.

852
853
854
855
856
857
858

859
860
861
862
863
864
865
866
867
868

869
870
871
872
873
874
875
} {0 {}}
do_test capi3c-11.9.3 {
  sqlite3_get_autocommit $DB
} 1
do_test capi3c-11.10 {
  sqlite3_step $STMT
} {SQLITE_ABORT}

do_test capi3c-11.11 {
  sqlite3_step $STMT
} {SQLITE_ROW}
do_test capi3c-11.12 {
  sqlite3_step $STMT
  sqlite3_step $STMT
} {SQLITE_DONE}
do_test capi3c-11.13 {
  sqlite3_finalize $STMT
} {SQLITE_OK}

do_test capi3c-11.14 {
  execsql {
    SELECT a FROM t2;
  }
} {1 2}
do_test capi3c-11.14.1 {
  sqlite3_get_autocommit $DB







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







852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
} {0 {}}
do_test capi3c-11.9.3 {
  sqlite3_get_autocommit $DB
} 1
do_test capi3c-11.10 {
  sqlite3_step $STMT
} {SQLITE_ABORT}
ifcapable {autoreset} {
  do_test capi3c-11.11 {
    sqlite3_step $STMT
  } {SQLITE_ROW}
  do_test capi3c-11.12 {
    sqlite3_step $STMT
    sqlite3_step $STMT
  } {SQLITE_DONE}
  do_test capi3c-11.13 {
    sqlite3_finalize $STMT
  } {SQLITE_OK}
}
do_test capi3c-11.14 {
  execsql {
    SELECT a FROM t2;
  }
} {1 2}
do_test capi3c-11.14.1 {
  sqlite3_get_autocommit $DB

Changes to test/incrvacuum2.test.

130
131
132
133
134
135
136


137
138
139
140
141
142
143
    DELETE FROM abc;
    PRAGMA incremental_vacuum;
    COMMIT;
  }
} {}

integrity_check incrvacuum2-3.3



ifcapable wal {
  # At one point, when a specific page was being extracted from the b-tree
  # free-list (e.g. during an incremental-vacuum), all trunk pages that
  # occurred before the specific page in the free-list trunk were being
  # written to the journal or wal file. This is not necessary. Only the 
  # extracted page and the page that contains the pointer to it need to







>
>







130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
    DELETE FROM abc;
    PRAGMA incremental_vacuum;
    COMMIT;
  }
} {}

integrity_check incrvacuum2-3.3

if ![wal_is_ok] { finish_test; return }

ifcapable wal {
  # At one point, when a specific page was being extracted from the b-tree
  # free-list (e.g. during an incremental-vacuum), all trunk pages that
  # occurred before the specific page in the free-list trunk were being
  # written to the journal or wal file. This is not necessary. Only the 
  # extracted page and the page that contains the pointer to it need to

Changes to test/pager1.test.

1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
....
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
....
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
....
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
#   $sql: SQL to execute.
#   $res: Expected result of executing $sql.
#   $js:  The expected size of the journal file, in bytes, after executing
#         the SQL script. Or -1 if the journal is not expected to exist.
#   $ws:  The expected size of the WAL file, in bytes, after executing
#         the SQL script. Or -1 if the WAL is not expected to exist.
#
ifcapable wal {
  faultsim_delete_and_reopen
  foreach {tn sql res js ws} [subst {
  
    1  {
      CREATE TABLE t1(a, b);
      PRAGMA auto_vacuum=OFF;
      PRAGMA synchronous=NORMAL;
................................................................................
do_test pager1-20.2.2 {
  execsql {
    BEGIN EXCLUSIVE;
    COMMIT;
  }
} {}

ifcapable wal {
  do_test pager1-20.3.1 {
    faultsim_delete_and_reopen
    db func a_string a_string
    execsql {
      PRAGMA cache_size = 10;
      PRAGMA journal_mode = wal;
      BEGIN;
................................................................................

#-------------------------------------------------------------------------
# Test that a WAL database may not be opened if:
#
#   pager1-21.1.*: The VFS has an iVersion less than 2, or
#   pager1-21.2.*: The VFS does not provide xShmXXX() methods.
#
ifcapable wal {
  do_test pager1-21.0 {
    faultsim_delete_and_reopen
    execsql {
      PRAGMA journal_mode = WAL;
      CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def');
      INSERT INTO ko DEFAULT VALUES;
    }
................................................................................

#-------------------------------------------------------------------------
# Test that attempting to open a write-transaction with 
# locking_mode=exclusive in WAL mode fails if there are other clients on 
# the same database.
#
catch { db close }
ifcapable wal {
  do_multiclient_test tn {
    do_test pager1-28.$tn.1 {
      sql1 { 
        PRAGMA journal_mode = WAL;
        CREATE TABLE t1(a, b);
        INSERT INTO t1 VALUES('a', 'b');
      }







|







 







|







 







|







 







|







1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
....
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
....
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
....
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
#   $sql: SQL to execute.
#   $res: Expected result of executing $sql.
#   $js:  The expected size of the journal file, in bytes, after executing
#         the SQL script. Or -1 if the journal is not expected to exist.
#   $ws:  The expected size of the WAL file, in bytes, after executing
#         the SQL script. Or -1 if the WAL is not expected to exist.
#
if {$::sqlite_options(wal) && [wal_is_ok]} {
  faultsim_delete_and_reopen
  foreach {tn sql res js ws} [subst {
  
    1  {
      CREATE TABLE t1(a, b);
      PRAGMA auto_vacuum=OFF;
      PRAGMA synchronous=NORMAL;
................................................................................
do_test pager1-20.2.2 {
  execsql {
    BEGIN EXCLUSIVE;
    COMMIT;
  }
} {}

if {$::sqlite_options(wal) && [wal_is_ok]} {
  do_test pager1-20.3.1 {
    faultsim_delete_and_reopen
    db func a_string a_string
    execsql {
      PRAGMA cache_size = 10;
      PRAGMA journal_mode = wal;
      BEGIN;
................................................................................

#-------------------------------------------------------------------------
# Test that a WAL database may not be opened if:
#
#   pager1-21.1.*: The VFS has an iVersion less than 2, or
#   pager1-21.2.*: The VFS does not provide xShmXXX() methods.
#
if {$::sqlite_options(wal) && [wal_is_ok]} {
  do_test pager1-21.0 {
    faultsim_delete_and_reopen
    execsql {
      PRAGMA journal_mode = WAL;
      CREATE TABLE ko(c DEFAULT 'abc', b DEFAULT 'def');
      INSERT INTO ko DEFAULT VALUES;
    }
................................................................................

#-------------------------------------------------------------------------
# Test that attempting to open a write-transaction with 
# locking_mode=exclusive in WAL mode fails if there are other clients on 
# the same database.
#
catch { db close }
if {$::sqlite_options(wal) && [wal_is_ok]} {
  do_multiclient_test tn {
    do_test pager1-28.$tn.1 {
      sql1 { 
        PRAGMA journal_mode = WAL;
        CREATE TABLE t1(a, b);
        INSERT INTO t1 VALUES('a', 'b');
      }

Changes to test/stat.test.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
register_dbstat_vtab db
do_execsql_test stat-0.0 {
  PRAGMA auto_vacuum = OFF;
  CREATE VIRTUAL TABLE temp.stat USING dbstat;
  SELECT * FROM stat;
} {}

ifcapable wal {
  do_execsql_test stat-0.1 {
    PRAGMA journal_mode = WAL;
    PRAGMA journal_mode = delete;
    SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
      FROM stat;
  } {wal delete sqlite_master / 1 leaf 0 0 916 0}
}







|







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
register_dbstat_vtab db
do_execsql_test stat-0.0 {
  PRAGMA auto_vacuum = OFF;
  CREATE VIRTUAL TABLE temp.stat USING dbstat;
  SELECT * FROM stat;
} {}

if {$::sqlite_options(wal) && [wal_is_ok]} {
  do_execsql_test stat-0.1 {
    PRAGMA journal_mode = WAL;
    PRAGMA journal_mode = delete;
    SELECT name, path, pageno, pagetype, ncell, payload, unused, mx_payload
      FROM stat;
  } {wal delete sqlite_master / 1 leaf 0 0 916 0}
}

Changes to test/superlock.test.

38
39
40
41
42
43
44





45
46
47
48
49
50
51
#        handler returns 0 before said clients relinquish their locks.
#
#   6.*: Test that if a superlocked WAL database is overwritten, existing
#        clients run the recovery to build the new wal-index after the 
#        superlock is released.
#        
#






do_execsql_test 1.1 {
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
  PRAGMA journal_mode = DELETE;
} {delete}








>
>
>
>
>







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#        handler returns 0 before said clients relinquish their locks.
#
#   6.*: Test that if a superlocked WAL database is overwritten, existing
#        clients run the recovery to build the new wal-index after the 
#        superlock is released.
#        
#

if {[forced_proxy_locking]} {
  finish_test 
  return 
}

do_execsql_test 1.1 {
  CREATE TABLE t1(a, b);
  INSERT INTO t1 VALUES(1, 2);
  PRAGMA journal_mode = DELETE;
} {delete}

Changes to test/tkt-2d1a5c67d.test.

16
17
18
19
20
21
22

23
24
25
26
27
28
29
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tkt-2d1a5c67d

ifcapable {!wal || !vtab} {finish_test; return}


for {set ii 1} {$ii<=10} {incr ii} {
  do_test tkt-2d1a5c67d.1.$ii {
    db close
    forcedelete test.db test.db-wal
    sqlite3 db test.db
    db eval "PRAGMA cache_size=$::ii"







>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix tkt-2d1a5c67d

ifcapable {!wal || !vtab} {finish_test; return}
if {![wal_is_ok]} {finish_test; return}

for {set ii 1} {$ii<=10} {incr ii} {
  do_test tkt-2d1a5c67d.1.$ii {
    db close
    forcedelete test.db test.db-wal
    sqlite3 db test.db
    db eval "PRAGMA cache_size=$::ii"

Changes to test/tkt-313723c356.test.

15
16
17
18
19
20
21

22
23
24
25
26
27
28
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

ifcapable !wal { finish_test ; return }


do_execsql_test tkt-313723c356.1 {
  PRAGMA page_size = 1024;
  PRAGMA journal_mode = WAL;
  CREATE TABLE t1(a, b);
  CREATE INDEX i1 ON t1(a, b);
  INSERT INTO t1 VALUES(randomblob(400), randomblob(400));







>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

ifcapable !wal { finish_test ; return }
if ![wal_is_ok] { finish_test; return }

do_execsql_test tkt-313723c356.1 {
  PRAGMA page_size = 1024;
  PRAGMA journal_mode = WAL;
  CREATE TABLE t1(a, b);
  CREATE INDEX i1 ON t1(a, b);
  INSERT INTO t1 VALUES(randomblob(400), randomblob(400));

Changes to test/tkt-5d863f876e.test.

13
14
15
16
17
18
19




20
21
22
23
24
25
26
# This file implements tests to verify that ticket [5d863f876e] has been
# fixed.  
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl





do_multiclient_test tn {
  do_test $tn.1 {
    sql1 {
      CREATE TABLE t1(a, b);
      CREATE INDEX i1 ON t1(a, b);
      INSERT INTO t1 VALUES(1, 2);







>
>
>
>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# This file implements tests to verify that ticket [5d863f876e] has been
# fixed.  
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl

ifcapable !wal { finish_test; return }
if ![wal_is_ok] { finish_test; return }


do_multiclient_test tn {
  do_test $tn.1 {
    sql1 {
      CREATE TABLE t1(a, b);
      CREATE INDEX i1 ON t1(a, b);
      INSERT INTO t1 VALUES(1, 2);

Changes to test/wal2.test.

18
19
20
21
22
23
24

25
26
27
28
29
30
31
..
65
66
67
68
69
70
71







72
73
74
75
76
77
78
...
938
939
940
941
942
943
944



945
946
947
948
949
950
951
....
1037
1038
1039
1040
1041
1042
1043





1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
....
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106




1107
1108
1109
1110
1111
1112
1113
1114
1115
1116



1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129



1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
....
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196





1197
1198
1199
1200
1201
1202
1203
....
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl

set testprefix wal2

ifcapable !wal {finish_test ; return }


set sqlite_sync_count 0
proc cond_incr_sync_count {adj} {
  global sqlite_sync_count
  if {$::tcl_platform(platform) == "windows"} {
    incr sqlite_sync_count $adj
  } {
................................................................................
proc incr_tvfs_hdr {file idx incrval} {
  set ints [set_tvfs_hdr $file]
  set v [lindex $ints $idx]
  incr v $incrval
  lset ints $idx $v
  set_tvfs_hdr $file $ints
}









#-------------------------------------------------------------------------
# Test case wal2-1.*:
#
# Set up a small database containing a single table. The database is not
# checkpointed during the test - all content resides in the log file.
................................................................................
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    PRAGMA wal_checkpoint;
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
  }



  faultsim_save_and_close
} {}
do_test wal2-10.1.2 {
  faultsim_restore_and_reopen
  execsql { SELECT * FROM t1 }
} {1 2 3 4}
do_test wal2-10.1.3 {
................................................................................

#-------------------------------------------------------------------------
# If a connection is required to create a WAL or SHM file, it creates 
# the new files with the same file-system permissions as the database 
# file itself. Test this.
#
if {$::tcl_platform(platform) == "unix"} {





  faultsim_delete_and_reopen
  # Changed on 2012-02-13: umask is deliberately ignored for -wal files.
  #set umask [exec /bin/sh -c umask]
  set umask 0
  

  do_test wal2-12.1 {
    sqlite3 db test.db
    execsql { 
      CREATE TABLE tx(y, z);
      PRAGMA journal_mode = WAL;
    }
    db close
    list [file exists test.db-wal] [file exists test.db-shm]
  } {0 0}
  
  foreach {tn permissions} {
   1 00644
   2 00666
   3 00600
   4 00755
................................................................................
  } {
    set effective [format %.5o [expr $permissions & ~$umask]]
    do_test wal2-12.2.$tn.1 {
      file attributes test.db -permissions $permissions
      file attributes test.db -permissions
    } $permissions
    do_test wal2-12.2.$tn.2 {
      list [file exists test.db-wal] [file exists test.db-shm]
    } {0 0}
    do_test wal2-12.2.$tn.3 {
      sqlite3 db test.db
      execsql { INSERT INTO tx DEFAULT VALUES }
      list [file exists test.db-wal] [file exists test.db-shm]
    } {1 1}
    do_test wal2-12.2.$tn.4 {
      list [file attr test.db-wal -perm] [file attr test.db-shm -perm]
    } [list $effective $effective]
    do_test wal2-12.2.$tn.5 {
      db close
      list [file exists test.db-wal] [file exists test.db-shm]
    } {0 0}
  }
}

#-------------------------------------------------------------------------
# Test the libraries response to discovering that one or more of the
# database, wal or shm files cannot be opened, or can only be opened
# read-only.
#
if {$::tcl_platform(platform) == "unix"} {
  proc perm {} {
    set L [list]
    foreach f {test.db test.db-wal test.db-shm} {
      if {[file exists $f]} {
        lappend L [file attr $f -perm]
      } else {
        lappend L {}
      }
    }
    set L
  }





  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    PRAGMA wal_checkpoint;
    INSERT INTO t1 VALUES('3.14', '2.72');
  }
  do_test wal2-13.1.1 {
    list [file exists test.db-shm] [file exists test.db-wal]
  } {1 1}



  faultsim_save_and_close

  foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} {
    2   00644   00644   00644   1   1   1
    3   00644   00400   00644   1   1   0
    4   00644   00644   00400   1   0   0
    5   00400   00644   00644   1   1   0

    7   00644   00000   00644   1   0   0
    8   00644   00644   00000   1   0   0
    9   00000   00644   00644   0   0   0
  } {
    faultsim_restore



    do_test wal2-13.$tn.1 {
      file attr test.db     -perm $db_perm
      file attr test.db-wal -perm $wal_perm
      file attr test.db-shm -perm $shm_perm

      set     L [file attr test.db -perm]
      lappend L [file attr test.db-wal -perm]
      lappend L [file attr test.db-shm -perm]
    } [list $db_perm $wal_perm $shm_perm]

    # If $can_open is true, then it should be possible to open a database
    # handle. Otherwise, if $can_open is 0, attempting to open the db
    # handle throws an "unable to open database file" exception.
    #
    set r(1) {0 ok}
................................................................................
    catch { db close }
  }
}

#-------------------------------------------------------------------------
# Test that "PRAGMA checkpoint_fullsync" appears to be working.
#
foreach {tn sql reslist} {
  1 { }                                 {10 0 4 0 6 0}
  2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2}
  3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0}
} {
  faultsim_delete_and_reopen

  execsql {PRAGMA auto_vacuum = 0}
  execsql $sql
  do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 }   {}
  do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0






  do_execsql_test wal2-14.$tn.2 {
    PRAGMA wal_autocheckpoint = 10;
    CREATE TABLE t1(a, b);                -- 2 wal syncs
    INSERT INTO t1 VALUES(1, 2);          -- 2 wal sync
    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
    BEGIN;
................................................................................
    COMMIT;                               -- 2 wal sync
    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
  } {10 0 3 3 0 1 1}

  do_test wal2-14.$tn.3 {
    cond_incr_sync_count 1
    list $sqlite_sync_count $sqlite_fullsync_count
  } [lrange $reslist 0 1]

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  do_test wal2-14.$tn.4 {
    execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) }
    list $sqlite_sync_count $sqlite_fullsync_count
  } [lrange $reslist 2 3]

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  do_test wal2-14.$tn.5 {
    execsql { PRAGMA wal_autocheckpoint = 1000 }
    execsql { INSERT INTO t1 VALUES(9, 10) }
    execsql { INSERT INTO t1 VALUES(11, 12) }
    execsql { INSERT INTO t1 VALUES(13, 14) }
    db close
    list $sqlite_sync_count $sqlite_fullsync_count
  } [lrange $reslist 4 5]
}

catch { db close }

# PRAGMA checkpoint_fullsync
# PRAGMA fullfsync
# PRAGMA synchronous







>







 







>
>
>
>
>
>
>







 







>
>
>







 







>
>
>
>
>













|







 







|




|


|



|












|









>
>
>
>








|

>
>
>













>
>
>



|



|







 







|
|
|
|










>
>
>
>
>







 







|







|











|







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
...
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
....
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
....
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
....
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
....
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl

set testprefix wal2

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] {finish_test ; return }

set sqlite_sync_count 0
proc cond_incr_sync_count {adj} {
  global sqlite_sync_count
  if {$::tcl_platform(platform) == "windows"} {
    incr sqlite_sync_count $adj
  } {
................................................................................
proc incr_tvfs_hdr {file idx incrval} {
  set ints [set_tvfs_hdr $file]
  set v [lindex $ints $idx]
  incr v $incrval
  lset ints $idx $v
  set_tvfs_hdr $file $ints
}

set shmpath test.db-shm
if {[forced_proxy_locking]} {
  sqlite3 db test.db
  set shmpath [execsql { pragma lock_proxy_file }]-shm
  db close
}


#-------------------------------------------------------------------------
# Test case wal2-1.*:
#
# Set up a small database containing a single table. The database is not
# checkpointed during the test - all content resides in the log file.
................................................................................
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    PRAGMA wal_checkpoint;
    INSERT INTO t1 VALUES(1, 2);
    INSERT INTO t1 VALUES(3, 4);
  }
  if {[forced_proxy_locking]} {
    forcecopy $shmpath sv_test.db-shm
  }
  faultsim_save_and_close
} {}
do_test wal2-10.1.2 {
  faultsim_restore_and_reopen
  execsql { SELECT * FROM t1 }
} {1 2 3 4}
do_test wal2-10.1.3 {
................................................................................

#-------------------------------------------------------------------------
# If a connection is required to create a WAL or SHM file, it creates 
# the new files with the same file-system permissions as the database 
# file itself. Test this.
#
if {$::tcl_platform(platform) == "unix"} {
  if {[forced_proxy_locking]} {
    # faultsim_delete_and_reopen doesn't know about the shm file redirect...
    forcedelete $shmpath
  }
  
  faultsim_delete_and_reopen
  # Changed on 2012-02-13: umask is deliberately ignored for -wal files.
  #set umask [exec /bin/sh -c umask]
  set umask 0
  

  do_test wal2-12.1 {
    sqlite3 db test.db
    execsql { 
      CREATE TABLE tx(y, z);
      PRAGMA journal_mode = WAL;
    }
    db close
    list [file exists test.db-wal] [file exists $shmpath]
  } {0 0}
  
  foreach {tn permissions} {
   1 00644
   2 00666
   3 00600
   4 00755
................................................................................
  } {
    set effective [format %.5o [expr $permissions & ~$umask]]
    do_test wal2-12.2.$tn.1 {
      file attributes test.db -permissions $permissions
      file attributes test.db -permissions
    } $permissions
    do_test wal2-12.2.$tn.2 {
      list [file exists test.db-wal] [file exists $shmpath]
    } {0 0}
    do_test wal2-12.2.$tn.3 {
      sqlite3 db test.db
      execsql { INSERT INTO tx DEFAULT VALUES }
      list [file exists test.db-wal] [file exists $shmpath]
    } {1 1}
    do_test wal2-12.2.$tn.4 {
      list [file attr test.db-wal -perm] [file attr $shmpath -perm]
    } [list $effective $effective]
    do_test wal2-12.2.$tn.5 {
      db close
      list [file exists test.db-wal] [file exists $shmpath]
    } {0 0}
  }
}

#-------------------------------------------------------------------------
# Test the libraries response to discovering that one or more of the
# database, wal or shm files cannot be opened, or can only be opened
# read-only.
#
if {$::tcl_platform(platform) == "unix"} {
  proc perm {} {
    set L [list]
    foreach f {test.db test.db-wal $shmpath} {
      if {[file exists $f]} {
        lappend L [file attr $f -perm]
      } else {
        lappend L {}
      }
    }
    set L
  }

  if {[forced_proxy_locking]} {
    # faultsim_delete_and_reopen doesn't know about the shm file redirect...
    forcedelete $shmpath
  }
  faultsim_delete_and_reopen
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE t1(a, b);
    PRAGMA wal_checkpoint;
    INSERT INTO t1 VALUES('3.14', '2.72');
  }
  do_test wal2-13.1.1 {
    list [file exists $shmpath] [file exists test.db-wal]
  } {1 1}
  if {[forced_proxy_locking]} {
    forcecopy $shmpath proxysv_test.db-shm 
  }
  faultsim_save_and_close

  foreach {tn db_perm wal_perm shm_perm can_open can_read can_write} {
    2   00644   00644   00644   1   1   1
    3   00644   00400   00644   1   1   0
    4   00644   00644   00400   1   0   0
    5   00400   00644   00644   1   1   0

    7   00644   00000   00644   1   0   0
    8   00644   00644   00000   1   0   0
    9   00000   00644   00644   0   0   0
  } {
    faultsim_restore
    if {[forced_proxy_locking]} {
      forcecopy proxysv_test.db-shm $shmpath  
    }
    do_test wal2-13.$tn.1 {
      file attr test.db     -perm $db_perm
      file attr test.db-wal -perm $wal_perm
      file attr $shmpath -perm $shm_perm

      set     L [file attr test.db -perm]
      lappend L [file attr test.db-wal -perm]
      lappend L [file attr $shmpath -perm]
    } [list $db_perm $wal_perm $shm_perm]

    # If $can_open is true, then it should be possible to open a database
    # handle. Otherwise, if $can_open is 0, attempting to open the db
    # handle throws an "unable to open database file" exception.
    #
    set r(1) {0 ok}
................................................................................
    catch { db close }
  }
}

#-------------------------------------------------------------------------
# Test that "PRAGMA checkpoint_fullsync" appears to be working.
#
foreach {tn sql reslist altreslist} {
  1 { }                                 {10 0 4 0 6 0} {7 4 3 2 3 2}
  2 { PRAGMA checkpoint_fullfsync = 1 } {10 4 4 2 6 2} {7 4 3 2 3 2}
  3 { PRAGMA checkpoint_fullfsync = 0 } {10 0 4 0 6 0} {7 0 3 0 3 0}
} {
  faultsim_delete_and_reopen

  execsql {PRAGMA auto_vacuum = 0}
  execsql $sql
  do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 }   {}
  do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  set useres $reslist
  if $::sqlite_options(default_wal_safetylevel) {
    set useres $altreslist
  }

  do_execsql_test wal2-14.$tn.2 {
    PRAGMA wal_autocheckpoint = 10;
    CREATE TABLE t1(a, b);                -- 2 wal syncs
    INSERT INTO t1 VALUES(1, 2);          -- 2 wal sync
    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
    BEGIN;
................................................................................
    COMMIT;                               -- 2 wal sync
    PRAGMA wal_checkpoint;                -- 1 wal sync, 1 db sync
  } {10 0 3 3 0 1 1}

  do_test wal2-14.$tn.3 {
    cond_incr_sync_count 1
    list $sqlite_sync_count $sqlite_fullsync_count
  } [lrange $useres 0 1]

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  do_test wal2-14.$tn.4 {
    execsql { INSERT INTO t1 VALUES(7, zeroblob(12*4096)) }
    list $sqlite_sync_count $sqlite_fullsync_count
  } [lrange $useres 2 3]

  set sqlite_sync_count 0
  set sqlite_fullsync_count 0

  do_test wal2-14.$tn.5 {
    execsql { PRAGMA wal_autocheckpoint = 1000 }
    execsql { INSERT INTO t1 VALUES(9, 10) }
    execsql { INSERT INTO t1 VALUES(11, 12) }
    execsql { INSERT INTO t1 VALUES(13, 14) }
    db close
    list $sqlite_sync_count $sqlite_fullsync_count
  } [lrange $useres 4 5]
}

catch { db close }

# PRAGMA checkpoint_fullsync
# PRAGMA fullfsync
# PRAGMA synchronous

Changes to test/wal3.test.

15
16
17
18
19
20
21

22
23
24
25
26
27
28
...
190
191
192
193
194
195
196
197
198

199
200
201

202
203

204
205
206
207
208
209





210
211
212
213
214
215
216
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
source $testdir/malloc_common.tcl
ifcapable !wal {finish_test ; return }


set a_string_counter 1
proc a_string {n} {
  global a_string_counter
  incr a_string_counter
  string range [string repeat "${a_string_counter}." $n] 1 $n
}
................................................................................
#   CREATE TABLE x(y);
#   INSERT INTO x VALUES('z');
#   PRAGMA wal_checkpoint;
#
# in WAL mode the xSync method is invoked as expected for each of
# synchronous=off, synchronous=normal and synchronous=full.
#
foreach {tn syncmode synccount} {
  1 off     

    {}
  2 normal  
    {test.db-wal normal test.db normal}

  3 full    
    {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}

} {

  proc sync_counter {args} { 
    foreach {method filename id flags} $args break
    lappend ::syncs [file tail $filename] $flags
  }





  do_test wal3-3.$tn {
    forcedelete test.db test.db-wal test.db-journal
  
    testvfs T
    T filter {} 
    T script sync_counter
    sqlite3 db test.db -vfs T
................................................................................
    execsql {
      CREATE TABLE x(y);
      INSERT INTO x VALUES('z');
      PRAGMA wal_checkpoint;
    }
    T filter {}
    set ::syncs
  } $synccount

  db close
  T delete
}

#-------------------------------------------------------------------------
# When recovering the contents of a WAL file, a process obtains the WRITER







>







 







|

>



>


>






>
>
>
>
>







 







|







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
source $testdir/malloc_common.tcl
ifcapable !wal {finish_test ; return }
if ![wal_is_ok] {finish_test ; return }

set a_string_counter 1
proc a_string {n} {
  global a_string_counter
  incr a_string_counter
  string range [string repeat "${a_string_counter}." $n] 1 $n
}
................................................................................
#   CREATE TABLE x(y);
#   INSERT INTO x VALUES('z');
#   PRAGMA wal_checkpoint;
#
# in WAL mode the xSync method is invoked as expected for each of
# synchronous=off, synchronous=normal and synchronous=full.
#
foreach {tn syncmode synccount altsynccount} {
  1 off     
    {}
    {}
  2 normal  
    {test.db-wal normal test.db normal}
    {test.db-wal full test.db full}
  3 full    
    {test.db-wal normal test.db-wal normal test.db-wal normal test.db normal}
    {test.db-wal normal test.db-wal normal test.db-wal full test.db full}
} {

  proc sync_counter {args} { 
    foreach {method filename id flags} $args break
    lappend ::syncs [file tail $filename] $flags
  }
  set usecount $synccount
  if $::sqlite_options(default_wal_safetylevel) {
    set usecount $altsynccount
  }

  do_test wal3-3.$tn {
    forcedelete test.db test.db-wal test.db-journal
  
    testvfs T
    T filter {} 
    T script sync_counter
    sqlite3 db test.db -vfs T
................................................................................
    execsql {
      CREATE TABLE x(y);
      INSERT INTO x VALUES('z');
      PRAGMA wal_checkpoint;
    }
    T filter {}
    set ::syncs
  } $usecount

  db close
  T delete
}

#-------------------------------------------------------------------------
# When recovering the contents of a WAL file, a process obtains the WRITER

Changes to test/wal5.test.

14
15
16
17
18
19
20

21
22
23
24
25
26
27
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
ifcapable !wal {finish_test ; return }


set testprefix wal5

proc db_page_count  {{file test.db}} { expr [file size $file] / 1024 }
proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 }









>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl
ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }

set testprefix wal5

proc db_page_count  {{file test.db}} { expr [file size $file] / 1024 }
proc wal_page_count {{file test.db}} { wal_frame_count ${file}-wal 1024 }


Changes to test/wal7.test.

12
13
14
15
16
17
18

19
20
21
22
23
24
25
# focus of this file is testing the PRAGMA journal_size_limit when
# in WAL mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }


# Case 1:  No size limit.  Journal can get large.
#
do_test wal7-1.0 {
  db close
  forcedelete test.db
  sqlite3 db test.db







>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# focus of this file is testing the PRAGMA journal_size_limit when
# in WAL mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }

# Case 1:  No size limit.  Journal can get large.
#
do_test wal7-1.0 {
  db close
  forcedelete test.db
  sqlite3 db test.db

Changes to test/wal8.test.

22
23
24
25
26
27
28


29
30
31
32
33
34
35
# size from the database file as soon as it is opened (even before the
# first read transaction is executed), and the "PRAGMA page_size = XXX"
# is a no-op.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix wal8



db close
forcedelete test.db test.db-wal

sqlite3 db test.db
sqlite3 db2 test.db








>
>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# size from the database file as soon as it is opened (even before the
# first read transaction is executed), and the "PRAGMA page_size = XXX"
# is a no-op.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set ::testprefix wal8
ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }

db close
forcedelete test.db test.db-wal

sqlite3 db test.db
sqlite3 db2 test.db

Changes to test/walbak.test.

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
source $testdir/tester.tcl
source $testdir/wal_common.tcl
source $testdir/malloc_common.tcl

do_not_use_codec

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}


# Test organization:
# 
#   walback-1.*: Simple tests.
#
#   walback-2.*: Test backups when the source db is modified mid-backup.







|
<
<
<







17
18
19
20
21
22
23
24



25
26
27
28
29
30
31
source $testdir/tester.tcl
source $testdir/wal_common.tcl
source $testdir/malloc_common.tcl

do_not_use_codec

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } { finish_test ; return }





# Test organization:
# 
#   walback-1.*: Simple tests.
#
#   walback-2.*: Test backups when the source db is modified mid-backup.

Changes to test/walbig.test.

17
18
19
20
21
22
23

24
25
26
27
28
29
30
set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {
  finish_test
  return
}


# Do not use a codec for this file, as the database is manipulated using
# external methods (the [fake_big_file] and [hexio_write] commands).
#
do_not_use_codec

# If SQLITE_DISABLE_LFS is defined, omit this file.







>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {
  finish_test
  return
}
if ![wal_is_ok] { finish_test; return }

# Do not use a codec for this file, as the database is manipulated using
# external methods (the [fake_big_file] and [hexio_write] commands).
#
do_not_use_codec

# If SQLITE_DISABLE_LFS is defined, omit this file.

Changes to test/walcksum.test.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

# Read and return the contents of file $filename. Treat the content as
# binary data.
#
proc readfile {filename} {
  set fd [open $filename]
  fconfigure $fd -encoding binary







|
<
<
<







12
13
14
15
16
17
18
19



20
21
22
23
24
25
26

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }




# Read and return the contents of file $filename. Treat the content as
# binary data.
#
proc readfile {filename} {
  set fd [open $filename]
  fconfigure $fd -encoding binary

Changes to test/walcrash.test.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# walcrash-3.*: Recover multiple databases where the failed transaction 
#               was a multi-file transaction.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

db close

set seed 0
set REPEATS 100

# walcrash-1.*







|
<
<
<







24
25
26
27
28
29
30
31



32
33
34
35
36
37
38
# walcrash-3.*: Recover multiple databases where the failed transaction 
#               was a multi-file transaction.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }




db close

set seed 0
set REPEATS 100

# walcrash-1.*

Changes to test/walcrash3.test.

14
15
16
17
18
19
20


21
22
23
24
25
26
27
# truncates the WAL file if "PRAGMA journal_size_limit" is configured.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {finish_test ; return }


set testprefix walcrash3

db close
testvfs tvfs
tvfs filter {xTruncate xWrite}
tvfs script tvfs_callback
proc tvfs_callback {args} {}







>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# truncates the WAL file if "PRAGMA journal_size_limit" is configured.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }

set testprefix walcrash3

db close
testvfs tvfs
tvfs filter {xTruncate xWrite}
tvfs script tvfs_callback
proc tvfs_callback {args} {}

Changes to test/walfault.test.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
source $testdir/lock_common.tcl

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

#-------------------------------------------------------------------------
# This test case, walfault-1-*, simulates faults while executing a
#
#   PRAGMA journal_mode = WAL;
#
# statement immediately after creating a new database.







|
<
<
<







15
16
17
18
19
20
21
22



23
24
25
26
27
28
29

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl
source $testdir/lock_common.tcl

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }




#-------------------------------------------------------------------------
# This test case, walfault-1-*, simulates faults while executing a
#
#   PRAGMA journal_mode = WAL;
#
# statement immediately after creating a new database.

Changes to test/walhook.test.

18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

set ::wal_hook [list]
proc wal_hook {zDb nEntry} {
  lappend ::wal_hook $zDb $nEntry
  return 0
}
db wal_hook wal_hook







|
<
<
<







18
19
20
21
22
23
24
25



26
27
28
29
30
31
32
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/wal_common.tcl

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }




set ::wal_hook [list]
proc wal_hook {zDb nEntry} {
  lappend ::wal_hook $zDb $nEntry
  return 0
}
db wal_hook wal_hook

Changes to test/walnoshm.test.

13
14
15
16
17
18
19

20
21
22
23
24
25
26
# using the xShm primitives if the connection is in exclusive-mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix walnoshm
ifcapable !wal {finish_test ; return }


db close
testvfs tvfsshm
testvfs tvfs -default 1 -iversion 1 
sqlite3 db test.db

#--------------------------------------------------------------------------







>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# using the xShm primitives if the connection is in exclusive-mode.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix walnoshm
ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }

db close
testvfs tvfsshm
testvfs tvfs -default 1 -iversion 1 
sqlite3 db test.db

#--------------------------------------------------------------------------

Changes to test/walpersist.test.

17
18
19
20
21
22
23






24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
..
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
source $testdir/lock_common.tcl
set ::testprefix walpersist

ifcapable !wal {
  finish_test
  return
}







do_test walpersist-1.0 {
  db eval {
    PRAGMA journal_mode=WAL;
    CREATE TABLE t1(a);
    INSERT INTO t1 VALUES(randomblob(5000));
  }
  file exists test.db-wal
} {1}
do_test walpersist-1.1 {
  file exists test.db-shm
} {1}
do_test walpersist-1.2 {
  db close
  list [file exists test.db] [file exists test.db-wal] [file exists test.db-shm]
} {1 0 0}
do_test walpersist-1.3 {
  sqlite3 db test.db
  db eval {SELECT length(a) FROM t1}
} {5000}
do_test walpersist-1.4 {
  list [file exists test.db] [file exists test.db-wal] [file exists test.db-shm]
} {1 1 1}
do_test walpersist-1.5 {
  file_control_persist_wal db -1
} {0 0}
do_test walpersist-1.6 {
  file_control_persist_wal db 1
} {0 1}
................................................................................
  file_control_persist_wal db -1
} {0 0}
do_test walpersist-1.10 {
  file_control_persist_wal db 1
} {0 1}
do_test walpersist-1.11 {
  db close
  list [file exists test.db] [file exists test.db-wal] [file exists test.db-shm]
} {1 1 1}

# Make sure the journal_size_limit works to limit the size of the
# persisted wal file.  In persistent-wal mode, any non-negative
# journal_size_limit causes the WAL file to be truncated to zero bytes
# when closing.
#
forcedelete test.db test.db-shm test.db-wal
do_test walpersist-2.1 {
  sqlite3 db test.db
  db eval {
    PRAGMA journal_mode=WAL;
    PRAGMA wal_autocheckpoint=OFF;
    PRAGMA journal_size_limit=12000;
    CREATE TABLE t1(x);
................................................................................
do_test walpersist-2.3 {
  sqlite3 db test.db
  execsql { PRAGMA integrity_check }
} {ok}

do_test 3.1 {
  catch {db close}
  forcedelete test.db test.db-shm test.db-wal
  sqlite3 db test.db
  execsql {
    PRAGMA page_size = 1024;
    PRAGMA journal_mode = WAL;
    PRAGMA wal_autocheckpoint=128;
    PRAGMA journal_size_limit=16384;
    CREATE TABLE t1(a, b, PRIMARY KEY(a, b));







>
>
>
>
>
>










|



|






|







 







|







|







 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
..
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
source $testdir/lock_common.tcl
set ::testprefix walpersist

ifcapable !wal {
  finish_test
  return
}
if ![wal_is_ok] { finish_test; return }

set shmpath test.db-shm
if {[forced_proxy_locking]} {
  set shmpath [execsql { pragma lock_proxy_file }]-shm
}

do_test walpersist-1.0 {
  db eval {
    PRAGMA journal_mode=WAL;
    CREATE TABLE t1(a);
    INSERT INTO t1 VALUES(randomblob(5000));
  }
  file exists test.db-wal
} {1}
do_test walpersist-1.1 {
  file exists $shmpath
} {1}
do_test walpersist-1.2 {
  db close
  list [file exists test.db] [file exists test.db-wal] [file exists $shmpath]
} {1 0 0}
do_test walpersist-1.3 {
  sqlite3 db test.db
  db eval {SELECT length(a) FROM t1}
} {5000}
do_test walpersist-1.4 {
  list [file exists test.db] [file exists test.db-wal] [file exists $shmpath]
} {1 1 1}
do_test walpersist-1.5 {
  file_control_persist_wal db -1
} {0 0}
do_test walpersist-1.6 {
  file_control_persist_wal db 1
} {0 1}
................................................................................
  file_control_persist_wal db -1
} {0 0}
do_test walpersist-1.10 {
  file_control_persist_wal db 1
} {0 1}
do_test walpersist-1.11 {
  db close
  list [file exists test.db] [file exists test.db-wal] [file exists $shmpath]
} {1 1 1}

# Make sure the journal_size_limit works to limit the size of the
# persisted wal file.  In persistent-wal mode, any non-negative
# journal_size_limit causes the WAL file to be truncated to zero bytes
# when closing.
#
forcedelete test.db $shmpath test.db-wal
do_test walpersist-2.1 {
  sqlite3 db test.db
  db eval {
    PRAGMA journal_mode=WAL;
    PRAGMA wal_autocheckpoint=OFF;
    PRAGMA journal_size_limit=12000;
    CREATE TABLE t1(x);
................................................................................
do_test walpersist-2.3 {
  sqlite3 db test.db
  execsql { PRAGMA integrity_check }
} {ok}

do_test 3.1 {
  catch {db close}
  forcedelete test.db $shmpath test.db-wal
  sqlite3 db test.db
  execsql {
    PRAGMA page_size = 1024;
    PRAGMA journal_mode = WAL;
    PRAGMA wal_autocheckpoint=128;
    PRAGMA journal_size_limit=16384;
    CREATE TABLE t1(a, b, PRIMARY KEY(a, b));

Changes to test/walro.test.

26
27
28
29
30
31
32








33
34
35
36
37
38
39
..
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
..
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167


168

# And only if the build is WAL-capable.
#
ifcapable !wal {
  finish_test
  return
}









do_multiclient_test tn {
  # Do not run tests with the connections in the same process.
  #
  if {$tn==2} continue
  
  # Close all connections and delete the database.
................................................................................
  do_test 1.1.1 {
    code2 { sqlite3 db2 test.db }
    sql2 { 
      PRAGMA journal_mode = WAL;
      CREATE TABLE t1(x, y);
      INSERT INTO t1 VALUES('a', 'b');
    }
    file exists test.db-shm
  } {1}

  do_test 1.1.2 {
    file attributes test.db-shm -permissions r--r--r--
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
  } {}

  do_test 1.1.3 { sql1 "SELECT * FROM t1" }                {a b}
  do_test 1.1.4 { sql2 "INSERT INTO t1 VALUES('c', 'd')" } {}
  do_test 1.1.5 { sql1 "SELECT * FROM t1" }                {a b c d}

................................................................................
  } {}
  do_test 1.1.12 { sql1 "SELECT * FROM t1" }                {a b c d e f g h}
  do_test 1.1.13  { sql2 "INSERT INTO t1 VALUES('i', 'j')" } {}

  do_test 1.2.1 {
    code2 { db2 close }
    code1 { db close }
    list [file exists test.db-wal] [file exists test.db-shm]
  } {1 1}
  do_test 1.2.2 {
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    sql1 { SELECT * FROM t1 }
  } {a b c d e f g h i j}

  do_test 1.2.3 {
    code1 { db close }
    file attributes test.db-shm -permissions rw-r--r--
    hexio_write test.db-shm 0 01020304 
    file attributes test.db-shm -permissions r--r--r--
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    csql1 { SELECT * FROM t1 }
  } {1 {attempt to write a readonly database}}
  do_test 1.2.4 {
    code1 { sqlite3_extended_errcode db } 
  } {SQLITE_READONLY_RECOVERY}

  do_test 1.2.5 {
    file attributes test.db-shm -permissions rw-r--r--
    code2 { sqlite3 db2 test.db }
    sql2 "SELECT * FROM t1" 
  } {a b c d e f g h i j}
  file attributes test.db-shm -permissions r--r--r--
  do_test 1.2.6 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j}

  do_test 1.2.7 { 
    sql2 {
      PRAGMA wal_checkpoint;
      INSERT INTO t1 VALUES('k', 'l');
    }
................................................................................
  } {1 {unable to open database file}}

  # Also test that if the -shm file can be opened for read/write access,
  # it is not if readonly_shm=1 is present in the URI.
  do_test 1.3.2.1 {
    code1 { db close }
    code2 { db2 close }
    file exists test.db-shm
  } {0}
  do_test 1.3.2.2 {
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    csql1 { SELECT * FROM sqlite_master }
  } {1 {unable to open database file}}
  do_test 1.3.2.3 {
    code1 { db close }
    close [open test.db-shm w]
    file attributes test.db-shm -permissions r--r--r--
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    csql1 { SELECT * FROM t1 }
  } {1 {attempt to write a readonly database}}
  do_test 1.3.2.4 {
    code1 { sqlite3_extended_errcode db } 
  } {SQLITE_READONLY_RECOVERY}
}



finish_test







>
>
>
>
>
>
>
>







 







|



|







 







|








|
|
|








|



|







 







|







|
|








>
>

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
...
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

# And only if the build is WAL-capable.
#
ifcapable !wal {
  finish_test
  return
}
if ![wal_is_ok] { finish_test; return }

set shmpath test.db-shm
if {[forced_proxy_locking]} {
  sqlite3 db test.db
  set shmpath [execsql { pragma lock_proxy_file }]-shm
  db close
}

do_multiclient_test tn {
  # Do not run tests with the connections in the same process.
  #
  if {$tn==2} continue
  
  # Close all connections and delete the database.
................................................................................
  do_test 1.1.1 {
    code2 { sqlite3 db2 test.db }
    sql2 { 
      PRAGMA journal_mode = WAL;
      CREATE TABLE t1(x, y);
      INSERT INTO t1 VALUES('a', 'b');
    }
    file exists $shmpath
  } {1}

  do_test 1.1.2 {
    file attributes $shmpath -permissions r--r--r--
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
  } {}

  do_test 1.1.3 { sql1 "SELECT * FROM t1" }                {a b}
  do_test 1.1.4 { sql2 "INSERT INTO t1 VALUES('c', 'd')" } {}
  do_test 1.1.5 { sql1 "SELECT * FROM t1" }                {a b c d}

................................................................................
  } {}
  do_test 1.1.12 { sql1 "SELECT * FROM t1" }                {a b c d e f g h}
  do_test 1.1.13  { sql2 "INSERT INTO t1 VALUES('i', 'j')" } {}

  do_test 1.2.1 {
    code2 { db2 close }
    code1 { db close }
    list [file exists test.db-wal] [file exists $shmpath]
  } {1 1}
  do_test 1.2.2 {
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    sql1 { SELECT * FROM t1 }
  } {a b c d e f g h i j}

  do_test 1.2.3 {
    code1 { db close }
    file attributes $shmpath -permissions rw-r--r--
    hexio_write $shmpath 0 01020304
    file attributes $shmpath -permissions r--r--r--
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    csql1 { SELECT * FROM t1 }
  } {1 {attempt to write a readonly database}}
  do_test 1.2.4 {
    code1 { sqlite3_extended_errcode db } 
  } {SQLITE_READONLY_RECOVERY}

  do_test 1.2.5 {
    file attributes $shmpath -permissions rw-r--r--
    code2 { sqlite3 db2 test.db }
    sql2 "SELECT * FROM t1" 
  } {a b c d e f g h i j}
  file attributes $shmpath -permissions r--r--r--
  do_test 1.2.6 { sql1 "SELECT * FROM t1" } {a b c d e f g h i j}

  do_test 1.2.7 { 
    sql2 {
      PRAGMA wal_checkpoint;
      INSERT INTO t1 VALUES('k', 'l');
    }
................................................................................
  } {1 {unable to open database file}}

  # Also test that if the -shm file can be opened for read/write access,
  # it is not if readonly_shm=1 is present in the URI.
  do_test 1.3.2.1 {
    code1 { db close }
    code2 { db2 close }
    file exists $shmpath
  } {0}
  do_test 1.3.2.2 {
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    csql1 { SELECT * FROM sqlite_master }
  } {1 {unable to open database file}}
  do_test 1.3.2.3 {
    code1 { db close }
    close [open $shmpath w]
    file attributes $shmpath -permissions r--r--r--
    code1 { sqlite3 db file:test.db?readonly_shm=1 }
    csql1 { SELECT * FROM t1 }
  } {1 {attempt to write a readonly database}}
  do_test 1.3.2.4 {
    code1 { sqlite3_extended_errcode db } 
  } {SQLITE_READONLY_RECOVERY}
}

forcedelete $shmpath

finish_test

Changes to test/walshared.test.

13
14
15
16
17
18
19

20
21
22
23
24
25
26
# "PRAGMA journal_mode=WAL" mode with shared-cache turned on.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {finish_test ; return }


db close
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]

sqlite3 db  test.db
sqlite3 db2 test.db








>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# "PRAGMA journal_mode=WAL" mode with shared-cache turned on.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }

db close
set ::enable_shared_cache [sqlite3_enable_shared_cache 1]

sqlite3 db  test.db
sqlite3 db2 test.db

Changes to test/walslow.test.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# brute force methods, so may take a while to run.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

proc reopen_db {} {
  catch { db close }
  forcedelete test.db test.db-wal
  sqlite3 db test.db
  execsql { PRAGMA journal_mode = wal }
}







|
<
<
<







14
15
16
17
18
19
20
21



22
23
24
25
26
27
28
# brute force methods, so may take a while to run.
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable !wal {finish_test ; return }
if ![wal_is_ok] { finish_test; return }




proc reopen_db {} {
  catch { db close }
  forcedelete test.db test.db-wal
  sqlite3 db test.db
  execsql { PRAGMA journal_mode = wal }
}

Changes to test/walthread.test.

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

set testdir [file dirname $argv0]

source $testdir/tester.tcl
source $testdir/lock_common.tcl
if {[run_thread_tests]==0} { finish_test ; return }
ifcapable !wal             { finish_test ; return }
if { ![wal_is_ok] } {
  finish_test 
  return 
}

set sqlite_walsummary_mmap_incr 64

# How long, in seconds, to run each test for. If a test is set to run for
# 0 seconds, it is omitted entirely.
#
unset -nocomplain seconds







|
<
<
<







15
16
17
18
19
20
21
22



23
24
25
26
27
28
29

set testdir [file dirname $argv0]

source $testdir/tester.tcl
source $testdir/lock_common.tcl
if {[run_thread_tests]==0} { finish_test ; return }
ifcapable !wal             { finish_test ; return }
if ![wal_is_ok] { finish_test; return }




set sqlite_walsummary_mmap_incr 64

# How long, in seconds, to run each test for. If a test is set to run for
# 0 seconds, it is omitted entirely.
#
unset -nocomplain seconds