SQLite

Check-in [9b230c43df]
Login

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

Overview
Comment:Support compile-time option SQLITE_OMIT_WAL, for building without WAL support.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | wal
Files: files | file ages | folders
SHA1: 9b230c43dfa112e3e1589f9775926807bd36b36e
User & Date: dan 2010-05-01 16:40:20.000
Context
2010-05-01
17:50
Rework mutexes on the SHM implemention for os_unix to avoid a deadlock during WAL recovery. (check-in: 1a0f69bef2 user: drh tags: wal)
16:40
Support compile-time option SQLITE_OMIT_WAL, for building without WAL support. (check-in: 9b230c43df user: dan tags: wal)
11:33
Fix a typo in the walmode.test test script introducted yesterday. (check-in: dcabc90ca5 user: drh tags: wal)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
2257
2258
2259
2260
2261
2262
2263









2264
2265
2266
2267
2268
2269
2270
    int pageSize;
    int usableSize;
    u8 *page1 = pPage1->aData;
    rc = SQLITE_NOTADB;
    if( memcmp(page1, zMagicHeader, 16)!=0 ){
      goto page1_init_failed;
    }









    if( page1[18]>2 ){
      pBt->readOnly = 1;
    }
    if( page1[19]>2 ){
      goto page1_init_failed;
    }








>
>
>
>
>
>
>
>
>







2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
    int pageSize;
    int usableSize;
    u8 *page1 = pPage1->aData;
    rc = SQLITE_NOTADB;
    if( memcmp(page1, zMagicHeader, 16)!=0 ){
      goto page1_init_failed;
    }

#ifdef SQLITE_OMIT_WAL
    if( page1[18]>1 ){
      pBt->readOnly = 1;
    }
    if( page1[19]>1 ){
      goto page1_init_failed;
    }
#else
    if( page1[18]>2 ){
      pBt->readOnly = 1;
    }
    if( page1[19]>2 ){
      goto page1_init_failed;
    }

2283
2284
2285
2286
2287
2288
2289

2290
2291
2292
2293
2294
2295
2296
        goto page1_init_failed;
      }else if( isOpen==0 ){
        releasePage(pPage1);
        return SQLITE_OK;
      }
      rc = SQLITE_NOTADB;
    }


    /* The maximum embedded fraction must be exactly 25%.  And the minimum
    ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
    ** The original design allowed these amounts to vary, but as of
    ** version 3.6.0, we require them to be fixed.
    */
    if( memcmp(&page1[21], "\100\040\040",3)!=0 ){







>







2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
        goto page1_init_failed;
      }else if( isOpen==0 ){
        releasePage(pPage1);
        return SQLITE_OK;
      }
      rc = SQLITE_NOTADB;
    }
#endif

    /* The maximum embedded fraction must be exactly 25%.  And the minimum
    ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
    ** The original design allowed these amounts to vary, but as of
    ** version 3.6.0, we require them to be fixed.
    */
    if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
Changes to src/main.c.
1191
1192
1193
1194
1195
1196
1197

1198
1199
1200
1201
1202
1203
1204



1205
1206
1207
1208
1209
1210
1211
** into the write-ahead-log by this database connection.
*/
void *sqlite3_wal_hook(
  sqlite3 *db,                    /* Attach the hook to this db handle */
  int(*xCallback)(void *, sqlite3*, const char*, int),
  void *pArg                      /* First argument passed to xCallback() */
){

  void *pRet;
  sqlite3_mutex_enter(db->mutex);
  pRet = db->pWalArg;
  db->xWalCallback = xCallback;
  db->pWalArg = pArg;
  sqlite3_mutex_leave(db->mutex);
  return pRet;



}

/*
** This function returns true if main-memory should be used instead of
** a temporary file for transient pager files and statement journals.
** The value returned depends on the value of db->temp_store (runtime
** parameter) and the compile time value of SQLITE_TEMP_STORE. The







>







>
>
>







1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
** into the write-ahead-log by this database connection.
*/
void *sqlite3_wal_hook(
  sqlite3 *db,                    /* Attach the hook to this db handle */
  int(*xCallback)(void *, sqlite3*, const char*, int),
  void *pArg                      /* First argument passed to xCallback() */
){
#ifndef SQLITE_OMIT_WAL
  void *pRet;
  sqlite3_mutex_enter(db->mutex);
  pRet = db->pWalArg;
  db->xWalCallback = xCallback;
  db->pWalArg = pArg;
  sqlite3_mutex_leave(db->mutex);
  return pRet;
#else
  return 0;
#endif
}

/*
** This function returns true if main-memory should be used instead of
** a temporary file for transient pager files and statement journals.
** The value returned depends on the value of db->temp_store (runtime
** parameter) and the compile time value of SQLITE_TEMP_STORE. The
Changes to src/os_unix.c.
4558
4559
4560
4561
4562
4563
4564


4565
4566
4567
4568
4569
4570
4571
static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
  UNUSED_PARAMETER(NotUsed);
  UNUSED_PARAMETER(NotUsed2);
  UNUSED_PARAMETER(NotUsed3);
  return 0;
}



/* Forward reference */
typedef struct unixShm unixShm;
typedef struct unixShmFile unixShmFile;

/*
** Object used to represent a single file opened and mmapped to provide
** shared memory.  When multiple threads all reference the same







>
>







4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
  UNUSED_PARAMETER(NotUsed);
  UNUSED_PARAMETER(NotUsed2);
  UNUSED_PARAMETER(NotUsed3);
  return 0;
}

#ifndef SQLITE_OMIT_WAL

/* Forward reference */
typedef struct unixShm unixShm;
typedef struct unixShmFile unixShmFile;

/*
** Object used to represent a single file opened and mmapped to provide
** shared memory.  When multiple threads all reference the same
5330
5331
5332
5333
5334
5335
5336









5337
5338
5339
5340
5341
5342
5343
/*
** Delete a shared-memory segment from the system.
*/
static int unixShmDelete(sqlite3_vfs *pVfs, const char *zName){
  return pVfs->xDelete(pVfs, zName, 0);
}











