Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -2379,10 +2379,11 @@ } sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut); } if( iCol>=0 ){ sqlite3ColumnDefault(v, pTab, iCol, regOut); + sqlite3VdbeOptimizeColumnOpcodes(v); } } /* ** Generate code that will extract the iColumn-th column from Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -704,11 +704,11 @@ pParse->nMem += nResultCol; } pDest->nSdst = nResultCol; regResult = pDest->iSdst; if( srcTab>=0 ){ - for(i=0; i=0; i--){ sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i); VdbeComment((v, "%s", pEList->a[i].zName)); } }else if( eDest!=SRT_Exists ){ /* If the destination is an EXISTS(...) expression, the actual @@ -1223,14 +1223,15 @@ codeOffset(v, p->iOffset, addrContinue); iSortTab = iTab; p5 = 0; bSeq = 1; } - for(i=0; i=0; i--){ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i); - if( i==0 ) sqlite3VdbeChangeP5(v, p5); + sqlite3VdbeChangeP5(v, p5); VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan)); + p5 = 0; } switch( eDest ){ case SRT_Table: case SRT_EphemTab: { testcase( eDest==SRT_Table ); Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -2663,10 +2663,11 @@ #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 */ +#define OPFLAG_MULTICOLUMN 0x10 /* OP_Column followed by another */ /* * Each trigger present in the database schema is stored as an instance of * struct Trigger. * Index: src/update.c ================================================================== --- src/update.c +++ src/update.c @@ -71,10 +71,11 @@ if( pValue ){ sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); } #ifndef SQLITE_OMIT_FLOATING_POINT if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ + sqlite3VdbeOptimizeColumnOpcodes(v); sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); } #endif } } Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -2264,13 +2264,15 @@ u32 offset; /* Offset into the data */ u32 szField; /* Number of bytes in the content of a field */ u32 avail; /* Number of bytes of available data */ u32 t; /* A type code from the record header */ u16 fx; /* pDest->flags value */ + u8 p5; Mem *pReg; /* PseudoTable input register */ p2 = pOp->p2; + p5 = 0; assert( pOp->p3>0 && pOp->p3<=(p->nMem-p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pOp->p1>=0 && pOp->p1nCursor ); pC = p->apCsr[pOp->p1]; @@ -2434,26 +2436,36 @@ } goto op_column_out; } } - /* Extract the content for the p2+1-th column. Control can only - ** reach this point if aOffset[p2], aOffset[p2+1], and pC->aType[p2] are - ** all valid. + /* At this point, all of the following values are set: + ** + ** p2 Index of column to extract. pOp->p2. + ** pDest Memory register into which to write result + ** pC->aRow Binary row content from the btree. Might be incomplete + ** pC->szRow Number of bytes in pC->szRow + ** aOffset[p2] Offset into the binary row where P2-th column starts + ** aOffste[p2+1] Offset into binary row of first byte past P2-th column + ** pC->aType[p2] Datatype of the P2-th column (as stored on disk) + ** + ** Extract the content for column number p2. */ +op_column_decode: assert( p2nHdrParsed ); assert( rc==SQLITE_OK ); assert( sqlite3VdbeCheckMemInvariants(pDest) ); if( VdbeMemDynamic(pDest) ) sqlite3VdbeMemSetNull(pDest); t = pC->aType[p2]; + p5 = pOp->p5; if( pC->szRow>=aOffset[p2+1] ){ /* This is the common case where the desired content fits on the original ** page - where the content is not on an overflow page */ sqlite3VdbeSerialGet(pC->aRow+aOffset[p2], t, pDest); }else{ /* This branch happens only when content is on overflow pages */ - if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 + if( ((p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0 && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0)) || (len = sqlite3VdbeSerialTypeLen(t))==0 ){ /* Content is irrelevant for ** 1. the typeof() function, @@ -2493,10 +2505,27 @@ pDest->flags = fx|MEM_Term; } op_column_error: UPDATE_MAX_BLOBSIZE(pDest); REGISTER_TRACE(pOp->p3, pDest); + + /* If the OPFLAG_MULTICOLUMN bit is set on P5, that means that this + ** OP_Column is immediately followed by another OP_Column with the same + ** P1 and a P2 that is no larger than the current P2. In that case, + ** process the following OP_Column as part of this instruction, without + ** returning to the main instruction dispatch loop. + */ + if( (p5 & OPFLAG_MULTICOLUMN)!=0 && rc==0 ){ + pc++; + assert( pOp[1].opcode==OP_Column ); + assert( pOp[1].p1==pOp[0].p1 ); + assert( pOp[1].p2<=pOp[0].p2 ); + pOp++; + p2 = pOp->p2; + pDest = &aMem[pOp->p3]; + goto op_column_decode; + } break; } /* Opcode: Affinity P1 P2 * P4 * ** Synopsis: affinity(r[P1@P2]) Index: src/vdbe.h ================================================================== --- src/vdbe.h +++ src/vdbe.h @@ -177,10 +177,11 @@ void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3); void sqlite3VdbeChangeP5(Vdbe*, u8 P5); void sqlite3VdbeJumpHere(Vdbe*, int addr); void sqlite3VdbeChangeToNoop(Vdbe*, int addr); int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op); +void sqlite3VdbeOptimizeColumnOpcodes(Vdbe*); void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N); void sqlite3VdbeSetP4KeyInfo(Parse*, Index*); void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -500,10 +500,19 @@ case OP_PrevIfOpen: { pOp->p4.xAdvance = sqlite3BtreePrevious; pOp->p4type = P4_ADVANCE; break; } + case OP_Column: { + if( pOp[1].opcode==OP_Column + && pOp[1].p1==pOp->p1 + && pOp[1].p2<=pOp->p2 + ){ + pOp->p5 |= OPFLAG_MULTICOLUMN; + } + break; + } } pOp->opflags = sqlite3OpcodeProperty[opcode]; if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){ assert( -1-pOp->p2nLabel ); @@ -761,10 +770,34 @@ return 1; }else{ return 0; } } + +/* +** When running multiple OP_Column opcodes in a row, it is advantagous +** to run the one with the largest P2 value (the largest column number) +** first. This routine checks the last few OP_Column opcodes and +** might reorder them so that a larger P2 value opertion occurs at +** the start of the list. +*/ +void sqlite3VdbeOptimizeColumnOpcodes(Vdbe *p){ + VdbeOp *aOp, tempOp; + int i; + aOp = p->aOp; + i = p->nOp-2; + while( i>p->pParse->iFixedOp + && aOp[i+1].opcode==OP_Column + && aOp[i].opcode==OP_Column + && aOp[i].p1==aOp[i+1].p1 + && aOp[i].p2