From b290f1177553b866b806e5b987c060b9eed99758 Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 17 Jan 2014 14:59:27 +0000 Subject: [PATCH] Fix some problems to do with WITH clauses and name resolution. FossilOrigin-Name: 6a549187ed8b5ed50daefa676ff666ae2ed43346 --- manifest | 21 +++++++++-------- manifest.uuid | 2 +- src/parse.y | 12 +++++----- src/select.c | 61 ++++++++++++++++++++++++------------------------- src/sqliteInt.h | 7 +++--- src/tokenize.c | 3 +-- src/walker.c | 23 ++++++++++--------- test/with2.test | 56 +++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 121 insertions(+), 64 deletions(-) create mode 100644 test/with2.test diff --git a/manifest b/manifest index 5d2226d267..586f354eac 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Remove\ssome\scode\sfrom\sresolve.c\sthat\swas\sonly\srequired\sfor\srecursive\scte\sreferences\sin\ssub-queries.\sAlso\sa\sstray\s"finish_test"\scommand\sin\spagerfault.test. -D 2014-01-17T11:48:24.294 +C Fix\ssome\sproblems\sto\sdo\swith\sWITH\sclauses\sand\sname\sresolution. +D 2014-01-17T14:59:27.898 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -209,7 +209,7 @@ F src/os_unix.c 3a4dcb554d3c915075766162f28c3fd4cdb75968 F src/os_win.c 1b21af72c5fa6f9e519a5fcab33e80d182b1aedb F src/pager.c efa923693e958696eee69b205a20bfbc402c8480 F src/pager.h ffd5607f7b3e4590b415b007a4382f693334d428 -F src/parse.y 824eeb752c026b551bda2b66163889d7664b42e4 +F src/parse.y 475896cb883bbf4782e98abda42efbbdcbdb75f5 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.h a5e4f5d9f5d592051d91212c5949517971ae6222 F src/pcache1.c 57fee9a9a617218f5037afbbe49b09da65bde56b @@ -219,12 +219,12 @@ F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269 F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0 -F src/select.c 65c13f22edfd6af04829439955c7cf5749ea4e87 +F src/select.c c77955f93121adc8b4b43a98add62fbaa2b48132 F src/shell.c 9f3bc02a658b8f61d2cbe60cfc482f660c1c6c48 F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc -F src/sqliteInt.h d49c0bea5282f15c1eb1eb9d705770f70d19c1e2 +F src/sqliteInt.h 9600eeb486c274fbdb815d040e4a7f262b7317e1 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -274,7 +274,7 @@ F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9 F src/test_vfstrace.c 3a0ab304682fecbceb689e7d9b904211fde11d78 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 -F src/tokenize.c 7dc42e9beb8c3263b79d10c195b3f5264b5f874a +F src/tokenize.c 6da2de6e12218ccb0aea5184b56727d011f4bee7 F src/trigger.c 5c1c0b899ac0ce284763dcb8fdbaa38ecf15ef98 F src/update.c c2706a6eb232a96345c35b7e1e75a188e26812bb F src/utf.c 6fc6c88d50448c469c5c196acf21617a24f90269 @@ -292,7 +292,7 @@ F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767 F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd F src/wal.c 7dc3966ef98b74422267e7e6e46e07ff6c6eb1b4 F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 -F src/walker.c e9e593d5bb798c3e67fc3893dfe7055c9e7d8d74 +F src/walker.c 11edb74d587bc87b33ca96a5173e3ec1b8389e45 F src/where.c 369b0259fabfb22644d197736ae622f762cbaba8 F src/whereInt.h 96a75c61f1d2b9d4a8e4bb17d89deb0cf7cba358 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 @@ -1092,6 +1092,7 @@ F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d F test/with1.test 90490c75e98e1914d84b7cef9e636b48917a020f +F test/with2.test 790c4b7ab3f4eb6984a3bbdae8d4ab429ebe9259 F test/withM.test 52448ce23e1c2ecba79d10e130ee49ce9f9a2a7a F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8 F test/without_rowid2.test af260339f79d13cb220288b67cd287fbcf81ad99 @@ -1150,7 +1151,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff -P 9efc120a1548c03f3d8aabbadf1050ff2a119c31 -R 175ad2c9f66168be6fabd0b7fdb44bd2 +P f68c6c4d36481526a9348244adc571ea282dc9eb +R d73fd639096ae3d6dc6dd6a7018ffc11 U dan -Z 6e3919f2cc39eeea18a32475675bdfc0 +Z 6339477b00dd8c67441747d0ac03cfea diff --git a/manifest.uuid b/manifest.uuid index 6e22de7d56..71dd74abb9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f68c6c4d36481526a9348244adc571ea282dc9eb \ No newline at end of file +6a549187ed8b5ed50daefa676ff666ae2ed43346 \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index 937669eb5c..0805407920 100644 --- a/src/parse.y +++ b/src/parse.y @@ -661,7 +661,7 @@ limit_opt(A) ::= LIMIT expr(X) COMMA expr(Y). %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W) orderby_opt(O) limit_opt(L). { - sqlite3WithPush(pParse,C); + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE"); sqlite3DeleteFrom(pParse,X,W); @@ -669,7 +669,7 @@ cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W) %endif %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W). { - sqlite3WithPush(pParse,C); + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3DeleteFrom(pParse,X,W); } @@ -686,7 +686,7 @@ where_opt(A) ::= WHERE expr(X). {A = X.pExpr;} %ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W) orderby_opt(O) limit_opt(L). { - sqlite3WithPush(pParse, C); + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE"); @@ -696,7 +696,7 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) %ifndef SQLITE_ENABLE_UPDATE_DELETE_LIMIT cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y) where_opt(W). { - sqlite3WithPush(pParse, C); + sqlite3WithPush(pParse, C, 1); sqlite3SrcListIndexedBy(pParse, X, &I); sqlite3ExprListCheckLength(pParse,Y,"set list"); sqlite3Update(pParse,X,Y,W,R); @@ -718,12 +718,12 @@ setlist(A) ::= nm(X) EQ expr(Y). { ////////////////////////// The INSERT command ///////////////////////////////// // cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) select(S). { - sqlite3WithPush(pParse, W); + sqlite3WithPush(pParse, W, 1); sqlite3Insert(pParse, X, S, F, R); } cmd ::= with(W) insert_cmd(R) INTO fullname(X) inscollist_opt(F) DEFAULT VALUES. { - sqlite3WithPush(pParse, W); + sqlite3WithPush(pParse, W, 1); sqlite3Insert(pParse, X, 0, F, R); } diff --git a/src/select.c b/src/select.c index dc784b3b10..d9625cba43 100644 --- a/src/select.c +++ b/src/select.c @@ -3527,12 +3527,19 @@ static struct Cte *searchWith(With *pWith, struct SrcList_item *pItem){ /* The code generator maintains a stack of active WITH clauses ** with the inner-most WITH clause being at the top of the stack. ** -** These routines push and pull WITH clauses on the stack. +** This routine pushes the WITH clause passed as the second argument +** onto the top of the stack. If argument bFree is true, then this +** WITH clause will never be popped from the stack. In this case it +** should be freed along with the Parse object. In other cases, when +** bFree==0, the With object will be freed along with the SELECT +** statement with which it is associated. */ -void sqlite3WithPush(Parse *pParse, With *pWith){ +void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){ + assert( bFree==0 || pParse->pWith==0 ); if( pWith ){ pWith->pOuter = pParse->pWith; pParse->pWith = pWith; + pParse->bFreeWith = bFree; } } @@ -3649,6 +3656,19 @@ static int withExpand( } #endif +#ifndef SQLITE_OMIT_CTE +static void selectPopWith(Walker *pWalker, Select *p){ + Parse *pParse = pWalker->pParse; + if( p->pWith ){ + assert( pParse->pWith==p->pWith ); + pParse->pWith = p->pWith->pOuter; + } + return WRC_Continue; +} +#else +#define selectPopWith 0 +#endif + /* ** This routine is a Walker callback for "expanding" a SELECT statement. ** "Expanding" means to do the following: @@ -3692,6 +3712,7 @@ static int selectExpander(Walker *pWalker, Select *p){ } pTabList = p->pSrc; pEList = p->pEList; + sqlite3WithPush(pParse, p->pWith, 0); /* Make sure cursor numbers have been assigned to all entries in ** the FROM clause of the SELECT statement. @@ -3710,6 +3731,9 @@ static int selectExpander(Walker *pWalker, Select *p){ /* This statement has already been prepared. There is no need ** to go further. */ assert( i==0 ); +#ifndef SQLITE_OMIT_CTE + selectPopWith(pWalker, p); +#endif return WRC_Prune; } #ifndef SQLITE_OMIT_CTE @@ -3941,30 +3965,6 @@ static int selectExpander(Walker *pWalker, Select *p){ return WRC_Continue; } -/* -** Function (or macro) selectExpanderWith is used as the SELECT callback -** by sqlite3SelectExpand(). In builds that do not support CTEs, this -** is equivalent to the selectExpander() function. In CTE-enabled builds, -** any WITH clause associated with the SELECT statement needs to be -** pushed onto the stack before calling selectExpander(), and popped -** off again afterwards. -*/ -#ifndef SQLITE_OMIT_CTE -static int selectExpanderWith(Walker *pWalker, Select *p){ - Parse *pParse = pWalker->pParse; - int res; - sqlite3WithPush(pParse, p->pWith); - res = selectExpander(pWalker, p); - if( p->pWith ){ - assert( pParse->pWith==p->pWith ); - pParse->pWith = p->pWith->pOuter; - } - return res; -} -#else -#define selectExpanderWith selectExpander -#endif - /* ** No-op routine for the parse-tree walker. ** @@ -4001,7 +4001,8 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ w.xSelectCallback = convertCompoundSelectToSubquery; sqlite3WalkSelect(&w, pSelect); } - w.xSelectCallback = selectExpanderWith; + w.xSelectCallback = selectExpander; + w.xSelectCallback2 = selectPopWith; sqlite3WalkSelect(&w, pSelect); } @@ -4020,7 +4021,7 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){ ** at that point because identifiers had not yet been resolved. This ** routine is called after identifier resolution. */ -static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ +static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ Parse *pParse; int i; SrcList *pTabList; @@ -4043,7 +4044,6 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){ } } } - return WRC_Continue; } #endif @@ -4059,10 +4059,9 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){ #ifndef SQLITE_OMIT_SUBQUERY Walker w; memset(&w, 0, sizeof(w)); - w.xSelectCallback = selectAddSubqueryTypeInfo; + w.xSelectCallback2 = selectAddSubqueryTypeInfo; w.xExprCallback = exprWalkNoop; w.pParse = pParse; - w.bSelectDepthFirst = 1; sqlite3WalkSelect(&w, pSelect); #endif } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 3f29f58d4e..1cdaf09dc7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2371,6 +2371,7 @@ struct Parse { Table *pZombieTab; /* List of Table objects to delete after code gen */ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ With *pWith; /* Current WITH clause, or NULL */ + u8 bFreeWith; /* True if pWith should be freed with parser */ }; /* @@ -2612,9 +2613,9 @@ struct Sqlite3Config { struct Walker { int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */ int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */ + void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */ Parse *pParse; /* Parser context. */ int walkerDepth; /* Number of subqueries */ - u8 bSelectDepthFirst; /* Do subqueries first */ union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ @@ -3354,9 +3355,9 @@ const char *sqlite3JournalModename(int); #ifndef SQLITE_OMIT_CTE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*); void sqlite3WithDelete(sqlite3*,With*); - void sqlite3WithPush(Parse*, With*); + void sqlite3WithPush(Parse*, With*, u8); #else -#define sqlite3WithPush(x,y) +#define sqlite3WithPush(x,y,z) #define sqlite3WithDelete(x,y) #endif diff --git a/src/tokenize.c b/src/tokenize.c index 3fd4f78f7a..87553e25b0 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -494,8 +494,7 @@ abort_parse: sqlite3DeleteTable(db, pParse->pNewTable); } - assert( pParse->pWith==0 || pParse->pWith->pOuter==0 ); - sqlite3WithDelete(db, pParse->pWith); + if( pParse->bFreeWith ) sqlite3WithDelete(db, pParse->pWith); sqlite3DeleteTrigger(db, pParse->pNewTrigger); for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]); sqlite3DbFree(db, pParse->azVar); diff --git a/src/walker.c b/src/walker.c index cde34ad780..016ae77a92 100644 --- a/src/walker.c +++ b/src/walker.c @@ -113,9 +113,12 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ /* ** Call sqlite3WalkExpr() for every expression in Select statement p. ** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and -** on the compound select chain, p->pPrior. Invoke the xSelectCallback() -** either before or after the walk of expressions and FROM clause, depending -** on whether pWalker->bSelectDepthFirst is false or true, respectively. +** on the compound select chain, p->pPrior. +** +** If it is not NULL, the xSelectCallback() callback is invoked before +** the walk of the expressions and FROM clause. The xSelectCallback2() +** method, if it is not NULL, is invoked following the walk of the +** expressions and FROM clause. ** ** Return WRC_Continue under normal conditions. Return WRC_Abort if ** there is an abort request. @@ -125,11 +128,13 @@ int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){ */ int sqlite3WalkSelect(Walker *pWalker, Select *p){ int rc; - if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue; + if( p==0 || (pWalker->xSelectCallback==0 && pWalker->xSelectCallback2==0) ){ + return WRC_Continue; + } rc = WRC_Continue; pWalker->walkerDepth++; while( p ){ - if( !pWalker->bSelectDepthFirst ){ + if( pWalker->xSelectCallback ){ rc = pWalker->xSelectCallback(pWalker, p); if( rc ) break; } @@ -139,12 +144,8 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){ pWalker->walkerDepth--; return WRC_Abort; } - if( pWalker->bSelectDepthFirst ){ - rc = pWalker->xSelectCallback(pWalker, p); - /* Depth-first search is currently only used for - ** selectAddSubqueryTypeInfo() and that routine always returns - ** WRC_Continue (0). So the following branch is never taken. */ - if( NEVER(rc) ) break; + if( pWalker->xSelectCallback2 ){ + pWalker->xSelectCallback2(pWalker, p); } p = p->pPrior; } diff --git a/test/with2.test b/test/with2.test new file mode 100644 index 0000000000..83b3b73646 --- /dev/null +++ b/test/with2.test @@ -0,0 +1,56 @@ +# 2014 January 11 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. The +# focus of this file is testing the WITH clause. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set ::testprefix with2 + +do_execsql_test 1.0 { + CREATE TABLE t1(a); + INSERT INTO t1 VALUES(1); + INSERT INTO t1 VALUES(2); +} + +do_execsql_test 1.1 { + WITH x1 AS (SELECT * FROM t1) + SELECT sum(a) FROM x1; +} {3} + +do_execsql_test 1.2 { + WITH x1 AS (SELECT * FROM t1) + SELECT (SELECT sum(a) FROM x1); +} {3} + +do_execsql_test 1.3 { + WITH x1 AS (SELECT * FROM t1) + SELECT (SELECT sum(a) FROM x1); +} {3} + +do_execsql_test 1.4 { + CREATE TABLE t2(i); + INSERT INTO t2 VALUES(2); + INSERT INTO t2 VALUES(3); + INSERT INTO t2 VALUES(5); + + WITH x1 AS (SELECT i FROM t2), + i(a) AS ( + SELECT min(i)-1 FROM x1 UNION SELECT a+1 FROM i WHERE a<10 + ) + SELECT a FROM i WHERE a NOT IN x1 +} {1 4 6 7 8 9 10} + +finish_test + + + -- 2.39.5