aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze.c6
-rw-r--r--src/attach.c8
-rw-r--r--src/backup.c20
-rw-r--r--src/btree.c13
-rw-r--r--src/btree.h2
-rw-r--r--src/main.c53
-rw-r--r--src/mutex_unix.c2
-rw-r--r--src/mutex_w32.c2
-rw-r--r--src/os_os2.c2
-rw-r--r--src/os_unix.c5
-rw-r--r--src/os_win.c5
-rw-r--r--src/pager.c87
-rw-r--r--src/pager.h2
-rw-r--r--src/pcache1.c49
-rw-r--r--src/pragma.c20
-rw-r--r--src/prepare.c6
-rw-r--r--src/sqlite.h.in118
-rw-r--r--src/sqliteInt.h2
-rw-r--r--src/test1.c6
-rw-r--r--src/test_mutex.c2
-rw-r--r--src/test_quota.c14
-rw-r--r--src/vdbe.c26
-rw-r--r--src/wal.c133
-rw-r--r--src/wal.h35
24 files changed, 470 insertions, 148 deletions
diff --git a/src/analyze.c b/src/analyze.c
index 3693aad83..0a8339baf 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -286,11 +286,11 @@ static void analyzeOneTable(
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
for(i=0; i<nCol; i++){
- int addr = sqlite3VdbeCurrentAddr(v) - (nCol*2);
+ int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
if( i==0 ){
- sqlite3VdbeJumpHere(v, addr-1); /* Set jump dest for the OP_IfNot */
+ sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
}
- sqlite3VdbeJumpHere(v, addr); /* Set jump dest for the OP_Ne */
+ sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
}
diff --git a/src/attach.c b/src/attach.c
index e3dc49d9e..37b61935f 100644
--- a/src/attach.c
+++ b/src/attach.c
@@ -312,9 +312,11 @@ static void codeAttach(
#ifndef SQLITE_OMIT_AUTHORIZATION
if( pAuthArg ){
- char *zAuthArg = pAuthArg->u.zToken;
- if( NEVER(zAuthArg==0) ){
- goto attach_end;
+ char *zAuthArg;
+ if( pAuthArg->op==TK_STRING ){
+ zAuthArg = pAuthArg->u.zToken;
+ }else{
+ zAuthArg = 0;
}
rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
if(rc!=SQLITE_OK ){
diff --git a/src/backup.c b/src/backup.c
index 7258b26aa..5d8ea7f3f 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -449,6 +449,16 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
));
+
+ /* This call ensures that all data required to recreate the original
+ ** database has been stored in the journal for pDestPager and the
+ ** journal synced to disk. So at this point we may safely modify
+ ** the database file in any way, knowing that if a power failure
+ ** occurs, the original database will be reconstructed from the
+ ** journal file. */
+ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
+
+ /* Write the extra pages and truncate the database file as required. */
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
for(
iOff=PENDING_BYTE+pgszSrc;
@@ -465,10 +475,12 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
sqlite3PagerUnref(pSrcPg);
}
if( rc==SQLITE_OK ){
- rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
- if( rc==SQLITE_OK ){
- rc = backupTruncateFile(pFile, iSize);
- }
+ rc = backupTruncateFile(pFile, iSize);
+ }
+
+ /* Sync the database file to disk. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerSync(pDestPager);
}
}else{
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
diff --git a/src/btree.c b/src/btree.c
index bec19e315..2c6014bdc 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -918,14 +918,9 @@ static void btreeParseCellPtr(
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
- int nSize; /* Total size of cell content in bytes */
- nSize = nPayload + n;
+ if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
pInfo->iOverflow = 0;
- if( (nSize & ~3)==0 ){
- nSize = 4; /* Minimum cell size is 4 */
- }
- pInfo->nSize = (u16)nSize;
}else{
/* If the payload will not fit completely on the local page, we have
** to decide how much to store locally and how much to spill onto
@@ -7937,8 +7932,10 @@ int sqlite3BtreeIsInTrans(Btree *p){
**
** Return SQLITE_LOCKED if this or any other connection has an open
** transaction on the shared-cache the argument Btree is connected to.
+**
+** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
*/
-int sqlite3BtreeCheckpoint(Btree *p){
+int sqlite3BtreeCheckpoint(Btree *p, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK;
if( p ){
BtShared *pBt = p->pBt;
@@ -7946,7 +7943,7 @@ int sqlite3BtreeCheckpoint(Btree *p){
if( pBt->inTransaction!=TRANS_NONE ){
rc = SQLITE_LOCKED;
}else{
- rc = sqlite3PagerCheckpoint(pBt->pPager);
+ rc = sqlite3PagerCheckpoint(pBt->pPager, eMode, pnLog, pnCkpt);
}
sqlite3BtreeLeave(p);
}
diff --git a/src/btree.h b/src/btree.h
index 90fa7a2e9..6886dd944 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -207,7 +207,7 @@ void sqlite3BtreeCursorList(Btree*);
#endif
#ifndef SQLITE_OMIT_WAL
- int sqlite3BtreeCheckpoint(Btree*);
+ int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#endif
/*
diff --git a/src/main.c b/src/main.c
index 4b93a2ba5..9f7d4b1fd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -816,7 +816,7 @@ const char *sqlite3ErrStr(int rc){
/* SQLITE_INTERRUPT */ "interrupted",
/* SQLITE_IOERR */ "disk I/O error",
/* SQLITE_CORRUPT */ "database disk image is malformed",
- /* SQLITE_NOTFOUND */ 0,
+ /* SQLITE_NOTFOUND */ "unknown operation",
/* SQLITE_FULL */ "database or disk is full",
/* SQLITE_CANTOPEN */ "unable to open database file",
/* SQLITE_PROTOCOL */ "locking protocol",
@@ -1340,19 +1340,29 @@ void *sqlite3_wal_hook(
#endif
}
-
/*
-** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points
-** to contains a zero-length string, all attached databases are
-** checkpointed.
-*/
-int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
+** Checkpoint database zDb.
+*/
+int sqlite3_wal_checkpoint_v2(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of attached database (or NULL) */
+ int eMode, /* SQLITE_CHECKPOINT_* value */
+ int *pnLog, /* OUT: Size of WAL log in frames */
+ int *pnCkpt /* OUT: Total number of frames checkpointed */
+){
#ifdef SQLITE_OMIT_WAL
return SQLITE_OK;
#else
int rc; /* Return code */
int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE
+ && eMode!=SQLITE_CHECKPOINT_FULL
+ && eMode!=SQLITE_CHECKPOINT_RESTART
+ ){
+ return SQLITE_MISUSE;
+ }
+
sqlite3_mutex_enter(db->mutex);
if( zDb && zDb[0] ){
iDb = sqlite3FindDbName(db, zDb);
@@ -1361,7 +1371,7 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
rc = SQLITE_ERROR;
sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb);
}else{
- rc = sqlite3Checkpoint(db, iDb);
+ rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt);
sqlite3Error(db, rc, 0);
}
rc = sqlite3ApiExit(db, rc);
@@ -1370,6 +1380,16 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
#endif
}
+
+/*
+** Checkpoint database zDb. If zDb is NULL, or if the buffer zDb points
+** to contains a zero-length string, all attached databases are
+** checkpointed.
+*/
+int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
+ return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0);
+}
+
#ifndef SQLITE_OMIT_WAL
/*
** Run a checkpoint on database iDb. This is a no-op if database iDb is
@@ -1387,20 +1407,29 @@ int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are
** checkpointed. If an error is encountered it is returned immediately -
** no attempt is made to checkpoint any remaining databases.
+**
+** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
*/
-int sqlite3Checkpoint(sqlite3 *db, int iDb){
+int sqlite3Checkpoint(sqlite3 *db, int iDb, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK; /* Return code */
int i; /* Used to iterate through attached dbs */
+ int bBusy = 0; /* True if SQLITE_BUSY has been encountered */
assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
- rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt);
+ rc = sqlite3BtreeCheckpoint(db->aDb[i].pBt, eMode, pnLog, pnCkpt);
+ pnLog = 0;
+ pnCkpt = 0;
+ if( rc==SQLITE_BUSY ){
+ bBusy = 1;
+ rc = SQLITE_OK;
+ }
}
}
- return rc;
+ return (rc==SQLITE_OK && bBusy) ? SQLITE_BUSY : rc;
}
#endif /* SQLITE_OMIT_WAL */
@@ -2364,6 +2393,8 @@ int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
rc = SQLITE_OK;
}else if( fd->pMethods ){
rc = sqlite3OsFileControl(fd, op, pArg);
+ }else{
+ rc = SQLITE_NOTFOUND;
}
sqlite3BtreeLeave(pBtree);
}
diff --git a/src/mutex_unix.c b/src/mutex_unix.c
index 196975e71..aa9a8cf26 100644
--- a/src/mutex_unix.c
+++ b/src/mutex_unix.c
@@ -99,7 +99,7 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
+** <li> SQLITE_MUTEX_STATIC_PMEM
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
diff --git a/src/mutex_w32.c b/src/mutex_w32.c
index fba964a46..8e257a91b 100644
--- a/src/mutex_w32.c
+++ b/src/mutex_w32.c
@@ -156,7 +156,7 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_STATIC_MEM2
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
+** <li> SQLITE_MUTEX_STATIC_PMEM
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
diff --git a/src/os_os2.c b/src/os_os2.c
index 7ac0cc7cd..df5ad1026 100644
--- a/src/os_os2.c
+++ b/src/os_os2.c
@@ -533,7 +533,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
}
- return SQLITE_ERROR;
+ return SQLITE_NOTFOUND;
}
/*
diff --git a/src/os_unix.c b/src/os_unix.c
index edc716c98..fa200ae80 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -3135,8 +3135,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return proxyFileControl(id,op,pArg);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
+ case SQLITE_FCNTL_SYNC_OMITTED: {
+ return SQLITE_OK; /* A no-op */
+ }
}
- return SQLITE_ERROR;
+ return SQLITE_NOTFOUND;
}
/*
diff --git a/src/os_win.c b/src/os_win.c
index 1be514999..70425178e 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -1183,8 +1183,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
SimulateIOErrorBenign(0);
return SQLITE_OK;
}
+ case SQLITE_FCNTL_SYNC_OMITTED: {
+ return SQLITE_OK;
+ }
}
- return SQLITE_ERROR;
+ return SQLITE_NOTFOUND;
}
/*
diff --git a/src/pager.c b/src/pager.c
index 39a3d5b86..218429838 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -2477,15 +2477,21 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
i64 currentSize, newSize;
+ int szPage = pPager->pageSize;
assert( pPager->eLock==EXCLUSIVE_LOCK );
/* TODO: Is it safe to use Pager.dbFileSize here? */
rc = sqlite3OsFileSize(pPager->fd, &currentSize);
- newSize = pPager->pageSize*(i64)nPage;
+ newSize = szPage*(i64)nPage;
if( rc==SQLITE_OK && currentSize!=newSize ){
if( currentSize>newSize ){
rc = sqlite3OsTruncate(pPager->fd, newSize);
}else{
- rc = sqlite3OsWrite(pPager->fd, "", 1, newSize-1);
+ char *pTmp = pPager->pTmpSpace;
+ memset(pTmp, 0, szPage);
+ testcase( (newSize-szPage) < currentSize );
+ testcase( (newSize-szPage) == currentSize );
+ testcase( (newSize-szPage) > currentSize );
+ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
}
if( rc==SQLITE_OK ){
pPager->dbFileSize = nPage;
@@ -2845,6 +2851,28 @@ static int readDbPage(PgHdr *pPg){
return rc;
}
+/*
+** Update the value of the change-counter at offsets 24 and 92 in
+** the header and the sqlite version number at offset 96.
+**
+** This is an unconditional update. See also the pager_incr_changecounter()
+** routine which only updates the change-counter if the update is actually
+** needed, as determined by the pPager->changeCountDone state variable.
+*/
+static void pager_write_changecounter(PgHdr *pPg){
+ u32 change_counter;
+
+ /* Increment the value just read and write it back to byte 24. */
+ change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
+ put32bits(((char*)pPg->pData)+24, change_counter);
+
+ /* Also store the SQLite version number in bytes 96..99 and in
+ ** bytes 92..95 store the change counter for which the version number
+ ** is valid. */
+ put32bits(((char*)pPg->pData)+92, change_counter);
+ put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER);
+}
+
#ifndef SQLITE_OMIT_WAL
/*
** This function is invoked once for each page that has already been
@@ -2915,34 +2943,11 @@ static int pagerRollbackWal(Pager *pPager){
return rc;
}
-
-/*
-** Update the value of the change-counter at offsets 24 and 92 in
-** the header and the sqlite version number at offset 96.
-**
-** This is an unconditional update. See also the pager_incr_changecounter()
-** routine which only updates the change-counter if the update is actually
-** needed, as determined by the pPager->changeCountDone state variable.
-*/
-static void pager_write_changecounter(PgHdr *pPg){
- u32 change_counter;
-
- /* Increment the value just read and write it back to byte 24. */
- change_counter = sqlite3Get4byte((u8*)pPg->pPager->dbFileVers)+1;
- put32bits(((char*)pPg->pData)+24, change_counter);
-
- /* Also store the SQLite version number in bytes 96..99 and in
- ** bytes 92..95 store the change counter for which the version number
- ** is valid. */
- put32bits(((char*)pPg->pData)+92, change_counter);
- put32bits(((char*)pPg->pData)+96, SQLITE_VERSION_NUMBER);
-}
-
/*
** 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.
+** changed.
**
** The list of pages passed into this routine is always sorted by page number.
** Hence, if page 1 appears anywhere on the list, it will be the first page.
@@ -5623,15 +5628,13 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
int sqlite3PagerSync(Pager *pPager){
- int rc; /* Return code */
- assert( !MEMDB );
- if( pPager->noSync ){
- rc = SQLITE_OK;
- }else{
+ int rc = SQLITE_OK;
+ if( !pPager->noSync ){
+ assert( !MEMDB );
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
- }
- if( isOpen(pPager->fd) ){
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, (void *)&rc);
+ }else if( isOpen(pPager->fd) ){
+ assert( !MEMDB );
+ sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc);
}
return rc;
}
@@ -6596,14 +6599,20 @@ sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
#ifndef SQLITE_OMIT_WAL
/*
-** This function is called when the user invokes "PRAGMA checkpoint".
+** This function is called when the user invokes "PRAGMA wal_checkpoint",
+** "PRAGMA wal_blocking_checkpoint" or calls the sqlite3_wal_checkpoint()
+** or wal_blocking_checkpoint() API functions.
+**
+** Parameter eMode is one of SQLITE_CHECKPOINT_PASSIVE, FULL or RESTART.
*/
-int sqlite3PagerCheckpoint(Pager *pPager){
+int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog, int *pnCkpt){
int rc = SQLITE_OK;
if( pPager->pWal ){
- u8 *zBuf = (u8 *)pPager->pTmpSpace;
- rc = sqlite3WalCheckpoint(pPager->pWal, pPager->ckptSyncFlags,
- pPager->pageSize, zBuf);
+ rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
+ pPager->xBusyHandler, pPager->pBusyHandlerArg,
+ pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
+ pnLog, pnCkpt
+ );
}
return rc;
}
diff --git a/src/pager.h b/src/pager.h
index e775b0c16..eab7ddaf8 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -138,7 +138,7 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
-int sqlite3PagerCheckpoint(Pager *pPager);
+int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*);
int sqlite3PagerWalSupported(Pager *pPager);
int sqlite3PagerWalCallback(Pager *pPager);
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
diff --git a/src/pcache1.c b/src/pcache1.c
index 4af41407f..ad443954b 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -50,6 +50,7 @@ struct PGroup {
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */
int nMaxPage; /* Sum of nMax for purgeable caches */
int nMinPage; /* Sum of nMin for purgeable caches */
+ int mxPinned; /* nMaxpage + 10 - nMinPage */
int nCurrentPage; /* Number of purgeable pages allocated */
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
};
@@ -73,6 +74,7 @@ struct PCache1 {
int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */
+ unsigned int n90pct; /* nMax*9/10 */
/* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the PGroup mutex.
@@ -226,7 +228,9 @@ static void *pcache1Alloc(int nByte){
p = sqlite3Malloc(nByte);
if( p ){
int sz = sqlite3MallocSize(p);
+ sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
+ sqlite3_mutex_leave(pcache1.mutex);
}
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
}
@@ -254,7 +258,9 @@ static void pcache1Free(void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
iSize = sqlite3MallocSize(p);
+ sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
+ sqlite3_mutex_leave(pcache1.mutex);
sqlite3_free(p);
}
}
@@ -516,6 +522,7 @@ static int pcache1Init(void *NotUsed){
pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);
}
+ pcache1.grp.mxPinned = 10;
pcache1.isInit = 1;
return SQLITE_OK;
}
@@ -565,6 +572,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
memset(pCache, 0, sz);
if( separateCache ){
pGroup = (PGroup*)&pCache[1];
+ pGroup->mxPinned = 10;
}else{
pGroup = &pcache1_g.grp;
}
@@ -575,6 +583,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
pCache->nMin = 10;
pcache1EnterMutex(pGroup);
pGroup->nMinPage += pCache->nMin;
+ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1LeaveMutex(pGroup);
}
}
@@ -592,7 +601,9 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
PGroup *pGroup = pCache->pGroup;
pcache1EnterMutex(pGroup);
pGroup->nMaxPage += (nMax - pCache->nMax);
+ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = nMax;
+ pCache->n90pct = pCache->nMax*9/10;
pcache1EnforceMaxPage(pGroup);
pcache1LeaveMutex(pGroup);
}
@@ -665,14 +676,16 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** 5. Otherwise, allocate and return a new page buffer.
*/
static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
- unsigned int nPinned;
+ int nPinned;
PCache1 *pCache = (PCache1 *)p;
- PGroup *pGroup = pCache->pGroup;
+ PGroup *pGroup;
PgHdr1 *pPage = 0;
assert( pCache->bPurgeable || createFlag!=1 );
- pcache1EnterMutex(pGroup);
- if( createFlag==1 ) sqlite3BeginBenignMalloc();
+ assert( pCache->bPurgeable || pCache->nMin==0 );
+ assert( pCache->bPurgeable==0 || pCache->nMin==10 );
+ assert( pCache->nMin==0 || pCache->bPurgeable );
+ pcache1EnterMutex(pGroup = pCache->pGroup);
/* Step 1: Search the hash table for an existing entry. */
if( pCache->nHash>0 ){
@@ -686,11 +699,26 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
goto fetch_out;
}
+ /* The pGroup local variable will normally be initialized by the
+ ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined,
+ ** then pcache1EnterMutex() is a no-op, so we have to initialize the
+ ** local variable here. Delaying the initialization of pGroup is an
+ ** optimization: The common case is to exit the module before reaching
+ ** this point.
+ */
+#ifdef SQLITE_MUTEX_OMIT
+ pGroup = pCache->pGroup;
+#endif
+
+
/* Step 3: Abort if createFlag is 1 but the cache is nearly full */
nPinned = pCache->nPage - pCache->nRecyclable;
+ assert( nPinned>=0 );
+ assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
+ assert( pCache->n90pct == pCache->nMax*9/10 );
if( createFlag==1 && (
- nPinned>=(pGroup->nMaxPage+pCache->nMin-pGroup->nMinPage)
- || nPinned>=(pCache->nMax * 9 / 10)
+ nPinned>=pGroup->mxPinned
+ || nPinned>=(int)pCache->n90pct
|| pcache1UnderMemoryPressure(pCache)
)){
goto fetch_out;
@@ -706,15 +734,16 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
|| pGroup->nCurrentPage>=pGroup->nMaxPage
|| pcache1UnderMemoryPressure(pCache)
)){
+ PCache1 *pOtherCache;
pPage = pGroup->pLruTail;
pcache1RemoveFromHash(pPage);
pcache1PinPage(pPage);
- if( pPage->pCache->szPage!=pCache->szPage ){
+ if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
pcache1FreePage(pPage);
pPage = 0;
}else{
pGroup->nCurrentPage -=
- (pPage->pCache->bPurgeable - pCache->bPurgeable);
+ (pOtherCache->bPurgeable - pCache->bPurgeable);
}
}
@@ -722,9 +751,11 @@ static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
** attempt to allocate a new one.
*/
if( !pPage ){
+ if( createFlag==1 ) sqlite3BeginBenignMalloc();
pcache1LeaveMutex(pGroup);
pPage = pcache1AllocPage(pCache);
pcache1EnterMutex(pGroup);
+ if( createFlag==1 ) sqlite3EndBenignMalloc();
}
if( pPage ){
@@ -743,7 +774,6 @@ fetch_out:
if( pPage && iKey>pCache->iMaxKey ){
pCache->iMaxKey = iKey;
}
- if( createFlag==1 ) sqlite3EndBenignMalloc();
pcache1LeaveMutex(pGroup);
return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
}
@@ -853,6 +883,7 @@ static void pcache1Destroy(sqlite3_pcache *p){
pcache1TruncateUnsafe(pCache, 0);
pGroup->nMaxPage -= pCache->nMax;
pGroup->nMinPage -= pCache->nMin;
+ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1EnforceMaxPage(pGroup);
pcache1LeaveMutex(pGroup);
sqlite3_free(pCache->apHash);
diff --git a/src/pragma.c b/src/pragma.c
index 31985438a..15e2bef59 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -1386,13 +1386,29 @@ void sqlite3Pragma(
#ifndef SQLITE_OMIT_WAL
/*
- ** PRAGMA [database.]wal_checkpoint
+ ** PRAGMA [database.]wal_checkpoint = passive|full|restart
**
** Checkpoint the database.
*/
if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){
+ int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
+ int eMode = SQLITE_CHECKPOINT_PASSIVE;
+ if( zRight ){
+ if( sqlite3StrICmp(zRight, "full")==0 ){
+ eMode = SQLITE_CHECKPOINT_FULL;
+ }else if( sqlite3StrICmp(zRight, "restart")==0 ){
+ eMode = SQLITE_CHECKPOINT_RESTART;
+ }
+ }
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
- sqlite3VdbeAddOp3(v, OP_Checkpoint, pId2->z?iDb:SQLITE_MAX_ATTACHED, 0, 0);
+ sqlite3VdbeSetNumCols(v, 3);
+ pParse->nMem = 3;
+ sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC);
+ sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC);
+
+ sqlite3VdbeAddOp2(v, OP_Checkpoint, iBt, eMode);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}else
/*
diff --git a/src/prepare.c b/src/prepare.c
index 816abdf11..62a33b671 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -784,7 +784,7 @@ int sqlite3_prepare_v2(
*/
static int sqlite3Prepare16(
sqlite3 *db, /* Database handle. */
- const void *zSql, /* UTF-8 encoded SQL statement. */
+ const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
int saveSqlFlag, /* True to save SQL text into the sqlite3_stmt */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
@@ -834,7 +834,7 @@ static int sqlite3Prepare16(
*/
int sqlite3_prepare16(
sqlite3 *db, /* Database handle. */
- const void *zSql, /* UTF-8 encoded SQL statement. */
+ const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const void **pzTail /* OUT: End of parsed string */
@@ -846,7 +846,7 @@ int sqlite3_prepare16(
}
int sqlite3_prepare16_v2(
sqlite3 *db, /* Database handle. */
- const void *zSql, /* UTF-8 encoded SQL statement. */
+ const void *zSql, /* UTF-16 encoded SQL statement. */
int nBytes, /* Length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const void **pzTail /* OUT: End of parsed string */
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 6fe0a3648..6bdc6afc9 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -385,7 +385,7 @@ int sqlite3_exec(
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
-#define SQLITE_NOTFOUND 12 /* NOT USED. Table or record not found */
+#define SQLITE_NOTFOUND 12 /* Unknown opcode in sqlite3_file_control() */
#define SQLITE_FULL 13 /* Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
@@ -617,7 +617,9 @@ struct sqlite3_file {
** core reserves all opcodes less than 100 for its own use.
** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.
** Applications that define a custom xFileControl method should use opcodes
-** greater than 100 to avoid conflicts.
+** greater than 100 to avoid conflicts. VFS implementations should
+** return [SQLITE_NOTFOUND] for file control opcodes that they do not
+** recognize.
**
** The xSectorSize() method returns the sector size of the
** device that underlies the file. The sector size is the
@@ -716,11 +718,15 @@ struct sqlite3_io_methods {
** connection. See the [sqlite3_file_control()] documentation for
** additional information.
**
-** The [SQLITE_FCNTL_SYNC] opcode is used internally. SQLite calls
-** the file-control method with this opcode immediately after the database
-** file is synced, or if the database is running in synchronous=off mode
-** immediately after it would have been synced otherwise. This makes it
-** easier to write special VFS modules that depend on the xSync call.
+** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
+** SQLite and sent to all VFSes in place of a call to the xSync method
+** when the database connection has [PRAGMA synchronous] set to OFF.)^
+** Some specialized VFSes need this signal in order to operate correctly
+** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most
+** VFSes do not need this signal and should silently ignore this opcode.
+** Applications should not call [sqlite3_file_control()] with this
+** opcode as doing so may disrupt the operation of the specilized VFSes
+** that do require it.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
@@ -729,7 +735,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_SIZE_HINT 5
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
-#define SQLITE_FCNTL_SYNC 8
+#define SQLITE_FCNTL_SYNC_OMITTED 8
/*
@@ -1849,7 +1855,7 @@ void sqlite3_free_table(char **result);
** NULL pointer if [sqlite3_malloc()] is unable to allocate enough
** memory to hold the resulting string.
**
-** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from
+** ^(The sqlite3_snprintf() routine is similar to "snprintf()" from
** the standard C library. The result is written into the
** buffer supplied as the second parameter whose size is given by
** the first parameter. Note that the order of the
@@ -2660,7 +2666,7 @@ const char *sqlite3_sql(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
**
-** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
+** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
** and only if the [prepared statement] X makes no direct changes to
** the content of the database file.
**
@@ -5528,24 +5534,21 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
** <dd>This parameter returns the number malloc attempts that were
** satisfied using lookaside memory. Only the high-water value is meaningful;
-** the current value is always zero.
-** checked out.</dd>)^
+** the current value is always zero.)^
**
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
** <dd>This parameter returns the number malloc attempts that might have
** been satisfied using lookaside memory but failed due to the amount of
** memory requested being larger than the lookaside slot size.
** Only the high-water value is meaningful;
-** the current value is always zero.
-** checked out.</dd>)^
+** the current value is always zero.)^
**
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
** <dd>This parameter returns the number malloc attempts that might have
** been satisfied using lookaside memory but failed due to all lookaside
** memory already being in use.
** Only the high-water value is meaningful;
-** the current value is always zero.
-** checked out.</dd>)^
+** the current value is always zero.)^
**
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
** <dd>This parameter returns the approximate number of of bytes of heap
@@ -6248,6 +6251,89 @@ int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
/*
+**
+** CAPI3REF: Checkpoint a database
+**
+** Run a checkpoint operation on WAL database zDb attached to database
+** handle db. The specific operation is determined by the value of the
+** eMode parameter:
+**
+** <dl>
+** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
+** Checkpoint as many frames as possible without waiting for any database
+** readers or writers to finish. Sync the db file if all frames in the log
+** are checkpointed. This mode is the same as calling
+** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.
+**
+** <dt>SQLITE_CHECKPOINT_FULL<dd>
+** This mode blocks (calls the busy-handler callback) until there is no
+** database writer and all readers are reading from the most recent database
+** snapshot. It then checkpoints all frames in the log file and syncs the
+** database file. This call blocks database writers while it is running,
+** but not database readers.
+**
+** <dt>SQLITE_CHECKPOINT_RESTART<dd>
+** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after
+** checkpointing the log file it blocks (calls the busy-handler callback)
+** until all readers are reading from the database file only. This ensures
+** that the next client to write to the database file restarts the log file
+** from the beginning. This call blocks database writers while it is running,
+** but not database readers.
+** </dl>
+**
+** If pnLog is not NULL, then *pnLog is set to the total number of frames in
+** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to
+** the total number of checkpointed frames (including any that were already
+** checkpointed when this function is called). *pnLog and *pnCkpt may be
+** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK.
+** If no values are available because of an error, they are both set to -1
+** before returning to communicate this to the caller.
+**
+** All calls obtain an exclusive "checkpoint" lock on the database file. If
+** any other process is running a checkpoint operation at the same time, the
+** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a
+** busy-handler configured, it will not be invoked in this case.
+**
+** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive
+** "writer" lock on the database file. If the writer lock cannot be obtained
+** immediately, and a busy-handler is configured, it is invoked and the writer
+** lock retried until either the busy-handler returns 0 or the lock is
+** successfully obtained. The busy-handler is also invoked while waiting for
+** database readers as described above. If the busy-handler returns 0 before
+** the writer lock is obtained or while waiting for database readers, the
+** checkpoint operation proceeds from that point in the same way as
+** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
+** without blocking any further. SQLITE_BUSY is returned in this case.
+**
+** If parameter zDb is NULL or points to a zero length string, then the
+** specified operation is attempted on all WAL databases. In this case the
+** values written to output parameters *pnLog and *pnCkpt are undefined. If
+** an SQLITE_BUSY error is encountered when processing one or more of the
+** attached WAL databases, the operation is still attempted on any remaining
+** attached databases and SQLITE_BUSY is returned to the caller. If any other
+** error occurs while processing an attached database, processing is abandoned
+** and the error code returned to the caller immediately. If no error
+** (SQLITE_BUSY or otherwise) is encountered while processing the attached
+** databases, SQLITE_OK is returned.
+**
+** If database zDb is the name of an attached database that is not in WAL
+** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If
+** zDb is not NULL (or a zero length string) and is not the name of any
+** attached database, SQLITE_ERROR is returned to the caller.
+*/
+int sqlite3_wal_checkpoint_v2(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of attached database (or NULL) */
+ int eMode, /* SQLITE_CHECKPOINT_* value */
+ int *pnLog, /* OUT: Size of WAL log in frames */
+ int *pnCkpt /* OUT: Total number of frames checkpointed */
+);
+#define SQLITE_CHECKPOINT_PASSIVE 0
+#define SQLITE_CHECKPOINT_FULL 1
+#define SQLITE_CHECKPOINT_RESTART 2
+
+
+/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 41c46fa26..2987dcd48 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3041,7 +3041,7 @@ CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
int sqlite3TempInMemory(const sqlite3*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
const char *sqlite3JournalModename(int);
-int sqlite3Checkpoint(sqlite3*, int);
+int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
/* Declarations for functions in fkey.c. All of these are replaced by
diff --git a/src/test1.c b/src/test1.c
index bab78451f..cf7e06b9c 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -4801,13 +4801,13 @@ static int file_control_test(
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
rc = sqlite3_file_control(db, 0, 0, &iArg);
- assert( rc==SQLITE_ERROR );
+ assert( rc==SQLITE_NOTFOUND );
rc = sqlite3_file_control(db, "notadatabase", SQLITE_FCNTL_LOCKSTATE, &iArg);
assert( rc==SQLITE_ERROR );
rc = sqlite3_file_control(db, "main", -1, &iArg);
- assert( rc==SQLITE_ERROR );
+ assert( rc==SQLITE_NOTFOUND );
rc = sqlite3_file_control(db, "temp", -1, &iArg);
- assert( rc==SQLITE_ERROR );
+ assert( rc==SQLITE_NOTFOUND || rc==SQLITE_ERROR );
return TCL_OK;
}
diff --git a/src/test_mutex.c b/src/test_mutex.c
index ff388f376..0bb74375d 100644
--- a/src/test_mutex.c
+++ b/src/test_mutex.c
@@ -247,7 +247,7 @@ static int test_read_mutex_counters(
int ii;
char *aName[8] = {
"fast", "recursive", "static_master", "static_mem",
- "static_open", "static_prng", "static_lru", "static_lru2"
+ "static_open", "static_prng", "static_lru", "static_pmem"
};
if( objc!=1 ){
diff --git a/src/test_quota.c b/src/test_quota.c
index 352f289d3..3c6db4d7c 100644
--- a/src/test_quota.c
+++ b/src/test_quota.c
@@ -31,6 +31,20 @@
#include <string.h>
#include <assert.h>
+/*
+** For an build without mutexes, no-op the mutex calls.
+*/
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
+#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
+#define sqlite3_mutex_free(X)
+#define sqlite3_mutex_enter(X)
+#define sqlite3_mutex_try(X) SQLITE_OK
+#define sqlite3_mutex_leave(X)
+#define sqlite3_mutex_held(X) ((void)(X),1)
+#define sqlite3_mutex_notheld(X) ((void)(X),1)
+#endif /* SQLITE_THREADSAFE==0 */
+
+
/************************ Object Definitions ******************************/
/* Forward declaration of all object types */
diff --git a/src/vdbe.c b/src/vdbe.c
index eddd1e599..90e4065e7 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -5215,13 +5215,33 @@ case OP_AggFinal: {
}
#ifndef SQLITE_OMIT_WAL
-/* Opcode: Checkpoint P1 * * * *
+/* Opcode: Checkpoint P1 P2 P3 * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
-** WAL mode.
+** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL
+** or RESTART.
*/
case OP_Checkpoint: {
- rc = sqlite3Checkpoint(db, pOp->p1);
+ int nLog = -1; /* Number of pages in WAL log */
+ int nCkpt = -1; /* Number of checkpointed pages */
+ int bBusy = 0;
+ assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
+ || pOp->p2==SQLITE_CHECKPOINT_FULL
+ || pOp->p2==SQLITE_CHECKPOINT_RESTART
+ );
+ rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &nLog, &nCkpt);
+ if( rc==SQLITE_BUSY ){
+ rc = SQLITE_OK;
+ bBusy = 1;
+ }
+
+ aMem[1].u.i = bBusy;
+ aMem[2].u.i = nLog;
+ aMem[3].u.i = nCkpt;
+ MemSetTypeFlag(&aMem[1], MEM_Int);
+ MemSetTypeFlag(&aMem[2], MEM_Int);
+ MemSetTypeFlag(&aMem[3], MEM_Int);
+
break;
};
#endif
diff --git a/src/wal.c b/src/wal.c
index ff327bf6b..64c966da9 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -1559,6 +1559,34 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
}
/*
+** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
+** n. If the attempt fails and parameter xBusy is not NULL, then it is a
+** busy-handler function. Invoke it and retry the lock until either the
+** lock is successfully obtained or the busy-handler returns 0.
+*/
+static int walBusyLock(
+ Wal *pWal, /* WAL connection */
+ int (*xBusy)(void*), /* Function to call when busy */
+ void *pBusyArg, /* Context argument for xBusyHandler */
+ int lockIdx, /* Offset of first byte to lock */
+ int n /* Number of bytes to lock */
+){
+ int rc;
+ do {
+ rc = walLockExclusive(pWal, lockIdx, n);
+ }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
+ return rc;
+}
+
+/*
+** The cache of the wal-index header must be valid to call this function.
+** Return the page-size in bytes used by the database.
+*/
+static int walPagesize(Wal *pWal){
+ return (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
+}
+
+/*
** Copy as much content as we can from the WAL back into the database file
** in response to an sqlite3_wal_checkpoint() request or the equivalent.
**
@@ -1591,9 +1619,12 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
*/
static int walCheckpoint(
Wal *pWal, /* Wal connection */
+ int eMode, /* One of PASSIVE, FULL or RESTART */
+ int (*xBusyCall)(void*), /* Function to call when busy */
+ void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags for OsSync() (or 0) */
- int nBuf, /* Size of zBuf in bytes */
- u8 *zBuf /* Temporary buffer to use */
+ u8 *zBuf, /* Temporary buffer to use */
+ int *pnCkpt /* Total frames checkpointed */
){
int rc; /* Return code */
int szPage; /* Database page-size */
@@ -1604,11 +1635,13 @@ static int walCheckpoint(
u32 mxPage; /* Max database page to write */
int i; /* Loop counter */
volatile WalCkptInfo *pInfo; /* The checkpoint status information */
+ int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */
- szPage = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
+ szPage = walPagesize(pWal);
testcase( szPage<=32768 );
testcase( szPage>=65536 );
pInfo = walCkptInfo(pWal);
+ if( pnCkpt ) *pnCkpt = pInfo->nBackfill;
if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
/* Allocate the iterator */
@@ -1618,11 +1651,8 @@ static int walCheckpoint(
}
assert( pIter );
- /*** TODO: Move this test out to the caller. Make it an assert() here ***/
- if( szPage!=nBuf ){
- rc = SQLITE_CORRUPT_BKPT;
- goto walcheckpoint_out;
- }
+ mxPage = pWal->hdr.nPage;
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall;
/* Compute in mxSafeFrame the index of the last frame of the WAL that is
** safe to write into the database. Frames beyond mxSafeFrame might
@@ -1633,14 +1663,15 @@ static int walCheckpoint(
mxPage = pWal->hdr.nPage;
for(i=1; i<WAL_NREADER; i++){
u32 y = pInfo->aReadMark[i];
- if( mxSafeFrame>=y ){
+ if( mxSafeFrame>y ){
assert( y<=pWal->hdr.mxFrame );
- rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
pInfo->aReadMark[i] = READMARK_NOT_USED;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
}else if( rc==SQLITE_BUSY ){
mxSafeFrame = y;
+ xBusy = 0;
}else{
goto walcheckpoint_out;
}
@@ -1648,7 +1679,7 @@ static int walCheckpoint(
}
if( pInfo->nBackfill<mxSafeFrame
- && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
+ && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
){
i64 nSize; /* Current size of database file */
u32 nBackfill = pInfo->nBackfill;
@@ -1696,18 +1727,38 @@ static int walCheckpoint(
}
if( rc==SQLITE_OK ){
pInfo->nBackfill = mxSafeFrame;
+ if( pnCkpt ) *pnCkpt = mxSafeFrame;
}
}
/* Release the reader lock held while backfilling */
walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
- }else if( rc==SQLITE_BUSY ){
+ }
+
+ if( rc==SQLITE_BUSY ){
/* Reset the return code so as not to report a checkpoint failure
- ** just because active readers prevent any backfill.
- */
+ ** just because there are active readers. */
rc = SQLITE_OK;
}
+ /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal
+ ** file has been copied into the database file, then block until all
+ ** readers have finished using the wal file. This ensures that the next
+ ** process to write to the database restarts the wal file.
+ */
+ if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
+ assert( pWal->writeLock );
+ if( pInfo->nBackfill<pWal->hdr.mxFrame ){
+ rc = SQLITE_BUSY;
+ }else if( eMode==SQLITE_CHECKPOINT_RESTART ){
+ assert( mxSafeFrame==pWal->hdr.mxFrame );
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
+ if( rc==SQLITE_OK ){
+ walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
+ }
+ }
+ }
+
walcheckpoint_out:
walIteratorFree(pIter);
return rc;
@@ -1739,7 +1790,9 @@ int sqlite3WalClose(
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
- rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf);
+ rc = sqlite3WalCheckpoint(
+ pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
+ );
if( rc==SQLITE_OK ){
isDelete = 1;
}
@@ -2654,17 +2707,27 @@ int sqlite3WalFrames(
**
** Obtain a CHECKPOINT lock and then backfill as much information as
** we can from WAL into the database.
+**
+** If parameter xBusy is not NULL, it is a pointer to a busy-handler
+** callback. In this case this function runs a blocking checkpoint.
*/
int sqlite3WalCheckpoint(
Wal *pWal, /* Wal connection */
+ int eMode, /* PASSIVE, FULL or RESTART */
+ int (*xBusy)(void*), /* Function to call when busy */
+ void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags to sync db file with (or 0) */
int nBuf, /* Size of temporary buffer */
- u8 *zBuf /* Temporary buffer to use */
+ u8 *zBuf, /* Temporary buffer to use */
+ int *pnLog, /* OUT: Number of frames in WAL */
+ int *pnCkpt /* OUT: Number of backfilled frames in WAL */
){
int rc; /* Return code */
int isChanged = 0; /* True if a new wal-index header is loaded */
+ int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
assert( pWal->ckptLock==0 );
+ assert( pWal->writeLock==0 );
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
@@ -2676,11 +2739,40 @@ int sqlite3WalCheckpoint(
}
pWal->ckptLock = 1;
- /* Copy data from the log to the database file. */
- rc = walIndexReadHdr(pWal, &isChanged);
+ /* If this is a blocking-checkpoint, then obtain the write-lock as well
+ ** to prevent any writers from running while the checkpoint is underway.
+ ** This has to be done before the call to walIndexReadHdr() below.
+ **
+ ** If the writer lock cannot be obtained, then a passive checkpoint is
+ ** run instead. Since the checkpointer is not holding the writer lock,
+ ** there is no point in blocking waiting for any readers. Assuming no
+ ** other error occurs, this function will return SQLITE_BUSY to the caller.
+ */
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
+ if( rc==SQLITE_OK ){
+ pWal->writeLock = 1;
+ }else if( rc==SQLITE_BUSY ){
+ eMode2 = SQLITE_CHECKPOINT_PASSIVE;
+ rc = SQLITE_OK;
+ }
+ }
+
+ /* Read the wal-index header. */
if( rc==SQLITE_OK ){
- rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf);
+ rc = walIndexReadHdr(pWal, &isChanged);
+ }
+
+ /* Copy data from the log to the database file. */
+ if( rc==SQLITE_OK && pWal->hdr.mxFrame ){
+ if( walPagesize(pWal)!=nBuf ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
+ rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags,zBuf,pnCkpt);
+ }
}
+
if( isChanged ){
/* If a new wal-index header was loaded before the checkpoint was
** performed, then the pager-cache associated with pWal is now
@@ -2692,10 +2784,11 @@ int sqlite3WalCheckpoint(
}
/* Release the locks. */
+ sqlite3WalEndWriteTransaction(pWal);
walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
pWal->ckptLock = 0;
WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
- return rc;
+ return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
}
/* Return the value to pass to a sqlite3_wal_hook callback, the
diff --git a/src/wal.h b/src/wal.h
index 35f695c88..2039c701c 100644
--- a/src/wal.h
+++ b/src/wal.h
@@ -20,22 +20,22 @@
#include "sqliteInt.h"
#ifdef SQLITE_OMIT_WAL
-# define sqlite3WalOpen(x,y,z) 0
-# define sqlite3WalClose(w,x,y,z) 0
-# define sqlite3WalBeginReadTransaction(y,z) 0
+# define sqlite3WalOpen(x,y,z) 0
+# define sqlite3WalClose(w,x,y,z) 0
+# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalEndReadTransaction(z)
-# define sqlite3WalRead(v,w,x,y,z) 0
-# define sqlite3WalDbsize(y) 0
-# define sqlite3WalBeginWriteTransaction(y) 0
-# define sqlite3WalEndWriteTransaction(x) 0
-# define sqlite3WalUndo(x,y,z) 0
+# define sqlite3WalRead(v,w,x,y,z) 0
+# define sqlite3WalDbsize(y) 0
+# define sqlite3WalBeginWriteTransaction(y) 0
+# define sqlite3WalEndWriteTransaction(x) 0
+# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalSavepoint(y,z)
-# define sqlite3WalSavepointUndo(y,z) 0
-# define sqlite3WalFrames(u,v,w,x,y,z) 0
-# define sqlite3WalCheckpoint(u,v,w,x) 0
-# define sqlite3WalCallback(z) 0
-# define sqlite3WalExclusiveMode(y,z) 0
-# define sqlite3WalHeapMemory(z) 0
+# define sqlite3WalSavepointUndo(y,z) 0
+# define sqlite3WalFrames(u,v,w,x,y,z) 0
+# define sqlite3WalCheckpoint(r,s,t,u,v,w,x,y,z) 0
+# define sqlite3WalCallback(z) 0
+# define sqlite3WalExclusiveMode(y,z) 0
+# define sqlite3WalHeapMemory(z) 0
#else
#define WAL_SAVEPOINT_NDATA 4
@@ -86,9 +86,14 @@ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int);
/* Copy pages from the log to the database file */
int sqlite3WalCheckpoint(
Wal *pWal, /* Write-ahead log connection */
+ int eMode, /* One of PASSIVE, FULL and RESTART */
+ int (*xBusy)(void*), /* Function to call when busy */
+ void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags to sync db file with (or 0) */
int nBuf, /* Size of buffer nBuf */
- u8 *zBuf /* Temporary buffer to use */
+ u8 *zBuf, /* Temporary buffer to use */
+ int *pnLog, /* OUT: Number of frames in WAL */
+ int *pnCkpt /* OUT: Number of backfilled frames in WAL */
);
/* Return the value to pass to a sqlite3_wal_hook callback, the