/*
************************ End of sqlite3_vfs methods ***************************
******************************************************************************/

/******************************************************************************
************************** Begin Proxy Locking ********************************







>
>
>
>
>
>
>
>
>







5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
/*
** Delete a shared-memory segment from the system.
*/
static int unixShmDelete(sqlite3_vfs *pVfs, const char *zName){
  return pVfs->xDelete(pVfs, zName, 0);
}

#else
# define unixShmOpen    0
# define unixShmSize    0
# define unixShmGet     0
# define unixShmRelease 0
# define unixShmLock    0
# define unixShmClose   0
# define unixShmDelete  0
#endif /* #ifndef SQLITE_OMIT_WAL */

/*
************************ End of sqlite3_vfs methods ***************************
******************************************************************************/

/******************************************************************************
************************** Begin Proxy Locking ********************************
Changes to src/pager.c.
395
396
397
398
399
400
401

402

403
404
405
406
407
408
409
  void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */
  void (*xCodecFree)(void*);             /* Destructor for the codec */
  void *pCodec;               /* First argument to xCodec... methods */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */
  sqlite3_backup *pBackup;    /* Pointer to list of ongoing backup processes */

  Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */

};

/*
** The following global variables hold counters used for
** testing purposes only.  These variables do not exist in
** a non-testing build.  These variables are not thread-safe.
*/







>

>







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */
  void (*xCodecFree)(void*);             /* Destructor for the codec */
  void *pCodec;               /* First argument to xCodec... methods */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */
  sqlite3_backup *pBackup;    /* Pointer to list of ongoing backup processes */
#ifndef SQLITE_OMIT_WAL
  Wal *pWal;                  /* Write-ahead log used by "journal_mode=wal" */
#endif
};

/*
** The following global variables hold counters used for
** testing purposes only.  These variables do not exist in
** a non-testing build.  These variables are not thread-safe.
*/
1188
1189
1190
1191
1192
1193
1194

1195
1196
1197






1198
1199
1200
1201
1202
1203
1204
  return rc;
}

/*
** Return true if this pager uses a write-ahead log instead of the usual
** rollback journal. Otherwise false.
*/

static int pagerUseWal(Pager *pPager){
  return (pPager->pWal!=0);
}







/*
** Unlock the database file. This function is a no-op if the pager
** is in exclusive mode.
**
** If the pager is currently in error state, discard the contents of 
** the cache and reset the Pager structure internal state. If there is







>



>
>
>
>
>
>







1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
  return rc;
}

/*
** Return true if this pager uses a write-ahead log instead of the usual
** rollback journal. Otherwise false.
*/
#ifndef SQLITE_OMIT_WAL
static int pagerUseWal(Pager *pPager){
  return (pPager->pWal!=0);
}
#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
# define pagerWalFrames(v,w,x,y,z) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
#endif

/*
** Unlock the database file. This function is a no-op if the pager
** is in exclusive mode.
**
** If the pager is currently in error state, discard the contents of 
** the cache and reset the Pager structure internal state. If there is
2238
2239
2240
2241
2242
2243
2244

2245
2246
2247
2248
2249
2250
2251
  IOTRACE(("PGIN %p %d\n", pPager, pgno));
  PAGERTRACE(("FETCH %d page %d hash(%08x)\n",
               PAGERID(pPager), pgno, pager_pagehash(pPg)));

  return rc;
}


/*
** This function is invoked once for each page that has already been 
** written into the log file when a WAL transaction is rolled back.
** Parameter iPg is the page number of said page. The pCtx argument 
** is actually a pointer to the Pager structure.
**
** If page iPg is present in the cache, and has no outstanding references,







>







2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
  IOTRACE(("PGIN %p %d\n", pPager, pgno));
  PAGERTRACE(("FETCH %d page %d hash(%08x)\n",
               PAGERID(pPager), pgno, pager_pagehash(pPg)));

  return rc;
}

#ifndef SQLITE_OMIT_WAL
/*
** This function is invoked once for each page that has already been 
** written into the log file when a WAL transaction is rolled back.
** Parameter iPg is the page number of said page. The pCtx argument 
** is actually a pointer to the Pager structure.
**
** If page iPg is present in the cache, and has no outstanding references,
2307
2308
2309
2310
2311
2312
2313













































































































2314
2315
2316
2317
2318
2319
2320
    rc = pagerUndoCallback((void *)pPager, pList->pgno);
    pList = pNext;
  }

  return rc;
}














































































































/*
** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback
** the entire master journal file. The case pSavepoint==NULL occurs when 
** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction 
** savepoint.
**
** When pSavepoint is not NULL (meaning a non-transaction savepoint is 







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







2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
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
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
    rc = pagerUndoCallback((void *)pPager, pList->pgno);
    pList = pNext;
  }

  return rc;
}

/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
** changed. 
*/ 
static int pagerWalFrames(
  Pager *pPager,                  /* Pager object */
  PgHdr *pList,                   /* List of frames to log */
  Pgno nTruncate,                 /* Database size after this commit */
  int isCommit,                   /* True if this is a commit */
  int sync_flags                  /* Flags to pass to OsSync() (or 0) */
){
  int rc;                         /* Return code */

  assert( pPager->pWal );
  rc = sqlite3WalFrames(pPager->pWal, 
      pPager->pageSize, pList, nTruncate, isCommit, sync_flags
  );
  if( rc==SQLITE_OK && pPager->pBackup ){
    PgHdr *p;
    for(p=pList; p; p=p->pDirty){
      sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
    }
  }
  return rc;
}

