Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -1169,23 +1169,19 @@ ){ Vdbe *v = pParse->pVdbe; /* The prepared statement */ int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */ int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */ int addr; - int addrOnce = 0; int iTab; ExprList *pOrderBy = pSort->pOrderBy; int eDest = pDest->eDest; int iParm = pDest->iSDParm; int regRow; int regRowid; int nKey; - int iSortTab; /* Sorter cursor to read from */ int nSortData; /* Trailing values to read from sorter */ - u8 p5; /* p5 parameter for 1st OP_Column */ int i; - int bSeq; /* True if sorter record includes seq. no. */ #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS struct ExprList_item *aOutEx = p->pEList->a; #endif if( pSort->labelBkOut ){ @@ -1204,33 +1200,22 @@ nSortData = 1; } nKey = pOrderBy->nExpr - pSort->nOBSat; if( pSort->sortFlags & SORTFLAG_UseSorter ){ int regSortOut = ++pParse->nMem; - iSortTab = pParse->nTab++; - if( pSort->labelBkOut ){ - addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v); - } - sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData); - if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce); addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut); - p5 = OPFLAG_CLEARCACHE; - bSeq = 0; + sqlite3VdbeAddOp4Int(v, OP_SorterColumns, nKey, nSortData, regRow, regSortOut); }else{ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v); codeOffset(v, p->iOffset, addrContinue); - iSortTab = iTab; - p5 = 0; - bSeq = 1; - } - for(i=0; inExpr; j++){ - if( groupBySort ){ - sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); - if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); - }else{ + j = pGroupBy->nExpr; + sqlite3VdbeAddOp4Int(v, OP_SorterColumns, 0, j, iBMem, sortOut); + sqlite3VdbeAddOp1(v, OP_NullRow, sortPTab); + }else{ + for(j=0; jnExpr; j++){ sAggInfo.directMode = 1; sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); } } sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr, Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -2664,11 +2664,10 @@ #define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */ #define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ #define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ #define OPFLAG_APPEND 0x08 /* This is likely to be an append */ #define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ -#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ #define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */ #define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */ #define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */ #define OPFLAG_P2ISREG 0x02 /* P2 to OP_Open** is a register number */ #define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */ Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2237,15 +2237,10 @@ ** ** If the column contains fewer than P2 fields, then extract a NULL. Or, ** if the P4 argument is a P4_MEM use the value of the P4 argument as ** the result. ** -** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor, -** then the cache of the cursor is reset prior to extracting the column. -** The first OP_Column against a pseudo-table after the value of the content -** register has changed should have this bit set. -** ** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when ** the result is guaranteed to only be used as the argument of a length() ** or typeof() function, respectively. The loading of large blobs can be ** skipped for length() and all content loading can be skipped for typeof(). */ @@ -2286,11 +2281,11 @@ assert( pCrsr!=0 || pC->nullRow ); /* pC->nullRow on PseudoTables */ /* If the cursor cache is stale, bring it up-to-date */ rc = sqlite3VdbeCursorMoveto(pC); if( rc ) goto abort_due_to_error; - if( pC->cacheStatus!=p->cacheCtr || (pOp->p5&OPFLAG_CLEARCACHE)!=0 ){ + if( pC->cacheStatus!=p->cacheCtr ){ if( pC->nullRow ){ if( pCrsr==0 ){ assert( pC->pseudoTableReg>0 ); pReg = &aMem[pC->pseudoTableReg]; assert( pReg->flags & MEM_Blob ); @@ -4249,19 +4244,68 @@ ** Synopsis: r[P2]=data ** ** Write into register P2 the current sorter data for sorter cursor P1. */ case OP_SorterData: { - VdbeCursor *pC; + VdbeCursor *pC; /* Sorting cursor defined by P1 */ pOut = &aMem[pOp->p2]; pC = p->apCsr[pOp->p1]; assert( isSorter(pC) ); rc = sqlite3VdbeSorterRowkey(pC, pOut); assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) ); break; } + +/* Opcode: SorterColumns P1 P2 P3 P4 * +** Synopsis: r[P3@P2]=decode(r[P4]) +** +** The P4 register contains a record that has just come out of a sorter. +** Decode columns P1 through P1+P2-1 into registers P3..P3+P2-1. +** +** This opcode is much faster than multiple calls to Column since it +** does not need to deal with corrupt record detection or default values +** or any of the other complications associated with a record read +** from disk. +*/ +case OP_SorterColumns: { + Mem *pDest; /* Register P3 output register */ + Mem *pLast; /* Register P3+P2-1 */ + u32 serial_type; /* Serial type of a column value */ + u32 idx; /* Index into the record header */ + u32 d; /* Index into the data of the record */ + int nSkip; /* Number of initial columns to skip */ + const u8 *aKey; /* Complete text of the record */ + + assert( pOp->p4type==P4_INT32 ); + assert( pOp->p4.i>0 && pOp->p4.i<=(p->nMem - p->nCursor) ); + assert( pOp->p3>pOp->p4.i || pOp->p3+pOp->p2<=pOp->p4.i ); + assert( pOp->p1>=0 ); + assert( pOp->p2>0 ); + assert( aMem[pOp->p4.i].flags & MEM_Blob ); + aKey = (const u8*)aMem[pOp->p4.i].z; + pDest = &aMem[pOp->p3]; + pLast = &pDest[pOp->p2-1]; + idx = getVarint32(aKey, d); + nSkip = pOp->p1; + while( nSkip-- ){ + assert( d<=aMem[pOp->p4.i].n ); + idx += getVarint32(&aKey[idx], serial_type); + d += sqlite3VdbeSerialTypeLen(serial_type); + } + do{ + assert( d<=aMem[pOp->p4.i].n ); + idx += getVarint32(&aKey[idx], serial_type); + if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest); + d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pDest); + pDest->enc = encoding; + Deephemeralize(pDest); + REGISTER_TRACE((int)(pDest-aMem), pDest); + pDest++; + }while( pDest<=pLast ); + break; +} /* Opcode: RowData P1 P2 * * * ** Synopsis: r[P2]=data ** ** Write into register P2 the complete row data for cursor P1.