aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2011-09-14 19:41:44 +0000
committerdan <dan@noemail.net>2011-09-14 19:41:44 +0000
commit93bca695786b8a7b1809ccd68113b6e13ed93fcc (patch)
tree700d141f49e670e446bde5a6e6963cda60b8cb01 /src
parent7cca2e6259ebfa598c6fca09b597693dff3eb309 (diff)
parent0fe5f95c7ffd39599b27918927eb424e63fd36ee (diff)
downloadsqlite-93bca695786b8a7b1809ccd68113b6e13ed93fcc.tar.gz
sqlite-93bca695786b8a7b1809ccd68113b6e13ed93fcc.zip
Merge latest changes from the trunk into the sessions branch.
FossilOrigin-Name: c00e45ede7cbf71a3a6d1ccad0b9275010ca8493
Diffstat (limited to 'src')
-rw-r--r--src/btree.c68
-rw-r--r--src/btree.h1
-rw-r--r--src/build.c51
-rw-r--r--src/ctime.c3
-rw-r--r--src/expr.c2
-rw-r--r--src/os_common.h2
-rw-r--r--src/os_unix.c44
-rw-r--r--src/os_win.c24
-rw-r--r--src/pager.c27
-rw-r--r--src/pager.h3
-rw-r--r--src/select.c58
-rw-r--r--src/sqliteInt.h10
-rw-r--r--src/test1.c2
-rw-r--r--src/test_thread.c17
-rw-r--r--src/vdbe.c194
-rw-r--r--src/vdbe.h8
-rw-r--r--src/vdbeInt.h12
-rw-r--r--src/vdbeapi.c24
-rw-r--r--src/vdbeaux.c154
-rw-r--r--src/vdbemem.c36
-rw-r--r--src/vdbesort.c533
21 files changed, 781 insertions, 492 deletions
diff --git a/src/btree.c b/src/btree.c
index 0c5fa38e4..7e6e02f14 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -656,18 +656,21 @@ static int btreeMoveto(
int rc; /* Status code */
UnpackedRecord *pIdxKey; /* Unpacked index key */
char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
+ char *pFree = 0;
if( pKey ){
assert( nKey==(i64)(int)nKey );
- pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey,
- aSpace, sizeof(aSpace));
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
+ );
if( pIdxKey==0 ) return SQLITE_NOMEM;
+ sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
}else{
pIdxKey = 0;
}
rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes);
- if( pKey ){
- sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
+ if( pFree ){
+ sqlite3DbFree(pCur->pKeyInfo->db, pFree);
}
return rc;
}
@@ -1734,22 +1737,11 @@ int sqlite3BtreeOpen(
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */
assert( (flags & BTREE_SINGLE)==0 || isTempDb );
- /* The BTREE_SORTER flag is only used if SQLITE_OMIT_MERGE_SORT is undef */
-#ifdef SQLITE_OMIT_MERGE_SORT
- assert( (flags & BTREE_SORTER)==0 );
-#endif
-
- /* BTREE_SORTER is always on a BTREE_SINGLE, BTREE_OMIT_JOURNAL */
- assert( (flags & BTREE_SORTER)==0 ||
- (flags & (BTREE_SINGLE|BTREE_OMIT_JOURNAL))
- ==(BTREE_SINGLE|BTREE_OMIT_JOURNAL) );
-
if( db->flags & SQLITE_NoReadlock ){
flags |= BTREE_NO_READLOCK;
}
if( isMemdb ){
flags |= BTREE_MEMORY;
- flags &= ~BTREE_SORTER;
}
if( (vfsFlags & SQLITE_OPEN_MAIN_DB)!=0 && (isMemdb || isTempDb) ){
vfsFlags = (vfsFlags & ~SQLITE_OPEN_MAIN_DB) | SQLITE_OPEN_TEMP_DB;
@@ -2754,11 +2746,12 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
if( eType==PTRMAP_OVERFLOW1 ){
CellInfo info;
btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow ){
- if( iFrom==get4byte(&pCell[info.iOverflow]) ){
- put4byte(&pCell[info.iOverflow], iTo);
- break;
- }
+ if( info.iOverflow
+ && pCell+info.iOverflow+3<=pPage->aData+pPage->maskPage
+ && iFrom==get4byte(&pCell[info.iOverflow])
+ ){
+ put4byte(&pCell[info.iOverflow], iTo);
+ break;
}
}else{
if( get4byte(pCell)==iFrom ){
@@ -3479,7 +3472,8 @@ static int btreeCursor(
return SQLITE_READONLY;
}
if( iTable==1 && btreePagecount(pBt)==0 ){
- return SQLITE_EMPTY;
+ assert( wrFlag==0 );
+ iTable = 0;
}
/* Now that no other errors can occur, finish filling in the BtCursor
@@ -4233,6 +4227,9 @@ static int moveToRoot(BtCursor *pCur){
releasePage(pCur->apPage[i]);
}
pCur->iPage = 0;
+ }else if( pCur->pgnoRoot==0 ){
+ pCur->eState = CURSOR_INVALID;
+ return SQLITE_OK;
}else{
rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]);
if( rc!=SQLITE_OK ){
@@ -4342,7 +4339,7 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
if( pCur->eState==CURSOR_INVALID ){
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->apPage[pCur->iPage]->nCell>0 );
@@ -4381,7 +4378,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
if( CURSOR_INVALID==pCur->eState ){
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
*pRes = 1;
}else{
assert( pCur->eState==CURSOR_VALID );
@@ -4454,12 +4451,12 @@ int sqlite3BtreeMovetoUnpacked(
if( rc ){
return rc;
}
- assert( pCur->apPage[pCur->iPage] );
- assert( pCur->apPage[pCur->iPage]->isInit );
- assert( pCur->apPage[pCur->iPage]->nCell>0 || pCur->eState==CURSOR_INVALID );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage] );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->isInit );
+ assert( pCur->eState==CURSOR_INVALID || pCur->apPage[pCur->iPage]->nCell>0 );
if( pCur->eState==CURSOR_INVALID ){
*pRes = -1;
- assert( pCur->apPage[pCur->iPage]->nCell==0 );
+ assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
assert( pCur->apPage[0]->intKey || pIdxKey );
@@ -5186,6 +5183,9 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
+ if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
+ return SQLITE_CORRUPT; /* Cell extends past end of page */
+ }
ovflPgno = get4byte(&pCell[info.iOverflow]);
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
@@ -7288,16 +7288,9 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return rc;
}
int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
- BtShared *pBt = p->pBt;
int rc;
sqlite3BtreeEnter(p);
- if( (pBt->openFlags&BTREE_SINGLE) ){
- pBt->nPage = 0;
- sqlite3PagerTruncateImage(pBt->pPager, 1);
- rc = newDatabase(pBt);
- }else{
- rc = btreeDropTable(p, iTable, piMoved);
- }
+ rc = btreeDropTable(p, iTable, piMoved);
sqlite3BtreeLeave(p);
return rc;
}
@@ -7376,6 +7369,11 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
i64 nEntry = 0; /* Value to return in *pnEntry */
int rc; /* Return code */
+
+ if( pCur->pgnoRoot==0 ){
+ *pnEntry = 0;
+ return SQLITE_OK;
+ }
rc = moveToRoot(pCur);
/* Unless an error occurs, the following loop runs one iteration for each
diff --git a/src/btree.h b/src/btree.h
index ce19826ad..9e3a73b3b 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -61,7 +61,6 @@ int sqlite3BtreeOpen(
#define BTREE_MEMORY 4 /* This is an in-memory DB */
#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */
#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
-#define BTREE_SORTER 32 /* Used as accumulator in external merge sort */
int sqlite3BtreeClose(Btree*);
int sqlite3BtreeSetCacheSize(Btree*,int);
diff --git a/src/build.c b/src/build.c
index 29fbf9271..27130ef28 100644
--- a/src/build.c
+++ b/src/build.c
@@ -2326,6 +2326,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
int iSorter = iTab; /* Cursor opened by OpenSorter (if in use) */
int addr1; /* Address of top of loop */
+ int addr2; /* Address to jump to for next iteration */
int tnum; /* Root page of index */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
@@ -2334,15 +2335,6 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3 *db = pParse->db; /* The database connection */
int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
- /* Set bUseSorter to use OP_OpenSorter, or clear it to insert directly
- ** into the index. The sorter is used unless either OMIT_MERGE_SORT is
- ** defined or the system is configured to store temp files in-memory. */
-#ifdef SQLITE_OMIT_MERGE_SORT
- static const int bUseSorter = 0;
-#else
- const int bUseSorter = !sqlite3TempInMemory(pParse->db);
-#endif
-
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
db->aDb[iDb].zName ) ){
@@ -2368,28 +2360,40 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3VdbeChangeP5(v, 1);
}
+#ifndef SQLITE_OMIT_MERGE_SORT
/* Open the sorter cursor if we are to use one. */
- if( bUseSorter ){
- iSorter = pParse->nTab++;
- sqlite3VdbeAddOp4(v, OP_OpenSorter, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
- sqlite3VdbeChangeP5(v, BTREE_SORTER);
- }
+ iSorter = pParse->nTab++;
+ sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
+#endif
/* Open the table. Loop through all rows of the table, inserting index
** records into the sorter. */
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
+ addr2 = addr1 + 1;
regRecord = sqlite3GetTempReg(pParse);
regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
- if( bUseSorter ){
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iSorter, regRecord);
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
- sqlite3VdbeJumpHere(v, addr1);
- addr1 = sqlite3VdbeAddOp2(v, OP_Sort, iSorter, 0);
- sqlite3VdbeAddOp2(v, OP_RowKey, iSorter, regRecord);
+#ifndef SQLITE_OMIT_MERGE_SORT
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
+ sqlite3VdbeJumpHere(v, addr1);
+ addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
+ if( pIndex->onError!=OE_None ){
+ int j2 = sqlite3VdbeCurrentAddr(v) + 3;
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
+ addr2 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord);
+ sqlite3HaltConstraint(
+ pParse, OE_Abort, "indexed columns are not unique", P4_STATIC
+ );
+ }else{
+ addr2 = sqlite3VdbeCurrentAddr(v);
}
-
+ sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+#else
if( pIndex->onError!=OE_None ){
const int regRowid = regIdxKey + pIndex->nColumn;
const int j2 = sqlite3VdbeCurrentAddr(v) + 2;
@@ -2408,10 +2412,11 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
sqlite3HaltConstraint(
pParse, OE_Abort, "indexed columns are not unique", P4_STATIC);
}
- sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, bUseSorter);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+#endif
sqlite3ReleaseTempReg(pParse, regRecord);
- sqlite3VdbeAddOp2(v, OP_Next, iSorter, addr1+1);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp1(v, OP_Close, iTab);
diff --git a/src/ctime.c b/src/ctime.c
index 77174d0da..cbf8ed55f 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -144,6 +144,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_LOCK_TRACE
"LOCK_TRACE",
#endif
+#ifdef SQLITE_MAX_SCHEMA_RETRY
+ "MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
+#endif
#ifdef SQLITE_MEMDEBUG
"MEMDEBUG",
#endif
diff --git a/src/expr.c b/src/expr.c
index ab4547db9..ab218078d 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2287,7 +2287,7 @@ int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target){
inReg = pCol->iMem;
break;
}else if( pAggInfo->useSortingIdx ){
- sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdx,
+ sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
break;
}
diff --git a/src/os_common.h b/src/os_common.h
index aa3e18a8c..f6c3e7ff8 100644
--- a/src/os_common.h
+++ b/src/os_common.h
@@ -29,7 +29,7 @@
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#ifdef SQLITE_DEBUG
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
# ifndef SQLITE_DEBUG_OS_TRACE
# define SQLITE_DEBUG_OS_TRACE 0
# endif
diff --git a/src/os_unix.c b/src/os_unix.c
index 868e029f8..d85a7949b 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -2525,11 +2525,12 @@ static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
+ afpLockingContext *context;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
- afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
+ context = (afpLockingContext *) pFile->lockingContext;
if( context->reserved ){
*pResOut = 1;
return SQLITE_OK;
@@ -2669,7 +2670,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){
** operating system calls for the specified lock.
*/
if( eFileLock==SHARED_LOCK ){
- int lrc1, lrc2, lrc1Errno;
+ int lrc1, lrc2, lrc1Errno = 0;
long lk, mask;
assert( pInode->nShared==0 );
@@ -3145,11 +3146,11 @@ int sqlite3_fullsync_count = 0;
/*
** We do not trust systems to provide a working fdatasync(). Some do.
-** Others do no. To be safe, we will stick with the (slower) fsync().
-** If you know that your system does support fdatasync() correctly,
+** Others do no. To be safe, we will stick with the (slightly slower)
+** fsync(). If you know that your system does support fdatasync() correctly,
** then simply compile with -Dfdatasync=fdatasync
*/
-#if !defined(fdatasync) && !defined(__linux__)
+#if !defined(fdatasync)
# define fdatasync fsync
#endif
@@ -3439,26 +3440,18 @@ static int proxyFileControl(sqlite3_file*,int,void*);
/*
** This function is called to handle the SQLITE_FCNTL_SIZE_HINT
-** file-control operation.
-**
-** If the user has configured a chunk-size for this file, it could be
-** that the file needs to be extended at this point. Otherwise, the
-** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix.
+** file-control operation. Enlarge the database to nBytes in size
+** (rounded up to the next chunk-size). If the database is already
+** nBytes or larger, this routine is a no-op.
*/
static int fcntlSizeHint(unixFile *pFile, i64 nByte){
- { /* preserve indentation of removed "if" */
+ if( pFile->szChunk>0 ){
i64 nSize; /* Required file size */
- i64 szChunk; /* Chunk size */
struct stat buf; /* Used to hold return values of fstat() */
if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
- szChunk = pFile->szChunk;
- if( szChunk==0 ){
- nSize = nByte;
- }else{
- nSize = ((nByte+szChunk-1) / szChunk) * szChunk;
- }
+ nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
if( nSize>(i64)buf.st_size ){
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
@@ -3647,11 +3640,9 @@ struct unixShm {
unixShmNode *pShmNode; /* The underlying unixShmNode object */
unixShm *pNext; /* Next unixShm with the same unixShmNode */
u8 hasMutex; /* True if holding the unixShmNode mutex */
+ u8 id; /* Id of this connection within its unixShmNode */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection within its unixShmNode */
-#endif
};
/*
@@ -4957,6 +4948,9 @@ static int unixOpen(
#if SQLITE_ENABLE_LOCKING_STYLE
int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY);
#endif
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
+ struct statfs fsInfo;
+#endif
/* If creating a master or main-file journal, this function will open
** a file-descriptor on the directory too. The first time unixSync()
@@ -5089,7 +5083,6 @@ static int unixOpen(
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
- struct statfs fsInfo;
if( fstatfs(fd, &fsInfo) == -1 ){
((unixFile*)pFile)->lastErrno = errno;
robust_close(p, fd, __LINE__);
@@ -5113,7 +5106,6 @@ static int unixOpen(
if( envforce!=NULL ){
useProxy = atoi(envforce)>0;
}else{
- struct statfs fsInfo;
if( statfs(zPath, &fsInfo) == -1 ){
/* In theory, the close(fd) call is sub-optimal. If the file opened
** with fd is a database file, and there are other connections open
@@ -5854,6 +5846,8 @@ static int proxyGetHostID(unsigned char *pHostID, int *pError){
return SQLITE_IOERR;
}
}
+#else
+ UNUSED_PARAMETER(pError);
#endif
#ifdef SQLITE_TEST
/* simulate multiple hosts by creating unique hostid file paths */
@@ -5946,6 +5940,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
int nTries = 0;
struct timespec conchModTime;
+ memset(&conchModTime, 0, sizeof(conchModTime));
do {
rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
nTries ++;
@@ -6177,11 +6172,12 @@ static int proxyTakeConch(unixFile *pFile){
end_takeconch:
OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h));
if( rc==SQLITE_OK && pFile->openFlags ){
+ int fd;
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
}
pFile->h = -1;
- int fd = robust_open(pCtx->dbPath, pFile->openFlags,
+ fd = robust_open(pCtx->dbPath, pFile->openFlags,
SQLITE_DEFAULT_FILE_PERMISSIONS);
OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
if( fd>=0 ){
diff --git a/src/os_win.c b/src/os_win.c
index 02a7a0c62..c16198bd5 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -1216,7 +1216,7 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
** actual file size after the operation may be larger than the requested
** size).
*/
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
@@ -1603,18 +1603,20 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
- winFile *pFile = (winFile*)id;
- sqlite3_int64 oldSz;
- int rc = winFileSize(id, &oldSz);
- if( rc==SQLITE_OK ){
- sqlite3_int64 newSz = *(sqlite3_int64*)pArg;
- if( newSz>oldSz ){
- SimulateIOErrorBenign(1);
- rc = winTruncate(id, newSz);
- SimulateIOErrorBenign(0);
+ if( pFile->szChunk>0 ){
+ sqlite3_int64 oldSz;
+ int rc = winFileSize(id, &oldSz);
+ if( rc==SQLITE_OK ){
+ sqlite3_int64 newSz = *(sqlite3_int64*)pArg;
+ if( newSz>oldSz ){
+ SimulateIOErrorBenign(1);
+ rc = winTruncate(id, newSz);
+ SimulateIOErrorBenign(0);
+ }
}
+ return rc;
}
- return rc;
+ return SQLITE_OK;
}
case SQLITE_FCNTL_PERSIST_WAL: {
int bPersist = *(int*)pArg;
diff --git a/src/pager.c b/src/pager.c
index 373d06aec..f40d2af74 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -621,7 +621,6 @@ struct Pager {
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
u8 hasSeenStress; /* pagerStress() called one or more times */
- u8 isSorter; /* True for a PAGER_SORTER */
/**************************************************************************
** The following block contains those class members that change during
@@ -845,15 +844,6 @@ static int assert_pager_state(Pager *p){
assert( pagerUseWal(p)==0 );
}
- /* A sorter is a temp file that never spills to disk and always has
- ** the doNotSpill flag set
- */
- if( p->isSorter ){
- assert( p->tempFile );
- assert( p->doNotSpill );
- assert( p->fd->pMethods==0 );
- }
-
/* If changeCountDone is set, a RESERVED lock or greater must be held
** on the file.
*/
@@ -4557,12 +4547,6 @@ int sqlite3PagerOpen(
/* pPager->pBusyHandlerArg = 0; */
pPager->xReiniter = xReinit;
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
-#ifndef SQLITE_OMIT_MERGE_SORT
- if( flags & PAGER_SORTER ){
- pPager->doNotSpill = 1;
- pPager->isSorter = 1;
- }
-#endif
*ppPager = pPager;
return SQLITE_OK;
@@ -6107,17 +6091,6 @@ int sqlite3PagerIsMemdb(Pager *pPager){
return MEMDB;
}
-#ifndef SQLITE_OMIT_MERGE_SORT
-/*
-** Return true if the pager has seen a pagerStress callback.
-*/
-int sqlite3PagerUnderStress(Pager *pPager){
- assert( pPager->isSorter );
- assert( pPager->doNotSpill );
- return pPager->hasSeenStress;
-}
-#endif
-
/*
** Check that there are at least nSavepoint savepoints open. If there are
** currently less than nSavepoints open, then open one or more savepoints
diff --git a/src/pager.h b/src/pager.h
index ccd7467d6..2a02eff75 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -156,9 +156,6 @@ const char *sqlite3PagerJournalname(Pager*);
int sqlite3PagerNosync(Pager*);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerIsMemdb(Pager*);
-#ifndef SQLITE_OMIT_MERGE_SORT
-int sqlite3PagerUnderStress(Pager*);
-#endif
/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);
diff --git a/src/select.c b/src/select.c
index bd5e964e7..a8ea08a78 100644
--- a/src/select.c
+++ b/src/select.c
@@ -419,12 +419,18 @@ static void pushOntoSorter(
int nExpr = pOrderBy->nExpr;
int regBase = sqlite3GetTempRange(pParse, nExpr+2);
int regRecord = sqlite3GetTempReg(pParse);
+ int op;
sqlite3ExprCacheClear(pParse);
sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, pOrderBy->iECursor, regRecord);
+ if( pSelect->selFlags & SF_UseSorter ){
+ op = OP_SorterInsert;
+ }else{
+ op = OP_IdxInsert;
+ }
+ sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
if( pSelect->iLimit ){
@@ -893,9 +899,20 @@ static void generateSortTail(
}else{
regRowid = sqlite3GetTempReg(pParse);
}
- addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
- codeOffset(v, p, addrContinue);
- sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow);
+ if( p->selFlags & SF_UseSorter ){
+ int regSortOut = ++pParse->nMem;
+ int ptab2 = pParse->nTab++;
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
+ codeOffset(v, p, addrContinue);
+ sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
+ sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
+ sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
+ }else{
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
+ codeOffset(v, p, addrContinue);
+ sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
+ }
switch( eDest ){
case SRT_Table:
case SRT_EphemTab: {
@@ -948,7 +965,11 @@ static void generateSortTail(
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, addrContinue);
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
+ if( p->selFlags & SF_UseSorter ){
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
+ }
sqlite3VdbeResolveLabel(v, addrBreak);
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
@@ -3914,6 +3935,10 @@ int sqlite3Select(
iEnd = sqlite3VdbeMakeLabel(v);
p->nSelectRow = (double)LARGEST_INT64;
computeLimitRegisters(pParse, p, iEnd);
+ if( p->iLimit==0 && addrSortIndex>=0 ){
+ sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
+ p->selFlags |= SF_UseSorter;
+ }
/* Open a virtual index to use for the distinct set.
*/
@@ -3949,7 +3974,7 @@ int sqlite3Select(
if( pWInfo->eDistinct ){
VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
- assert( addrDistinctIndex>0 );
+ assert( addrDistinctIndex>=0 );
pOp = sqlite3VdbeGetOp(v, addrDistinctIndex);
assert( isDistinct );
@@ -4008,6 +4033,8 @@ int sqlite3Select(
int iAbortFlag; /* Mem address which causes query abort if positive */
int groupBySort; /* Rows come from source in GROUP BY order */
int addrEnd; /* End of processing for this SELECT */
+ int sortPTab = 0; /* Pseudotable used to decode sorting results */
+ int sortOut = 0; /* Output register from the sorter */
/* Remove any and all aliases between the result set and the
** GROUP BY clause.
@@ -4069,12 +4096,12 @@ int sqlite3Select(
/* If there is a GROUP BY clause we might need a sorting index to
** implement it. Allocate that sorting index now. If it turns out
- ** that we do not need it after all, the OpenEphemeral instruction
+ ** that we do not need it after all, the OP_SorterOpen instruction
** will be converted into a Noop.
*/
sAggInfo.sortingIdx = pParse->nTab++;
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
- addrSortingIdx = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
@@ -4155,11 +4182,14 @@ int sqlite3Select(
}
regRecord = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, sAggInfo.sortingIdx, regRecord);
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord);
sqlite3ReleaseTempReg(pParse, regRecord);
sqlite3ReleaseTempRange(pParse, regBase, nCol);
sqlite3WhereEnd(pWInfo);
- sqlite3VdbeAddOp2(v, OP_Sort, sAggInfo.sortingIdx, addrEnd);
+ sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++;
+ sortOut = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
+ sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
VdbeComment((v, "GROUP BY sort"));
sAggInfo.useSortingIdx = 1;
sqlite3ExprCacheClear(pParse);
@@ -4172,9 +4202,13 @@ int sqlite3Select(
*/
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3ExprCacheClear(pParse);
+ if( groupBySort ){
+ sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut);
+ }
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
- sqlite3VdbeAddOp3(v, OP_Column, sAggInfo.sortingIdx, j, iBMem+j);
+ sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
+ if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}else{
sAggInfo.directMode = 1;
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
@@ -4213,7 +4247,7 @@ int sqlite3Select(
/* End of the loop
*/
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_Next, sAggInfo.sortingIdx, addrTopOfLoop);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop);
}else{
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx, 1);
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index a4d7cb80d..390e40e31 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -373,14 +373,6 @@
#endif
/*
-** If all temporary storage is in-memory, then omit the external merge-sort
-** logic since it is superfluous.
-*/
-#if SQLITE_TEMP_STORE==3 && !defined(SQLITE_OMIT_MERGE_SORT)
-# define SQLITE_OMIT_MERGE_SORT
-#endif
-
-/*
** GCC does not define the offsetof() macro so we'll have to do it
** ourselves.
*/
@@ -1558,6 +1550,7 @@ struct AggInfo {
u8 useSortingIdx; /* In direct mode, reference the sorting index rather
** than the source table */
int sortingIdx; /* Cursor number of the sorting index */
+ int sortingIdxPTab; /* Cursor number of pseudo-table */
ExprList *pGroupBy; /* The group by clause */
int nSortingColumn; /* Number of columns in the sorting index */
struct AggInfo_col { /* For each column used in source tables */
@@ -2090,6 +2083,7 @@ struct Select {
#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
+#define SF_UseSorter 0x0040 /* Sort using a sorter */
/*
diff --git a/src/test1.c b/src/test1.c
index 59b570c28..26342522c 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -4395,7 +4395,7 @@ static u8 *sqlite3_stack_baseline = 0;
static void prepStack(void){
int i;
u32 bigBuf[65536];
- for(i=0; i<sizeof(bigBuf); i++) bigBuf[i] = 0xdeadbeef;
+ for(i=0; i<sizeof(bigBuf)/sizeof(bigBuf[0]); i++) bigBuf[i] = 0xdeadbeef;
sqlite3_stack_baseline = (u8*)&bigBuf[65536];
}
diff --git a/src/test_thread.c b/src/test_thread.c
index 08df14c2c..aa8946707 100644
--- a/src/test_thread.c
+++ b/src/test_thread.c
@@ -282,6 +282,21 @@ static int sqlthread_open(
zFilename = Tcl_GetString(objv[2]);
rc = sqlite3_open(zFilename, &db);
+#ifdef SQLITE_HAS_CODEC
+ if( db && objc>=4 ){
+ const char *zKey;
+ int nKey;
+ zKey = Tcl_GetStringFromObj(objv[3], &nKey);
+ rc = sqlite3_key(db, zKey, nKey);
+ if( rc!=SQLITE_OK ){
+ char *zErrMsg = sqlite3_mprintf("error %d: %s", rc, sqlite3_errmsg(db));
+ sqlite3_close(db);
+ Tcl_AppendResult(interp, zErrMsg, (char*)0);
+ sqlite3_free(zErrMsg);
+ return TCL_ERROR;
+ }
+ }
+#endif
Md5_Register(db);
sqlite3_busy_handler(db, xBusy, 0);
@@ -349,7 +364,7 @@ static int sqlthread_proc(
if( rc!=TCL_OK ) return rc;
pSub = &aSub[iIndex];
- if( objc!=(pSub->nArg+2) ){
+ if( objc<(pSub->nArg+2) ){
Tcl_WrongNumArgs(interp, 2, objv, pSub->zUsage);
return TCL_ERROR;
}
diff --git a/src/vdbe.c b/src/vdbe.c
index 924cb2012..b6bf46304 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -683,7 +683,7 @@ int sqlite3VdbeExec(
assert( pOp->p2<=p->nMem );
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
- sqlite3VdbeMemReleaseExternal(pOut);
+ MemReleaseExt(pOut);
pOut->flags = MEM_Int;
}
@@ -2128,6 +2128,7 @@ case OP_Column: {
u32 szField; /* Number of bytes in the content of a field */
int szHdr; /* Size of the header size field at start of record */
int avail; /* Number of bytes of available data */
+ u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
@@ -2139,7 +2140,6 @@ case OP_Column: {
assert( pOp->p3>0 && pOp->p3<=p->nMem );
pDest = &aMem[pOp->p3];
memAboutToChange(p, pDest);
- MemSetTypeFlag(pDest, MEM_Null);
zRec = 0;
/* This block sets the variable payloadSize to be the total number of
@@ -2183,7 +2183,7 @@ case OP_Column: {
rc = sqlite3BtreeDataSize(pCrsr, &payloadSize);
assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
}
- }else if( pC->pseudoTableReg>0 ){
+ }else if( ALWAYS(pC->pseudoTableReg>0) ){
pReg = &aMem[pC->pseudoTableReg];
assert( pReg->flags & MEM_Blob );
assert( memIsValid(pReg) );
@@ -2196,9 +2196,10 @@ case OP_Column: {
payloadSize = 0;
}
- /* If payloadSize is 0, then just store a NULL */
+ /* If payloadSize is 0, then just store a NULL. This can happen because of
+ ** nullRow or because of a corrupt database. */
if( payloadSize==0 ){
- assert( pDest->flags&MEM_Null );
+ MemSetTypeFlag(pDest, MEM_Null);
goto op_column_out;
}
assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 );
@@ -2305,8 +2306,14 @@ case OP_Column: {
for(i=0; i<nField; i++){
if( zIdx<zEndHdr ){
aOffset[i] = offset;
- zIdx += getVarint32(zIdx, aType[i]);
- szField = sqlite3VdbeSerialTypeLen(aType[i]);
+ if( zIdx[0]<0x80 ){
+ t = zIdx[0];
+ zIdx++;
+ }else{
+ zIdx += sqlite3GetVarint32(zIdx, &t);
+ }
+ aType[i] = t;
+ szField = sqlite3VdbeSerialTypeLen(t);
offset += szField;
if( offset<szField ){ /* True if offset overflows */
zIdx = &zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
@@ -2347,7 +2354,7 @@ case OP_Column: {
if( aOffset[p2] ){
assert( rc==SQLITE_OK );
if( zRec ){
- sqlite3VdbeMemReleaseExternal(pDest);
+ MemReleaseExt(pDest);
sqlite3VdbeSerialGet((u8 *)&zRec[aOffset[p2]], aType[p2], pDest);
}else{
len = sqlite3VdbeSerialTypeLen(aType[p2]);
@@ -2364,7 +2371,7 @@ case OP_Column: {
if( pOp->p4type==P4_MEM ){
sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
}else{
- assert( pDest->flags&MEM_Null );
+ MemSetTypeFlag(pDest, MEM_Null);
}
}
@@ -2560,7 +2567,7 @@ case OP_Count: { /* out2-prerelease */
BtCursor *pCrsr;
pCrsr = p->apCsr[pOp->p1]->pCursor;
- if( pCrsr ){
+ if( ALWAYS(pCrsr) ){
rc = sqlite3BtreeCount(pCrsr, &nEntry);
}else{
nEntry = 0;
@@ -3122,15 +3129,9 @@ case OP_OpenWrite: {
rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->pCursor);
pCur->pKeyInfo = pKeyInfo;
- /* Since it performs no memory allocation or IO, the only values that
- ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK.
- ** SQLITE_EMPTY is only returned when attempting to open the table
- ** rooted at page 1 of a zero-byte database. */
- assert( rc==SQLITE_EMPTY || rc==SQLITE_OK );
- if( rc==SQLITE_EMPTY ){
- pCur->pCursor = 0;
- rc = SQLITE_OK;
- }
+ /* Since it performs no memory allocation or IO, the only value that
+ ** sqlite3BtreeCursor() may return is SQLITE_OK. */
+ assert( rc==SQLITE_OK );
/* Set the VdbeCursor.isTable and isIndex variables. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
@@ -3171,13 +3172,6 @@ case OP_OpenWrite: {
** by this opcode will be used for automatically created transient
** indices in joins.
*/
-/* Opcode: OpenSorter P1 P2 * P4 *
-**
-** This opcode works like OP_OpenEphemeral except that it opens
-** a transient index that is specifically designed to sort large
-** tables using an external merge-sort algorithm.
-*/
-case OP_OpenSorter:
case OP_OpenAutoindex:
case OP_OpenEphemeral: {
VdbeCursor *pCx;
@@ -3189,7 +3183,6 @@ case OP_OpenEphemeral: {
SQLITE_OPEN_TRANSIENT_DB;
assert( pOp->p1>=0 );
- assert( (pOp->opcode==OP_OpenSorter)==((pOp->p5 & BTREE_SORTER)!=0) );
pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
if( pCx==0 ) goto no_mem;
pCx->nullRow = 1;
@@ -3223,10 +3216,27 @@ case OP_OpenEphemeral: {
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
pCx->isIndex = !pCx->isTable;
+ break;
+}
+
+/* Opcode: OpenSorter P1 P2 * P4 *
+**
+** This opcode works like OP_OpenEphemeral except that it opens
+** a transient index that is specifically designed to sort large
+** tables using an external merge-sort algorithm.
+*/
+case OP_SorterOpen: {
+ VdbeCursor *pCx;
#ifndef SQLITE_OMIT_MERGE_SORT
- if( rc==SQLITE_OK && pOp->opcode==OP_OpenSorter ){
- rc = sqlite3VdbeSorterInit(db, pCx);
- }
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
+ if( pCx==0 ) goto no_mem;
+ pCx->pKeyInfo = pOp->p4.pKeyInfo;
+ pCx->pKeyInfo->enc = ENC(p->db);
+ pCx->isSorter = 1;
+ rc = sqlite3VdbeSorterInit(db, pCx);
+#else
+ pOp->opcode = OP_OpenEphemeral;
+ pc--;
#endif
break;
}
@@ -3343,7 +3353,7 @@ case OP_SeekGt: { /* jump, in3 */
assert( OP_SeekGe == OP_SeekLt+2 );
assert( OP_SeekGt == OP_SeekLt+3 );
assert( pC->isOrdered );
- if( pC->pCursor!=0 ){
+ if( ALWAYS(pC->pCursor!=0) ){
oc = pOp->opcode;
pC->nullRow = 0;
if( pC->isTable ){
@@ -3531,6 +3541,7 @@ case OP_Found: { /* jump, in3 */
int alreadyExists;
VdbeCursor *pC;
int res;
+ char *pFree;
UnpackedRecord *pIdxKey;
UnpackedRecord r;
char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
@@ -3558,18 +3569,18 @@ case OP_Found: { /* jump, in3 */
r.flags = UNPACKED_PREFIX_MATCH;
pIdxKey = &r;
}else{
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
+ );
+ if( pIdxKey==0 ) goto no_mem;
assert( pIn3->flags & MEM_Blob );
assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
- pIdxKey = sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z,
- aTempRec, sizeof(aTempRec));
- if( pIdxKey==0 ){
- goto no_mem;
- }
+ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
}
rc = sqlite3BtreeMovetoUnpacked(pC->pCursor, pIdxKey, 0, 0, &res);
if( pOp->p4.i==0 ){
- sqlite3VdbeDeleteUnpackedRecord(pIdxKey);
+ sqlite3DbFree(db, pFree);
}
if( rc!=SQLITE_OK ){
break;
@@ -3701,7 +3712,7 @@ case OP_NotExists: { /* jump, in3 */
assert( pC->isTable );
assert( pC->pseudoTableReg==0 );
pCrsr = pC->pCursor;
- if( pCrsr!=0 ){
+ if( ALWAYS(pCrsr!=0) ){
res = 0;
iKey = pIn3->u.i;
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
@@ -4121,6 +4132,45 @@ case OP_ResetCount: {
break;
}
+/* Opcode: SorterCompare P1 P2 P3
+**
+** P1 is a sorter cursor. This instruction compares the record blob in
+** register P3 with the entry that the sorter cursor currently points to.
+** If, excluding the rowid fields at the end, the two records are a match,
+** fall through to the next instruction. Otherwise, jump to instruction P2.
+*/
+case OP_SorterCompare: {
+ VdbeCursor *pC;
+ int res;
+
+ pC = p->apCsr[pOp->p1];
+ assert( isSorter(pC) );
+ pIn3 = &aMem[pOp->p3];
+ rc = sqlite3VdbeSorterCompare(pC, pIn3, &res);
+ if( res ){
+ pc = pOp->p2-1;
+ }
+ break;
+};
+
+/* Opcode: SorterData P1 P2 * * *
+**
+** Write into register P2 the current sorter data for sorter cursor P1.
+*/
+case OP_SorterData: {
+ VdbeCursor *pC;
+#ifndef SQLITE_OMIT_MERGE_SORT
+ pOut = &aMem[pOp->p2];
+ pC = p->apCsr[pOp->p1];
+ assert( pC->isSorter );
+ rc = sqlite3VdbeSorterRowkey(pC, pOut);
+#else
+ pOp->opcode = OP_RowKey;
+ pc--;
+#endif
+ break;
+}
+
/* Opcode: RowData P1 P2 * * *
**
** Write into register P2 the complete row data for cursor P1.
@@ -4154,18 +4204,13 @@ case OP_RowData: {
/* Note that RowKey and RowData are really exactly the same instruction */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
- assert( pC->isTable || pOp->opcode==OP_RowKey );
+ assert( pC->isSorter==0 );
+ assert( pC->isTable || pOp->opcode!=OP_RowData );
assert( pC->isIndex || pOp->opcode==OP_RowData );
assert( pC!=0 );
assert( pC->nullRow==0 );
assert( pC->pseudoTableReg==0 );
-
- if( isSorter(pC) ){
- assert( pOp->opcode==OP_RowKey );
- rc = sqlite3VdbeSorterRowkey(pC, pOut);
- break;
- }
-
+ assert( !pC->isSorter );
assert( pC->pCursor!=0 );
pCrsr = pC->pCursor;
assert( sqlite3BtreeCursorIsValid(pCrsr) );
@@ -4270,6 +4315,7 @@ case OP_NullRow: {
assert( pC!=0 );
pC->nullRow = 1;
pC->rowidIsValid = 0;
+ assert( pC->pCursor || pC->pVtabCursor );
if( pC->pCursor ){
sqlite3BtreeClearCursor(pC->pCursor);
}
@@ -4293,7 +4339,7 @@ case OP_Last: { /* jump */
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pCrsr = pC->pCursor;
- if( pCrsr==0 ){
+ if( NEVER(pCrsr==0) ){
res = 1;
}else{
rc = sqlite3BtreeLast(pCrsr, &res);
@@ -4321,6 +4367,10 @@ case OP_Last: { /* jump */
** regression tests can determine whether or not the optimizer is
** correctly optimizing out sorts.
*/
+case OP_SorterSort: /* jump */
+#ifdef SQLITE_OMIT_MERGE_SORT
+ pOp->opcode = OP_Sort;
+#endif
case OP_Sort: { /* jump */
#ifdef SQLITE_TEST
sqlite3_sort_count++;
@@ -4345,10 +4395,13 @@ case OP_Rewind: { /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+ assert( pC->isSorter==(pOp->opcode==OP_SorterSort) );
res = 1;
if( isSorter(pC) ){
rc = sqlite3VdbeSorterRewind(db, pC, &res);
- }else if( (pCrsr = pC->pCursor)!=0 ){
+ }else{
+ pCrsr = pC->pCursor;
+ assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
pC->atFirst = res==0 ?1:0;
pC->deferredMoveto = 0;
@@ -4363,7 +4416,7 @@ case OP_Rewind: { /* jump */
break;
}
-/* Opcode: Next P1 P2 * * P5
+/* Opcode: Next P1 P2 * P4 P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
** table or index. If there are no more key/value pairs then fall through
@@ -4372,6 +4425,9 @@ case OP_Rewind: { /* jump */
**
** The P1 cursor must be for a real table, not a pseudo-table.
**
+** P4 is always of type P4_ADVANCE. The function pointer points to
+** sqlite3BtreeNext().
+**
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
**
@@ -4386,13 +4442,19 @@ case OP_Rewind: { /* jump */
**
** The P1 cursor must be for a real table, not a pseudo-table.
**
+** P4 is always of type P4_ADVANCE. The function pointer points to
+** sqlite3BtreePrevious().
+**
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
*/
+case OP_SorterNext: /* jump */
+#ifdef SQLITE_OMIT_MERGE_SORT
+ pOp->opcode = OP_Next;
+#endif
case OP_Prev: /* jump */
case OP_Next: { /* jump */
VdbeCursor *pC;
- BtCursor *pCrsr;
int res;
CHECK_FOR_INTERRUPT;
@@ -4402,19 +4464,17 @@ case OP_Next: { /* jump */
if( pC==0 ){
break; /* See ticket #2273 */
}
+ assert( pC->isSorter==(pOp->opcode==OP_SorterNext) );
if( isSorter(pC) ){
- assert( pOp->opcode==OP_Next );
+ assert( pOp->opcode==OP_SorterNext );
rc = sqlite3VdbeSorterNext(db, pC, &res);
}else{
- pCrsr = pC->pCursor;
- if( pCrsr==0 ){
- pC->nullRow = 1;
- break;
- }
res = 1;
assert( pC->deferredMoveto==0 );
- rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
- sqlite3BtreePrevious(pCrsr, &res);
+ assert( pC->pCursor );
+ assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
+ assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
+ rc = pOp->p4.xAdvance(pC->pCursor, &res);
}
pC->nullRow = (u8)res;
pC->cacheStatus = CACHE_STALE;
@@ -4441,6 +4501,10 @@ case OP_Next: { /* jump */
** This instruction only works for indices. The equivalent instruction
** for tables is OP_Insert.
*/
+case OP_SorterInsert: /* in2 */
+#ifdef SQLITE_OMIT_MERGE_SORT
+ pOp->opcode = OP_IdxInsert;
+#endif
case OP_IdxInsert: { /* in2 */
VdbeCursor *pC;
BtCursor *pCrsr;
@@ -4450,6 +4514,7 @@ case OP_IdxInsert: { /* in2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
+ assert( pC->isSorter==(pOp->opcode==OP_SorterInsert) );
pIn2 = &aMem[pOp->p2];
assert( pIn2->flags & MEM_Blob );
pCrsr = pC->pCursor;
@@ -4457,16 +4522,17 @@ case OP_IdxInsert: { /* in2 */
assert( pC->isTable==0 );
rc = ExpandBlob(pIn2);
if( rc==SQLITE_OK ){
- nKey = pIn2->n;
- zKey = pIn2->z;
- rc = sqlite3VdbeSorterWrite(db, pC, nKey);
- if( rc==SQLITE_OK ){
+ if( isSorter(pC) ){
+ rc = sqlite3VdbeSorterWrite(db, pC, pIn2);
+ }else{
+ nKey = pIn2->n;
+ zKey = pIn2->z;
rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0, 0, pOp->p3,
((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
- );
+ );
assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
}
- pC->cacheStatus = CACHE_STALE;
}
}
break;
diff --git a/src/vdbe.h b/src/vdbe.h
index d0333d82f..9332b9851 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -62,6 +62,7 @@ struct VdbeOp {
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
Table *pTab; /* Used when p4type is P4_TABLE */
+ int (*xAdvance)(BtCursor *, int *);
} p4;
#ifdef SQLITE_DEBUG
char *zComment; /* Comment to improve readability */
@@ -117,7 +118,8 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INT32 (-14) /* P4 is a 32-bit signed integer */
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
-#define P4_TABLE (-19) /* P4 is a pointer to a Table structure */
+#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
+#define P4_TABLE (-20) /* P4 is a pointer to a Table structure */
/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
** is made. That copy is freed when the Vdbe is finalized. But if the
@@ -212,9 +214,9 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
-UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int);
-void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*);
+void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
+UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
#ifndef SQLITE_OMIT_TRIGGER
void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index 92bc34d78..cf23d64b4 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -59,12 +59,13 @@ struct VdbeCursor {
Bool isTable; /* True if a table requiring integer keys */
Bool isIndex; /* True if an index containing keys only - no data */
Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
+ Bool isSorter; /* True if a new-style sorter */
sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
- VdbeSorter *pSorter; /* Sorter object for OP_OpenSorter cursors */
+ VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
/* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
** OP_IsUnique opcode on this cursor. */
@@ -403,6 +404,9 @@ int sqlite3VdbeMemNumerify(Mem*);
int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
void sqlite3VdbeMemRelease(Mem *p);
void sqlite3VdbeMemReleaseExternal(Mem *p);
+#define MemReleaseExt(X) \
+ if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \
+ sqlite3VdbeMemReleaseExternal(X);
int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
const char *sqlite3OpcodeName(int);
int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
@@ -420,13 +424,15 @@ void sqlite3VdbePreUpdateHook(
# define sqlite3VdbeSorterRowkey(Y,Z) SQLITE_OK
# define sqlite3VdbeSorterRewind(X,Y,Z) SQLITE_OK
# define sqlite3VdbeSorterNext(X,Y,Z) SQLITE_OK
+# define sqlite3VdbeSorterCompare(X,Y,Z) SQLITE_OK
#else
int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
-int sqlite3VdbeSorterWrite(sqlite3 *, VdbeCursor *, int);
void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
int sqlite3VdbeSorterRowkey(VdbeCursor *, Mem *);
-int sqlite3VdbeSorterRewind(sqlite3 *, VdbeCursor *, int *);
int sqlite3VdbeSorterNext(sqlite3 *, VdbeCursor *, int *);
+int sqlite3VdbeSorterRewind(sqlite3 *, VdbeCursor *, int *);
+int sqlite3VdbeSorterWrite(sqlite3 *, VdbeCursor *, Mem *);
+int sqlite3VdbeSorterCompare(VdbeCursor *, Mem *, int *);
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index dfe8f87a1..775a541f8 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -1327,6 +1327,26 @@ int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
/*
+** Allocate and populate an UnpackedRecord structure based on the serialized
+** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure
+** if successful, or a NULL pointer if an OOM error is encountered.
+*/
+static UnpackedRecord *vdbeUnpackRecord(
+ KeyInfo *pKeyInfo,
+ int nKey,
+ const void *pKey
+){
+ char *dummy; /* Dummy argument for AllocUnpackedRecord() */
+ UnpackedRecord *pRet; /* Return value */
+
+ pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo, 0, 0, &dummy);
+ if( pRet ){
+ sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
+ }
+ return pRet;
+}
+
+/*
** This function is called from within a pre-update callback to retrieve
** a field of the row currently being updated or deleted.
*/
@@ -1356,7 +1376,7 @@ int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
if( !aRec ) goto preupdate_old_out;
rc = sqlite3BtreeData(p->pCsr->pCursor, 0, nRec, aRec);
if( rc==SQLITE_OK ){
- p->pUnpacked = sqlite3VdbeRecordUnpack(&p->keyinfo, nRec, aRec, 0, 0);
+ p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
}
if( rc!=SQLITE_OK ){
@@ -1438,7 +1458,7 @@ int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
Mem *pData = &p->v->aMem[p->iNewReg];
rc = sqlite3VdbeMemExpandBlob(pData);
if( rc!=SQLITE_OK ) goto preupdate_new_out;
- pUnpack = sqlite3VdbeRecordUnpack(&p->keyinfo, pData->n, pData->z, 0, 0);
+ pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
if( !pUnpack ){
rc = SQLITE_NOMEM;
goto preupdate_new_out;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 8b86e073f..b7239bb68 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -434,6 +434,12 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
n = pOp[-1].p1;
if( n>nMaxArgs ) nMaxArgs = n;
#endif
+ }else if( opcode==OP_Next || opcode==OP_SorterNext ){
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ }else if( opcode==OP_Prev ){
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
}
if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
@@ -832,7 +838,7 @@ void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
** zeros, which is correct. MSVC generates a warning, nevertheless. */
- static const VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
+ static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->magic==VDBE_MAGIC_INIT );
if( addr<0 ){
#ifdef SQLITE_OMIT_TRACE
@@ -940,6 +946,10 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
sqlite3_snprintf(nTemp, zTemp, "program");
break;
}
+ case P4_ADVANCE: {
+ zTemp[0] = 0;
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
@@ -2821,57 +2831,70 @@ u32 sqlite3VdbeSerialGet(
return 0;
}
-
/*
-** Given the nKey-byte encoding of a record in pKey[], parse the
-** record into a UnpackedRecord structure. Return a pointer to
-** that structure.
+** This routine is used to allocate sufficient space for an UnpackedRecord
+** structure large enough to be used with sqlite3VdbeRecordUnpack() if
+** the first argument is a pointer to KeyInfo structure pKeyInfo.
**
-** The calling function might provide szSpace bytes of memory
-** space at pSpace. This space can be used to hold the returned
-** VDbeParsedRecord structure if it is large enough. If it is
-** not big enough, space is obtained from sqlite3_malloc().
+** The space is either allocated using sqlite3DbMallocRaw() or from within
+** the unaligned buffer passed via the second and third arguments (presumably
+** stack space). If the former, then *ppFree is set to a pointer that should
+** be eventually freed by the caller using sqlite3DbFree(). Or, if the
+** allocation comes from the pSpace/szSpace buffer, *ppFree is set to NULL
+** before returning.
**
-** The returned structure should be closed by a call to
-** sqlite3VdbeDeleteUnpackedRecord().
-*/
-UnpackedRecord *sqlite3VdbeRecordUnpack(
- KeyInfo *pKeyInfo, /* Information about the record format */
- int nKey, /* Size of the binary record */
- const void *pKey, /* The binary record */
- char *pSpace, /* Unaligned space available to hold the object */
- int szSpace /* Size of pSpace[] in bytes */
+** If an OOM error occurs, NULL is returned.
+*/
+UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
+ KeyInfo *pKeyInfo, /* Description of the record */
+ char *pSpace, /* Unaligned space available */
+ int szSpace, /* Size of pSpace[] in bytes */
+ char **ppFree /* OUT: Caller should free this pointer */
){
- const unsigned char *aKey = (const unsigned char *)pKey;
- UnpackedRecord *p; /* The unpacked record that we will return */
- int nByte; /* Memory space needed to hold p, in bytes */
- int d;
- u32 idx;
- u16 u; /* Unsigned loop counter */
- u32 szHdr;
- Mem *pMem;
- int nOff; /* Increase pSpace by this much to 8-byte align it */
-
- /*
- ** We want to shift the pointer pSpace up such that it is 8-byte aligned.
+ UnpackedRecord *p; /* Unpacked record to return */
+ int nOff; /* Increment pSpace by nOff to align it */
+ int nByte; /* Number of bytes required for *p */
+
+ /* We want to shift the pointer pSpace up such that it is 8-byte aligned.
** Thus, we need to calculate a value, nOff, between 0 and 7, to shift
** it by. If pSpace is already 8-byte aligned, nOff should be zero.
*/
nOff = (8 - (SQLITE_PTR_TO_INT(pSpace) & 7)) & 7;
- pSpace += nOff;
- szSpace -= nOff;
nByte = ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nField+1);
- if( nByte>szSpace ){
- p = sqlite3DbMallocRaw(pKeyInfo->db, nByte);
- if( p==0 ) return 0;
- p->flags = UNPACKED_NEED_FREE | UNPACKED_NEED_DESTROY;
+ if( nByte>szSpace+nOff ){
+ p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
+ *ppFree = (char *)p;
+ if( !p ) return 0;
}else{
- p = (UnpackedRecord*)pSpace;
- p->flags = UNPACKED_NEED_DESTROY;
+ p = (UnpackedRecord*)&pSpace[nOff];
+ *ppFree = 0;
}
+
+ p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nField + 1;
- p->aMem = pMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
+ return p;
+}
+
+/*
+** Given the nKey-byte encoding of a record in pKey[], populate the
+** UnpackedRecord structure indicated by the fourth argument with the
+** contents of the decoded record.
+*/
+void sqlite3VdbeRecordUnpack(
+ KeyInfo *pKeyInfo, /* Information about the record format */
+ int nKey, /* Size of the binary record */
+ const void *pKey, /* The binary record */
+ UnpackedRecord *p /* Populate this structure before returning. */
+){
+ const unsigned char *aKey = (const unsigned char *)pKey;
+ int d;
+ u32 idx; /* Offset in aKey[] to read from */
+ u16 u; /* Unsigned loop counter */
+ u32 szHdr;
+ Mem *pMem = p->aMem;
+
+ p->flags = 0;
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
idx = getVarint32(aKey, szHdr);
d = szHdr;
@@ -2891,31 +2914,6 @@ UnpackedRecord *sqlite3VdbeRecordUnpack(
}
assert( u<=pKeyInfo->nField + 1 );
p->nField = u;
- return (void*)p;
-}
-
-/*
-** This routine destroys a UnpackedRecord object.
-*/
-void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord *p){
-#ifdef SQLITE_DEBUG
- int i;
- Mem *pMem;
-
- assert( p!=0 );
- assert( p->flags & UNPACKED_NEED_DESTROY );
- for(i=0, pMem=p->aMem; i<p->nField; i++, pMem++){
- /* The unpacked record is always constructed by the
- ** sqlite3VdbeUnpackRecord() function above, which makes all
- ** strings and blobs static. And none of the elements are
- ** ever transformed, so there is never anything to delete.
- */
- if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
- }
-#endif
- if( p->flags & UNPACKED_NEED_FREE ){
- sqlite3DbFree(p->pKeyInfo->db, p);
- }
}
/*
@@ -3245,6 +3243,26 @@ void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
}
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+
+/*
+** If the second argument is not NULL, release any allocations associated
+** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord
+** structure itself, using sqlite3DbFree().
+**
+** This function is used to free UnpackedRecord structures allocated by
+** the vdbeUnpackRecord() function found in vdbeapi.c.
+*/
+static void vdbeFreeUnpacked(sqlite3 *db, UnpackedRecord *p){
+ if( p ){
+ int i;
+ for(i=0; i<p->nField; i++){
+ Mem *pMem = &p->aMem[i];
+ if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
+ }
+ sqlite3DbFree(db, p);
+ }
+}
+
/*
** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call,
** then cursor passed as the second argument should point to the row about
@@ -3292,12 +3310,8 @@ void sqlite3VdbePreUpdateHook(
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
- if( preupdate.pUnpacked ){
- sqlite3VdbeDeleteUnpackedRecord(preupdate.pUnpacked);
- }
- if( preupdate.pNewUnpacked ){
- sqlite3VdbeDeleteUnpackedRecord(preupdate.pNewUnpacked);
- }
+ vdbeFreeUnpacked(db, preupdate.pUnpacked);
+ vdbeFreeUnpacked(db, preupdate.pNewUnpacked);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
diff --git a/src/vdbemem.c b/src/vdbemem.c
index 882c68633..d51257282 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -271,24 +271,18 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
*/
void sqlite3VdbeMemReleaseExternal(Mem *p){
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
- testcase( p->flags & MEM_Agg );
- testcase( p->flags & MEM_Dyn );
- testcase( p->flags & MEM_RowSet );
- testcase( p->flags & MEM_Frame );
- if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){
- if( p->flags&MEM_Agg ){
- sqlite3VdbeMemFinalize(p, p->u.pDef);
- assert( (p->flags & MEM_Agg)==0 );
- sqlite3VdbeMemRelease(p);
- }else if( p->flags&MEM_Dyn && p->xDel ){
- assert( (p->flags&MEM_RowSet)==0 );
- p->xDel((void *)p->z);
- p->xDel = 0;
- }else if( p->flags&MEM_RowSet ){
- sqlite3RowSetClear(p->u.pRowSet);
- }else if( p->flags&MEM_Frame ){
- sqlite3VdbeMemSetNull(p);
- }
+ if( p->flags&MEM_Agg ){
+ sqlite3VdbeMemFinalize(p, p->u.pDef);
+ assert( (p->flags & MEM_Agg)==0 );
+ sqlite3VdbeMemRelease(p);
+ }else if( p->flags&MEM_Dyn && p->xDel ){
+ assert( (p->flags&MEM_RowSet)==0 );
+ p->xDel((void *)p->z);
+ p->xDel = 0;
+ }else if( p->flags&MEM_RowSet ){
+ sqlite3RowSetClear(p->u.pRowSet);
+ }else if( p->flags&MEM_Frame ){
+ sqlite3VdbeMemSetNull(p);
}
}
@@ -298,7 +292,7 @@ void sqlite3VdbeMemReleaseExternal(Mem *p){
** (Mem.type==SQLITE_TEXT).
*/
void sqlite3VdbeMemRelease(Mem *p){
- sqlite3VdbeMemReleaseExternal(p);
+ MemReleaseExt(p);
sqlite3DbFree(p->db, p->zMalloc);
p->z = 0;
p->zMalloc = 0;
@@ -620,7 +614,7 @@ void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
*/
void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
assert( (pFrom->flags & MEM_RowSet)==0 );
- sqlite3VdbeMemReleaseExternal(pTo);
+ MemReleaseExt(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->xDel = 0;
if( (pFrom->flags&MEM_Static)==0 ){
@@ -638,7 +632,7 @@ int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;
assert( (pFrom->flags & MEM_RowSet)==0 );
- sqlite3VdbeMemReleaseExternal(pTo);
+ MemReleaseExt(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
diff --git a/src/vdbesort.c b/src/vdbesort.c
index be99d397d..c44999705 100644
--- a/src/vdbesort.c
+++ b/src/vdbesort.c
@@ -21,6 +21,7 @@
#ifndef SQLITE_OMIT_MERGE_SORT
typedef struct VdbeSorterIter VdbeSorterIter;
+typedef struct SorterRecord SorterRecord;
/*
** NOTES ON DATA STRUCTURE USED FOR N-WAY MERGES:
@@ -92,8 +93,7 @@ typedef struct VdbeSorterIter VdbeSorterIter;
** being merged (rounded up to the next power of 2).
*/
struct VdbeSorter {
- int nWorking; /* Start a new b-tree after this many pages */
- int nBtree; /* Current size of b-tree contents as PMA */
+ int nInMemory; /* Current size of pRecord list as PMA */
int nTree; /* Used size of aTree/aIter (power of 2) */
VdbeSorterIter *aIter; /* Array of iterators to merge */
int *aTree; /* Current state of incremental merge */
@@ -101,6 +101,10 @@ struct VdbeSorter {
i64 iReadOff; /* Current read offset within file pTemp1 */
sqlite3_file *pTemp1; /* PMA file 1 */
int nPMA; /* Number of PMAs stored in pTemp1 */
+ SorterRecord *pRecord; /* Head of in-memory record list */
+ int mnPmaSize; /* Minimum PMA size, in bytes */
+ int mxPmaSize; /* Maximum PMA size, in bytes. 0==no limit */
+ UnpackedRecord *pUnpacked; /* Used to unpack keys */
};
/*
@@ -117,6 +121,17 @@ struct VdbeSorterIter {
u8 *aKey; /* Pointer to current key */
};
+/*
+** A structure to store a single record. All in-memory records are connected
+** together into a linked list headed at VdbeSorter.pRecord using the
+** SorterRecord.pNext pointer.
+*/
+struct SorterRecord {
+ void *pVal;
+ int nVal;
+ SorterRecord *pNext;
+};
+
/* Minimum allowable value for the VdbeSorter.nWorking variable */
#define SORTER_MIN_WORKING 10
@@ -142,11 +157,15 @@ static int vdbeSorterIterNext(
){
int rc; /* Return Code */
int nRead; /* Number of bytes read */
- int nRec; /* Size of record in bytes */
- int iOff; /* Size of serialized size varint in bytes */
+ int nRec = 0; /* Size of record in bytes */
+ int iOff = 0; /* Size of serialized size varint in bytes */
- nRead = pIter->iEof - pIter->iReadOff;
- if( nRead>5 ) nRead = 5;
+ assert( pIter->iEof>=pIter->iReadOff );
+ if( pIter->iEof-pIter->iReadOff>5 ){
+ nRead = 5;
+ }else{
+ nRead = (int)(pIter->iEof - pIter->iReadOff);
+ }
if( nRead<=0 ){
/* This is an EOF condition */
vdbeSorterIterZero(db, pIter);
@@ -154,25 +173,26 @@ static int vdbeSorterIterNext(
}
rc = sqlite3OsRead(pIter->pFile, pIter->aAlloc, nRead, pIter->iReadOff);
- iOff = getVarint32(pIter->aAlloc, nRec);
-
- if( rc==SQLITE_OK && (iOff+nRec)>nRead ){
- int nRead2; /* Number of extra bytes to read */
- if( (iOff+nRec)>pIter->nAlloc ){
- int nNew = pIter->nAlloc*2;
- while( (iOff+nRec)>nNew ) nNew = nNew*2;
- pIter->aAlloc = sqlite3DbReallocOrFree(db, pIter->aAlloc, nNew);
- if( !pIter->aAlloc ) return SQLITE_NOMEM;
- pIter->nAlloc = nNew;
+ if( rc==SQLITE_OK ){
+ iOff = getVarint32(pIter->aAlloc, nRec);
+ if( (iOff+nRec)>nRead ){
+ int nRead2; /* Number of extra bytes to read */
+ if( (iOff+nRec)>pIter->nAlloc ){
+ int nNew = pIter->nAlloc*2;
+ while( (iOff+nRec)>nNew ) nNew = nNew*2;
+ pIter->aAlloc = sqlite3DbReallocOrFree(db, pIter->aAlloc, nNew);
+ if( !pIter->aAlloc ) return SQLITE_NOMEM;
+ pIter->nAlloc = nNew;
+ }
+
+ nRead2 = iOff + nRec - nRead;
+ rc = sqlite3OsRead(
+ pIter->pFile, &pIter->aAlloc[nRead], nRead2, pIter->iReadOff+nRead
+ );
}
-
- nRead2 = iOff + nRec - nRead;
- rc = sqlite3OsRead(
- pIter->pFile, &pIter->aAlloc[nRead], nRead2, pIter->iReadOff+nRead
- );
}
- assert( nRec>0 || rc!=SQLITE_OK );
+ assert( rc!=SQLITE_OK || nRec>0 );
pIter->iReadOff += iOff+nRec;
pIter->nKey = nRec;
pIter->aKey = &pIter->aAlloc[iOff];
@@ -216,21 +236,14 @@ static int vdbeSorterWriteVarint(
*/
static int vdbeSorterReadVarint(
sqlite3_file *pFile, /* File to read from */
- i64 iEof, /* Total number of bytes in file */
i64 *piOffset, /* IN/OUT: Read offset in pFile */
i64 *piVal /* OUT: Value read from file */
){
u8 aVarint[9]; /* Buffer large enough for a varint */
i64 iOff = *piOffset; /* Offset in file to read from */
- int nRead = 9; /* Number of bytes to read from file */
int rc; /* Return code */
- assert( iEof>iOff );
- if( (iEof-iOff)<nRead ){
- nRead = iEof-iOff;
- }
-
- rc = sqlite3OsRead(pFile, aVarint, nRead, iOff);
+ rc = sqlite3OsRead(pFile, aVarint, 9, iOff);
if( rc==SQLITE_OK ){
*piOffset += getVarint(aVarint, (u64 *)piVal);
}
@@ -262,9 +275,8 @@ static int vdbeSorterIterInit(
if( !pIter->aAlloc ){
rc = SQLITE_NOMEM;
}else{
- i64 iEof = pSorter->iWriteOff; /* EOF of file pSorter->pTemp1 */
i64 nByte; /* Total size of PMA in bytes */
- rc = vdbeSorterReadVarint(pSorter->pTemp1, iEof, &pIter->iReadOff, &nByte);
+ rc = vdbeSorterReadVarint(pSorter->pTemp1, &pIter->iReadOff, &nByte);
*pnByte += nByte;
pIter->iEof = pIter->iReadOff + nByte;
}
@@ -274,6 +286,53 @@ static int vdbeSorterIterInit(
return rc;
}
+
+/*
+** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
+** size nKey2 bytes). Argument pKeyInfo supplies the collation functions
+** used by the comparison. If an error occurs, return an SQLite error code.
+** Otherwise, return SQLITE_OK and set *pRes to a negative, zero or positive
+** value, depending on whether key1 is smaller, equal to or larger than key2.
+**
+** If the bOmitRowid argument is non-zero, assume both keys end in a rowid
+** field. For the purposes of the comparison, ignore it. Also, if bOmitRowid
+** is true and key1 contains even a single NULL value, it is considered to
+** be less than key2. Even if key2 also contains NULL values.
+**
+** If pKey2 is passed a NULL pointer, then it is assumed that the pCsr->aSpace
+** has been allocated and contains an unpacked record that is used as key2.
+*/
+static void vdbeSorterCompare(
+ VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
+ int bOmitRowid, /* Ignore rowid field at end of keys */
+ void *pKey1, int nKey1, /* Left side of comparison */
+ void *pKey2, int nKey2, /* Right side of comparison */
+ int *pRes /* OUT: Result of comparison */
+){
+ KeyInfo *pKeyInfo = pCsr->pKeyInfo;
+ VdbeSorter *pSorter = pCsr->pSorter;
+ UnpackedRecord *r2 = pSorter->pUnpacked;
+ int i;
+
+ if( pKey2 ){
+ sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2);
+ }
+
+ if( bOmitRowid ){
+ r2->nField = pKeyInfo->nField;
+ assert( r2->nField>0 );
+ for(i=0; i<r2->nField; i++){
+ if( r2->aMem[i].flags & MEM_Null ){
+ *pRes = -1;
+ return;
+ }
+ }
+ r2->flags |= UNPACKED_PREFIX_MATCH;
+ }
+
+ *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
+}
+
/*
** This function is called to compare two iterator keys when merging
** multiple b-tree segments. Parameter iOut is the index of the aTree[]
@@ -305,20 +364,16 @@ static int vdbeSorterDoCompare(VdbeCursor *pCsr, int iOut){
}else if( p2->pFile==0 ){
iRes = i1;
}else{
- char aSpace[150];
- UnpackedRecord *r1;
-
- r1 = sqlite3VdbeRecordUnpack(
- pCsr->pKeyInfo, p1->nKey, p1->aKey, aSpace, sizeof(aSpace)
+ int res;
+ assert( pCsr->pSorter->pUnpacked!=0 ); /* allocated in vdbeSorterMerge() */
+ vdbeSorterCompare(
+ pCsr, 0, p1->aKey, p1->nKey, p2->aKey, p2->nKey, &res
);
- if( r1==0 ) return SQLITE_NOMEM;
-
- if( sqlite3VdbeRecordCompare(p2->nKey, p2->aKey, r1)>=0 ){
+ if( res<=0 ){
iRes = i1;
}else{
iRes = i2;
}
- sqlite3VdbeDeleteUnpackedRecord(r1);
}
pSorter->aTree[iOut] = iRes;
@@ -329,9 +384,42 @@ static int vdbeSorterDoCompare(VdbeCursor *pCsr, int iOut){
** Initialize the temporary index cursor just opened as a sorter cursor.
*/
int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
- assert( pCsr->pKeyInfo && pCsr->pBt );
- pCsr->pSorter = sqlite3DbMallocZero(db, sizeof(VdbeSorter));
- return (pCsr->pSorter ? SQLITE_OK : SQLITE_NOMEM);
+ int pgsz; /* Page size of main database */
+ int mxCache; /* Cache size */
+ VdbeSorter *pSorter; /* The new sorter */
+ char *d; /* Dummy */
+
+ assert( pCsr->pKeyInfo && pCsr->pBt==0 );
+ pCsr->pSorter = pSorter = sqlite3DbMallocZero(db, sizeof(VdbeSorter));
+ if( pSorter==0 ){
+ return SQLITE_NOMEM;
+ }
+
+ pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d);
+ if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM;
+ assert( pSorter->pUnpacked==(UnpackedRecord *)d );
+
+ if( !sqlite3TempInMemory(db) ){
+ pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
+ pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz;
+ mxCache = db->aDb[0].pSchema->cache_size;
+ if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
+ pSorter->mxPmaSize = mxCache * pgsz;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Free the list of sorted records starting at pRecord.
+*/
+static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
+ SorterRecord *p;
+ SorterRecord *pNext;
+ for(p=pRecord; p; p=pNext){
+ pNext = p->pNext;
+ sqlite3DbFree(db, p);
+ }
}
/*
@@ -350,6 +438,8 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
if( pSorter->pTemp1 ){
sqlite3OsCloseFree(pSorter->pTemp1);
}
+ vdbeSorterRecordFree(db, pSorter->pRecord);
+ sqlite3DbFree(db, pSorter->pUnpacked);
sqlite3DbFree(db, pSorter);
pCsr->pSorter = 0;
}
@@ -369,10 +459,82 @@ static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
);
}
+/*
+** Merge the two sorted lists p1 and p2 into a single list.
+** Set *ppOut to the head of the new list.
+*/
+static void vdbeSorterMerge(
+ VdbeCursor *pCsr, /* For pKeyInfo */
+ SorterRecord *p1, /* First list to merge */
+ SorterRecord *p2, /* Second list to merge */
+ SorterRecord **ppOut /* OUT: Head of merged list */
+){
+ SorterRecord *pFinal = 0;
+ SorterRecord **pp = &pFinal;
+ void *pVal2 = p2 ? p2->pVal : 0;
+
+ while( p1 && p2 ){
+ int res;
+ vdbeSorterCompare(pCsr, 0, p1->pVal, p1->nVal, pVal2, p2->nVal, &res);
+ if( res<=0 ){
+ *pp = p1;
+ pp = &p1->pNext;
+ p1 = p1->pNext;
+ pVal2 = 0;
+ }else{
+ *pp = p2;
+ pp = &p2->pNext;
+ p2 = p2->pNext;
+ if( p2==0 ) break;
+ pVal2 = p2->pVal;
+ }
+ }
+ *pp = p1 ? p1 : p2;
+ *ppOut = pFinal;
+}
+
+/*
+** Sort the linked list of records headed at pCsr->pRecord. Return SQLITE_OK
+** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error
+** occurs.
+*/
+static int vdbeSorterSort(VdbeCursor *pCsr){
+ int i;
+ SorterRecord **aSlot;
+ SorterRecord *p;
+ VdbeSorter *pSorter = pCsr->pSorter;
+
+ aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
+ if( !aSlot ){
+ return SQLITE_NOMEM;
+ }
+
+ p = pSorter->pRecord;
+ while( p ){
+ SorterRecord *pNext = p->pNext;
+ p->pNext = 0;
+ for(i=0; aSlot[i]; i++){
+ vdbeSorterMerge(pCsr, p, aSlot[i], &p);
+ aSlot[i] = 0;
+ }
+ aSlot[i] = p;
+ p = pNext;
+ }
+
+ p = 0;
+ for(i=0; i<64; i++){
+ vdbeSorterMerge(pCsr, p, aSlot[i], &p);
+ }
+ pSorter->pRecord = p;
+
+ sqlite3_free(aSlot);
+ return SQLITE_OK;
+}
+
/*
-** Write the current contents of the b-tree to a PMA. Return SQLITE_OK
-** if successful, or an SQLite error code otherwise.
+** Write the current contents of the in-memory linked-list to a PMA. Return
+** SQLITE_OK if successful, or an SQLite error code otherwise.
**
** The format of a PMA is:
**
@@ -383,19 +545,19 @@ static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
** Each record consists of a varint followed by a blob of data (the
** key). The varint is the number of bytes in the blob of data.
*/
-static int vdbeSorterBtreeToPMA(sqlite3 *db, VdbeCursor *pCsr){
+static int vdbeSorterListToPMA(sqlite3 *db, VdbeCursor *pCsr){
int rc = SQLITE_OK; /* Return code */
VdbeSorter *pSorter = pCsr->pSorter;
- int res = 0;
- /* sqlite3BtreeFirst() cannot fail because sorter btrees are always held
- ** in memory and so an I/O error is not possible. */
- rc = sqlite3BtreeFirst(pCsr->pCursor, &res);
- if( NEVER(rc!=SQLITE_OK) || res ) return rc;
- assert( pSorter->nBtree>0 );
+ if( pSorter->nInMemory==0 ){
+ assert( pSorter->pRecord==0 );
+ return rc;
+ }
+
+ rc = vdbeSorterSort(pCsr);
/* If the first temporary PMA file has not been opened, open it now. */
- if( pSorter->pTemp1==0 ){
+ if( rc==SQLITE_OK && pSorter->pTemp1==0 ){
rc = vdbeSorterOpenTempFile(db, &pSorter->pTemp1);
assert( rc!=SQLITE_OK || pSorter->pTemp1 );
assert( pSorter->iWriteOff==0 );
@@ -403,129 +565,87 @@ static int vdbeSorterBtreeToPMA(sqlite3 *db, VdbeCursor *pCsr){
}
if( rc==SQLITE_OK ){
- i64 iWriteOff = pSorter->iWriteOff;
- void *aMalloc = 0; /* Array used to hold a single record */
- int nMalloc = 0; /* Allocated size of aMalloc[] in bytes */
+ i64 iOff = pSorter->iWriteOff;
+ SorterRecord *p;
+ SorterRecord *pNext = 0;
+ static const char eightZeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
pSorter->nPMA++;
- for(
- rc = vdbeSorterWriteVarint(pSorter->pTemp1, pSorter->nBtree, &iWriteOff);
- rc==SQLITE_OK && res==0;
- rc = sqlite3BtreeNext(pCsr->pCursor, &res)
- ){
- i64 nKey; /* Size of this key in bytes */
-
- /* Write the size of the record in bytes to the output file */
- (void)sqlite3BtreeKeySize(pCsr->pCursor, &nKey);
- rc = vdbeSorterWriteVarint(pSorter->pTemp1, nKey, &iWriteOff);
-
- /* Make sure the aMalloc[] buffer is large enough for the record */
- if( rc==SQLITE_OK && nKey>nMalloc ){
- aMalloc = sqlite3DbReallocOrFree(db, aMalloc, nKey);
- if( !aMalloc ){
- rc = SQLITE_NOMEM;
- }else{
- nMalloc = nKey;
- }
- }
+ rc = vdbeSorterWriteVarint(pSorter->pTemp1, pSorter->nInMemory, &iOff);
+ for(p=pSorter->pRecord; rc==SQLITE_OK && p; p=pNext){
+ pNext = p->pNext;
+ rc = vdbeSorterWriteVarint(pSorter->pTemp1, p->nVal, &iOff);
- /* Write the record itself to the output file */
if( rc==SQLITE_OK ){
- /* sqlite3BtreeKey() cannot fail because sorter btrees held in memory */
- rc = sqlite3BtreeKey(pCsr->pCursor, 0, nKey, aMalloc);
- if( ALWAYS(rc==SQLITE_OK) ){
- rc = sqlite3OsWrite(pSorter->pTemp1, aMalloc, nKey, iWriteOff);
- iWriteOff += nKey;
- }
+ rc = sqlite3OsWrite(pSorter->pTemp1, p->pVal, p->nVal, iOff);
+ iOff += p->nVal;
}
- if( rc!=SQLITE_OK ) break;
+ sqlite3DbFree(db, p);
}
/* This assert verifies that unless an error has occurred, the size of
** the PMA on disk is the same as the expected size stored in
- ** pSorter->nBtree. */
- assert( rc!=SQLITE_OK || pSorter->nBtree==(
- iWriteOff-pSorter->iWriteOff-sqlite3VarintLen(pSorter->nBtree)
+ ** pSorter->nInMemory. */
+ assert( rc!=SQLITE_OK || pSorter->nInMemory==(
+ iOff-pSorter->iWriteOff-sqlite3VarintLen(pSorter->nInMemory)
));
- pSorter->iWriteOff = iWriteOff;
- sqlite3DbFree(db, aMalloc);
+ pSorter->iWriteOff = iOff;
+ if( rc==SQLITE_OK ){
+ /* Terminate each file with 8 extra bytes so that from any offset
+ ** in the file we can always read 9 bytes without a SHORT_READ error */
+ rc = sqlite3OsWrite(pSorter->pTemp1, eightZeros, 8, iOff);
+ }
+ pSorter->pRecord = p;
}
- pSorter->nBtree = 0;
return rc;
}
/*
-** This function is called on a sorter cursor by the VDBE before each row
-** is inserted into VdbeCursor.pCsr. Argument nKey is the size of the key, in
-** bytes, about to be inserted.
-**
-** If it is determined that the temporary b-tree accessed via VdbeCursor.pCsr
-** is large enough, its contents are written to a sorted PMA on disk and the
-** tree emptied. This prevents the b-tree (which must be small enough to
-** fit entirely in the cache in order to support efficient inserts) from
-** growing too large.
-**
-** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
+** Add a record to the sorter.
*/
-int sqlite3VdbeSorterWrite(sqlite3 *db, VdbeCursor *pCsr, int nKey){
- int rc = SQLITE_OK; /* Return code */
+int sqlite3VdbeSorterWrite(
+ sqlite3 *db, /* Database handle */
+ VdbeCursor *pCsr, /* Sorter cursor */
+ Mem *pVal /* Memory cell containing record */
+){
VdbeSorter *pSorter = pCsr->pSorter;
- if( pSorter ){
- Pager *pPager = sqlite3BtreePager(pCsr->pBt);
- int nPage; /* Current size of temporary file in pages */
-
- /* Sorters never spill to disk */
- assert( sqlite3PagerFile(pPager)->pMethods==0 );
-
- /* Determine how many pages the temporary b-tree has grown to */
- sqlite3PagerPagecount(pPager, &nPage);
-
- /* If pSorter->nWorking is still zero, but the temporary file has been
- ** created in the file-system, then the most recent insert into the
- ** current b-tree segment probably caused the cache to overflow (it is
- ** also possible that sqlite3_release_memory() was called). So set the
- ** size of the working set to a little less than the current size of the
- ** file in pages. */
- if( pSorter->nWorking==0 && sqlite3PagerUnderStress(pPager) ){
- pSorter->nWorking = nPage-5;
- if( pSorter->nWorking<SORTER_MIN_WORKING ){
- pSorter->nWorking = SORTER_MIN_WORKING;
- }
- }
-
- /* If the number of pages used by the current b-tree segment is greater
- ** than the size of the working set (VdbeSorter.nWorking), start a new
- ** segment b-tree. */
- if( pSorter->nWorking && nPage>=pSorter->nWorking ){
- BtCursor *p = pCsr->pCursor;/* Cursor structure to close and reopen */
- int iRoot; /* Root page of new tree */
+ int rc = SQLITE_OK; /* Return Code */
+ SorterRecord *pNew; /* New list element */
- /* Copy the current contents of the b-tree into a PMA in sorted order.
- ** Close the currently open b-tree cursor. */
- rc = vdbeSorterBtreeToPMA(db, pCsr);
- sqlite3BtreeCloseCursor(p);
+ assert( pSorter );
+ pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n;
- if( rc==SQLITE_OK ){
- rc = sqlite3BtreeDropTable(pCsr->pBt, 2, 0);
-#ifdef SQLITE_DEBUG
- sqlite3PagerPagecount(pPager, &nPage);
- assert( rc!=SQLITE_OK || nPage==1 );
-#endif
- }
- if( rc==SQLITE_OK ){
- rc = sqlite3BtreeCreateTable(pCsr->pBt, &iRoot, BTREE_BLOBKEY);
- }
- if( rc==SQLITE_OK ){
- assert( iRoot==2 );
- rc = sqlite3BtreeCursor(pCsr->pBt, iRoot, 1, pCsr->pKeyInfo, p);
- }
- }
+ pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord));
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pNew->pVal = (void *)&pNew[1];
+ memcpy(pNew->pVal, pVal->z, pVal->n);
+ pNew->nVal = pVal->n;
+ pNew->pNext = pSorter->pRecord;
+ pSorter->pRecord = pNew;
+ }
- pSorter->nBtree += sqlite3VarintLen(nKey) + nKey;
+ /* See if the contents of the sorter should now be written out. They
+ ** are written out when either of the following are true:
+ **
+ ** * The total memory allocated for the in-memory list is greater
+ ** than (page-size * cache-size), or
+ **
+ ** * The total memory allocated for the in-memory list is greater
+ ** than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
+ */
+ if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && (
+ (pSorter->nInMemory>pSorter->mxPmaSize)
+ || (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
+ )){
+ rc = vdbeSorterListToPMA(db, pCsr);
+ pSorter->nInMemory = 0;
}
+
return rc;
}
@@ -543,12 +663,12 @@ static int vdbeSorterInitMerge(
i64 nByte = 0; /* Total bytes in all opened PMAs */
/* Initialize the iterators. */
- for(i=0; rc==SQLITE_OK && i<SORTER_MAX_MERGE_COUNT; i++){
+ for(i=0; i<SORTER_MAX_MERGE_COUNT; i++){
VdbeSorterIter *pIter = &pSorter->aIter[i];
rc = vdbeSorterIterInit(db, pSorter, pSorter->iReadOff, pIter, &nByte);
pSorter->iReadOff = pIter->iEof;
- assert( pSorter->iReadOff<=pSorter->iWriteOff || rc!=SQLITE_OK );
- if( pSorter->iReadOff>=pSorter->iWriteOff ) break;
+ assert( rc!=SQLITE_OK || pSorter->iReadOff<=pSorter->iWriteOff );
+ if( rc!=SQLITE_OK || pSorter->iReadOff>=pSorter->iWriteOff ) break;
}
/* Initialize the aTree[] array. */
@@ -575,15 +695,19 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
assert( pSorter );
- /* Write the current b-tree to a PMA. Close the b-tree cursor. */
- rc = vdbeSorterBtreeToPMA(db, pCsr);
- sqlite3BtreeCloseCursor(pCsr->pCursor);
- if( rc!=SQLITE_OK ) return rc;
+ /* If no data has been written to disk, then do not do so now. Instead,
+ ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly
+ ** from the in-memory list. */
if( pSorter->nPMA==0 ){
- *pbEof = 1;
- return SQLITE_OK;
+ *pbEof = !pSorter->pRecord;
+ assert( pSorter->aTree==0 );
+ return vdbeSorterSort(pCsr);
}
+ /* Write the current b-tree to a PMA. Close the b-tree cursor. */
+ rc = vdbeSorterListToPMA(db, pCsr);
+ if( rc!=SQLITE_OK ) return rc;
+
/* Allocate space for aIter[] and aTree[]. */
nIter = pSorter->nPMA;
if( nIter>SORTER_MAX_MERGE_COUNT ) nIter = SORTER_MAX_MERGE_COUNT;
@@ -670,41 +794,88 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
*/
int sqlite3VdbeSorterNext(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
VdbeSorter *pSorter = pCsr->pSorter;
- int iPrev = pSorter->aTree[1]; /* Index of iterator to advance */
- int i; /* Index of aTree[] to recalculate */
int rc; /* Return code */
- rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]);
- for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){
- rc = vdbeSorterDoCompare(pCsr, i);
- }
+ if( pSorter->aTree ){
+ int iPrev = pSorter->aTree[1];/* Index of iterator to advance */
+ int i; /* Index of aTree[] to recalculate */
- *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+ rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]);
+ for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){
+ rc = vdbeSorterDoCompare(pCsr, i);
+ }
+
+ *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+ }else{
+ SorterRecord *pFree = pSorter->pRecord;
+ pSorter->pRecord = pFree->pNext;
+ pFree->pNext = 0;
+ vdbeSorterRecordFree(db, pFree);
+ *pbEof = !pSorter->pRecord;
+ rc = SQLITE_OK;
+ }
return rc;
}
/*
+** Return a pointer to a buffer owned by the sorter that contains the
+** current key.
+*/
+static void *vdbeSorterRowkey(
+ VdbeSorter *pSorter, /* Sorter object */
+ int *pnKey /* OUT: Size of current key in bytes */
+){
+ void *pKey;
+ if( pSorter->aTree ){
+ VdbeSorterIter *pIter;
+ pIter = &pSorter->aIter[ pSorter->aTree[1] ];
+ *pnKey = pIter->nKey;
+ pKey = pIter->aKey;
+ }else{
+ *pnKey = pSorter->pRecord->nVal;
+ pKey = pSorter->pRecord->pVal;
+ }
+ return pKey;
+}
+
+/*
** Copy the current sorter key into the memory cell pOut.
*/
int sqlite3VdbeSorterRowkey(VdbeCursor *pCsr, Mem *pOut){
VdbeSorter *pSorter = pCsr->pSorter;
- VdbeSorterIter *pIter;
-
- pIter = &pSorter->aIter[ pSorter->aTree[1] ];
+ void *pKey; int nKey; /* Sorter key to copy into pOut */
- /* Coverage testing note: As things are currently, this call will always
- ** succeed. This is because the memory cell passed by the VDBE layer
- ** happens to be the same one as was used to assemble the keys before they
- ** were passed to the sorter - meaning it is always large enough for the
- ** largest key. But this could change very easily, so we leave the call
- ** to sqlite3VdbeMemGrow() in. */
- if( NEVER(sqlite3VdbeMemGrow(pOut, pIter->nKey, 0)) ){
+ pKey = vdbeSorterRowkey(pSorter, &nKey);
+ if( sqlite3VdbeMemGrow(pOut, nKey, 0) ){
return SQLITE_NOMEM;
}
- pOut->n = pIter->nKey;
+ pOut->n = nKey;
MemSetTypeFlag(pOut, MEM_Blob);
- memcpy(pOut->z, pIter->aKey, pIter->nKey);
+ memcpy(pOut->z, pKey, nKey);
+
+ return SQLITE_OK;
+}
+
+/*
+** Compare the key in memory cell pVal with the key that the sorter cursor
+** passed as the first argument currently points to. For the purposes of
+** the comparison, ignore the rowid field at the end of each record.
+**
+** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM).
+** Otherwise, set *pRes to a negative, zero or positive value if the
+** key in pVal is smaller than, equal to or larger than the current sorter
+** key.
+*/
+int sqlite3VdbeSorterCompare(
+ VdbeCursor *pCsr, /* Sorter cursor */
+ Mem *pVal, /* Value to compare to current sorter key */
+ int *pRes /* OUT: Result of comparison */
+){
+ VdbeSorter *pSorter = pCsr->pSorter;
+ void *pKey; int nKey; /* Sorter key to compare pVal with */
+ pKey = vdbeSorterRowkey(pSorter, &nKey);
+ vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes);
return SQLITE_OK;
}