-C Remove\san\salways-true\sbranch\sfrom\sthe\spreupdate\shook\slogic.
-D 2018-04-13T16:23:22.787
+C Experimental\schange\sto\s"SELECT\s*\sFROM\s...\sORDER\sBY"\sprocessing\sto\sload\ssome\ncolumn\svalues\sfrom\sthe\sdb\safter\ssorting.
+D 2018-04-14T18:46:20.429
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 7016fc56c6b9bfe5daac4f34be8be38d8c0b5fab79ccbfb764d3b23bf1c6fff3
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
F src/delete.c 20c8788451dc737a967c87ea53ad43544d617f5b57d32ccce8bd52a0daf9e89b
-F src/expr.c 6a41ceb27924dcfb6dc910a283ce74e136c9c305aba87a5acbfca32f5c49caa7
+F src/expr.c 2448a255ce627c4e772bd68cf5529877c2bdfb6b580803d5fadc8528bdf7c1ef
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 66c73fcb7719b8ff0e841b58338f13604ff3e2b50a723f9b8f383595735262f6
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c 41e857c107be5fb972b87a4cbb948c8b1191f339c1e32a65742581573932e119
+F src/select.c c87425f0d56340a4ccc5aac52d07264b73c348ca45103caef9f289b09cb0e052
F src/shell.c.in cc960721e56ebc1a78773bb5d2f5608b54275f945cbe49e4afe919d6888062a7
F src/sqlite.h.in e0be726ea6e4e6571724d39d242472ecd8bd1ba6f84ade88e1641bde98a6d02b
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
-F src/sqliteInt.h a4837c57f9a3e2af100bc59f4be60d16b823f18131f8cef6a6685440f775eebd
+F src/sqliteInt.h 31a7f214a6b52d413f144d303d22c6e9a940059d9fb86e6f49e5ec7ad65636e9
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
F test/sort3.test 1480ed7c4c157682542224e05e3b75faf4a149e5
F test/sort4.test 5c34d9623a4ae5921d956dfa2b70e77ed0fc6e5c
F test/sort5.test 6b43ae0e2169b5ceed441844492e55ba7f1ae0790528395ddf7888ab3094525d
+F test/sorterref.test a13ed207a0eea3c7898f308f979bfb518f68c598ec737d2c494dfd3deaa83506
F test/sortfault.test d4ccf606a0c77498e2beb542764fd9394acb4d66
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P d7ec7b5970df9e542401f8299e098a5039d1875e2e3383dad06a0ec998589b6e
-R f9cb5caefaf1e23db60ecde977cce853
-U drh
-Z 2e05817b48e1d8380863fbfa532bc935
+P 0ab4518811b23bdb57feba55014cef07de66028f8fcbf8cf0831a712b2954b91
+R 58c773e5cd03f7f4dcbed950a8e0accb
+T *branch * sorter-reference
+T *sym-sorter-reference *
+T +closed caee77d9a8e1da2551cef44ba7a11e2de36eb8c047273148b4126bfc1edd21da
+T -sym-trunk *
+U dan
+Z 5d1ae4ddc55c798d695f83fc2835288d
/*
** An instance of the following object is used to record information about
** the ORDER BY (or GROUP BY) clause of query is being coded.
+**
+** The aDefer[] array is used by the sorter-references optimization. For
+** example, assuming there is no index that can be used for the ORDER BY,
+** for the query:
+**
+** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10;
+**
+** it may be more efficient to add just the "a" values to the sorter, and
+** retrieve the associated "bigblob" values directly from table t1 as the
+** 10 smallest "a" values are extracted from the sorter.
+**
+** When the sorter-reference optimization is used, there is one entry in the
+** aDefer[] array for each database table that may be read as values are
+** extracted from the sorter.
*/
typedef struct SortCtx SortCtx;
struct SortCtx {
int labelDone; /* Jump here when done, ex: LIMIT reached */
u8 sortFlags; /* Zero or more SORTFLAG_* bits */
u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ u8 nDefer; /* Number of valid entries in aDefer[] */
+ struct DeferredCsr {
+ Table *pTab; /* Table definition */
+ int iCsr; /* Cursor number for table */
+ int nKey; /* Number of PK columns for table pTab (>=1) */
+ } aDefer[4];
+#endif
};
#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
sqlite3ReleaseTempReg(pParse, r1);
}
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+/*
+** This function is called as part of inner-loop generation for a SELECT
+** statement with an ORDER BY that is not optimized by an index. It
+** determines the expressions, if any, that the sorter-reference
+** optimization should be used for. The sorter-reference optimization
+** is used for SELECT queries like:
+**
+** SELECT a, bigblob FROM t1 ORDER BY a LIMIT 10
+**
+** If the optimization is used for expression "bigblob", then instead of
+** storing values read from that column in the sorter records, the PK of
+** the row from table t1 is stored instead. Then, as records are extracted from
+** the sorter to return to the user, the required value of bigblob is
+** retrieved directly from table t1. If the values are very large, this
+** can be more efficient than storing them directly in the sorter records.
+**
+** The ExprList_item.bSorterRef flag is set for each expression in pEList
+** for which the sorter-reference optimization should be enabled.
+** Additionally, the pSort->aDefer[] array is populated with entries
+** for all cursors required to evaluate all selected expressions. Finally.
+** output variable (*ppExtra) is set to an expression list containing
+** expressions for all extra PK values that should be stored in the
+** sorter records.
+*/
+static void selectExprDefer(
+ Parse *pParse, /* Leave any error here */
+ SortCtx *pSort, /* Sorter context */
+ ExprList *pEList, /* Expressions destined for sorter */
+ ExprList **ppExtra /* Expressions to append to sorter record */
+){
+ int i;
+ int nDefer = 0;
+ ExprList *pExtra = 0;
+ for(i=0; i<pEList->nExpr; i++){
+ struct ExprList_item *pItem = &pEList->a[i];
+ if( pItem->u.x.iOrderByCol==0 ){
+ Expr *pExpr = pItem->pExpr;
+ Table *pTab = pExpr->pTab;
+ if( pExpr->op==TK_COLUMN && pTab && pTab->pSchema && pTab->pSelect==0
+ && !IsVirtual(pTab)
+ ){
+ int j;
+ for(j=0; j<nDefer; j++){
+ if( pSort->aDefer[j].iCsr==pExpr->iTable ) break;
+ }
+ if( j==nDefer ){
+ if( nDefer==ArraySize(pSort->aDefer) ){
+ continue;
+ }else{
+ int nKey = 1;
+ int k;
+ Index *pPk = 0;
+ if( !HasRowid(pTab) ){
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ nKey = pPk->nKeyCol;
+ }
+ for(k=0; k<nKey; k++){
+ Expr *pNew = sqlite3PExpr(pParse, TK_COLUMN, 0, 0);
+ if( pNew ){
+ pNew->iTable = pExpr->iTable;
+ pNew->pTab = pExpr->pTab;
+ pNew->iColumn = pPk ? pPk->aiColumn[k] : -1;
+ pExtra = sqlite3ExprListAppend(pParse, pExtra, pNew);
+ }
+ }
+ pSort->aDefer[nDefer].pTab = pExpr->pTab;
+ pSort->aDefer[nDefer].iCsr = pExpr->iTable;
+ pSort->aDefer[nDefer].nKey = nKey;
+ nDefer++;
+ }
+ }
+ pItem->bSorterRef = 1;
+ }
+ }
+ }
+ pSort->nDefer = (u8)nDefer;
+ *ppExtra = pExtra;
+}
+#endif
+
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
VdbeComment((v, "%s", p->pEList->a[i].zName));
}
}else if( eDest!=SRT_Exists ){
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ ExprList *pExtra = 0;
+#endif
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
p->pEList->a[j-1].u.x.iOrderByCol = i+1-pSort->nOBSat;
}
}
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ selectExprDefer(pParse, pSort, p->pEList, &pExtra);
+ if( pExtra ){
+ /* If there are any extra PK columns to add to the sorter records,
+ ** allocate extra memory cells and adjust the OpenEphemeral
+ ** instruction to account for the larger records. This is only
+ ** required if there are one or more WITHOUT ROWID tables with
+ ** composite primary keys in the SortCtx.aDefer[] array. */
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
+ pOp->p2 += (pExtra->nExpr - pSort->nDefer);
+ pOp->p4.pKeyInfo->nAllField += (pExtra->nExpr - pSort->nDefer);
+ pParse->nMem += pExtra->nExpr;
+ }
+#endif
regOrig = 0;
assert( eDest==SRT_Set || eDest==SRT_Mem
|| eDest==SRT_Coroutine || eDest==SRT_Output );
}
nResultCol = sqlite3ExprCodeExprList(pParse,p->pEList,regResult,
0,ecelFlags);
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( pExtra ){
+ nResultCol += sqlite3ExprCodeExprList(
+ pParse, pExtra, regResult + nResultCol, 0, 0
+ );
+ sqlite3ExprListDelete(pParse->db, pExtra);
+ }
+#endif
}
/* If the DISTINCT keyword was present on the SELECT statement
Vdbe *v = pParse->pVdbe; /* The prepared statement */
int addrBreak = pSort->labelDone; /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
- int addr;
+ int addr; /* Top of output loop. Jump for Next. */
int addrOnce = 0;
int iTab;
ExprList *pOrderBy = pSort->pOrderBy;
int regRow;
int regRowid;
int iCol;
- int nKey;
+ int nKey; /* Number of key columns in sorter record */
int iSortTab; /* Sorter cursor to read from */
int i;
int bSeq; /* True if sorter record includes seq. no. */
+ int nRefKey = 0;
struct ExprList_item *aOutEx = p->pEList->a;
assert( addrBreak<0 );
sqlite3VdbeGoto(v, addrBreak);
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
}
+
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ /* Open any cursors needed for sorter-reference expressions */
+ for(i=0; i<pSort->nDefer; i++){
+ Table *pTab = pSort->aDefer[i].pTab;
+ int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ sqlite3OpenTable(pParse, pSort->aDefer[i].iCsr, iDb, pTab, OP_OpenRead);
+ nRefKey = MAX(nRefKey, pSort->aDefer[i].nKey);
+ }
+#endif
+
iTab = pSort->iECursor;
if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
regRowid = 0;
if( pSort->labelBkOut ){
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nColumn);
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut,
+ nKey+1+nColumn+nRefKey);
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
bSeq = 1;
}
for(i=0, iCol=nKey+bSeq-1; i<nColumn; i++){
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( aOutEx[i].bSorterRef ) continue;
+#endif
if( aOutEx[i].u.x.iOrderByCol==0 ) iCol++;
}
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( pSort->nDefer ){
+ int iKey = iCol+1;
+ int regKey = sqlite3GetTempRange(pParse, nRefKey);
+
+ for(i=0; i<pSort->nDefer; i++){
+ int iCsr = pSort->aDefer[i].iCsr;
+ Table *pTab = pSort->aDefer[i].pTab;
+ int nKey = pSort->aDefer[i].nKey;
+
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey);
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iCsr,
+ sqlite3VdbeCurrentAddr(v)+1, regKey);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ int k;
+ int iJmp;
+ assert( pPk->nKeyCol==nKey );
+ for(k=0; k<nKey; k++){
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iKey++, regKey+k);
+ }
+ iJmp = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp4Int(v, OP_SeekGE, iCsr, iJmp+2, regKey, nKey);
+ sqlite3VdbeAddOp4Int(v, OP_IdxLE, iCsr, iJmp+3, regKey, nKey);
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCsr);
+ }
+ }
+ sqlite3ReleaseTempRange(pParse, regKey, nRefKey);
+ }
+#endif
for(i=nColumn-1; i>=0; i--){
- int iRead;
- if( aOutEx[i].u.x.iOrderByCol ){
- iRead = aOutEx[i].u.x.iOrderByCol-1;
- }else{
- iRead = iCol--;
+#ifdef SQLITE_ENABLE_SORTER_REFERENCES
+ if( aOutEx[i].bSorterRef ){
+ sqlite3ExprCode(pParse, aOutEx[i].pExpr, regRow+i);
+ }else
+#endif
+ {
+ int iRead;
+ if( aOutEx[i].u.x.iOrderByCol ){
+ iRead = aOutEx[i].u.x.iOrderByCol-1;
+ }else{
+ iRead = iCol--;
+ }
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
+ VdbeComment((v, "%s", aOutEx[i].zName?aOutEx[i].zName : aOutEx[i].zSpan));
}
- sqlite3VdbeAddOp3(v, OP_Column, iSortTab, iRead, regRow+i);
- VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
case SRT_Table:
if( sSort.pOrderBy ){
explainTempTable(pParse,
sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
+ assert( p->pEList==pEList );
generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}