aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2015-07-14 15:39:22 +0000
committerdrh <drh@noemail.net>2015-07-14 15:39:22 +0000
commit38151adfe2f51c07ca50e9cbc6f44a9811097eb1 (patch)
tree44ecb68858c63553a92e23f56db4481f7a620327 /src
parentbed6de557a5282f92ed5019d5c89129a6ece6bab (diff)
parent201e0c68f7d5d19ba759f56fadd0d58c838c41f9 (diff)
downloadsqlite-38151adfe2f51c07ca50e9cbc6f44a9811097eb1.tar.gz
sqlite-38151adfe2f51c07ca50e9cbc6f44a9811097eb1.zip
Merge trunk changes, including the addition of FTS5 and pcache1 performance
enhancements. FossilOrigin-Name: db4cbefb8674c6cfff27c1e918741de1885c845c
Diffstat (limited to 'src')
-rw-r--r--src/btree.c25
-rw-r--r--src/global.c2
-rw-r--r--src/malloc.c3
-rw-r--r--src/mutex_noop.c2
-rw-r--r--src/mutex_unix.c6
-rw-r--r--src/mutex_w32.c6
-rw-r--r--src/os_unix.c6
-rw-r--r--src/os_win.c6
-rw-r--r--src/pcache1.c261
-rw-r--r--src/select.c12
-rw-r--r--src/sqlite.h.in3
-rw-r--r--src/sqliteInt.h10
-rw-r--r--src/test_mutex.c97
-rw-r--r--src/util.c4
-rw-r--r--src/vdbeapi.c50
-rw-r--r--src/vdbeaux.c1
16 files changed, 367 insertions, 127 deletions
diff --git a/src/btree.c b/src/btree.c
index ca0dd0e59..17c7bb67c 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -8955,10 +8955,11 @@ static int checkTreePage(
u32 usableSize; /* Usable size of the page */
u32 contentOffset; /* Offset to the start of the cell content area */
u32 *heap = 0; /* Min-heap used for checking cell coverage */
- u32 x, prev = 0;
+ u32 x, prev = 0; /* Next and previous entry on the min-heap */
const char *saved_zPfx = pCheck->zPfx;
int saved_v1 = pCheck->v1;
int saved_v2 = pCheck->v2;
+ u8 savedIsInit;
/* Check that the page exists
*/
@@ -8976,6 +8977,7 @@ static int checkTreePage(
/* Clear MemPage.isInit to make sure the corruption detection code in
** btreeInitPage() is executed. */
+ savedIsInit = pPage->isInit;
pPage->isInit = 0;
if( (rc = btreeInitPage(pPage))!=0 ){
assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */
@@ -9018,7 +9020,6 @@ static int checkTreePage(
** as the other cell checks, so initialize the heap. */
heap = pCheck->heap;
heap[0] = 0;
- btreeHeapInsert(heap, contentOffset-1);
}
/* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
@@ -9099,7 +9100,6 @@ static int checkTreePage(
if( !pPage->leaf ){
heap = pCheck->heap;
heap[0] = 0;
- btreeHeapInsert(heap, contentOffset-1);
for(i=nCell-1; i>=0; i--){
u32 size;
pc = get2byteAligned(&data[cellStart+i*2]);
@@ -9119,7 +9119,7 @@ static int checkTreePage(
assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */
size = get2byte(&data[i+2]);
assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */
- btreeHeapInsert(heap, (i<<16)|(i+size-1));
+ btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1));
/* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a
** big-endian integer which is the offset in the b-tree page of the next
** freeblock in the chain, or zero if the freeblock is the last on the
@@ -9133,13 +9133,21 @@ static int checkTreePage(
}
/* Analyze the min-heap looking for overlap between cells and/or
** freeblocks, and counting the number of untracked bytes in nFrag.
+ **
+ ** Each min-heap entry is of the form: (start_address<<16)|end_address.
+ ** There is an implied first entry the covers the page header, the cell
+ ** pointer index, and the gap between the cell pointer index and the start
+ ** of cell content.
+ **
+ ** The loop below pulls entries from the min-heap in order and compares
+ ** the start_address against the previous end_address. If there is an
+ ** overlap, that means bytes are used multiple times. If there is a gap,
+ ** that gap is added to the fragmentation count.
*/
nFrag = 0;
- assert( heap[0]>0 );
- assert( (heap[1]>>16)==0 );
- btreeHeapPull(heap,&prev);
+ prev = contentOffset - 1; /* Implied first min-heap entry */
while( btreeHeapPull(heap,&x) ){
- if( (prev&0xffff)+1>(x>>16) ){
+ if( (prev&0xffff)>=(x>>16) ){
checkAppendMsg(pCheck,
"Multiple uses for byte %u of page %d", x>>16, iPage);
break;
@@ -9162,6 +9170,7 @@ static int checkTreePage(
}
end_of_check:
+ if( !doCoverageCheck ) pPage->isInit = savedIsInit;
releasePage(pPage);
pCheck->zPfx = saved_zPfx;
pCheck->v1 = saved_v1;
diff --git a/src/global.c b/src/global.c
index 61450b3d3..ef4fe56ae 100644
--- a/src/global.c
+++ b/src/global.c
@@ -186,7 +186,7 @@ SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* nScratch */
(void*)0, /* pPage */
0, /* szPage */
- 0, /* nPage */
+ SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */
0, /* mxParserStack */
0, /* sharedCacheEnabled */
SQLITE_SORTER_PMASZ, /* szPma */
diff --git a/src/malloc.c b/src/malloc.c
index 97b9cd577..1e77734ec 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -193,10 +193,9 @@ int sqlite3MallocInit(void){
sqlite3GlobalConfig.nScratch = 0;
}
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
- || sqlite3GlobalConfig.nPage<1 ){
+ || sqlite3GlobalConfig.nPage<=0 ){
sqlite3GlobalConfig.pPage = 0;
sqlite3GlobalConfig.szPage = 0;
- sqlite3GlobalConfig.nPage = 0;
}
rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0));
diff --git a/src/mutex_noop.c b/src/mutex_noop.c
index 7f68aea6c..ecc84b4a9 100644
--- a/src/mutex_noop.c
+++ b/src/mutex_noop.c
@@ -107,7 +107,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
** that means that a mutex could not be allocated.
*/
static sqlite3_mutex *debugMutexAlloc(int id){
- static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_APP3 - 1];
+ static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1];
sqlite3_debug_mutex *pNew = 0;
switch( id ){
case SQLITE_MUTEX_FAST:
diff --git a/src/mutex_unix.c b/src/mutex_unix.c
index e08448e02..0a493fa6a 100644
--- a/src/mutex_unix.c
+++ b/src/mutex_unix.c
@@ -105,6 +105,9 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_STATIC_APP1
** <li> SQLITE_MUTEX_STATIC_APP2
** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -141,6 +144,9 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
sqlite3_mutex *p;
diff --git a/src/mutex_w32.c b/src/mutex_w32.c
index 6786614d8..fc943acaa 100644
--- a/src/mutex_w32.c
+++ b/src/mutex_w32.c
@@ -89,6 +89,9 @@ static sqlite3_mutex winMutex_staticMutexes[] = {
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
@@ -160,6 +163,9 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_STATIC_APP1
** <li> SQLITE_MUTEX_STATIC_APP2
** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
diff --git a/src/os_unix.c b/src/os_unix.c
index 9ec100323..d7a94ab09 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -629,14 +629,14 @@ static int robust_open(const char *z, int f, mode_t m){
** unixEnterLeave()
*/
static void unixEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
static void unixLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#ifdef SQLITE_DEBUG
static int unixMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#endif
diff --git a/src/os_win.c b/src/os_win.c
index d84bda4ef..41bd94098 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -3390,14 +3390,14 @@ static SYSTEM_INFO winSysInfo;
** winShmLeaveMutex()
*/
static void winShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
static void winShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#ifndef NDEBUG
static int winShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#endif
diff --git a/src/pcache1.c b/src/pcache1.c
index 7185ab441..a345e6760 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -15,8 +15,71 @@
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
** If the default page cache implementation is overridden, then neither of
** these two features are available.
+**
+** A Page cache line looks like this:
+**
+** -------------------------------------------------------------
+** | database page content | PgHdr1 | MemPage | PgHdr |
+** -------------------------------------------------------------
+**
+** The database page content is up front (so that buffer overreads tend to
+** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage
+** is the extension added by the btree.c module containing information such
+** as the database page number and how that database page is used. PgHdr
+** is added by the pcache.c layer and contains information used to keep track
+** of which pages are "dirty". PgHdr1 is an extension added by this
+** module (pcache1.c). The PgHdr1 header is a subclass of sqlite3_pcache_page.
+** PgHdr1 contains information needed to look up a page by its page number.
+** The superclass sqlite3_pcache_page.pBuf points to the start of the
+** database page content and sqlite3_pcache_page.pExtra points to PgHdr.
+**
+** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at
+** runtime using sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The
+** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this
+** size can vary according to architecture, compile-time options, and
+** SQLite library version number.
+**
+** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained
+** using a separate memory allocation from the database page content. This
+** seeks to overcome the "clownshoe" problem (also called "internal
+** fragmentation" in academic literature) of allocating a few bytes more
+** than a power of two with the memory allocator rounding up to the next
+** power of two, and leaving the rounded-up space unused.
+**
+** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates
+** with this module. Information is passed back and forth as PgHdr1 pointers.
+**
+** The pcache.c and pager.c modules deal pointers to PgHdr objects.
+** The btree.c module deals with pointers to MemPage objects.
+**
+** SOURCE OF PAGE CACHE MEMORY:
+**
+** Memory for a page might come from any of three sources:
+**
+** (1) The general-purpose memory allocator - sqlite3Malloc()
+** (2) Global page-cache memory provided using sqlite3_config() with
+** SQLITE_CONFIG_PAGECACHE.
+** (3) PCache-local bulk allocation.
+**
+** The third case is a chunk of heap memory (defaulting to 100 pages worth)
+** that is allocated when the page cache is created. The size of the local
+** bulk allocation can be adjusted using
+**
+** sqlite3_config(SQLITE_CONFIG_PCACHE, 0, 0, N).
+**
+** If N is positive, then N pages worth of memory are allocated using a single
+** sqlite3Malloc() call and that memory is used for the first N pages allocated.
+** Or if N is negative, then -1024*N bytes of memory are allocated and used
+** for as many pages as can be accomodated.
+**
+** Only one of (2) or (3) can be used. Once the memory available to (2) or
+** (3) is exhausted, subsequent allocations fail over to the general-purpose
+** memory allocator (1).
+**
+** Earlier versions of SQLite used only methods (1) and (2). But experiments
+** show that method (3) with N==100 provides about a 5% performance boost for
+** common workloads.
*/
-
#include "sqliteInt.h"
typedef struct PCache1 PCache1;
@@ -70,8 +133,9 @@ struct PCache1 {
** The PGroup mutex must be held when accessing nMax.
*/
PGroup *pGroup; /* PGroup this cache belongs to */
- int szPage; /* Size of allocated pages in bytes */
- int szExtra; /* Size of extra space in bytes */
+ int szPage; /* Size of database content section */
+ int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
+ int szAlloc; /* Total size of one pcache line */
int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */
@@ -85,6 +149,8 @@ struct PCache1 {
unsigned int nPage; /* Total number of pages in apHash */
unsigned int nHash; /* Number of slots in apHash[] */
PgHdr1 **apHash; /* Hash table for fast lookup by key */
+ PgHdr1 *pFree; /* List of unused pcache-local pages */
+ void *pBulk; /* Bulk memory used by pcache-local */
};
/*
@@ -97,6 +163,7 @@ struct PgHdr1 {
sqlite3_pcache_page page;
unsigned int iKey; /* Key value (page number) */
u8 isPinned; /* Page in use, not on the LRU list */
+ u8 isBulkLocal; /* This page from bulk local storage */
PgHdr1 *pNext; /* Next in hash table chain */
PCache1 *pCache; /* Cache that currently owns this page */
PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
@@ -104,8 +171,8 @@ struct PgHdr1 {
};
/*
-** Free slots in the allocator used to divide up the buffer provided using
-** the SQLITE_CONFIG_PAGECACHE mechanism.
+** Free slots in the allocator used to divide up the global page cache
+** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism.
*/
struct PgFreeslot {
PgFreeslot *pNext; /* Next free slot */
@@ -123,10 +190,11 @@ static SQLITE_WSD struct PCacheGlobal {
** The nFreeSlot and pFree values do require mutex protection.
*/
int isInit; /* True if initialized */
+ int separateCache; /* Use a new PGroup for each PCache */
int szSlot; /* Size of each free slot */
int nSlot; /* The number of pcache slots */
int nReserve; /* Try to keep nFreeSlot above this */
- void *pStart, *pEnd; /* Bounds of pagecache malloc range */
+ void *pStart, *pEnd; /* Bounds of global page cache memory */
/* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
PgFreeslot *pFree; /* Free page blocks */
@@ -173,6 +241,7 @@ static SQLITE_WSD struct PCacheGlobal {
void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
if( pcache1.isInit ){
PgFreeslot *p;
+ if( pBuf==0 ) sz = n = 0;
sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz;
pcache1.nSlot = pcache1.nFreeSlot = n;
@@ -237,9 +306,9 @@ static void *pcache1Alloc(int nByte){
/*
** Free an allocated buffer obtained from pcache1Alloc().
*/
-static int pcache1Free(void *p){
+static void pcache1Free(void *p){
int nFreed = 0;
- if( p==0 ) return 0;
+ if( p==0 ) return;
if( p>=pcache1.pStart && p<pcache1.pEnd ){
PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex);
@@ -254,15 +323,14 @@ static int pcache1Free(void *p){
}else{
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- nFreed = sqlite3MallocSize(p);
#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
+ nFreed = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
sqlite3_mutex_leave(pcache1.mutex);
#endif
sqlite3_free(p);
}
- return nFreed;
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
@@ -290,54 +358,65 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
PgHdr1 *p = 0;
void *pPg;
- /* The group mutex must be released before pcache1Alloc() is called. This
- ** is because it may call sqlite3_release_memory(), which assumes that
- ** this mutex is not held. */
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
- pcache1LeaveMutex(pCache->pGroup);
+ if( pCache->pFree ){
+ p = pCache->pFree;
+ pCache->pFree = p->pNext;
+ p->pNext = 0;
+ }else{
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ /* The group mutex must be released before pcache1Alloc() is called. This
+ ** is because it might call sqlite3_release_memory(), which assumes that
+ ** this mutex is not held. */
+ assert( pcache1.separateCache==0 );
+ assert( pCache->pGroup==&pcache1.grp );
+ pcache1LeaveMutex(pCache->pGroup);
+#endif
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
- pPg = pcache1Alloc(pCache->szPage);
- p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
- if( !pPg || !p ){
- pcache1Free(pPg);
- sqlite3_free(p);
- pPg = 0;
- }
+ pPg = pcache1Alloc(pCache->szPage);
+ p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
+ if( !pPg || !p ){
+ pcache1Free(pPg);
+ sqlite3_free(p);
+ pPg = 0;
+ }
#else
- pPg = pcache1Alloc(ROUND8(sizeof(PgHdr1)) + pCache->szPage + pCache->szExtra);
- p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
+ pPg = pcache1Alloc(pCache->szAlloc);
+ p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
#endif
- pcache1EnterMutex(pCache->pGroup);
-
- if( pPg ){
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ pcache1EnterMutex(pCache->pGroup);
+#endif
+ if( pPg==0 ) return 0;
p->page.pBuf = pPg;
p->page.pExtra = &p[1];
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage++;
- }
- return p;
+ p->isBulkLocal = 0;
+ }
+ if( pCache->bPurgeable ){
+ pCache->pGroup->nCurrentPage++;
}
- return 0;
+ return p;
}
/*
** Free a page object allocated by pcache1AllocPage().
-**
-** The pointer is allowed to be NULL, which is prudent. But it turns out
-** that the current implementation happens to never call this routine
-** with a NULL pointer, so we mark the NULL test with ALWAYS().
*/
static void pcache1FreePage(PgHdr1 *p){
- if( ALWAYS(p) ){
- PCache1 *pCache = p->pCache;
- assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ PCache1 *pCache;
+ assert( p!=0 );
+ pCache = p->pCache;
+ assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ if( p->isBulkLocal ){
+ p->pNext = pCache->pFree;
+ pCache->pFree = p;
+ }else{
pcache1Free(p->page.pBuf);
#ifdef SQLITE_PCACHE_SEPARATE_HEADER
sqlite3_free(p);
#endif
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage--;
- }
+ }
+ if( pCache->bPurgeable ){
+ pCache->pGroup->nCurrentPage--;
}
}
@@ -537,6 +616,31 @@ static int pcache1Init(void *NotUsed){
UNUSED_PARAMETER(NotUsed);
assert( pcache1.isInit==0 );
memset(&pcache1, 0, sizeof(pcache1));
+
+
+ /*
+ ** The pcache1.separateCache variable is true if each PCache has its own
+ ** private PGroup (mode-1). pcache1.separateCache is false if the single
+ ** PGroup in pcache1.grp is used for all page caches (mode-2).
+ **
+ ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
+ **
+ ** * Use a unified cache in single-threaded applications that have
+ ** configured a start-time buffer for use as page-cache memory using
+ ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL
+ ** pBuf argument.
+ **
+ ** * Otherwise use separate caches (mode-1)
+ */
+#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
+ pcache1.separateCache = 0;
+#elif SQLITE_THREADSAFE
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0
+ || sqlite3GlobalConfig.bCoreMutex>0;
+#else
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0;
+#endif
+
#if SQLITE_THREADSAFE
if( sqlite3GlobalConfig.bCoreMutex ){
pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
@@ -572,31 +676,13 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
PGroup *pGroup; /* The group the new page cache will belong to */
int sz; /* Bytes of memory required to allocate the new cache */
- /*
- ** The separateCache variable is true if each PCache has its own private
- ** PGroup. In other words, separateCache is true for mode (1) where no
- ** mutexing is required.
- **
- ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
- **
- ** * Always use a unified cache in single-threaded applications
- **
- ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off)
- ** use separate caches (mode-1)
- */
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
- const int separateCache = 0;
-#else
- int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
-#endif
-
assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
assert( szExtra < 300 );
- sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
+ sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache;
pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){
- if( separateCache ){
+ if( pcache1.separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
@@ -605,6 +691,7 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
pCache->pGroup = pGroup;
pCache->szPage = szPage;
pCache->szExtra = szExtra;
+ pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
pCache->bPurgeable = (bPurgeable ? 1 : 0);
pcache1EnterMutex(pGroup);
pcache1ResizeHash(pCache);
@@ -614,6 +701,36 @@ static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
}
pcache1LeaveMutex(pGroup);
+ /* Try to initialize the local bulk pagecache line allocation if using
+ ** separate caches and if nPage!=0 */
+ if( pcache1.separateCache
+ && sqlite3GlobalConfig.nPage!=0
+ && sqlite3GlobalConfig.pPage==0
+ ){
+ int szBulk;
+ char *zBulk;
+ sqlite3BeginBenignMalloc();
+ if( sqlite3GlobalConfig.nPage>0 ){
+ szBulk = pCache->szAlloc * sqlite3GlobalConfig.nPage;
+ }else{
+ szBulk = -1024*sqlite3GlobalConfig.nPage;
+ }
+ zBulk = pCache->pBulk = sqlite3Malloc( szBulk );
+ sqlite3EndBenignMalloc();
+ if( zBulk ){
+ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc;
+ int i;
+ for(i=0; i<nBulk; i++){
+ PgHdr1 *pX = (PgHdr1*)&zBulk[szPage];
+ pX->page.pBuf = zBulk;
+ pX->page.pExtra = &pX[1];
+ pX->isBulkLocal = 1;
+ pX->pNext = pCache->pFree;
+ pCache->pFree = pX;
+ zBulk += pCache->szAlloc;
+ }
+ }
+ }
if( pCache->nHash==0 ){
pcache1Destroy((sqlite3_pcache*)pCache);
pCache = 0;
@@ -707,26 +824,17 @@ static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
assert( pCache->nHash>0 && pCache->apHash );
/* Step 4. Try to recycle a page. */
- if( pCache->bPurgeable && pGroup->pLruTail && (
- (pCache->nPage+1>=pCache->nMax)
- || pGroup->nCurrentPage>=pGroup->nMaxPage
- || pcache1UnderMemoryPressure(pCache)
- )){
+ if( pCache->bPurgeable
+ && pGroup->pLruTail
+ && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache))
+ ){
PCache1 *pOther;
pPage = pGroup->pLruTail;
assert( pPage->isPinned==0 );
pcache1RemoveFromHash(pPage, 0);
pcache1PinPage(pPage);
pOther = pPage->pCache;
-
- /* We want to verify that szPage and szExtra are the same for pOther
- ** and pCache. Assert that we can verify this by comparing sums. */
- assert( (pCache->szPage & (pCache->szPage-1))==0 && pCache->szPage>=512 );
- assert( pCache->szExtra<512 );
- assert( (pOther->szPage & (pOther->szPage-1))==0 && pOther->szPage>=512 );
- assert( pOther->szExtra<512 );
-
- if( pOther->szPage+pOther->szExtra != pCache->szPage+pCache->szExtra ){
+ if( pOther->szAlloc != pCache->szAlloc ){
pcache1FreePage(pPage);
pPage = 0;
}else{
@@ -1002,6 +1110,7 @@ static void pcache1Destroy(sqlite3_pcache *p){
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pcache1EnforceMaxPage(pGroup);
pcache1LeaveMutex(pGroup);
+ sqlite3_free(pCache->pBulk);
sqlite3_free(pCache->apHash);
sqlite3_free(pCache);
}
@@ -1057,7 +1166,7 @@ int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
- if( pcache1.pStart==0 ){
+ if( sqlite3GlobalConfig.nPage==0 ){
PgHdr1 *p;
pcache1EnterMutex(&pcache1.grp);
while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
diff --git a/src/select.c b/src/select.c
index ed5d2c6bf..13de02942 100644
--- a/src/select.c
+++ b/src/select.c
@@ -2070,10 +2070,14 @@ static void generateWithRecursiveQuery(
/* Execute the recursive SELECT taking the single row in Current as
** the value for the recursive-table. Store the results in the Queue.
*/
- p->pPrior = 0;
- sqlite3Select(pParse, p, &destQueue);
- assert( p->pPrior==0 );
- p->pPrior = pSetup;
+ if( p->selFlags & SF_Aggregate ){
+ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
+ }else{
+ p->pPrior = 0;
+ sqlite3Select(pParse, p, &destQueue);
+ assert( p->pPrior==0 );
+ p->pPrior = pSetup;
+ }
/* Keep running the loop until the Queue is empty */
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 963b55c27..fcae1b147 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -6291,6 +6291,9 @@ int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */
+#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */
+#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
+#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
/*
** CAPI3REF: Retrieve the mutex for a database connection
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 31e054fd5..6edb885c1 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -509,6 +509,16 @@
# define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS
#endif
+/*
+** The default initial allocation for the pagecache when using separate
+** pagecaches for each database connection. A positive number is the
+** number of pages. A negative number N translations means that a buffer
+** of -1024*N bytes is allocated and used for as many pages as it will hold.
+*/
+#ifndef SQLITE_DEFAULT_PCACHE_INITSZ
+# define SQLITE_DEFAULT_PCACHE_INITSZ 100
+#endif
+
/*
** GCC does not define the offsetof() macro so we'll have to do it
diff --git a/src/test_mutex.c b/src/test_mutex.c
index c9b4a29ab..995b89a4c 100644
--- a/src/test_mutex.c
+++ b/src/test_mutex.c
@@ -19,9 +19,19 @@
#include <assert.h>
#include <string.h>
+#define MAX_MUTEXES (SQLITE_MUTEX_STATIC_VFS3+1)
+#define STATIC_MUTEXES (MAX_MUTEXES-(SQLITE_MUTEX_RECURSIVE+1))
+
/* defined in main.c */
extern const char *sqlite3ErrName(int);
+static const char *aName[MAX_MUTEXES+1] = {
+ "fast", "recursive", "static_master", "static_mem",
+ "static_open", "static_prng", "static_lru", "static_pmem",
+ "static_app1", "static_app2", "static_app3", "static_vfs1",
+ "static_vfs2", "static_vfs3", 0
+};
+
/* A countable mutex */
struct sqlite3_mutex {
sqlite3_mutex *pReal;
@@ -30,13 +40,13 @@ struct sqlite3_mutex {
/* State variables */
static struct test_mutex_globals {
- int isInstalled; /* True if installed */
- int disableInit; /* True to cause sqlite3_initalize() to fail */
- int disableTry; /* True to force sqlite3_mutex_try() to fail */
- int isInit; /* True if initialized */
- sqlite3_mutex_methods m; /* Interface to "real" mutex system */
- int aCounter[8]; /* Number of grabs of each type of mutex */
- sqlite3_mutex aStatic[6]; /* The six static mutexes */
+ int isInstalled; /* True if installed */
+ int disableInit; /* True to cause sqlite3_initalize() to fail */
+ int disableTry; /* True to force sqlite3_mutex_try() to fail */
+ int isInit; /* True if initialized */
+ sqlite3_mutex_methods m; /* Interface to "real" mutex system */
+ int aCounter[MAX_MUTEXES]; /* Number of grabs of each type of mutex */
+ sqlite3_mutex aStatic[STATIC_MUTEXES]; /* The static mutexes */
} g = {0};
/* Return true if the countable mutex is currently held */
@@ -78,7 +88,8 @@ static sqlite3_mutex *counterMutexAlloc(int eType){
sqlite3_mutex *pRet = 0;
assert( g.isInit );
- assert(eType<8 && eType>=0);
+ assert( eType>=SQLITE_MUTEX_FAST );
+ assert( eType<=SQLITE_MUTEX_STATIC_VFS3 );
pReal = g.m.xMutexAlloc(eType);
if( !pReal ) return 0;
@@ -86,7 +97,10 @@ static sqlite3_mutex *counterMutexAlloc(int eType){
if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){
pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex));
}else{
- pRet = &g.aStatic[eType-2];
+ int eStaticType = eType - (MAX_MUTEXES - STATIC_MUTEXES);
+ assert( eStaticType>=0 );
+ assert( eStaticType<STATIC_MUTEXES );
+ pRet = &g.aStatic[eStaticType];
}
pRet->eType = eType;
@@ -110,6 +124,8 @@ static void counterMutexFree(sqlite3_mutex *p){
*/
static void counterMutexEnter(sqlite3_mutex *p){
assert( g.isInit );
+ assert( p->eType>=0 );
+ assert( p->eType<MAX_MUTEXES );
g.aCounter[p->eType]++;
g.m.xMutexEnter(p->pReal);
}
@@ -119,6 +135,8 @@ static void counterMutexEnter(sqlite3_mutex *p){
*/
static int counterMutexTry(sqlite3_mutex *p){
assert( g.isInit );
+ assert( p->eType>=0 );
+ assert( p->eType<MAX_MUTEXES );
g.aCounter[p->eType]++;
if( g.disableTry ) return SQLITE_BUSY;
return g.m.xMutexTry(p->pReal);
@@ -245,10 +263,6 @@ static int test_read_mutex_counters(
){
Tcl_Obj *pRet;
int ii;
- char *aName[8] = {
- "fast", "recursive", "static_master", "static_mem",
- "static_open", "static_prng", "static_lru", "static_pmem"
- };
if( objc!=1 ){
Tcl_WrongNumArgs(interp, 1, objv, "");
@@ -257,7 +271,7 @@ static int test_read_mutex_counters(
pRet = Tcl_NewObj();
Tcl_IncrRefCount(pRet);
- for(ii=0; ii<8; ii++){
+ for(ii=0; ii<MAX_MUTEXES; ii++){
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1));
Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii]));
}
@@ -283,7 +297,7 @@ static int test_clear_mutex_counters(
return TCL_ERROR;
}
- for(ii=0; ii<8; ii++){
+ for(ii=0; ii<MAX_MUTEXES; ii++){
g.aCounter[ii] = 0;
}
return TCL_OK;
@@ -371,6 +385,56 @@ static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){
return db;
}
+static sqlite3_mutex *getStaticMutexPointer(
+ Tcl_Interp *pInterp,
+ Tcl_Obj *pObj
+){
+ int iMutex;
+ if( Tcl_GetIndexFromObj(pInterp, pObj, aName, "mutex name", 0, &iMutex) ){
+ return 0;
+ }
+ assert( iMutex!=SQLITE_MUTEX_FAST && iMutex!=SQLITE_MUTEX_RECURSIVE );
+ return counterMutexAlloc(iMutex);
+}
+
+static int test_enter_static_mutex(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_mutex *pMutex;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NAME");
+ return TCL_ERROR;
+ }
+ pMutex = getStaticMutexPointer(interp, objv[1]);
+ if( !pMutex ){
+ return TCL_ERROR;
+ }
+ sqlite3_mutex_enter(pMutex);
+ return TCL_OK;
+}
+
+static int test_leave_static_mutex(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_mutex *pMutex;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "NAME");
+ return TCL_ERROR;
+ }
+ pMutex = getStaticMutexPointer(interp, objv[1]);
+ if( !pMutex ){
+ return TCL_ERROR;
+ }
+ sqlite3_mutex_leave(pMutex);
+ return TCL_OK;
+}
+
static int test_enter_db_mutex(
void * clientData,
Tcl_Interp *interp,
@@ -418,6 +482,9 @@ int Sqlitetest_mutex_Init(Tcl_Interp *interp){
{ "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize },
{ "sqlite3_config", (Tcl_ObjCmdProc*)test_config },
+ { "enter_static_mutex", (Tcl_ObjCmdProc*)test_enter_static_mutex },
+ { "leave_static_mutex", (Tcl_ObjCmdProc*)test_leave_static_mutex },
+
{ "enter_db_mutex", (Tcl_ObjCmdProc*)test_enter_db_mutex },
{ "leave_db_mutex", (Tcl_ObjCmdProc*)test_leave_db_mutex },
diff --git a/src/util.c b/src/util.c
index bafa3cefa..091481d92 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1082,7 +1082,7 @@ u32 sqlite3Get4byte(const u8 *p){
u32 x;
memcpy(&x,p,4);
return x;
-#elif SQLITE_BYTEORDER==1234 && defined(__GNUC__)
+#elif SQLITE_BYTEORDER==1234 && defined(__GNUC__) && GCC_VERSION>=4003000
u32 x;
memcpy(&x,p,4);
return __builtin_bswap32(x);
@@ -1098,7 +1098,7 @@ u32 sqlite3Get4byte(const u8 *p){
void sqlite3Put4byte(unsigned char *p, u32 v){
#if SQLITE_BYTEORDER==4321
memcpy(p,&v,4);
-#elif SQLITE_BYTEORDER==1234 && defined(__GNUC__)
+#elif SQLITE_BYTEORDER==1234 && defined(__GNUC__) && GCC_VERSION>=4003000
u32 x = __builtin_bswap32(v);
memcpy(p,&x,4);
#elif SQLITE_BYTEORDER==1234 && defined(_MSC_VER) && _MSC_VER>=1300
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index fa5444cf2..a35225732 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -53,6 +53,31 @@ static int vdbeSafetyNotNull(Vdbe *p){
}
}
+#ifndef SQLITE_OMIT_TRACE
+/*
+** Invoke the profile callback. This routine is only called if we already
+** know that the profile callback is defined and needs to be invoked.
+*/
+static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
+ sqlite3_int64 iNow;
+ assert( p->startTime>0 );
+ assert( db->xProfile!=0 );
+ assert( db->init.busy==0 );
+ assert( p->zSql!=0 );
+ sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
+ db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
+ p->startTime = 0;
+}
+/*
+** The checkProfileCallback(DB,P) macro checks to see if a profile callback
+** is needed, and it invokes the callback if it is needed.
+*/
+# define checkProfileCallback(DB,P) \
+ if( ((P)->startTime)>0 ){ invokeProfileCallback(DB,P); }
+#else
+# define checkProfileCallback(DB,P) /*no-op*/
+#endif
+
/*
** The following routine destroys a virtual machine that is created by
** the sqlite3_compile() routine. The integer returned is an SQLITE_
@@ -73,6 +98,7 @@ int sqlite3_finalize(sqlite3_stmt *pStmt){
sqlite3 *db = v->db;
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
sqlite3_mutex_enter(db->mutex);
+ checkProfileCallback(db, v);
rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
sqlite3LeaveMutexAndCloseZombie(db);
@@ -94,12 +120,14 @@ int sqlite3_reset(sqlite3_stmt *pStmt){
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
- sqlite3_mutex_enter(v->db->mutex);
+ sqlite3 *db = v->db;
+ sqlite3_mutex_enter(db->mutex);
+ checkProfileCallback(db, v);
rc = sqlite3VdbeReset(v);
sqlite3VdbeRewind(v);
- assert( (rc & (v->db->errMask))==rc );
- rc = sqlite3ApiExit(v->db, rc);
- sqlite3_mutex_leave(v->db->mutex);
+ assert( (rc & (db->errMask))==rc );
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
}
return rc;
}
@@ -450,6 +478,7 @@ static int doWalCallbacks(sqlite3 *db){
return rc;
}
+
/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
@@ -518,8 +547,10 @@ static int sqlite3Step(Vdbe *p){
);
#ifndef SQLITE_OMIT_TRACE
- if( db->xProfile && !db->init.busy ){
+ if( db->xProfile && !db->init.busy && p->zSql ){
sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
+ }else{
+ assert( p->startTime==0 );
}
#endif
@@ -543,13 +574,8 @@ static int sqlite3Step(Vdbe *p){
}
#ifndef SQLITE_OMIT_TRACE
- /* Invoke the profile callback if there is one
- */
- if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){
- sqlite3_int64 iNow;
- sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
- db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
- }
+ /* If the statement completed successfully, invoke the profile callback */
+ if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
#endif
if( rc==SQLITE_DONE ){
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index c2e1c56f0..5c4f31b55 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -3346,6 +3346,7 @@ static int vdbeRecordCompareDebug(
/* mem1.u.i = 0; // not needed, here to silence compiler warning */
idx1 = getVarint32(aKey1, szHdr1);
+ if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
assert( pKeyInfo->aSortOrder!=0 );