1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-14 00:22:38 +03:00

More efficient handling of the LIMIT clause. Scalar subqueries and EXISTS

on compound SELECT statements now working properly.  Ticket #1473. (CVS 2747)

FossilOrigin-Name: edca8913ca012fc0c17343a27f819de95147b1bd
This commit is contained in:
drh
2005-10-06 16:53:14 +00:00
parent 23cc57f6a3
commit ec7429ae27
9 changed files with 829 additions and 69 deletions

View File

@@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
**
** $Id: select.c,v 1.276 2005/09/20 18:13:24 drh Exp $
** $Id: select.c,v 1.277 2005/10/06 16:53:15 drh Exp $
*/
#include "sqliteInt.h"
@@ -360,13 +360,12 @@ static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){
}
/*
** Add code to implement the OFFSET and LIMIT
** Add code to implement the OFFSET
*/
static void codeLimiter(
static void codeOffset(
Vdbe *v, /* Generate code into this VM */
Select *p, /* The SELECT statement being coded */
int iContinue, /* Jump here to skip the current record */
int iBreak, /* Jump here to end the loop */
int nPop /* Number of times to pop stack when jumping */
){
if( p->iOffset>=0 && iContinue!=0 ){
@@ -380,10 +379,6 @@ static void codeLimiter(
sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue);
VdbeComment((v, "# skip OFFSET records"));
}
if( p->iLimit>=0 && iBreak!=0 ){
sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, iBreak);
VdbeComment((v, "# exit when LIMIT reached"));
}
}
/*
@@ -449,7 +444,7 @@ static int selectInnerLoop(
*/
hasDistinct = distinct>=0 && pEList && pEList->nExpr>0;
if( pOrderBy==0 && !hasDistinct ){
codeLimiter(v, p, iContinue, iBreak, 0);
codeOffset(v, p, iContinue, 0);
}
/* Pull the requested columns.
@@ -471,7 +466,7 @@ static int selectInnerLoop(
int n = pEList->nExpr;
codeDistinct(v, distinct, iContinue, n, n+1);
if( pOrderBy==0 ){
codeLimiter(v, p, iContinue, iBreak, nColumn);
codeOffset(v, p, iContinue, nColumn);
}
}
@@ -547,18 +542,26 @@ static int selectInnerLoop(
break;
}
/* If any row exists in the result set, record that fact and abort.
*/
case SRT_Exists: {
sqlite3VdbeAddOp(v, OP_MemInt, 1, iParm);
sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
/* The LIMIT clause will terminate the loop for us */
break;
}
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
** of the scan loop.
*/
case SRT_Exists:
case SRT_Mem: {
assert( nColumn==1 );
if( pOrderBy ){
pushOntoSorter(pParse, v, pOrderBy);
}else{
sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
sqlite3VdbeAddOp(v, OP_Goto, 0, iBreak);
/* The LIMIT clause will jump out of the loop for us */
}
break;
}
@@ -594,6 +597,13 @@ static int selectInnerLoop(
}
#endif
}
/* Jump to the end of the loop if the LIMIT is reached.
*/
if( p->iLimit>=0 && pOrderBy==0 ){
sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, 0);
sqlite3VdbeAddOp(v, OP_IfMemZero, p->iLimit, iBreak);
}
return 0;
}
@@ -661,7 +671,7 @@ static void generateSortTail(
iTab = pOrderBy->iECursor;
addr = 1 + sqlite3VdbeAddOp(v, OP_Sort, iTab, brk);
codeLimiter(v, p, cont, brk, 0);
codeOffset(v, p, cont, 0);
sqlite3VdbeAddOp(v, OP_Column, iTab, pOrderBy->nExpr + 1);
switch( eDest ){
case SRT_Table:
@@ -681,11 +691,10 @@ static void generateSortTail(
sqlite3VdbeAddOp(v, OP_IdxInsert, (iParm&0x0000FFFF), 0);
break;
}
case SRT_Exists:
case SRT_Mem: {
assert( nColumn==1 );
sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
sqlite3VdbeAddOp(v, OP_Goto, 0, brk);
/* The LIMIT clause will terminate the loop for us */
break;
}
#endif
@@ -710,6 +719,16 @@ static void generateSortTail(
break;
}
}
/* Jump to the end of the loop when the LIMIT is reached
*/
if( p->iLimit>=0 ){
sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, 0);
sqlite3VdbeAddOp(v, OP_IfMemZero, p->iLimit, brk);
}
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, cont);
sqlite3VdbeAddOp(v, OP_Next, iTab, addr);
sqlite3VdbeResolveLabel(v, brk);
@@ -1328,7 +1347,7 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
/*
** Compute the iLimit and iOffset fields of the SELECT based on the
** pLimit and pOffset expressions. nLimit and nOffset hold the expressions
** pLimit and pOffset expressions. pLimit and pOffset hold the expressions
** that appear in the original SQL statement after the LIMIT and OFFSET
** keywords. Or NULL if those keywords are omitted. iLimit and iOffset
** are the integer memory register numbers for counters used to compute
@@ -1336,15 +1355,15 @@ Vdbe *sqlite3GetVdbe(Parse *pParse){
** iLimit and iOffset are negative.
**
** This routine changes the values if iLimit and iOffset only if
** a limit or offset is defined by nLimit and nOffset. iLimit and
** a limit or offset is defined by pLimit and pOffset. iLimit and
** iOffset should have been preset to appropriate default values
** (usually but not always -1) prior to calling this routine.
** Only if nLimit>=0 or nOffset>0 do the limit registers get
** Only if pLimit!=0 or pOffset!=0 do the limit registers get
** redefined. The UNION ALL operator uses this property to force
** the reuse of the same limit and offset registers across multiple
** SELECT statements.
*/
static void computeLimitRegisters(Parse *pParse, Select *p){
static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
/*
** "LIMIT -1" always shows all rows. There is some
** contraversy about what the correct behavior should be.
@@ -1360,6 +1379,7 @@ static void computeLimitRegisters(Parse *pParse, Select *p){
sqlite3VdbeAddOp(v, OP_Negative, 0, 0);
sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1);
VdbeComment((v, "# LIMIT counter"));
sqlite3VdbeAddOp(v, OP_IfMemZero, iMem, iBreak);
p->iLimit = iMem;
}
if( p->pOffset ){
@@ -1519,6 +1539,7 @@ static int multiSelect(
switch( p->op ){
case TK_ALL: {
if( pOrderBy==0 ){
int addr = 0;
assert( !pPrior->pLimit );
pPrior->pLimit = p->pLimit;
pPrior->pOffset = p->pOffset;
@@ -1531,11 +1552,18 @@ static int multiSelect(
p->iOffset = pPrior->iOffset;
p->pLimit = 0;
p->pOffset = 0;
if( p->iLimit>=0 ){
addr = sqlite3VdbeAddOp(v, OP_IfMemZero, p->iLimit, 0);
VdbeComment((v, "# Jump ahead if LIMIT reached"));
}
rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff);
p->pPrior = pPrior;
if( rc ){
goto multi_select_end;
}
if( addr ){
sqlite3VdbeJumpHere(v, addr);
}
break;
}
/* For UNION ALL ... ORDER BY fall through to the next case */
@@ -1622,8 +1650,8 @@ static int multiSelect(
}
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
sqlite3VdbeAddOp(v, OP_Rewind, unionTab, iBreak);
computeLimitRegisters(pParse, p);
iStart = sqlite3VdbeCurrentAddr(v);
rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
pOrderBy, -1, eDest, iParm,
@@ -1698,8 +1726,8 @@ static int multiSelect(
}
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
sqlite3VdbeAddOp(v, OP_Rewind, tab1, iBreak);
computeLimitRegisters(pParse, p);
iStart = sqlite3VdbeAddOp(v, OP_RowKey, tab1, 0);
sqlite3VdbeAddOp(v, OP_NotFound, tab2, iCont);
rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
@@ -2166,10 +2194,10 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
int base;
Vdbe *v;
int seekOp;
int cont;
ExprList *pEList, *pList, eList;
struct ExprList_item eListItem;
SrcList *pSrc;
int brk;
/* Check to see if this query is a simple min() or max() query. Return
** zero if it is not.
@@ -2233,11 +2261,11 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
*/
sqlite3CodeVerifySchema(pParse, pTab->iDb);
base = pSrc->a[0].iCursor;
computeLimitRegisters(pParse, p);
brk = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, brk);
if( pSrc->a[0].pSelect==0 ){
sqlite3OpenTableForReading(v, base, pTab);
}
cont = sqlite3VdbeMakeLabel(v);
if( pIdx==0 ){
sqlite3VdbeAddOp(v, seekOp, base, 0);
}else{
@@ -2266,8 +2294,8 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
memset(&eListItem, 0, sizeof(eListItem));
eList.a = &eListItem;
eList.a[0].pExpr = pExpr;
selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, cont, cont, 0);
sqlite3VdbeResolveLabel(v, cont);
selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, brk, brk, 0);
sqlite3VdbeResolveLabel(v, brk);
sqlite3VdbeAddOp(v, OP_Close, base, 0);
return 1;
@@ -2615,6 +2643,7 @@ int sqlite3Select(
int rc = 1; /* Value to return from this function */
int addrSortIndex; /* Address of an OP_OpenVirtual instruction */
AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
if( sqlite3_malloc_failed || pParse->nErr || p==0 ) return 1;
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
@@ -2663,7 +2692,6 @@ int sqlite3Select(
/* If writing to memory or generating a set
** only a single column may be output.
*/
assert( eDest!=SRT_Exists || pEList->nExpr==1 );
#ifndef SQLITE_OMIT_SUBQUERY
if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){
sqlite3ErrorMsg(pParse, "only a single result allowed for "
@@ -2772,7 +2800,8 @@ int sqlite3Select(
/* Set the limiter.
*/
computeLimitRegisters(pParse, p);
iEnd = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iEnd);
/* If the output is destined for a temporary table, open that table.
*/
@@ -2780,15 +2809,6 @@ int sqlite3Select(
sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, pEList->nExpr);
}
/* Initialize the memory cell to NULL for SRT_Mem or 0 for SRT_Exists
*/
if( eDest==SRT_Mem ){
sqlite3VdbeAddOp(v, OP_MemNull, iParm, 0);
}else if( eDest==SRT_Exists ){
sqlite3VdbeAddOp(v, OP_MemInt, 0, iParm);
}
/* Open a virtual index to use for the distinct set.
*/
if( isDistinct ){
@@ -3112,6 +3132,10 @@ int sqlite3Select(
}
#endif
/* Jump here to skip this query
*/
sqlite3VdbeResolveLabel(v, iEnd);
/* The SELECT was successfully coded. Set the return code to 0
** to indicate no errors.
*/