/*
** Open a WAL snapshot on the log file this pager is connected to.
*/
static int pagerOpenSnapshot(Pager *pPager){
  int rc;                         /* Return code */
  int changed = 0;                /* True if cache must be reset */

  assert( pagerUseWal(pPager) );

  rc = sqlite3WalOpenSnapshot(pPager->pWal, &changed);
  if( rc==SQLITE_OK ){
    int dummy;
    if( changed ){
      pager_reset(pPager);
      assert( pPager->errCode || pPager->dbSizeValid==0 );
    }
    rc = sqlite3PagerPagecount(pPager, &dummy);
  }
  pPager->state = PAGER_SHARED;

  return rc;
}

/*
** Check if the *-wal file that corresponds to the database opened by pPager
** exists. Assuming no error occurs, set *pExists to 1 if the file exists,
** or 0 otherwise and return SQLITE_OK. If an IO or OOM error occurs, return
** an SQLite error code.
*/
static int pagerHasWAL(Pager *pPager, int *pExists){
  int rc;                         /* Return code */

  if( !pPager->tempFile ){
    char *zWal = sqlite3_mprintf("%s-wal", pPager->zFilename);
    if( !zWal ){
      rc = SQLITE_NOMEM;
    }else{
      rc = sqlite3OsAccess(pPager->pVfs, zWal, SQLITE_ACCESS_EXISTS, pExists);
      sqlite3_free(zWal);
    }
  }else{
    rc = SQLITE_OK;
    *pExists = 0;
  }
  return rc;
}

/*
** Check if the *-wal file that corresponds to the database opened by pPager
** exists. If it does, open the pager in WAL mode. Otherwise, if no error
** occurs, make sure Pager.journalMode is not set to PAGER_JOURNALMODE_WAL.
** If an IO or OOM error occurs, return an SQLite error code.
**
** If the WAL file is opened, also open a snapshot (read transaction).
**
** The caller must hold a SHARED lock on the database file to call this
** function. Because an EXCLUSIVE lock on the db file is required to delete 
** a WAL, this ensures there is no race condition between the xAccess() 
** below and an xDelete() being executed by some other connection.
*/
static int pagerOpenWalIfPresent(Pager *pPager){
  int rc = SQLITE_OK;
  if( !pPager->tempFile ){
    int isWal;                    /* True if WAL file exists */
    rc = pagerHasWAL(pPager, &isWal);
    if( rc==SQLITE_OK ){
      if( isWal ){
        pager_reset(pPager);
        rc = sqlite3PagerOpenWal(pPager, 0);
        if( rc==SQLITE_OK ){
          rc = pagerOpenSnapshot(pPager);
        }
      }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
        pPager->journalMode = PAGER_JOURNALMODE_DELETE;
      }
    }
  }
  return rc;
}
#endif

/*
** Playback savepoint pSavepoint. Or, if pSavepoint==NULL, then playback
** the entire master journal file. The case pSavepoint==NULL occurs when 
** a ROLLBACK TO command is invoked on a SAVEPOINT that is a transaction 
** savepoint.
**
** When pSavepoint is not NULL (meaning a non-transaction savepoint is 
2930
2931
2932
2933
2934
2935
2936

2937
2938
2939
2940

2941
2942
2943
2944
2945
2946
2947
int sqlite3PagerClose(Pager *pPager){
  u8 *pTmp = (u8 *)pPager->pTmpSpace;

  disable_simulated_io_errors();
  sqlite3BeginBenignMalloc();
  pPager->errCode = 0;
  pPager->exclusiveMode = 0;

  sqlite3WalClose(pPager->pWal, pPager->fd, 
    (pPager->noSync ? 0 : pPager->sync_flags), pTmp
  );
  pPager->pWal = 0;

  pager_reset(pPager);
  if( MEMDB ){
    pager_unlock(pPager);
  }else{
    /* Set Pager.journalHdr to -1 for the benefit of the pager_playback() 
    ** call which may be made from within pagerUnlockAndRollback(). If it
    ** is not -1, then the unsynced portion of an open journal file may







>




>







3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
int sqlite3PagerClose(Pager *pPager){
  u8 *pTmp = (u8 *)pPager->pTmpSpace;

  disable_simulated_io_errors();
  sqlite3BeginBenignMalloc();
  pPager->errCode = 0;
  pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
  sqlite3WalClose(pPager->pWal, pPager->fd, 
    (pPager->noSync ? 0 : pPager->sync_flags), pTmp
  );
  pPager->pWal = 0;
#endif
  pager_reset(pPager);
  if( MEMDB ){
    pager_unlock(pPager);
  }else{
    /* Set Pager.journalHdr to -1 for the benefit of the pager_playback() 
    ** call which may be made from within pagerUnlockAndRollback(). If it
    ** is not -1, then the unsynced portion of an open journal file may
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
    }
  }
  if( rc==SQLITE_OK ){
    pPager->nSubRec++;
    assert( pPager->nSavepoint>0 );
    rc = addToSavepointBitvecs(pPager, pPg->pgno);
  }
  return rc;
}

/*
** This function is a wrapper around sqlite3WalFrames(). As well as logging
** the contents of the list of pages headed by pList (connected by pDirty),
** this function notifies any active backup processes that the pages have
** changed. 
*/ 
static int pagerWalFrames(
  Pager *pPager,                  /* Pager object */
  PgHdr *pList,                   /* List of frames to log */
  Pgno nTruncate,                 /* Database size after this commit */
  int isCommit,                   /* True if this is a commit */
  int sync_flags                  /* Flags to pass to OsSync() (or 0) */
){
  int rc;                         /* Return code */

  assert( pPager->pWal );
  rc = sqlite3WalFrames(pPager->pWal, 
      pPager->pageSize, pList, nTruncate, isCommit, sync_flags
  );
  if( rc==SQLITE_OK && pPager->pBackup ){
    PgHdr *p;
    for(p=pList; p; p=p->pDirty){
      sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
    }
  }
  return rc;
}

