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