Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -514,13 +514,15 @@ break; } #endif case SQLITE_CONFIG_WORKER_THREADS: { +#if SQLITE_MAX_WORKER_THREADS>0 int n = va_arg(ap, int); if( n>SQLITE_MAX_WORKER_THREADS ) n = SQLITE_MAX_WORKER_THREADS; if( n>=0 ) sqlite3GlobalConfig.nWorker = n; +#endif break; } default: { rc = SQLITE_ERROR; Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -447,11 +447,11 @@ */ static void vdbePmaReaderClear(PmaReader *pIter){ sqlite3_free(pIter->aAlloc); sqlite3_free(pIter->aBuffer); if( pIter->aMap ) sqlite3OsUnfetch(pIter->pFile, 0, pIter->aMap); - if( pIter->pIncr ) vdbeIncrFree(pIter->pIncr); + vdbeIncrFree(pIter->pIncr); memset(pIter, 0, sizeof(PmaReader)); } /* ** Read nByte bytes of data from the stream of data iterated by object p. @@ -826,11 +826,15 @@ VdbeSorter *pSorter; /* The new sorter */ KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */ int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */ int sz; /* Size of pSorter in bytes */ int rc = SQLITE_OK; +#if SQLITE_MAX_WORKER_THREADS==0 + const int nWorker = 0; +#else int nWorker = (sqlite3GlobalConfig.bCoreMutex?sqlite3GlobalConfig.nWorker:0); +#endif assert( pCsr->pKeyInfo && pCsr->pBt==0 ); szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*); sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); @@ -856,14 +860,17 @@ pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz; mxCache = db->aDb[0].pSchema->cache_size; if( mxCachemxPmaSize = mxCache * pgsz; - /* If the application is using memsys3 or memsys5, use a separate - ** allocation for each sort-key in memory. Otherwise, use a single big - ** allocation at pSorter->aMemory for all sort-keys. */ - if( sqlite3GlobalConfig.pHeap==0 ){ + /* If the application has not configure scratch memory using + ** SQLITE_CONFIG_SCRATCH then we assume it is OK to do large memory + ** allocations. If scratch memory has been configured, then assume + ** large memory allocations should be avoided to prevent heap + ** fragmentation. + */ + if( sqlite3GlobalConfig.pScratch==0 ){ assert( pSorter->iMemory==0 ); pSorter->nMemory = pgsz; pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz); if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM; } @@ -1919,11 +1926,19 @@ /* Set up the required files for pIncr. A multi-theaded IncrMerge object ** requires two temp files to itself, whereas a single-threaded object ** only requires a region of pTask->file2. */ if( rc==SQLITE_OK ){ int mxSz = pIncr->mxSz; - if( pIncr->bUseThread==0 ){ +#if SQLITE_MAX_WORKER_THREADS>0 + if( pIncr->bUseThread ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); + if( rc==SQLITE_OK ){ + rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); + } + }else +#endif + /*if( !pIncr->bUseThread )*/{ if( pTask->file2.pFd==0 ){ assert( pTask->file2.iEof>0 ); rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd); pTask->file2.iEof = 0; } @@ -1930,25 +1945,22 @@ if( rc==SQLITE_OK ){ pIncr->aFile[1].pFd = pTask->file2.pFd; pIncr->iStartOff = pTask->file2.iEof; pTask->file2.iEof += mxSz; } - }else{ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd); - if( rc==SQLITE_OK ){ - rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd); - } } } +#if SQLITE_MAX_WORKER_THREADS>0 if( rc==SQLITE_OK && pIncr->bUseThread ){ /* Use the current thread to populate aFile[1], even though this ** iterator is multi-threaded. The reason being that this function ** is already running in background thread pIncr->pTask->thread. */ assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK ); rc = vdbeIncrPopulate(pIncr); } +#endif if( rc==SQLITE_OK && eMode!=INCRINIT_TASK ){ rc = vdbePmaReaderNext(pIter); } } @@ -2109,18 +2121,20 @@ static int vdbeSorterMergeTreeBuild(VdbeSorter *pSorter, MergeEngine **ppOut){ MergeEngine *pMain = 0; int rc = SQLITE_OK; int iTask; +#if SQLITE_MAX_WORKER_THREADS>0 /* If the sorter uses more than one task, then create the top-level ** MergeEngine here. This MergeEngine will read data from exactly ** one PmaReader per sub-task. */ assert( pSorter->bUseThreads || pSorter->nTask==1 ); if( pSorter->nTask>1 ){ pMain = vdbeMergeEngineNew(pSorter->nTask); if( pMain==0 ) rc = SQLITE_NOMEM; } +#endif for(iTask=0; iTasknTask && rc==SQLITE_OK; iTask++){ SortSubtask *pTask = &pSorter->aTask[iTask]; if( pTask->nPMA ){ MergeEngine *pRoot = 0; /* Root node of tree for this task */ @@ -2299,14 +2313,17 @@ assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) ); if( pSorter->bUsePMA ){ assert( pSorter->pReader==0 || pSorter->pMerger==0 ); assert( pSorter->bUseThreads==0 || pSorter->pReader ); assert( pSorter->bUseThreads==1 || pSorter->pMerger ); +#if SQLITE_MAX_WORKER_THREADS>0 if( pSorter->bUseThreads ){ rc = vdbePmaReaderNext(pSorter->pReader); *pbEof = (pSorter->pReader->pFile==0); - }else{ + }else +#endif + /*if( !pSorter->bUseThreads )*/ { rc = vdbeSorterNext(&pSorter->aTask[0], pSorter->pMerger, pbEof); } }else{ SorterRecord *pFree = pSorter->list.pList; pSorter->list.pList = pFree->u.pNext; @@ -2326,13 +2343,19 @@ const VdbeSorter *pSorter, /* Sorter object */ int *pnKey /* OUT: Size of current key in bytes */ ){ void *pKey; if( pSorter->bUsePMA ){ - PmaReader *pReader = (pSorter->bUseThreads ? - pSorter->pReader : &pSorter->pMerger->aIter[pSorter->pMerger->aTree[1]] - ); + PmaReader *pReader; +#if SQLITE_MAX_WORKER_THREADS>0 + if( pSorter->bUseThreads ){ + pReader = pSorter->pReader; + }else +#endif + /*if( !pSorter->bUseThreads )*/{ + pReader = &pSorter->pMerger->aIter[pSorter->pMerger->aTree[1]]; + } *pnKey = pReader->nKey; pKey = pReader->aKey; }else{ *pnKey = pSorter->list.pList->nVal; pKey = SRVAL(pSorter->list.pList);