/*
** This function is called by the pcache layer when it has reached some
** soft memory limit. The first argument is a pointer to a Pager object
** (cast as a void*). The pager is always 'purgeable' (not an in-memory







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







3394
3395
3396
3397
3398
3399
3400




























3401
3402
3403
3404
3405
3406
3407
    }
  }
  if( rc==SQLITE_OK ){
    pPager->nSubRec++;
    assert( pPager->nSavepoint>0 );
    rc = addToSavepointBitvecs(pPager, pPg->pgno);
  }




























  return rc;
}

/*
** This function is called by the pcache layer when it has reached some
** soft memory limit. The first argument is a pointer to a Pager object
** (cast as a void*). The pager is always 'purgeable' (not an in-memory
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
      }
    }
  }

  return rc;
}

/*
** Check if the *-wal file that corresponds to the database opened by pPager
** exists. Assuming no error occurs, set *pExists to 1 if the file exists,
** or 0 otherwise and return SQLITE_OK. If an IO or OOM error occurs, return
** an SQLite error code.
**
** The caller must hold a SHARED lock on the database file to call this
** function. Because an EXCLUSIVE lock on the db file is required to delete 
** a WAL, this ensures there is no race condition between the xAccess() 
** below and an xDelete() being executed by some other connection.
*/
static int pagerHasWAL(Pager *pPager, int *pExists){
  int rc;                         /* Return code */

  if( !pPager->tempFile ){
    char *zWal = sqlite3_mprintf("%s-wal", pPager->zFilename);
    if( !zWal ){
      rc = SQLITE_NOMEM;
    }else{
      rc = sqlite3OsAccess(pPager->pVfs, zWal, SQLITE_ACCESS_EXISTS, pExists);
      sqlite3_free(zWal);
    }
  }else{
    rc = SQLITE_OK;
    *pExists = 0;
  }
  return rc;
}

static int pagerOpenSnapshot(Pager *pPager){
  int rc;                         /* Return code */
  int changed = 0;                /* True if cache must be reset */

  assert( pagerUseWal(pPager) );

  rc = sqlite3WalOpenSnapshot(pPager->pWal, &changed);
  if( rc==SQLITE_OK ){
    int dummy;
    if( changed ){
      pager_reset(pPager);
      assert( pPager->errCode || pPager->dbSizeValid==0 );
    }
    rc = sqlite3PagerPagecount(pPager, &dummy);
  }
  pPager->state = PAGER_SHARED;

  return rc;
}

/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerAcquire() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
** The following operations are also performed by this function.







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







3908
3909
3910
3911
3912
3913
3914

















































3915
3916
3917
3918
3919
3920
3921
      }
    }
  }

  return rc;
}


















































/*
** This function is called to obtain a shared lock on the database file.
** It is illegal to call sqlite3PagerAcquire() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
** The following operations are also performed by this function.
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
  }

  if( pagerUseWal(pPager) ){
    rc = pagerOpenSnapshot(pPager);
  }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
    sqlite3_vfs * const pVfs = pPager->pVfs;
    int isHotJournal = 0;
    int isWal = 0;
    assert( !MEMDB );
    assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
    if( pPager->noReadlock ){
      assert( pPager->readOnly );
      pPager->state = PAGER_SHARED;
    }else{
      rc = pager_wait_on_lock(pPager, SHARED_LOCK);







<







3966
3967
3968
3969
3970
3971
3972

3973
3974
3975
3976
3977
3978
3979
  }

  if( pagerUseWal(pPager) ){
    rc = pagerOpenSnapshot(pPager);
  }else if( pPager->state==PAGER_UNLOCK || isErrorReset ){
    sqlite3_vfs * const pVfs = pPager->pVfs;
    int isHotJournal = 0;

    assert( !MEMDB );
    assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
    if( pPager->noReadlock ){
      assert( pPager->readOnly );
      pPager->state = PAGER_SHARED;
    }else{
      rc = pager_wait_on_lock(pPager, SHARED_LOCK);
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091

4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);
      }
    }
    assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );

    rc = pagerHasWAL(pPager, &isWal);
    if( rc!=SQLITE_OK ){
      goto failed;
    }
    if( isWal ){
      pager_reset(pPager);
      rc = sqlite3PagerOpenWal(pPager, 0);
      if( rc==SQLITE_OK ){

        rc = pagerOpenSnapshot(pPager);
      }
    }else if( pPager->journalMode==PAGER_JOURNALMODE_WAL ){
      pPager->journalMode = PAGER_JOURNALMODE_DELETE;
    }
  }

 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);
  }







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







4120
4121
4122
4123
4124
4125
4126
4127
4128






4129
4130




4131
4132
4133
4134
4135
4136
4137

      if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
        pager_reset(pPager);
      }
    }
    assert( pPager->exclusiveMode || pPager->state==PAGER_SHARED );

    /* If there is a WAL file in the file-system, open this database in WAL
    ** mode. Otherwise, the following function call is a no-op.






    */
    rc = pagerOpenWalIfPresent(pPager);




  }

 failed:
  if( rc!=SQLITE_OK ){
    /* pager_unlock() is a no-op for exclusive mode and in-memory databases. */
    pager_unlock(pPager);
  }
5785
5786
5787
5788
5789
5790
5791

5792
5793
5794
5795
5796
5797
5798
** uses it opaquely as an argument to sqlite3BackupRestart() and
** sqlite3BackupUpdate() only.
*/
sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
  return &pPager->pBackup;
}


