From e4de1feb3e3e0cccb45099cadeb094d8ad53b7ea Mon Sep 17 00:00:00 2001 From: drh Date: Sun, 2 Jun 2002 16:09:01 +0000 Subject: [PATCH] Enhance the ORDER BY clause so that an integer term means to sort by the corresponding column. (CVS 602) FossilOrigin-Name: 7acbf84b492202d8b5a05276a95b475027eb5f58 --- manifest | 20 ++++++++-------- manifest.uuid | 2 +- src/expr.c | 37 +++++++++++++++++++++++++++- src/select.c | 61 ++++++++++++++++++++++++++++++++--------------- src/sqliteInt.h | 3 ++- test/select1.test | 49 +++++++++++++++++++++++++++++++++++-- test/select4.test | 50 +++++++++++++++++++++++++++++++++++++- 7 files changed, 187 insertions(+), 35 deletions(-) diff --git a/manifest b/manifest index fd0af44d60..b8c64658d2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Multiplying\sNULL\sby\szero\sgives\sNULL,\snot\szero.\s\sI\smisread\sthe\stest\sdata\nand\scoded\sit\swrong.\s\sThis\scheck-in\sfixes\sthe\sproblem.\s(CVS\s601) -D 2002-06-01T21:41:10 +C Enhance\sthe\sORDER\sBY\sclause\sso\sthat\san\sinteger\sterm\smeans\sto\ssort\sby\sthe\ncorresponding\scolumn.\s(CVS\s602) +D 2002-06-02T16:09:02 F Makefile.in 6291a33b87d2a395aafd7646ee1ed562c6f2c28c F Makefile.template 4e11752e0b5c7a043ca50af4296ec562857ba495 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -23,7 +23,7 @@ F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 F src/build.c 36e42718a7a94f554ea39508993378482f5335c7 F src/delete.c a2b098cbbf518e6b641847e26de85827793bc523 F src/encode.c 346b12b46148506c32038524b95c4631ab46d760 -F src/expr.c 6bb74762c3d86b1665b32a6a388a70eeaaca93f8 +F src/expr.c 4ee07b536119e7ee4f572e1ca83ad86c1aef0058 F src/func.c 061a520a122da7e4f9dcac15697bb996aac7d5df F src/hash.c 6a6236b89c8c060c65dabd300a1c8ce7c10edb72 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 @@ -37,11 +37,11 @@ F src/pager.h 6fddfddd3b73aa8abc081b973886320e3c614f0e F src/parse.y c681da701bf142967325b8791f22418e2d81552d F src/printf.c d8032ee18b860c812eeff596c9bebfdacb7930fd F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c 9a9f76d7d647284f41616bc120003961bea403ef +F src/select.c ad9061b4735ccd79fc073415979882cd5c424c71 F src/shell.c 1d22fe870ee852cfb975fd000dbe3973713d0a15 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 0038faa6d642de06b91143ee65a131bd831d020b -F src/sqliteInt.h 55824b7c87dcead71801354f90e4f1b1481f1d1e +F src/sqliteInt.h 8ec47ae045cf8525c2bcc1a650853d814ca7675c F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/tclsqlite.c 9300c9606a38bc0c75d6c0bc8a6197ab979353d1 F src/test1.c 09d95048b66ce6dcd2bae90f443589043d7d631e @@ -87,10 +87,10 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05 F test/quote.test 08f23385c685d3dc7914ec760d492cacea7f6e3d F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274 -F test/select1.test a19a8026b5c2c5bdf5384d761f3d446954b7ebf9 +F test/select1.test 0d708cec567104653ec9aa49fecf3444a2e7d150 F test/select2.test aceea74fd895b9d007512f72499db589735bd8e4 F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18 -F test/select4.test 32018b97fde7202dcb906748836fcd79410604d3 +F test/select4.test 9b5c87b7a372172d65928cf860bcd5a54694c775 F test/select5.test c2a6c4a003316ee42cbbd689eebef8fdce0db2ac F test/select6.test efb8d0c07a440441db87db2c4ade6904e1407e85 F test/sort.test 3b996ce7ca385f9cd559944ac0f4027a23aa546b @@ -136,7 +136,7 @@ F www/speed.tcl da8afcc1d3ccc5696cfb388a68982bc3d9f7f00f F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P da61aa1d238539dff9c43fd9f464d311e28d669f -R f5c3f541431552f938b5386e692916f8 +P df9cc852ad02dbec5558d3915a0303f7e7b79b2b +R d820e9b413015f44b726f7ec86a28058 U drh -Z 64d47e65013a776dcf32882cb49edbc6 +Z 52b5b2062ca7af0b018c152f36b11673 diff --git a/manifest.uuid b/manifest.uuid index 1659a80ea6..56899c72e7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -df9cc852ad02dbec5558d3915a0303f7e7b79b2b \ No newline at end of file +7acbf84b492202d8b5a05276a95b475027eb5f58 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index c414e39377..b5bb89c99e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,7 +12,7 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.66 2002/05/31 15:51:25 drh Exp $ +** $Id: expr.c,v 1.67 2002/06/02 16:09:02 drh Exp $ */ #include "sqliteInt.h" @@ -335,6 +335,41 @@ int sqliteExprIsConstant(Expr *p){ return 0; } +/* +** If the given expression codes a constant integer, return 1 and put +** the value of the integer in *pValue. If the expression is not an +** integer, return 0 and leave *pValue unchanged. +*/ +int sqliteExprIsInteger(Expr *p, int *pValue){ + switch( p->op ){ + case TK_INTEGER: { + *pValue = atoi(p->token.z); + return 1; + } + case TK_STRING: { + char *z = p->token.z; + int n = p->token.n; + if( n>0 && z=='-' ){ z++; n--; } + while( n>0 && *z && isdigit(*z) ){ z++; n--; } + if( n==0 ){ + *pValue = atoi(p->token.z); + return 1; + } + break; + } + case TK_UMINUS: { + int v; + if( sqliteExprIsInteger(p->pLeft, &v) ){ + *pValue = -v; + return 1; + } + break; + } + default: break; + } + return 0; +} + /* ** Return TRUE if the given string is a row-id column name. */ diff --git a/src/select.c b/src/select.c index 5a97be8dda..708305a331 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.89 2002/05/31 15:51:25 drh Exp $ +** $Id: select.c,v 1.90 2002/06/02 16:09:02 drh Exp $ */ #include "sqliteInt.h" @@ -790,7 +790,7 @@ static int matchOrderbyToColumn( Parse *pParse, /* A place to leave error messages */ Select *pSelect, /* Match to result columns of this SELECT */ ExprList *pOrderBy, /* The ORDER BY values to match against columns */ - int iTable, /* Insert this this value in iTable */ + int iTable, /* Insert this value in iTable */ int mustComplete /* If TRUE all ORDER BYs must match */ ){ int nErr = 0; @@ -812,9 +812,21 @@ static int matchOrderbyToColumn( pEList = pSelect->pEList; for(i=0; inExpr; i++){ Expr *pE = pOrderBy->a[i].pExpr; - int match = 0; + int iCol = -1; if( pOrderBy->a[i].done ) continue; - for(j=0; jnExpr; j++){ + if( sqliteExprIsInteger(pE, &iCol) ){ + if( iCol<=0 || iCol>pEList->nExpr ){ + char zBuf[200]; + sprintf(zBuf,"ORDER BY position %d should be between 1 and %d", + iCol, pEList->nExpr); + sqliteSetString(&pParse->zErrMsg, zBuf, 0); + pParse->nErr++; + nErr++; + break; + } + iCol--; + } + for(j=0; iCol<0 && jnExpr; j++){ if( pEList->a[j].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){ char *zName, *zLabel; zName = pEList->a[j].zName; @@ -822,22 +834,21 @@ static int matchOrderbyToColumn( zLabel = sqliteStrNDup(pE->token.z, pE->token.n); sqliteDequote(zLabel); if( sqliteStrICmp(zName, zLabel)==0 ){ - match = 1; + iCol = j; } sqliteFree(zLabel); } - if( match==0 && sqliteExprCompare(pE, pEList->a[j].pExpr) ){ - match = 1; - } - if( match ){ - pE->op = TK_COLUMN; - pE->iColumn = j; - pE->iTable = iTable; - pOrderBy->a[i].done = 1; - break; + if( iCol<0 && sqliteExprCompare(pE, pEList->a[j].pExpr) ){ + iCol = j; } } - if( !match && mustComplete ){ + if( iCol>=0 ){ + pE->op = TK_COLUMN; + pE->iColumn = iCol; + pE->iTable = iTable; + pOrderBy->a[i].done = 1; + } + if( iCol<0 && mustComplete ){ char zBuf[30]; sprintf(zBuf,"%d",i+1); sqliteSetString(&pParse->zErrMsg, "ORDER BY term number ", zBuf, @@ -1520,10 +1531,22 @@ int sqliteSelect( for(i=0; inExpr; i++){ Expr *pE = pOrderBy->a[i].pExpr; if( sqliteExprIsConstant(pE) ){ - sqliteSetString(&pParse->zErrMsg, - "ORDER BY expressions should not be constant", 0); - pParse->nErr++; - goto select_end; + int iCol; + if( sqliteExprIsInteger(pE, &iCol)==0 ){ + sqliteSetString(&pParse->zErrMsg, + "ORDER BY terms must not be non-integer constants", 0); + pParse->nErr++; + goto select_end; + }else if( iCol<=0 || iCol>pEList->nExpr ){ + char zBuf[2000]; + sprintf(zBuf,"ORDER BY column number %d out of range - should be " + "between 1 and %d", iCol, pEList->nExpr); + sqliteSetString(&pParse->zErrMsg, zBuf, 0); + pParse->nErr++; + goto select_end; + } + sqliteExprDelete(pE); + pE = pOrderBy->a[i].pExpr = sqliteExprDup(pEList->a[iCol-1].pExpr); } if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){ goto select_end; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 77154dc61f..8855b109b0 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.118 2002/05/27 12:24:49 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.119 2002/06/02 16:09:02 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -841,6 +841,7 @@ void sqliteCommitTransaction(Parse*); void sqliteRollbackTransaction(Parse*); char *sqlite_mprintf(const char *, ...); int sqliteExprIsConstant(Expr*); +int sqliteExprIsInteger(Expr*, int*); void sqliteGenerateRowDelete(Vdbe*, Table*, int, int); void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*); void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int); diff --git a/test/select1.test b/test/select1.test index 6d3e45d360..b7624c0836 100644 --- a/test/select1.test +++ b/test/select1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select1.test,v 1.29 2002/05/29 23:22:23 drh Exp $ +# $Id: select1.test,v 1.30 2002/06/02 16:09:03 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -285,7 +285,52 @@ do_test select1-4.5 { catchsql { SELECT f1 FROM test1 ORDER BY 8.4; } -} {1 {ORDER BY expressions should not be constant}} +} {1 {ORDER BY terms must not be non-integer constants}} +do_test select1-4.6 { + catchsql { + SELECT f1 FROM test1 ORDER BY '8.4'; + } +} {1 {ORDER BY terms must not be non-integer constants}} +do_test select1-4.7 { + catchsql { + SELECT f1 FROM test1 ORDER BY 'xyz'; + } +} {1 {ORDER BY terms must not be non-integer constants}} +do_test select1-4.8 { + execsql { + CREATE TABLE t5(a,b); + INSERT INTO t5 VALUES(1,10); + INSERT INTO t5 VALUES(2,9); + SELECT * FROM t5 ORDER BY 1; + } +} {1 10 2 9} +do_test select1-4.9 { + execsql { + SELECT * FROM t5 ORDER BY 2; + } +} {2 9 1 10} +do_test select1-4.10 { + catchsql { + SELECT * FROM t5 ORDER BY 3; + } +} {1 {ORDER BY column number 3 out of range - should be between 1 and 2}} +do_test select1-4.11 { + execsql { + INSERT INTO t5 VALUES(3,10); + SELECT * FROM t5 ORDER BY 2, 1 DESC; + } +} {2 9 3 10 1 10} +do_test select1-4.12 { + execsql { + SELECT * FROM t5 ORDER BY 1 DESC, b; + } +} {3 10 2 9 1 10} +do_test select1-4.13 { + execsql { + SELECT * FROM t5 ORDER BY b DESC, 1; + } +} {1 10 3 10 2 9} + # ORDER BY ignored on an aggregate query # diff --git a/test/select4.test b/test/select4.test index dc412a058b..c586641476 100644 --- a/test/select4.test +++ b/test/select4.test @@ -12,7 +12,7 @@ # focus of this file is testing UNION, INTERSECT and EXCEPT operators # in SELECT statements. # -# $Id: select4.test,v 1.9 2002/05/31 15:51:26 drh Exp $ +# $Id: select4.test,v 1.10 2002/06/02 16:09:03 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -209,6 +209,54 @@ do_test select4-5.2e { }} msg] lappend v $msg } {0 {0 1 2 3 4 5 5 6 7 8}} +do_test select4-5.2f { + catchsql { + SELECT DISTINCT log FROM t1 + UNION ALL + SELECT n FROM t1 WHERE log=3 + ORDER BY log; + } +} {0 {0 1 2 3 4 5 5 6 7 8}} +do_test select4-5.2g { + catchsql { + SELECT DISTINCT log FROM t1 + UNION ALL + SELECT n FROM t1 WHERE log=3 + ORDER BY 1; + } +} {0 {0 1 2 3 4 5 5 6 7 8}} +do_test select4-5.2h { + catchsql { + SELECT DISTINCT log FROM t1 + UNION ALL + SELECT n FROM t1 WHERE log=3 + ORDER BY 2; + } +} {1 {ORDER BY position 2 should be between 1 and 1}} +do_test select4-5.2i { + catchsql { + SELECT DISTINCT 1, log FROM t1 + UNION ALL + SELECT 2, n FROM t1 WHERE log=3 + ORDER BY 2, 1; + } +} {0 {1 0 1 1 1 2 1 3 1 4 1 5 2 5 2 6 2 7 2 8}} +do_test select4-5.2j { + catchsql { + SELECT DISTINCT 1, log FROM t1 + UNION ALL + SELECT 2, n FROM t1 WHERE log=3 + ORDER BY 1, 2 DESC; + } +} {0 {1 5 1 4 1 3 1 2 1 1 1 0 2 8 2 7 2 6 2 5}} +do_test select4-5.2k { + catchsql { + SELECT DISTINCT 1, log FROM t1 + UNION ALL + SELECT 2, n FROM t1 WHERE log=3 + ORDER BY n, 1; + } +} {0 {1 0 1 1 1 2 1 3 1 4 1 5 2 5 2 6 2 7 2 8}} do_test select4-5.3 { set v [catch {execsql { SELECT DISTINCT log, n FROM t1