diff options
author | drh <drh@noemail.net> | 2015-07-14 15:39:22 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2015-07-14 15:39:22 +0000 |
commit | 38151adfe2f51c07ca50e9cbc6f44a9811097eb1 (patch) | |
tree | 44ecb68858c63553a92e23f56db4481f7a620327 /src | |
parent | bed6de557a5282f92ed5019d5c89129a6ece6bab (diff) | |
parent | 201e0c68f7d5d19ba759f56fadd0d58c838c41f9 (diff) | |
download | sqlite-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.c | 25 | ||||
-rw-r--r-- | src/global.c | 2 | ||||
-rw-r--r-- | src/malloc.c | 3 | ||||
-rw-r--r-- | src/mutex_noop.c | 2 | ||||
-rw-r--r-- | src/mutex_unix.c | 6 | ||||
-rw-r--r-- | src/mutex_w32.c | 6 | ||||
-rw-r--r-- | src/os_unix.c | 6 | ||||
-rw-r--r-- | src/os_win.c | 6 | ||||
-rw-r--r-- | src/pcache1.c | 261 | ||||
-rw-r--r-- | src/select.c | 12 | ||||
-rw-r--r-- | src/sqlite.h.in | 3 | ||||
-rw-r--r-- | src/sqliteInt.h | 10 | ||||
-rw-r--r-- | src/test_mutex.c | 97 | ||||
-rw-r--r-- | src/util.c | 4 | ||||
-rw-r--r-- | src/vdbeapi.c | 50 | ||||
-rw-r--r-- | src/vdbeaux.c | 1 |
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 ); |