/*
** This function is called when the user invokes "PRAGMA checkpoint".
*/
int sqlite3PagerCheckpoint(Pager *pPager){
  int rc = SQLITE_OK;
  if( pPager->pWal ){
    u8 *zBuf = (u8 *)pPager->pTmpSpace;







>







5819
5820
5821
5822
5823
5824
5825
5826
5827
5828
5829
5830
5831
5832
5833
** uses it opaquely as an argument to sqlite3BackupRestart() and
** sqlite3BackupUpdate() only.
*/
sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
  return &pPager->pBackup;
}

#ifndef SQLITE_OMIT_WAL
/*
** This function is called when the user invokes "PRAGMA checkpoint".
*/
int sqlite3PagerCheckpoint(Pager *pPager){
  int rc = SQLITE_OK;
  if( pPager->pWal ){
    u8 *zBuf = (u8 *)pPager->pTmpSpace;
5832
5833
5834
5835
5836
5837
5838
5839
5840
5841
5842
5843
5844
5845
5846
  }else{
    *pisOpen = 1;
  }

  return rc;
}


/*
** This function is called to close the connection to the log file prior
** to switching from WAL to rollback mode.
**
** Before closing the log file, this function attempts to take an 
** EXCLUSIVE lock on the database file. If this cannot be obtained, an
** error (SQLITE_BUSY) is returned and the log connection is not closed.







<







5867
5868
5869
5870
5871
5872
5873

5874
5875
5876
5877
5878
5879
5880
  }else{
    *pisOpen = 1;
  }

  return rc;
}


/*
** This function is called to close the connection to the log file prior
** to switching from WAL to rollback mode.
**
** Before closing the log file, this function attempts to take an 
** EXCLUSIVE lock on the database file. If this cannot be obtained, an
** error (SQLITE_BUSY) is returned and the log connection is not closed.
5877
5878
5879
5880
5881
5882
5883

5884
5885
        (u8*)pPager->pTmpSpace
      );
      pPager->pWal = 0;
    }
  }
  return rc;
}


#endif /* SQLITE_OMIT_DISKIO */







>


5911
5912
5913
5914
5915
5916
5917
5918
5919
5920
        (u8*)pPager->pTmpSpace
      );
      pPager->pWal = 0;
    }
  }
  return rc;
}
#endif

#endif /* SQLITE_OMIT_DISKIO */
Changes to src/pragma.c.
261
262
263
264
265
266
267
268



269
270
271
272
273
274
275
/*
** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants
** defined in pager.h. This function returns the associated lowercase
** journal-mode name.
*/
const char *sqlite3JournalModename(int eMode){
  static char * const azModeName[] = {
    "delete", "persist", "off", "truncate", "memory", "wal"



  };
  assert( PAGER_JOURNALMODE_DELETE==0 );
  assert( PAGER_JOURNALMODE_PERSIST==1 );
  assert( PAGER_JOURNALMODE_OFF==2 );
  assert( PAGER_JOURNALMODE_TRUNCATE==3 );
  assert( PAGER_JOURNALMODE_MEMORY==4 );
  assert( PAGER_JOURNALMODE_WAL==5 );







|
>
>
>







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants
** defined in pager.h. This function returns the associated lowercase
** journal-mode name.
*/
const char *sqlite3JournalModename(int eMode){
  static char * const azModeName[] = {
    "delete", "persist", "off", "truncate", "memory"
#ifndef SQLITE_OMIT_WAL
     , "wal"
#endif
  };
  assert( PAGER_JOURNALMODE_DELETE==0 );
  assert( PAGER_JOURNALMODE_PERSIST==1 );
  assert( PAGER_JOURNALMODE_OFF==2 );
  assert( PAGER_JOURNALMODE_TRUNCATE==3 );
  assert( PAGER_JOURNALMODE_MEMORY==4 );
  assert( PAGER_JOURNALMODE_WAL==5 );
1393
1394
1395
1396
1397
1398
1399






1400
1401
1402
1403

1404
1405
1406
1407
1408
1409
1410
    while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){
      sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0);
      sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
    }
  }else
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */







  if( sqlite3StrICmp(zLeft, "checkpoint")==0 ){
    sqlite3VdbeUsesBtree(v, iDb);
    sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0);
  }else


#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /*
  ** Report the current state of file logs for all databases
  */
  if( sqlite3StrICmp(zLeft, "lock_status")==0 ){
    static const char *const azLockName[] = {







>
>
>
>
>
>




>







1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
    while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){
      sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0);
      sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
    }
  }else
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */

#ifndef SQLITE_OMIT_WAL
  /*
  **   PRAGMA [database.]checkpoint
  **
  ** Checkpoint the database.
  */
  if( sqlite3StrICmp(zLeft, "checkpoint")==0 ){
    sqlite3VdbeUsesBtree(v, iDb);
    sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0);
  }else
