mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-29 08:01:23 +03:00
Make the LIMIT clause work even if the destination of the SELECT is
something other than a callback. (Ticket #66) (CVS 619) FossilOrigin-Name: 699cf362083043615eb88635a228bfa46a315c9c
This commit is contained in:
71
src/select.c
71
src/select.c
@ -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.92 2002/06/06 23:42:28 drh Exp $
|
||||
** $Id: select.c,v 1.93 2002/06/14 22:38:42 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -294,6 +294,7 @@ static void sqliteAggregateInfoReset(Parse *pParse){
|
||||
*/
|
||||
static int selectInnerLoop(
|
||||
Parse *pParse, /* The parser context */
|
||||
Select *p, /* The complete select statement being coded */
|
||||
ExprList *pEList, /* List of values being extracted */
|
||||
int srcTab, /* Pull data from this table */
|
||||
int nColumn, /* Number of columns in the source table */
|
||||
@ -308,6 +309,18 @@ static int selectInnerLoop(
|
||||
int i;
|
||||
if( v==0 ) return 0;
|
||||
|
||||
/* If there was a LIMIT clause on the SELECT statement, then do the check
|
||||
** to see if this row should be output.
|
||||
*/
|
||||
if( pOrderBy==0 ){
|
||||
if( p->nOffset>0 ){
|
||||
sqliteVdbeAddOp(v, OP_LimitCk, 1, iContinue);
|
||||
}
|
||||
if( p->nLimit>0 ){
|
||||
sqliteVdbeAddOp(v, OP_LimitCk, 0, iBreak);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pull the requested columns.
|
||||
*/
|
||||
if( pEList ){
|
||||
@ -419,7 +432,8 @@ static int selectInnerLoop(
|
||||
/* If none of the above, send the data to the callback function.
|
||||
*/
|
||||
{
|
||||
sqliteVdbeAddOp(v, OP_Callback, nColumn, iBreak);
|
||||
assert( eDest==SRT_Callback );
|
||||
sqliteVdbeAddOp(v, OP_Callback, nColumn, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -430,12 +444,18 @@ static int selectInnerLoop(
|
||||
** we need to run the sorter and output the results. The following
|
||||
** routine generates the code needed to do that.
|
||||
*/
|
||||
static void generateSortTail(Vdbe *v, int nColumn){
|
||||
static void generateSortTail(Select *p, Vdbe *v, int nColumn){
|
||||
int end = sqliteVdbeMakeLabel(v);
|
||||
int addr;
|
||||
sqliteVdbeAddOp(v, OP_Sort, 0, 0);
|
||||
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end);
|
||||
sqliteVdbeAddOp(v, OP_SortCallback, nColumn, end);
|
||||
if( p->nOffset>0 ){
|
||||
sqliteVdbeAddOp(v, OP_LimitCk, 1, addr);
|
||||
}
|
||||
if( p->nLimit>0 ){
|
||||
sqliteVdbeAddOp(v, OP_LimitCk, 0, end);
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_SortCallback, nColumn, 0);
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, addr);
|
||||
sqliteVdbeResolveLabel(v, end);
|
||||
sqliteVdbeAddOp(v, OP_SortReset, 0, 0);
|
||||
@ -975,7 +995,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
iCont = sqliteVdbeMakeLabel(v);
|
||||
sqliteVdbeAddOp(v, OP_Rewind, unionTab, iBreak);
|
||||
iStart = sqliteVdbeCurrentAddr(v);
|
||||
rc = selectInnerLoop(pParse, 0, unionTab, p->pEList->nExpr,
|
||||
rc = selectInnerLoop(pParse, p, 0, unionTab, p->pEList->nExpr,
|
||||
p->pOrderBy, -1, eDest, iParm,
|
||||
iCont, iBreak);
|
||||
if( rc ) return 1;
|
||||
@ -984,7 +1004,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
sqliteVdbeResolveLabel(v, iBreak);
|
||||
sqliteVdbeAddOp(v, OP_Close, unionTab, 0);
|
||||
if( p->pOrderBy ){
|
||||
generateSortTail(v, p->pEList->nExpr);
|
||||
generateSortTail(p, v, p->pEList->nExpr);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1031,7 +1051,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
sqliteVdbeAddOp(v, OP_Rewind, tab1, iBreak);
|
||||
iStart = sqliteVdbeAddOp(v, OP_FullKey, tab1, 0);
|
||||
sqliteVdbeAddOp(v, OP_NotFound, tab2, iCont);
|
||||
rc = selectInnerLoop(pParse, 0, tab1, p->pEList->nExpr,
|
||||
rc = selectInnerLoop(pParse, p, 0, tab1, p->pEList->nExpr,
|
||||
p->pOrderBy, -1, eDest, iParm,
|
||||
iCont, iBreak);
|
||||
if( rc ) return 1;
|
||||
@ -1041,7 +1061,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
sqliteVdbeAddOp(v, OP_Close, tab2, 0);
|
||||
sqliteVdbeAddOp(v, OP_Close, tab1, 0);
|
||||
if( p->pOrderBy ){
|
||||
generateSortTail(v, p->pEList->nExpr);
|
||||
generateSortTail(p, v, p->pEList->nExpr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1176,6 +1196,14 @@ substExprList(ExprList *pList, int iTable, ExprList *pEList, int iSub){
|
||||
**
|
||||
** (7) The subquery has a FROM clause.
|
||||
**
|
||||
** (8) The subquery does not use LIMIT or the outer query is not a join.
|
||||
**
|
||||
** (9) The subquery does not use LIMIT or the outer query does not use
|
||||
** aggregates.
|
||||
**
|
||||
** (10) The subquery does not use aggregates or the outer query does not
|
||||
** use LIMIT.
|
||||
**
|
||||
** In this routine, the "p" parameter is a pointer to the outer query.
|
||||
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
|
||||
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
|
||||
@ -1207,9 +1235,10 @@ int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){
|
||||
pSubSrc = pSub->pSrc;
|
||||
assert( pSubSrc );
|
||||
if( pSubSrc->nSrc!=1 ) return 0;
|
||||
if( pSub->isDistinct && pSrc->nSrc>1 ) return 0;
|
||||
if( pSub->isDistinct && isAgg ) return 0;
|
||||
if( p->isDistinct && subqueryIsAgg ) return 0;
|
||||
if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){
|
||||
return 0;
|
||||
}
|
||||
if( (p->isDistinct || p->nLimit) && subqueryIsAgg ) return 0;
|
||||
|
||||
/* If we reach this point, it means flattening is permitted for the
|
||||
** i-th entry of the FROM clause in the outer query.
|
||||
@ -1267,6 +1296,14 @@ int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){
|
||||
}
|
||||
}
|
||||
p->isDistinct = p->isDistinct || pSub->isDistinct;
|
||||
if( pSub->nLimit>=0 ){
|
||||
if( p->nLimit<0 ){
|
||||
p->nLimit = pSub->nLimit;
|
||||
}else if( p->nLimit+p->nOffset > pSub->nLimit+pSub->nOffset ){
|
||||
p->nLimit = pSub->nLimit + pSub->nOffset - p->nOffset;
|
||||
}
|
||||
}
|
||||
p->nOffset += pSub->nOffset;
|
||||
if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
|
||||
sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
|
||||
}
|
||||
@ -1386,7 +1423,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
|
||||
eList.a = &eListItem;
|
||||
eList.a[0].pExpr = pExpr;
|
||||
cont = sqliteVdbeMakeLabel(v);
|
||||
selectInnerLoop(pParse, &eList, base, 1, 0, -1, eDest, iParm, cont, cont);
|
||||
selectInnerLoop(pParse, p, &eList, base, 1, 0, -1, eDest, iParm, cont, cont);
|
||||
sqliteVdbeResolveLabel(v, cont);
|
||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||
return 1;
|
||||
@ -1720,8 +1757,8 @@ int sqliteSelect(
|
||||
** aggregates
|
||||
*/
|
||||
if( !isAgg ){
|
||||
if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
|
||||
pWInfo->iContinue, pWInfo->iBreak) ){
|
||||
if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
|
||||
iParm, pWInfo->iContinue, pWInfo->iBreak) ){
|
||||
goto select_end;
|
||||
}
|
||||
}
|
||||
@ -1779,8 +1816,8 @@ int sqliteSelect(
|
||||
if( pHaving ){
|
||||
sqliteExprIfFalse(pParse, pHaving, startagg, 1);
|
||||
}
|
||||
if( selectInnerLoop(pParse, pEList, 0, 0, pOrderBy, distinct, eDest, iParm,
|
||||
startagg, endagg) ){
|
||||
if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
|
||||
iParm, startagg, endagg) ){
|
||||
goto select_end;
|
||||
}
|
||||
sqliteVdbeAddOp(v, OP_Goto, 0, startagg);
|
||||
@ -1793,7 +1830,7 @@ int sqliteSelect(
|
||||
** and send them to the callback one by one.
|
||||
*/
|
||||
if( pOrderBy ){
|
||||
generateSortTail(v, pEList->nExpr);
|
||||
generateSortTail(p, v, pEList->nExpr);
|
||||
}
|
||||
|
||||
|
||||
|
89
src/vdbe.c
89
src/vdbe.c
@ -30,7 +30,7 @@
|
||||
** But other routines are also provided to help in building up
|
||||
** a program instruction by instruction.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.155 2002/06/11 02:25:42 danielk1977 Exp $
|
||||
** $Id: vdbe.c,v 1.156 2002/06/14 22:38:43 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@ -1080,6 +1080,7 @@ static char *zOpName[] = { 0,
|
||||
"Gt", "Ge", "IsNull", "NotNull",
|
||||
"Negative", "And", "Or", "Not",
|
||||
"Concat", "Noop", "Function", "Limit",
|
||||
"LimitCk",
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1585,18 +1586,11 @@ case OP_ColumnName: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Callback P1 P2 *
|
||||
/* Opcode: Callback P1 * *
|
||||
**
|
||||
** Pop P1 values off the stack and form them into an array. Then
|
||||
** invoke the callback function using the newly formed array as the
|
||||
** 3rd parameter.
|
||||
**
|
||||
** If the offset counter (set by the OP_Limit opcode) is positive,
|
||||
** then decrement the counter and do not invoke the callback.
|
||||
**
|
||||
** If the callback is invoked, then after the callback returns
|
||||
** decrement the limit counter. When the limit counter reaches
|
||||
** zero, jump to address P2.
|
||||
*/
|
||||
case OP_Callback: {
|
||||
int i = p->tos - pOp->p1 + 1;
|
||||
@ -1612,22 +1606,12 @@ case OP_Callback: {
|
||||
}
|
||||
zStack[p->tos+1] = 0;
|
||||
if( xCallback!=0 ){
|
||||
if( p->iOffset>0 ){
|
||||
p->iOffset--;
|
||||
}else{
|
||||
if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
|
||||
if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){
|
||||
rc = SQLITE_ABORT;
|
||||
}
|
||||
if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
|
||||
p->nCallback++;
|
||||
if( p->iLimit>0 ){
|
||||
p->iLimit--;
|
||||
if( p->iLimit==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
}
|
||||
if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
|
||||
if( xCallback(pArg, pOp->p1, &zStack[i], p->azColName)!=0 ){
|
||||
rc = SQLITE_ABORT;
|
||||
}
|
||||
if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
|
||||
p->nCallback++;
|
||||
}
|
||||
PopStack(p, pOp->p1);
|
||||
if( sqlite_malloc_failed ) goto no_mem;
|
||||
@ -3917,6 +3901,35 @@ case OP_Limit: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: LimitCk P1 P2 *
|
||||
**
|
||||
** If P1 is 1, then check to see if the offset counter (set by the
|
||||
** P2 argument of OP_Limit) is positive. If the offset counter is
|
||||
** positive then decrement the counter and jump immediately to P2.
|
||||
** Otherwise fall straight through.
|
||||
**
|
||||
** If P1 is 0, then check the value of the limit counter (set by the
|
||||
** P1 argument of OP_Limit). If the limit counter is negative or
|
||||
** zero then jump immedately to P2. Otherwise decrement the limit
|
||||
** counter and fall through.
|
||||
*/
|
||||
case OP_LimitCk: {
|
||||
if( pOp->p1 ){
|
||||
if( p->iOffset ){
|
||||
p->iOffset--;
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
}else{
|
||||
if( p->iLimit>0 ){
|
||||
p->iLimit--;
|
||||
}else{
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Opcode: ListWrite * * *
|
||||
**
|
||||
** Write the integer on the top of the stack
|
||||
@ -4207,40 +4220,22 @@ case OP_SortNext: {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: SortCallback P1 P2 *
|
||||
/* Opcode: SortCallback P1 * *
|
||||
**
|
||||
** The top of the stack contains a callback record built using
|
||||
** the SortMakeRec operation with the same P1 value as this
|
||||
** instruction. Pop this record from the stack and invoke the
|
||||
** callback on it.
|
||||
**
|
||||
** If the offset counter (set by the OP_Limit opcode) is positive,
|
||||
** then decrement the counter and do not invoke the callback.
|
||||
**
|
||||
** If the callback is invoked, then after the callback returns
|
||||
** decrement the limit counter. When the limit counter reaches
|
||||
** zero, jump to address P2.
|
||||
*/
|
||||
case OP_SortCallback: {
|
||||
int i = p->tos;
|
||||
VERIFY( if( i<0 ) goto not_enough_stack; )
|
||||
if( xCallback!=0 ){
|
||||
if( p->iOffset>0 ){
|
||||
p->iOffset--;
|
||||
}else{
|
||||
if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
|
||||
if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){
|
||||
rc = SQLITE_ABORT;
|
||||
}
|
||||
if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
|
||||
p->nCallback++;
|
||||
if( p->iLimit>0 ){
|
||||
p->iLimit--;
|
||||
if( p->iLimit==0 ){
|
||||
pc = pOp->p2 - 1;
|
||||
}
|
||||
}
|
||||
if( sqliteSafetyOff(db) ) goto abort_due_to_misuse;
|
||||
if( xCallback(pArg, pOp->p1, (char**)zStack[i], p->azColName)!=0 ){
|
||||
rc = SQLITE_ABORT;
|
||||
}
|
||||
if( sqliteSafetyOn(db) ) goto abort_due_to_misuse;
|
||||
p->nCallback++;
|
||||
}
|
||||
POPSTACK;
|
||||
|
@ -15,7 +15,7 @@
|
||||
** or VDBE. The VDBE implements an abstract machine that runs a
|
||||
** simple program to access and modify the underlying database.
|
||||
**
|
||||
** $Id: vdbe.h,v 1.54 2002/06/08 23:25:09 drh Exp $
|
||||
** $Id: vdbe.h,v 1.55 2002/06/14 22:38:43 drh Exp $
|
||||
*/
|
||||
#ifndef _SQLITE_VDBE_H_
|
||||
#define _SQLITE_VDBE_H_
|
||||
@ -204,9 +204,10 @@ typedef struct VdbeOp VdbeOp;
|
||||
#define OP_Function 119
|
||||
|
||||
#define OP_Limit 120
|
||||
#define OP_LimitCk 121
|
||||
|
||||
|
||||
#define OP_MAX 120
|
||||
#define OP_MAX 121
|
||||
|
||||
/*
|
||||
** Prototypes for the VDBE interface. See comments on the implementation
|
||||
|
@ -13,7 +13,7 @@
|
||||
** the WHERE clause of SQL statements. Also found here are subroutines
|
||||
** to generate VDBE code to evaluate expressions.
|
||||
**
|
||||
** $Id: where.c,v 1.51 2002/06/14 20:58:45 drh Exp $
|
||||
** $Id: where.c,v 1.52 2002/06/14 22:38:43 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@ -258,6 +258,7 @@ WhereInfo *sqliteWhereBegin(
|
||||
*/
|
||||
if( pWhere && sqliteExprIsConstant(pWhere) ){
|
||||
sqliteExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1);
|
||||
pWhere = 0;
|
||||
}
|
||||
|
||||
/* Split the WHERE clause into as many as 32 separate subexpressions
|
||||
|
Reference in New Issue
Block a user