]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix for ticket #147: Honor the ORDER BY and LIMIT clauses in a SELECT even
authordrh <drh@noemail.net>
Sun, 8 Sep 2002 17:23:41 +0000 (17:23 +0000)
committerdrh <drh@noemail.net>
Sun, 8 Sep 2002 17:23:41 +0000 (17:23 +0000)
if the destination of that SELECT is a subroutine call. (CVS 747)

FossilOrigin-Name: 23fe36c7e88282f6d1b7547ab892ea88f0e65262

main.mk
manifest
manifest.uuid
src/select.c
src/vdbe.c
test/limit.test

diff --git a/main.mk b/main.mk
index 92b940891bd380d36e40a41acc02062dad30cf57..f84e064ab890693156d467db929977de1120cd59 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -141,12 +141,12 @@ sqlite$(EXE):     $(TOP)/src/shell.c libsqlite.a sqlite.h
 # files are automatically generated.  This target takes care of
 # all that automatic generation.
 #
-target_source: $(SRC) $(HDR) 
+target_source: $(SRC) $(HDR) opcodes.c
        rm -rf tsrc
        mkdir tsrc
        cp $(SRC) $(HDR) tsrc
        rm tsrc/sqlite.h.in tsrc/parse.y
-       cp parse.c tsrc
+       cp parse.c opcodes.c tsrc
 
 # Rules to build the LEMON compiler generator
 #
index 27b7289a609a6e460865a2e48ccc2ebb5eb6ee51..b96723ab41f8059f71230c4a3027a1e0ef3fb416 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Modify\sthe\sbuild\sprocess\sso\sthat\sthe\sVDBE\sopcode\snumbers\sand\sthe\stable\nthat\scontains\sthe\sopcode\snames\sare\sboth\sautomatically\sgenerated.\sThis\smakes\nit\smuch\seasier\sto\screate\snew\sVDBE\sopcodes.\s(CVS\s1727)
-D 2002-09-08T00:04:54
+C Fix\sfor\sticket\s#147:\sHonor\sthe\sORDER\sBY\sand\sLIMIT\sclauses\sin\sa\sSELECT\seven\nif\sthe\sdestination\sof\sthat\sSELECT\sis\sa\ssubroutine\scall.\s(CVS\s747)
+D 2002-09-08T17:23:42
 F Makefile.in d6c9a85c2a5e696843201d090dcf8bf2f8716f2a
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -14,7 +14,7 @@ F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F libtool c56e618713c9510a103bda6b95f3ea3900dcacd6
 F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1
-F main.mk e9c6f552f230b8befc25346c515c494fecd2e823
+F main.mk d7a9dfc003ab1eeb4fdb46dfcee8af0ab0e38a99
 F publish.sh a7a8d23e6525bd25d4f5ba9b0fc6edc107d94050
 F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
 F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
@@ -37,7 +37,7 @@ F src/pager.h 6991c9c2dc5e4c7f2df4d4ba47d1c6458f763a32
 F src/parse.y 818b03a73f6b3b8b284b515c5b1d9998d4663dc3
 F src/printf.c 5c50fc1da75c8f5bf432b1ad17d91d6653acd167
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 6cd3673edbb36a8f8027341093085e01c04dd3d4
+F src/select.c 74a025cd6887b636fc06a79ff6246c4eb6826ec4
 F src/shell.c 9e9a6eb6bca07f01e6472a603f908a0127ea50ff
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in d3999a9c6374675779058d6cfe5431131618e92b
@@ -52,7 +52,7 @@ F src/tokenize.c 62c98842447effe92eba9622bb2f9a2a8a4b97ad
 F src/trigger.c c90a292a4bef25e478fd5deda6d300319be6a023
 F src/update.c f07e6ed2c517c92871e54d3f5886d1cf56121b11
 F src/util.c c70d5da5357e01b58392faebae3c3620c1d71f14
-F src/vdbe.c 8637ddb9072df676f50dce3f7894cdaec714fe4a
+F src/vdbe.c 7e7392f2a92187ba1d2351fed0524c2dd607cffb
 F src/vdbe.h b7584044223104ba7896a7f87b66daebdd6022ba
 F src/where.c 53959c9d94adaf93b409271815e26eafa6ddd515
 F test/all.test efd958d048c70a3247997c482f0b33561f7759f0