#endif

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /*
  ** Report the current state of file logs for all databases
  */
  if( sqlite3StrICmp(zLeft, "lock_status")==0 ){
    static const char *const azLockName[] = {
Changes to src/sqliteInt.h.
819
820
821
822
823
824
825

826
827

828
829
830
831
832
833
834
  void *pProfileArg;                        /* Argument to profile function */
  void *pCommitArg;                 /* Argument to xCommitCallback() */   
  int (*xCommitCallback)(void*);    /* Invoked at every commit. */
  void *pRollbackArg;               /* Argument to xRollbackCallback() */   
  void (*xRollbackCallback)(void*); /* Invoked at every commit. */
  void *pUpdateArg;
  void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);

  int (*xWalCallback)(void *, sqlite3 *, const char *, int);
  void *pWalArg;

  void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
  void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
  void *pCollNeededArg;
  sqlite3_value *pErr;          /* Most recent error message */
  char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */
  char *zErrMsg16;              /* Most recent error message (UTF-16 encoded) */
  union {







>


>







819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
  void *pProfileArg;                        /* Argument to profile function */
  void *pCommitArg;                 /* Argument to xCommitCallback() */   
  int (*xCommitCallback)(void*);    /* Invoked at every commit. */
  void *pRollbackArg;               /* Argument to xRollbackCallback() */   
  void (*xRollbackCallback)(void*); /* Invoked at every commit. */
  void *pUpdateArg;
  void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
#ifndef SQLITE_OMIT_WAL
  int (*xWalCallback)(void *, sqlite3 *, const char *, int);
  void *pWalArg;
#endif
  void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
  void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
  void *pCollNeededArg;
  sqlite3_value *pErr;          /* Most recent error message */
  char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */
  char *zErrMsg16;              /* Most recent error message (UTF-16 encoded) */
  union {
Changes to src/test_config.c.
494
495
496
497
498
499
500






501
502
503
504
505
506
507
#endif

#ifdef SQLITE_OMIT_VIRTUALTABLE
  Tcl_SetVar2(interp, "sqlite_options", "vtab", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY);
#endif







#ifdef SQLITE_OMIT_WSD
  Tcl_SetVar2(interp, "sqlite_options", "wsd", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "wsd", "1", TCL_GLOBAL_ONLY);
#endif








>
>
>
>
>
>







494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
#endif

#ifdef SQLITE_OMIT_VIRTUALTABLE
  Tcl_SetVar2(interp, "sqlite_options", "vtab", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_OMIT_WAL
  Tcl_SetVar2(interp, "sqlite_options", "wal", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "wal", "1", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_OMIT_WSD
  Tcl_SetVar2(interp, "sqlite_options", "wsd", "0", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "wsd", "1", TCL_GLOBAL_ONLY);
#endif

Changes to src/vdbe.c.
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
  UPDATE_MAX_BLOBSIZE(pMem);
  if( sqlite3VdbeMemTooBig(pMem) ){
    goto too_big;
  }
  break;
}


/* Opcode: Checkpoint P1 * * * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
*/
case OP_Checkpoint: {
  Btree *pBt;                     /* Btree to checkpoint */

  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );
  pBt = db->aDb[pOp->p1].pBt;
  rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
  break;
};  


/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the
** PAGER_JOURNALMODE_XXX values. If changing between the various rollback
** modes (delete, truncate, persist, off and memory), this is a simple
** operation. No IO is required.







>














>







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
  UPDATE_MAX_BLOBSIZE(pMem);
  if( sqlite3VdbeMemTooBig(pMem) ){
    goto too_big;
  }
  break;
}

#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 * * * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
*/
case OP_Checkpoint: {
  Btree *pBt;                     /* Btree to checkpoint */

  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );
  pBt = db->aDb[pOp->p1].pBt;
  rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
  break;
};  
#endif

/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the
** PAGER_JOURNALMODE_XXX values. If changing between the various rollback
** modes (delete, truncate, persist, off and memory), this is a simple
** operation. No IO is required.
5230
5231
5232
5233
5234
5235
5236


5237
5238
5239
5240
5241
5242
5243
       || eNew==PAGER_JOURNALMODE_QUERY
  );
  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );

  pBt = db->aDb[pOp->p1].pBt;
  pPager = sqlite3BtreePager(pBt);


  zFilename = sqlite3PagerFilename(pPager);
  pVfs = sqlite3PagerVfs(pPager);

  /* Do not allow a transition to journal_mode=WAL for a database
  ** in temporary storage or if the VFS does not support xShmOpen.
  */
  if( eNew==PAGER_JOURNALMODE_WAL







>
>







5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
       || eNew==PAGER_JOURNALMODE_QUERY
  );
  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  assert( (p->btreeMask & (1<<pOp->p1))!=0 );

  pBt = db->aDb[pOp->p1].pBt;
  pPager = sqlite3BtreePager(pBt);

#ifndef SQLITE_OMIT_WAL
  zFilename = sqlite3PagerFilename(pPager);
  pVfs = sqlite3PagerVfs(pPager);

  /* Do not allow a transition to journal_mode=WAL for a database
  ** in temporary storage or if the VFS does not support xShmOpen.
  */
  if( eNew==PAGER_JOURNALMODE_WAL
5278
5279
5280
5281
5282
5283
5284

5285
5286
5287
5288
5289
5290
5291
        */
        assert( sqlite3BtreeIsInTrans(pBt)==0 );
        rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
        if( rc!=SQLITE_OK ) goto abort_due_to_error;
      }
    }
  }


  eNew = sqlite3PagerJournalMode(pPager, eNew);
  pOut = &aMem[pOp->p2];
  pOut->flags = MEM_Str|MEM_Static|MEM_Term;
  pOut->z = (char *)sqlite3JournalModename(eNew);
  pOut->n = sqlite3Strlen30(pOut->z);
  pOut->enc = SQLITE_UTF8;







>







5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
        */
        assert( sqlite3BtreeIsInTrans(pBt)==0 );
        rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
        if( rc!=SQLITE_OK ) goto abort_due_to_error;
      }
    }
  }
#endif /* ifndef SQLITE_OMIT_WAL */

  eNew = sqlite3PagerJournalMode(pPager, eNew);
  pOut = &aMem[pOp->p2];
  pOut->flags = MEM_Str|MEM_Static|MEM_Term;
  pOut->z = (char *)sqlite3JournalModename(eNew);
  pOut->n = sqlite3Strlen30(pOut->z);
  pOut->enc = SQLITE_UTF8;
Changes to src/vdbeapi.c.
302
303
304
305
306
307
308




309
310
311


312
313
314
315
316
317
318
319
320
321
322

323
324
325
326
327
328
329
void sqlite3_result_error_nomem(sqlite3_context *pCtx){
  assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
  sqlite3VdbeMemSetNull(&pCtx->s);
  pCtx->isError = SQLITE_NOMEM;
  pCtx->s.db->mallocFailed = 1;
}





static int doWalCallbacks(sqlite3 *db){
  int i;
  int rc = SQLITE_OK;


  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
      if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK
       && db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry)
      ){
        rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
      }
    }
  }

  return rc;
}

/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
**







>
>
>
>

<

>
>











>







302
303
304
305
306
307
308
309
310
311
312
313

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
void sqlite3_result_error_nomem(sqlite3_context *pCtx){
  assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
  sqlite3VdbeMemSetNull(&pCtx->s);
  pCtx->isError = SQLITE_NOMEM;
  pCtx->s.db->mallocFailed = 1;
}

