Fix for bugs #77 and #80: Rework the LIMIT mechanism to be reentrant and to
clean up the VDBE stack properly. (CVS 636)
FossilOrigin-Name: 9d5523107937e3700c76666fb058694babdd672c
diff --git a/src/select.c b/src/select.c
index 98ede32..4cd71a2 100644
--- a/src/select.c
+++ b/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.95 2002/06/20 03:38:26 drh Exp $
+** $Id: select.c,v 1.96 2002/06/21 23:01:50 drh Exp $
*/
#include "sqliteInt.h"
@@ -334,10 +334,12 @@
*/
if( pOrderBy==0 ){
if( p->nOffset>0 ){
- sqliteVdbeAddOp(v, OP_LimitCk, 1, iContinue);
+ int addr = sqliteVdbeCurrentAddr(v);
+ sqliteVdbeAddOp(v, OP_MemIncr, p->nOffset, addr+2);
+ sqliteVdbeAddOp(v, OP_Goto, 0, iContinue);
}
- if( p->nLimit>0 ){
- sqliteVdbeAddOp(v, OP_LimitCk, 0, iBreak);
+ if( p->nLimit>=0 ){
+ sqliteVdbeAddOp(v, OP_MemIncr, p->nLimit, iBreak);
}
}
@@ -483,10 +485,12 @@
sqliteVdbeAddOp(v, OP_Sort, 0, 0);
addr = sqliteVdbeAddOp(v, OP_SortNext, 0, end);
if( p->nOffset>0 ){
- sqliteVdbeAddOp(v, OP_LimitCk, 1, addr);
+ sqliteVdbeAddOp(v, OP_MemIncr, p->nOffset, addr+4);
+ sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+ sqliteVdbeAddOp(v, OP_Goto, 0, addr);
}
- if( p->nLimit>0 ){
- sqliteVdbeAddOp(v, OP_LimitCk, 0, end);
+ if( p->nLimit>=0 ){
+ sqliteVdbeAddOp(v, OP_MemIncr, p->nLimit, end);
}
switch( eDest ){
case SRT_Callback: {
@@ -1305,7 +1309,7 @@
if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){
return 0;
}
- if( (p->isDistinct || p->nLimit) && subqueryIsAgg ) return 0;
+ if( (p->isDistinct || p->nLimit>=0) && 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.
@@ -1722,10 +1726,21 @@
/* Set the limiter
*/
if( p->nLimit<=0 ){
+ p->nLimit = -1;
p->nOffset = 0;
}else{
- if( p->nOffset<0 ) p->nOffset = 0;
- sqliteVdbeAddOp(v, OP_Limit, p->nLimit, p->nOffset);
+ int iMem = pParse->nMem++;
+ sqliteVdbeAddOp(v, OP_Integer, -p->nLimit, 0);
+ sqliteVdbeAddOp(v, OP_MemStore, iMem, 0);
+ p->nLimit = iMem;
+ if( p->nOffset<=0 ){
+ p->nOffset = 0;
+ }else{
+ iMem = pParse->nMem++;
+ sqliteVdbeAddOp(v, OP_Integer, -p->nOffset, 0);
+ sqliteVdbeAddOp(v, OP_MemStore, iMem, 0);
+ p->nOffset = iMem;
+ }
}
/* Generate code for all sub-queries in the FROM clause
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 79a44f9..ef2d5ed 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.127 2002/06/20 11:36:50 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.128 2002/06/21 23:01:50 drh Exp $
*/
#include "sqlite.h"
#include "hash.h"
@@ -570,6 +570,12 @@
** a VIEW) we have to make a copy of the input string so that the nodes
** of the expression tree will have something to point to. zSelect is used
** to hold that copy.
+**
+** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0.
+** If there is a LIMIT clause, the parser sets nLimit to the value of the
+** limit and nOffset to the value of the offset (or 0 if there is not
+** offset). But later on, nLimit and nOffset become the memory locations
+** in the VDBE that record the limit and offset counters.
*/
struct Select {
int isDistinct; /* True if the DISTINCT keyword is present */
diff --git a/src/vdbe.c b/src/vdbe.c
index d5ad988..74978b2 100644
--- a/src/vdbe.c
+++ b/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.157 2002/06/20 11:36:50 drh Exp $
+** $Id: vdbe.c,v 1.158 2002/06/21 23:01:50 drh Exp $
*/
#include "sqliteInt.h"
#include <ctype.h>
@@ -248,8 +248,6 @@
int nSet; /* Number of sets allocated */
Set *aSet; /* An array of sets */
int nCallback; /* Number of callbacks invoked so far */
- int iLimit; /* Limit on the number of callbacks remaining */
- int iOffset; /* Offset before beginning to do callbacks */
int keylistStackDepth; /* The size of the "keylist" stack */
Keylist **keylistStack; /* The stack used by opcodes ListPush & ListPop */
};
@@ -1060,28 +1058,28 @@
"Last", "Rewind", "Next", "Destroy",
"Clear", "CreateIndex", "CreateTable", "IntegrityCk",
"IdxPut", "IdxDelete", "IdxRecno", "IdxGT",
- "IdxGE", "MemLoad", "MemStore", "ListWrite",
- "ListRewind", "ListRead", "ListReset", "ListPush",
- "ListPop", "SortPut", "SortMakeRec", "SortMakeKey",
- "Sort", "SortNext", "SortCallback", "SortReset",
- "FileOpen", "FileRead", "FileColumn", "AggReset",
- "AggFocus", "AggNext", "AggSet", "AggGet",
- "AggFunc", "AggInit", "AggPush", "AggPop",
- "SetInsert", "SetFound", "SetNotFound", "SetFirst",
- "SetNext", "MakeRecord", "MakeKey", "MakeIdxKey",
- "IncrKey", "Goto", "If", "IfNot",
- "Halt", "ColumnCount", "ColumnName", "Callback",
- "NullCallback", "Integer", "String", "Pop",
- "Dup", "Pull", "Push", "MustBeInt",
- "Add", "AddImm", "Subtract", "Multiply",
- "Divide", "Remainder", "BitAnd", "BitOr",
- "BitNot", "ShiftLeft", "ShiftRight", "AbsValue",
- "Eq", "Ne", "Lt", "Le",
- "Gt", "Ge", "StrEq", "StrNe",
- "StrLt", "StrLe", "StrGt", "StrGe",
- "IsNull", "NotNull", "Negative", "And",
- "Or", "Not", "Concat", "Noop",
- "Function", "Limit", "LimitCk",
+ "IdxGE", "MemLoad", "MemStore", "MemIncr",
+ "ListWrite", "ListRewind", "ListRead", "ListReset",
+ "ListPush", "ListPop", "SortPut", "SortMakeRec",
+ "SortMakeKey", "Sort", "SortNext", "SortCallback",
+ "SortReset", "FileOpen", "FileRead", "FileColumn",
+ "AggReset", "AggFocus", "AggNext", "AggSet",
+ "AggGet", "AggFunc", "AggInit", "AggPush",
+ "AggPop", "SetInsert", "SetFound", "SetNotFound",
+ "SetFirst", "SetNext", "MakeRecord", "MakeKey",
+ "MakeIdxKey", "IncrKey", "Goto", "If",
+ "IfNot", "Halt", "ColumnCount", "ColumnName",
+ "Callback", "NullCallback", "Integer", "String",
+ "Pop", "Dup", "Pull", "Push",
+ "MustBeInt", "Add", "AddImm", "Subtract",
+ "Multiply", "Divide", "Remainder", "BitAnd",
+ "BitOr", "BitNot", "ShiftLeft", "ShiftRight",
+ "AbsValue", "Eq", "Ne", "Lt",
+ "Le", "Gt", "Ge", "StrEq",
+ "StrNe", "StrLt", "StrLe", "StrGt",
+ "StrGe", "IsNull", "NotNull", "Negative",
+ "And", "Or", "Not", "Concat",
+ "Noop", "Function",
};
/*
@@ -1300,8 +1298,6 @@
zStack = p->zStack;
aStack = p->aStack;
p->tos = -1;
- p->iLimit = 0;
- p->iOffset = 0;
/* Initialize the aggregrate hash table.
*/
@@ -4068,54 +4064,6 @@
break;
}
-/* Opcode: Limit P1 P2 *
-**
-** Set a limit and offset on callbacks. P1 is the limit and P2 is
-** the offset. If the offset counter is positive, no callbacks are
-** invoked but instead the counter is decremented. Once the offset
-** counter reaches zero, callbacks are invoked and the limit
-** counter is decremented. When the limit counter reaches zero,
-** the OP_Callback or OP_SortCallback instruction executes a jump
-** that should end the query.
-**
-** This opcode is used to implement the "LIMIT x OFFSET y" clause
-** of a SELECT statement.
-*/
-case OP_Limit: {
- p->iLimit = pOp->p1;
- p->iOffset = pOp->p2;
- 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
@@ -4675,6 +4623,28 @@
break;
}
+/* Opcode: MemIncr P1 P2 *
+**
+** Increment the integer valued memory cell P1 by 1. If P2 is not zero
+** and the result after the increment is greater than zero, then jump
+** to P2.
+**
+** This instruction throws an error if the memory cell is not initially
+** an integer.
+*/
+case OP_MemIncr: {
+ int i = pOp->p1;
+ Mem *pMem;
+ VERIFY( if( i<0 || i>=p->nMem ) goto bad_instruction; )
+ pMem = &p->aMem[i];
+ VERIFY( if( pMem->s.flags != STK_Int ) goto bad_instruction; )
+ pMem->s.i++;
+ if( pOp->p2>0 && pMem->s.i>0 ){
+ pc = pOp->p2 - 1;
+ }
+ break;
+}
+
/* Opcode: AggReset * P2 *
**
** Reset the aggregator so that it no longer contains any data.
diff --git a/src/vdbe.h b/src/vdbe.h
index 91b34f2..833f71d 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -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.56 2002/06/20 11:36:50 drh Exp $
+** $Id: vdbe.h,v 1.57 2002/06/21 23:01:50 drh Exp $
*/
#ifndef _SQLITE_VDBE_H_
#define _SQLITE_VDBE_H_
@@ -116,108 +116,105 @@
#define OP_MemLoad 42
#define OP_MemStore 43
+#define OP_MemIncr 44
-#define OP_ListWrite 44
-#define OP_ListRewind 45
-#define OP_ListRead 46
-#define OP_ListReset 47
-#define OP_ListPush 48
-#define OP_ListPop 49
+#define OP_ListWrite 45
+#define OP_ListRewind 46
+#define OP_ListRead 47
+#define OP_ListReset 48
+#define OP_ListPush 49
+#define OP_ListPop 50
-#define OP_SortPut 50
-#define OP_SortMakeRec 51
-#define OP_SortMakeKey 52
-#define OP_Sort 53
-#define OP_SortNext 54
-#define OP_SortCallback 55
-#define OP_SortReset 56
+#define OP_SortPut 51
+#define OP_SortMakeRec 52
+#define OP_SortMakeKey 53
+#define OP_Sort 54
+#define OP_SortNext 55
+#define OP_SortCallback 56
+#define OP_SortReset 57
-#define OP_FileOpen 57
-#define OP_FileRead 58
-#define OP_FileColumn 59
+#define OP_FileOpen 58
+#define OP_FileRead 59
+#define OP_FileColumn 60
-#define OP_AggReset 60
-#define OP_AggFocus 61
-#define OP_AggNext 62
-#define OP_AggSet 63
-#define OP_AggGet 64
-#define OP_AggFunc 65
-#define OP_AggInit 66
-#define OP_AggPush 67
-#define OP_AggPop 68
+#define OP_AggReset 61
+#define OP_AggFocus 62
+#define OP_AggNext 63
+#define OP_AggSet 64
+#define OP_AggGet 65
+#define OP_AggFunc 66
+#define OP_AggInit 67
+#define OP_AggPush 68
+#define OP_AggPop 69
-#define OP_SetInsert 69
-#define OP_SetFound 70
-#define OP_SetNotFound 71
-#define OP_SetFirst 72
-#define OP_SetNext 73
+#define OP_SetInsert 70
+#define OP_SetFound 71
+#define OP_SetNotFound 72
+#define OP_SetFirst 73
+#define OP_SetNext 74
-#define OP_MakeRecord 74
-#define OP_MakeKey 75
-#define OP_MakeIdxKey 76
-#define OP_IncrKey 77
+#define OP_MakeRecord 75
+#define OP_MakeKey 76
+#define OP_MakeIdxKey 77
+#define OP_IncrKey 78
-#define OP_Goto 78
-#define OP_If 79
-#define OP_IfNot 80
-#define OP_Halt 81
+#define OP_Goto 79
+#define OP_If 80
+#define OP_IfNot 81
+#define OP_Halt 82
-#define OP_ColumnCount 82
-#define OP_ColumnName 83
-#define OP_Callback 84
-#define OP_NullCallback 85
+#define OP_ColumnCount 83
+#define OP_ColumnName 84
+#define OP_Callback 85
+#define OP_NullCallback 86
-#define OP_Integer 86
-#define OP_String 87
-#define OP_Pop 88
-#define OP_Dup 89
-#define OP_Pull 90
-#define OP_Push 91
-#define OP_MustBeInt 92
+#define OP_Integer 87
+#define OP_String 88
+#define OP_Pop 89
+#define OP_Dup 90
+#define OP_Pull 91
+#define OP_Push 92
+#define OP_MustBeInt 93
-#define OP_Add 93
-#define OP_AddImm 94
-#define OP_Subtract 95
-#define OP_Multiply 96
-#define OP_Divide 97
-#define OP_Remainder 98
-#define OP_BitAnd 99
-#define OP_BitOr 100
-#define OP_BitNot 101
-#define OP_ShiftLeft 102
-#define OP_ShiftRight 103
-#define OP_AbsValue 104
+#define OP_Add 94
+#define OP_AddImm 95
+#define OP_Subtract 96
+#define OP_Multiply 97
+#define OP_Divide 98
+#define OP_Remainder 99
+#define OP_BitAnd 100
+#define OP_BitOr 101
+#define OP_BitNot 102
+#define OP_ShiftLeft 103
+#define OP_ShiftRight 104
+#define OP_AbsValue 105
/* Note: The code generator assumes that OP_XX+6==OP_StrXX */
-#define OP_Eq 105
-#define OP_Ne 106
-#define OP_Lt 107
-#define OP_Le 108
-#define OP_Gt 109
-#define OP_Ge 110
-#define OP_StrEq 111
-#define OP_StrNe 112
-#define OP_StrLt 113
-#define OP_StrLe 114
-#define OP_StrGt 115
-#define OP_StrGe 116
+#define OP_Eq 106
+#define OP_Ne 107
+#define OP_Lt 108
+#define OP_Le 109
+#define OP_Gt 110
+#define OP_Ge 111
+#define OP_StrEq 112
+#define OP_StrNe 113
+#define OP_StrLt 114
+#define OP_StrLe 115
+#define OP_StrGt 116
+#define OP_StrGe 117
/* Note: the code generator assumes that OP_XX+6==OP_StrXX */
-#define OP_IsNull 117
-#define OP_NotNull 118
-#define OP_Negative 119
-#define OP_And 120
-#define OP_Or 121
-#define OP_Not 122
-#define OP_Concat 123
-#define OP_Noop 124
-#define OP_Function 125
+#define OP_IsNull 118
+#define OP_NotNull 119
+#define OP_Negative 120
+#define OP_And 121
+#define OP_Or 122
+#define OP_Not 123
+#define OP_Concat 124
+#define OP_Noop 125
+#define OP_Function 126
-#define OP_Limit 126
-#define OP_LimitCk 127
-
-
-#define OP_MAX 127
+#define OP_MAX 126
/*
** Prototypes for the VDBE interface. See comments on the implementation