1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-12 13:01:09 +03:00

Experimental change to "SELECT * FROM ... ORDER BY" processing to load some

column values from the db after sorting.

FossilOrigin-Name: 9719cb46bbf501ce80f185159d594f593dd0b2639b9ef5a71a6c7b70046cb08d
This commit is contained in:
dan
2018-04-14 18:46:20 +00:00
parent 54f2cd905b
commit 24e25d32ce
6 changed files with 268 additions and 21 deletions

View File

@@ -44,6 +44,20 @@ struct DistinctCtx {
/*
** 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 {
@@ -56,6 +70,14 @@ 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 */
@@ -678,6 +700,87 @@ static void codeDistinct(
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.
@@ -750,6 +853,9 @@ static void selectInnerLoop(
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.
*/
@@ -773,12 +879,34 @@ static void selectInnerLoop(
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
@@ -1236,7 +1364,7 @@ static void generateSortTail(
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;
@@ -1245,10 +1373,11 @@ static void generateSortTail(
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 );
@@ -1257,6 +1386,17 @@ static void generateSortTail(
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;
@@ -1272,7 +1412,8 @@ static void generateSortTail(
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);
@@ -1286,17 +1427,59 @@ static void generateSortTail(
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++;
}
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( 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--){
#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:
@@ -6072,6 +6255,7 @@ int sqlite3Select(
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);
}