From: drh Date: Sun, 8 Sep 2002 17:23:41 +0000 (+0000) Subject: Fix for ticket #147: Honor the ORDER BY and LIMIT clauses in a SELECT even X-Git-Tag: version-3.6.10~5326 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ac82fcf5f04a5a948e35f0d4ee8bea470781761e;p=thirdparty%2Fsqlite.git Fix for ticket #147: Honor the ORDER BY and LIMIT clauses in a SELECT even if the destination of that SELECT is a subroutine call. (CVS 747) FossilOrigin-Name: 23fe36c7e88282f6d1b7547ab892ea88f0e65262 --- diff --git a/main.mk b/main.mk index 92b940891b..f84e064ab8 100644 --- 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 # diff --git a/manifest b/manifest index 27b7289a60..b96723ab41 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index dd7cd86283..c86ef09957 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c4f0bb02387a6e6f254320e1e5eef3d5d8a3c538 \ No newline at end of file +23fe36c7e88282f6d1b7547ab892ea88f0e65262 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 4cbf0344e3..11b3ea0dc5 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.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 @@ -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 && inCursor && ) (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 && inCursor && ) (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 diff --git a/test/limit.test b/test/limit.test index 2d655e80e9..c7e93d783e 100644 --- a/test/limit.test +++ b/test/limit.test @@ -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