@@ -74,7 +74,7 @@ F test/insert2.test c288375a64dad3295044714f0dfed4a193cf067f
 F test/intpkey.test f3620158fd7963af1306b01047277f10ae91a30b
 F test/ioerr.test 57d9bffaca18b34f9e976f786eadc2591d6efc6a
 F test/join.test 90a620f2a2d015e5139d5a4cde0eeb4cf62523bf
-F test/limit.test dd932f7572fe78e82ef621ef62f0cc480c7e541e
+F test/limit.test 9f26f874bc765df5b3f5c92d26d1b12eac6d4cf9
 F test/lock.test 5079615ba0ef0899c4cbade42ffec291620a2819
 F test/main.test c66b564554b770ee7fdbf6a66c0cd90329bc2c85
 F test/malloc.test 7ba32a9ebd3aeed52ae4aaa6d42ca37e444536fd
@@ -149,7 +149,7 @@ F www/speed.tcl a20a792738475b68756ea7a19321600f23d1d803
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P eb54d455b0325d3be96daf6c220c4ee3e0da1a85
-R ec4eceb930ae5f7d03668e5150997da1
+P c4f0bb02387a6e6f254320e1e5eef3d5d8a3c538
+R d53bc433eee4d832716a49a9ea35550f
 U drh
-Z ef4086cb3b45d72139e3d7498833a607
+Z fdf23733e2dbe5e105dfffa0429ea82b
index dd7cd86283b4e99e7e26bc841b3bfd00fa161031..c86ef09957714c073d91a00f84b17537d00a9a6a 100644 (file)
@@ -1 +1 @@
-c4f0bb02387a6e6f254320e1e5eef3d5d8a3c538
\ No newline at end of file
+23fe36c7e88282f6d1b7547ab892ea88f0e65262
\ No newline at end of file
index 4cbf0344e3efd36e4674756ada0bafbb05f3cadb..11b3ea0dc54ec3bca95e83b729ebeda3bd889563 100644 (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.111 2002/08/28 03:00:59 drh Exp $
+** $Id: select.c,v 1.112 2002/09/08 17:23:43 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -524,7 +524,12 @@ static int selectInnerLoop(
     ** is responsible for popping the results off of the stack.
     */
     case SRT_Subroutine: {
-      sqliteVdbeAddOp(v, OP_Gosub, 0, iParm);
+      if( pOrderBy ){
+        sqliteVdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+        pushOntoSorter(pParse, v, pOrderBy);
+      }else{
+        sqliteVdbeAddOp(v, OP_Gosub, 0, iParm);
+      }
       break;
     }
 
@@ -593,6 +598,15 @@ static void generateSortTail(
       sqliteVdbeAddOp(v, OP_Goto, 0, end);
       break;
     }
+    case SRT_Subroutine: {
+      int i;
+      for(i=0; i<nColumn; i++){
+        sqliteVdbeAddOp(v, OP_Column, -1-i, i);
+      }
+      sqliteVdbeAddOp(v, OP_Gosub, 0, iParm);
+      sqliteVdbeAddOp(v, OP_Pop, 1, 0);
+      break;
+    }
     default: {
       /* Do nothing */
       break;
index c227ef930acc889b08087ffcac8b3dc637eae505..6f5d3c062dc894d1e9c9637c5226c4a02c806d0d 100644 (file)
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.176 2002/09/08 00:04:52 drh Exp $
+** Various scripts scan this source file in order to generate HTML
+** documentation, headers files, or other derived files.  The formatting
+** of the code in this file is, therefore, important.  See other comments
+** in this file for details.  If in doubt, do not deviate from existing
+** commenting and indentation practices when changing or adding code.
+**
+** $Id: vdbe.c,v 1.177 2002/09/08 17:23:43 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -47,7 +53,8 @@ extern char *sqliteOpcodeNames[];
 ** The following global variable is incremented every time a cursor
 ** moves, either by the OP_MoveTo or the OP_Next opcode.  The test
 ** procedures use this information to make sure that indices are
-** working correctly.
+** working correctly.  This variable has no function other than to
+** help verify the correct operation of the library.
 */
 int sqlite_search_count = 0;
 
@@ -261,7 +268,7 @@ struct Vdbe {
 };
 
 /*
-** When debugging the code generator in a symbolic debugger, on can
+** When debugging the code generator in a symbolic debugger, one can
 ** set the sqlite_vdbe_addop_trace to 1 and all opcodes will be printed
 ** as they are added to the instruction stream.
 */
@@ -371,7 +378,8 @@ int sqliteVdbeMakeLabel(Vdbe *p){
 
 /*
 ** Resolve label "x" to be the address of the next instruction to
-** be inserted.
+** be inserted.  The parameter "x" must have been obtained from
+** a prior call to sqliteVdbeMakeLabel().
 */
 void sqliteVdbeResolveLabel(Vdbe *p, int x){
   int j;
@@ -566,7 +574,7 @@ int sqliteVdbeFindOp(Vdbe *p, int op, int p2){
 ** The sqlite_set_result_string() routine can be used to return a string
 ** value or to return a NULL.  To return a NULL, pass in NULL for zResult.
 ** A copy is made of the string before this routine returns so it is safe
-** to pass in a ephemeral string.
+** to pass in an ephemeral string.
 **
 ** sqlite_set_result_error() works like sqlite_set_result_string() except
 ** that it signals a fatal error.  The string argument, if any, is the
@@ -577,8 +585,8 @@ int sqliteVdbeFindOp(Vdbe *p, int op, int p2){
 ** value of the user function to an integer or a double.
 **
 ** These routines are defined here in vdbe.c because they depend on knowing
-** the internals of the sqlite_func structure which is only defined in that
-** one source file.
+** the internals of the sqlite_func structure which is only defined in 
+** this source file.
 */
 char *sqlite_set_result_string(sqlite_func *p, const char *zResult, int n){
   assert( !p->isStep );
@@ -636,8 +644,8 @@ void sqlite_set_result_error(sqlite_func *p, const char *zMsg, int n){
 ** pointer to it.
 **
 ** This routine is defined here in vdbe.c because it depends on knowing
-** the internals of the sqlite_func structure which is only defined in that
-** one source file.
+** the internals of the sqlite_func structure which is only defined in 
+** this source file.
 */
 void *sqlite_user_data(sqlite_func *p){
   assert( p && p->pFunc );
@@ -650,8 +658,8 @@ void *sqlite_user_data(sqlite_func *p){
 ** same context that was returned on prior calls.
 **
 ** This routine is defined here in vdbe.c because it depends on knowing
-** the internals of the sqlite_func structure which is only defined in that
-** one source file.
+** the internals of the sqlite_func structure which is only defined in
+** this source file.
 */
 void *sqlite_aggregate_context(sqlite_func *p, int nByte){
   assert( p && p->pFunc && p->pFunc->xStep );
@@ -670,8 +678,8 @@ void *sqlite_aggregate_context(sqlite_func *p, int nByte){
 ** called.
 **
 ** This routine is defined here in vdbe.c because it depends on knowing
-** the internals of the sqlite_func structure which is only defined in that
-** one source file.
+** the internals of the sqlite_func structure which is only defined in
+** this source file.
 */
 int sqlite_aggregate_count(sqlite_func *p){
   assert( p && p->pFunc && p->pFunc->xStep );
@@ -723,7 +731,8 @@ static void AggReset(Agg *pAgg){
 }
 
 /*
-** Insert a new element and make it the current element.  
+** Insert a new aggregate element and make it the element that
+** has focus.
 **
 ** Return 0 on success and 1 if memory is exhausted.
 */
@@ -1391,6 +1400,24 @@ int sqliteVdbeExec(
 ** the switch statement will break with convention and be flush-left. Another
 ** big comment (similar to this one) will mark the point in the code where
 ** we transition back to normal indentation.
+**
+** The formatting of each case is important.  The makefile for SQLite
+** generates two C files "opcodes.h" and "opcodes.c" by scanning this
+** file looking for lines that begin with "case OP_".  The opcodes.h files
+** will be filled with #defines that give unique integer values to each
+** opcode and the opcodes.c file is filled with an array of strings where
+** each string is the symbolic name for the corresponding opcode.
+**
+** Documentation about VDBE opcodes is generated by scanning this file
+** for lines of that contain "Opcode:".  That line and all subsequent
+** comment lines are used in the generation of the opcode.html documentation
+** file.
+**
+** SUMMARY:
+**
+**     Formatting is important to scripts that scan this file.
+**     Do not deviate from the formatting style currently in use.
+**
 *****************************************************************************/
 
 /* Opcode:  Goto * P2 *
@@ -1604,7 +1631,7 @@ case OP_Pull: {
 **
 ** Overwrite the value of the P1-th element down on the
 ** stack (P1==0 is the top of the stack) with the value
-** of the top of the stack.  The pop the top of the stack.
+** of the top of the stack.  Then pop the top of the stack.
 */
 case OP_Push: {
   int from = p->tos;
@@ -3664,6 +3691,12 @@ case OP_KeyAsData: {
 ** If the KeyAsData opcode has previously executed on this cursor,
 ** then the field might be extracted from the key rather than the
 ** data.
+**
+** If P1 is negative, then the record is stored on the stack rather
+** than in a table.  For P1==-1, the top of the stack is used.
+** For P1==-2, the next on the stack is used.  And so forth.  The
+** value pushed is always just a pointer into the record which is
+** stored further down on the stack.  The column value is not copied.
 */
 case OP_Column: {
   int amt, offset, end, payloadSize;
@@ -3671,92 +3704,106 @@ case OP_Column: {
   int p2 = pOp->p2;
   int tos = p->tos+1;
   Cursor *pC;
+  char *zRec;
   BtCursor *pCrsr;
   int idxWidth;
   unsigned char aHdr[10];
-  int (*xRead)(BtCursor*, int, int, char*);
 
   VERIFY( if( NeedStack(p, tos+1) ) goto no_mem; )
-  if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){
-
-    /* Use different access functions depending on whether the information
-    ** is coming from the key or the data of the record.
-    */
+  if( i<0 ){
+    VERIFY( if( tos+i<0 ) goto bad_instruction; )
+    VERIFY( if( (aStack[tos+i].flags & STK_Str)==0 ) goto bad_instruction; )
+    zRec = zStack[tos+i];
+    payloadSize = aStack[tos+i].n;
+  }else if( VERIFY( i>=0 && i<p->nCursor && ) (pC = &p->aCsr[i])->pCursor!=0 ){
+    zRec = 0;
     pCrsr = pC->pCursor;
     if( pC->nullRow ){
       payloadSize = 0;
     }else if( pC->keyAsData ){
       sqliteBtreeKeySize(pCrsr, &payloadSize);
-      xRead = sqliteBtreeKey;
     }else{
       sqliteBtreeDataSize(pCrsr, &payloadSize);
-      xRead = sqliteBtreeData;
     }
+  }else{
+    payloadSize = 0;
+  }
 
-    /* Figure out how many bytes in the column data and where the column
-    ** data begins.
-    */
-    if( payloadSize==0 ){
-      aStack[tos].flags = STK_Null;
-      p->tos = tos;
-      break;
-    }else if( payloadSize<256 ){
-      idxWidth = 1;
-    }else if( payloadSize<65536 ){
-      idxWidth = 2;
-    }else{
-      idxWidth = 3;
-    }
+  /* Figure out how many bytes in the column data and where the column
+  ** data begins.
+  */
+  if( payloadSize==0 ){
+    aStack[tos].flags = STK_Null;
+    p->tos = tos;
+    break;
+  }else if( payloadSize<256 ){
+    idxWidth = 1;
+  }else if( payloadSize<65536 ){
+    idxWidth = 2;
+  }else{
+    idxWidth = 3;
+  }
 
-    /* Figure out where the requested column is stored and how big it is.
-    */
-    if( payloadSize < idxWidth*(p2+1) ){
-      rc = SQLITE_CORRUPT;
-      goto abort_due_to_error;
-    }
-    (*xRead)(pCrsr, idxWidth*p2, idxWidth*2, (char*)aHdr);
-    offset = aHdr[0];
-    end = aHdr[idxWidth];
-    if( idxWidth>1 ){
-      offset |= aHdr[1]<<8;
-      end |= aHdr[idxWidth+1]<<8;
-      if( idxWidth>2 ){
-        offset |= aHdr[2]<<16;
-        end |= aHdr[idxWidth+2]<<16;
-      }
-    }
-    amt = end - offset;
-    if( amt<0 || offset<0 || end>payloadSize ){
-      rc = SQLITE_CORRUPT;
-      goto abort_due_to_error;
+  /* Figure out where the requested column is stored and how big it is.
+  */
+  if( payloadSize < idxWidth*(p2+1) ){
+    rc = SQLITE_CORRUPT;
+    goto abort_due_to_error;
+  }
+  if( zRec ){
+    memcpy(aHdr, &zRec[idxWidth*p2], idxWidth*2);
+  }else if( pC->keyAsData ){
+    sqliteBtreeKey(pCrsr, idxWidth*p2, idxWidth*2, (char*)aHdr);
+  }else{
+    sqliteBtreeData(pCrsr, idxWidth*p2, idxWidth*2, (char*)aHdr);
+  }
+  offset = aHdr[0];
+  end = aHdr[idxWidth];
+  if( idxWidth>1 ){
+    offset |= aHdr[1]<<8;
+    end |= aHdr[idxWidth+1]<<8;
+    if( idxWidth>2 ){
+      offset |= aHdr[2]<<16;
+      end |= aHdr[idxWidth+2]<<16;
     }
+  }
+  amt = end - offset;
+  if( amt<0 || offset<0 || end>payloadSize ){
+    rc = SQLITE_CORRUPT;
+    goto abort_due_to_error;
+  }
 
-    /* amt and offset now hold the offset to the start of data and the
-    ** amount of data.  Go get the data and put it on the stack.
-    */
-    if( amt==0 ){
-      aStack[tos].flags = STK_Null;
-    }else if( amt<=NBFS ){
-      (*xRead)(pCrsr, offset, amt, aStack[tos].z);
+  /* amt and offset now hold the offset to the start of data and the
+  ** amount of data.  Go get the data and put it on the stack.
+  */
+  if( amt==0 ){
+    aStack[tos].flags = STK_Null;
+  }else if( zRec ){
+    aStack[tos].flags = STK_Str | STK_Static;
+    aStack[tos].n = amt;
+    zStack[tos] = &zRec[offset];
+  }else{
+    if( amt<=NBFS ){
       aStack[tos].flags = STK_Str;
       zStack[tos] = aStack[tos].z;
       aStack[tos].n = amt;
     }else{
       char *z = sqliteMalloc( amt );
       if( z==0 ) goto no_mem;
-      (*xRead)(pCrsr, offset, amt, z);
       aStack[tos].flags = STK_Str | STK_Dyn;
       zStack[tos] = z;
       aStack[tos].n = amt;
     }
-    p->tos = tos;
+    if( pC->keyAsData ){
+      sqliteBtreeKey(pCrsr, offset, amt, zStack[tos]);
+    }else{
+      sqliteBtreeData(pCrsr, offset, amt, zStack[tos]);
+    }
   }
+  p->tos = tos;
   break;
 }
 
-/* Opcode: Extract * * *
-*/
-
 /* Opcode: Recno P1 * *
 **
 ** Push onto the stack an integer which is the first 4 bytes of the
index 2d655e80e9f034f02a6acffe66be3f6722181cc1..c7e93d783e1b8c29ae8f08d81e4a8abb3f62a046 100644 (file)
@@ -12,7 +12,7 @@
 # focus of this file is testing the LIMIT ... OFFSET ... clause
 #  of SELECT statements.
 #
-# $Id: limit.test,v 1.5 2002/08/13 23:02:58 drh Exp $
+# $Id: limit.test,v 1.6 2002/09/08 17:23:45 drh Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -117,4 +117,41 @@ do_test limit-4.3 {
   }
 } {1000}
 
+do_test limit-5.1 {
+  execsql {
+    CREATE TABLE t5(x,y);
+    INSERT INTO t5 SELECT x-y, x+y FROM t1 WHERE x BETWEEN 10 AND 15
+        ORDER BY x LIMIT 2;
+    SELECT * FROM t5 ORDER BY x;
+  }
+} {5 15 6 16}
+do_test limit-5.2 {
+  execsql {
+    DELETE FROM t5;
+    INSERT INTO t5 SELECT x-y, x+y FROM t1 WHERE x BETWEEN 10 AND 15
+        ORDER BY x DESC LIMIT 2;
+    SELECT * FROM t5 ORDER BY x;
+  }
+} {9 19 10 20}
+do_test limit-5.3 {
+  execsql {
+    DELETE FROM t5;
+    INSERT INTO t5 SELECT x-y, x+y FROM t1 WHERE x ORDER BY x DESC LIMIT 31;
+    SELECT * FROM t5 ORDER BY x LIMIT 2;
+  }
+} {-4 6 -3 7}
+do_test limit-5.4 {
+  execsql {
+    SELECT * FROM t5 ORDER BY x DESC, y DESC LIMIT 2;
+  }
+} {21 41 21 39}
+do_test limit-5.5 {
+  execsql {
+    DELETE FROM t5;
+    INSERT INTO t5 SELECT a.x*100+b.x, a.y*100+b.y FROM t1 AS a, t1 AS b
+                   ORDER BY 1, 2 LIMIT 1000;
+    SELECT count(*), sum(x), sum(y), min(x), max(x), min(y), max(y) FROM t5;
+  }
+} {1000 1528204 593161 0 3107 505 1005}
+
 finish_test