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:
206
src/select.c
206
src/select.c
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user