/*
** This function is called after a transaction has been committed. It 
** invokes callbacks registered with sqlite3_wal_hook() as required.
*/
static int doWalCallbacks(sqlite3 *db){

  int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
  int i;
  for(i=0; i<db->nDb; i++){
    Btree *pBt = db->aDb[i].pBt;
    if( pBt ){
      int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
      if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK
       && db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry)
      ){
        rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
      }
    }
  }
#endif
  return rc;
}

/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
**
Changes to src/wal.c.
9
10
11
12
13
14
15


16
17
18
19
20
21
22
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the implementation of a write-ahead log file used in 
** "journal_mode=wal" mode.
*/


#include "wal.h"


/*
** WRITE-AHEAD LOG (WAL) FILE FORMAT
**
** A wal file consists of a header followed by zero or more "frames".







>
>







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the implementation of a write-ahead log file used in 
** "journal_mode=wal" mode.
*/
#ifndef SQLITE_OMIT_WAL

#include "wal.h"


/*
** WRITE-AHEAD LOG (WAL) FILE FORMAT
**
** A wal file consists of a header followed by zero or more "frames".
1267
1268
1269
1270
1271
1272
1273

1274
1275
1276
1277
1278
1279
1280
  int (*xBusyHandler)(void *),    /* Pointer to busy-handler function */
  void *pBusyHandlerArg           /* Argument to pass to xBusyHandler */
){
  int rc;                         /* Return code */
  int isChanged = 0;              /* True if a new wal-index header is loaded */

  assert( pWal->lockState==SQLITE_SHM_UNLOCK );


  /* Get the CHECKPOINT lock */
  do {
    rc = walSetLock(pWal, SQLITE_SHM_CHECKPOINT);
  }while( rc==SQLITE_BUSY && xBusyHandler(pBusyHandlerArg) );
  if( rc!=SQLITE_OK ){
    walSetLock(pWal, SQLITE_SHM_UNLOCK);







>







1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
  int (*xBusyHandler)(void *),    /* Pointer to busy-handler function */
  void *pBusyHandlerArg           /* Argument to pass to xBusyHandler */
){
  int rc;                         /* Return code */
  int isChanged = 0;              /* True if a new wal-index header is loaded */

  assert( pWal->lockState==SQLITE_SHM_UNLOCK );
  assert( pWal->pWiData==0 );

  /* Get the CHECKPOINT lock */
  do {
    rc = walSetLock(pWal, SQLITE_SHM_CHECKPOINT);
  }while( rc==SQLITE_BUSY && xBusyHandler(pBusyHandlerArg) );
  if( rc!=SQLITE_OK ){
    walSetLock(pWal, SQLITE_SHM_UNLOCK);
1311
1312
1313
1314
1315
1316
1317

  u32 ret = 0;
  if( pWal ){
    ret = pWal->iCallback;
    pWal->iCallback = 0;
  }
  return (int)ret;
}








>
1314
1315
1316
1317
1318
1319
1320
1321
  u32 ret = 0;
  if( pWal ){
    ret = pWal->iCallback;
    pWal->iCallback = 0;
  }
  return (int)ret;
}
#endif /* #ifndef SQLITE_OMIT_WAL */
Changes to src/wal.h.
14
15
16
17
18
19
20
















21
22
23
24
25
26
27
** the implementation of each function in log.c for further details.
*/

#ifndef _WAL_H_
#define _WAL_H_

#include "sqliteInt.h"

















/* Connection to a write-ahead log (WAL) file. 
** There is one object of this type for each pager. 
*/
typedef struct Wal Wal;

/* Open and close a connection to a write-ahead log. */







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







14
15
16
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
** the implementation of each function in log.c for further details.
*/

#ifndef _WAL_H_
#define _WAL_H_

#include "sqliteInt.h"

#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z)             0
# define sqlite3WalClose(w,x,y,z)          0
# define sqlite3WalOpenSnapshot(y,z)       0
# define sqlite3WalCloseSnapshot(z) 
# define sqlite3WalRead(w,x,y,z)           0
# define sqlite3WalDbsize(y,z)
# define sqlite3WalWriteLock(y,z)          0
# define sqlite3WalUndo(x,y,z)             0
# define sqlite3WalSavepoint(z)            0
# define sqlite3WalSavepointUndo(y,z)      0
# define sqlite3WalFrames(u,v,w,x,y,z)     0
# define sqlite3WalCheckpoint(u,v,w,x,y,z) 0
# define sqlite3WalCallback(z)             0
#else

/* Connection to a write-ahead log (WAL) file. 
** There is one object of this type for each pager. 
*/
typedef struct Wal Wal;

/* Open and close a connection to a write-ahead log. */
75
76
77
78
79
80
81

82
/* Return the value to pass to a sqlite3_wal_hook callback, the
** number of frames in the WAL at the point of the last commit since
** sqlite3WalCallback() was called.  If no commits have occurred since
** the last call, then return 0.
*/
int sqlite3WalCallback(Wal *pWal);


#endif /* _WAL_H_ */







>

91
92
93
94
95
96
97
98
99
/* Return the value to pass to a sqlite3_wal_hook callback, the
** number of frames in the WAL at the point of the last commit since
** sqlite3WalCallback() was called.  If no commits have occurred since
** the last call, then return 0.
*/
int sqlite3WalCallback(Wal *pWal);

#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */
Changes to test/corruptA.test.
33
34
35
36
37
38
39


40
41
42
43
44
45
46
47
48
49

# Corrupt the file header in various ways and make sure the corruption
# is detected when opening the database file.
#
db close
file copy -force test.db test.db-template



do_test corruptA-2.1 {
  file copy -force test.db-template test.db
  hexio_write test.db 19 03   ;# the read format number
  sqlite3 db test.db
  catchsql {SELECT * FROM t1}  
} {1 {file is encrypted or is not a database}}
 
do_test corruptA-2.2 {
  db close
  file copy -force test.db-template test.db







>
>


|







33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# Corrupt the file header in various ways and make sure the corruption
# is detected when opening the database file.
#
db close
file copy -force test.db test.db-template

set unreadable_version 02
ifcapable wal { set unreadable_version 03 }
do_test corruptA-2.1 {
  file copy -force test.db-template test.db
  hexio_write test.db 19 $unreadable_version   ;# the read format number
  sqlite3 db test.db
  catchsql {SELECT * FROM t1}  
} {1 {file is encrypted or is not a database}}
 
do_test corruptA-2.2 {
  db close
  file copy -force test.db-template test.db
Changes to test/rdonly.test.
63
64
65
66
67
68
69


70
71
72
73
74
75
76
77
78
} {0 {1 2}}

# Now, after connection [db] has loaded the database schema, modify the
# write-version of the file (and the change-counter, so that the 
# write-version is reloaded). This way, SQLite does not discover that
# the database is read-only until after it is locked.
#


do_test rdonly-1.6 {
  hexio_write test.db 18 03              ; # write-version
  hexio_write test.db 24 11223344        ; # change-counter
  catchsql {
    INSERT INTO t1 VALUES(2);
  }
} {1 {attempt to write a readonly database}}

finish_test







>
>

|







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
} {0 {1 2}}

# Now, after connection [db] has loaded the database schema, modify the
# write-version of the file (and the change-counter, so that the 
# write-version is reloaded). This way, SQLite does not discover that
# the database is read-only until after it is locked.
#
set ro_version 02
ifcapable wal { set ro_version 03 }
do_test rdonly-1.6 {
  hexio_write test.db 18 $ro_version     ; # write-version
  hexio_write test.db 24 11223344        ; # change-counter
  catchsql {
    INSERT INTO t1 VALUES(2);
  }
} {1 {attempt to write a readonly database}}

finish_test
Changes to test/wal.test.
12
13
14
15
16
17
18


19
20
21
22
23
24
25
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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



proc reopen_db {} {
  catch { db close }
  file delete -force test.db test.db-wal test.db-wal-summary
  sqlite3_wal db test.db
}








>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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

ifcapable !wal {finish_test ; return }

proc reopen_db {} {
  catch { db close }
  file delete -force test.db test.db-wal test.db-wal-summary
  sqlite3_wal db test.db
}

Changes to test/walbak.test.
11
12
13
14
15
16
17


18
19
20
21
22
23
24
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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



proc log_file_size {nFrame pgsz} {
  expr {12 + ($pgsz+16)*$nFrame}
}

# Test organization:
# 







>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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

ifcapable !wal {finish_test ; return }

proc log_file_size {nFrame pgsz} {
  expr {12 + ($pgsz+16)*$nFrame}
}

# Test organization:
# 
Changes to test/walcrash.test.
23
24
25
26
27
28
29


30
31
32
33
34
35
36
#               than one page.
# walcrash-3.*: Recover multiple databases where the failed transaction 
#               was a multi-file transaction.
#

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


db close

set seed 0
set REPEATS 100

# walcrash-1.*
#







>
>







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#               than one page.
# 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 }

db close

set seed 0
set REPEATS 100

# walcrash-1.*
#
Changes to test/walhook.test.
11
12
13
14
15
16
17


18
19
20
21
22
23
24
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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



proc sqlite3_wal {args} {
  eval sqlite3 $args
  [lindex $args 0] eval { 
    PRAGMA journal_mode = wal;
    PRAGMA synchronous = normal;
    PRAGMA page_size = 1024;







>
>







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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

ifcapable !wal {finish_test ; return }

proc sqlite3_wal {args} {
  eval sqlite3 $args
  [lindex $args 0] eval { 
    PRAGMA journal_mode = wal;
    PRAGMA synchronous = normal;
    PRAGMA page_size = 1024;
Changes to test/walmode.test.
11
12
13
14
15
16
17



















18
19
20
21
22
23
24
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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




















do_test walmode-1.1 {
  set sqlite_sync_count 0
  execsql { PRAGMA page_size = 1024 }
  execsql { PRAGMA journal_mode = wal }
} {wal}
do_test walmode-1.2 {







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







11
12
13
14
15
16
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
# This file implements regression tests for SQLite library.  The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#

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

# If the library was compiled without WAL support, check that the 
# "PRAGMA journal_mode=WAL" treats "WAL" as an unrecognized mode.
#
ifcapable !wal {

  do_test walmode-0.1 {
    execsql { PRAGMA journal_mode = wal }
  } {delete}
  do_test walmode-0.2 {
    execsql { PRAGMA main.journal_mode = wal }
  } {delete}
  do_test walmode-0.3 {
    execsql { PRAGMA main.journal_mode }
  } {delete}

  finish_test
  return
}

do_test walmode-1.1 {
  set sqlite_sync_count 0
  execsql { PRAGMA page_size = 1024 }
  execsql { PRAGMA journal_mode = wal }
} {wal}
do_test walmode-1.2 {
Changes to test/walslow.test.
12
13
14
15
16
17
18


19
20
21
22
23
24
25
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode. The tests in this file use 
# brute force methods, so may take a while to run.
#

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



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







>
>







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode. The tests in this file use 
# brute force methods, so may take a while to run.
#

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

ifcapable !wal {finish_test ; return }

proc reopen_db {} {
  catch { db close }
  file delete -force test.db test.db-wal
  sqlite3 db test.db
  execsql { PRAGMA journal_mode = wal }
}
Changes to test/walthread.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
if {[run_thread_tests]==0} { 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.
#
set seconds(walthread-1) 20







>







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
if {[run_thread_tests]==0} { finish_test ; return }
ifcapable !wal             { 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.
#
set seconds(walthread-1) 20