diff --git a/manifest b/manifest index f3c9ed8a94..5812a1a736 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Modification\sto\sschema.test\sso\sthat\sit\sworks\swith\sSQLITE_OMIT_TRIGGER\sand\sSQLITE_OMIT_UTF16\sdefined.\s(CVS\s2285) -D 2005-01-29T01:54:18 +C Modify\ssub-query\shandling.\sTickets\s#1083\sand\s#1084.\s(CVS\s2286) +D 2005-01-29T08:32:44 F Makefile.in ffd81f5e926d40b457071b4de8d7c1fa18f39b5a F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -28,21 +28,21 @@ F sqlite3.1 01fdb467ce387a83248857c92f9e801df9e4611c F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/attach.c f78f76bc6a8e5e487ca53636e21ccba2484a9a61 -F src/auth.c 4b15c85335417752cc1045eae18b8186e08c8184 +F src/auth.c 18c5a0befe20f3a58a41e3ddd78f372faeeefe1f F src/btree.c e68ae12c8b12ef9d45d58d931c36c184055a3880 F src/btree.h 74d19cf40ab49fd69abe9e4e12a6c321ad86c497 -F src/build.c c894a91251c226eb40c54b6f86cbee166ab13334 +F src/build.c e35b7f93c4761b7e757dcce908d336a9adf9b43d F src/cursor.c de73c00aefc4747ad59b5105cf38bbff0667922e F src/date.c f3d1f5cd1503dabf426a198f3ebef5afbc122a7f -F src/delete.c b3accca9c38d9a67dbd724f67b04151a13735ebd +F src/delete.c 4b94395b52a8f7785acd71135c2ce54f3f5550b3 F src/experimental.c 8cc66b2be6a011055d75ef19ed2584bcfbb585ad -F src/expr.c abadaf7b858084949ac36316a335384498c5b0e2 +F src/expr.c 9965ce8a6f416377ddcace8fb1d796101cf02ea9 F src/func.c f096b6771cc0aaa11790aca95773a50a8f74ba73 F src/hash.c a97721a55440b7bea31ffe471bb2f6b4123cddd5 F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84 -F src/insert.c 037eb46630f027d0f93584db180d08ce163f3dbb +F src/insert.c 6ab596846d52bd63d6227f9128a29e4f5b2cf524 F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b -F src/main.c 302e21bdaa015f590fdb7e69d50f45788da0fb0b +F src/main.c 612531a2e6fba994274732acae76d7d19b9f90fd F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070 F src/os.h ae44064dc118b20d39450cb331409a775e8bb1c6 F src/os_common.h 0e7f428ba0a6c40a61bc56c4e96f493231301b73 @@ -52,16 +52,16 @@ F src/os_unix.c 1f17ceff056c64939e5f2e98bf909fc64d0929ca F src/os_unix.h f3097815e041e82e24d92505e1ff61ba24172d13 F src/os_win.c 3c0b0a3bc33318cf555a1cd130232ad1b9a5a711 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b -F src/pager.c 886a1ae43365ae3b2599d8c6eb6091d5dc91ca7c +F src/pager.c d21565d0e844712809140632062a7b72b768fdff F src/pager.h 9eba8c53dd91eae7f3f90743b2ee242da02a9862 -F src/parse.y 5f2c197fcb63c6aed1787da436ec5a35247ab7a4 +F src/parse.y 959948ee97434a7bab3aa04094cd5be6b7501e8d F src/pragma.c c893f03104e94e0921861bd2d3dbd80c47515f7b F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 -F src/select.c 32fe60f1aff8a540b709008451013f480dc22d55 +F src/select.c b34d70980c08b6d324b06bee9f63ef91c18c6010 F src/shell.c 1f0da77ef0520afd6df71f4781076021874310f3 F src/sqlite.h.in 7d7c28344e2bd770491b56ed9169be20859c707d -F src/sqliteInt.h be6fa5e31c65e2b8e10112ee47a6e63ec7de37b5 +F src/sqliteInt.h d5052f5a9badbde9e746e40522e8ab823b1670d5 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/tclsqlite.c 101994a2c4c0eaa69f1de9bfe4a02167f6049e7d F src/test1.c 8c320f043b869c08fca86c4f01de027774eb85a8 @@ -70,18 +70,18 @@ F src/test3.c 683e1e3819152ffd35da2f201e507228921148d0 F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5 F src/tokenize.c 88bef43fe3e3c8865a7447f934296ac13238c4f6 -F src/trigger.c 5da126eeedca7d25c892311f21f909ff1f3825ba -F src/update.c 6e5c6eb660a5508c449c6d637571e24ef13f70a1 +F src/trigger.c 038c8e128d4551cd016426cd11bbf5c478816481 +F src/update.c b6f4668c11059f86b71581187d09197fa28ec4be F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c F src/util.c a858b93ba06bbafab55ba41e4d58538eb51f4b6a F src/vacuum.c 1a9db113a027461daaf44724c71dd1ebbd064203 -F src/vdbe.c 8e877a9cdc92f30a71510e427db5e99d1f989c54 -F src/vdbe.h 067ca8d6750ba4f69a50284765e5883dee860181 -F src/vdbeInt.h 24d411de9efc6919a1e580069a597182be269bcf +F src/vdbe.c 84ccc6be09e13ee5825f32a94b289117cc903ab2 +F src/vdbe.h bb9186484f749a839c6c43953e79a6530253f7cd +F src/vdbeInt.h e80721cd8ff611789e20743eec43363a9fb5a48e F src/vdbeapi.c 467caa6e6fb9247528b1c7ab9132ae1b4748e8ac -F src/vdbeaux.c 083c5fcde08120d7857dcac2b296a1eb7e390a18 +F src/vdbeaux.c 8d8cc8992cb78cab35e034fa81ad0c1a771c39f1 F src/vdbemem.c 62fe89471b656a922e9879be005abf690509ead3 -F src/where.c f4127cc2633ee0f74790ab7f09f5af832489e44e +F src/where.c b733d3a2e866bb31a3c5d0acf94d8dc599d55a81 F tclinstaller.tcl 36478c3bbfc5b93ceac42d94e3c736937b808432 F test/all.test 7f0988442ab811dfa41793b5b550f5828ce316f3 F test/alter.test b146ddd669b45a880d40bfdacd6037666137c3f4 @@ -96,7 +96,7 @@ F test/autovacuum_ioerr.test 9cf27275ca47b72e188a47c53b61b6d583a01d24 F test/autovacuum_ioerr2.test 8feb1cfb4d8177c639cd1e0b8c41d3c88a2a1518 F test/bigfile.test d3744a8821ce9abb8697f2826a3e3d22b719e89f F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747 -F test/bind.test d83cf2cdc5e2aae3066bbb9c6d12db51e6512fc9 +F test/bind.test bc33135f91d1b59572ad2bcbc84d2201c5d4455d F test/blob.test fc41fe95bdc10da51f0dee73ce86e75ce1d6eb9d F test/btree.test 8aa7424aeec844df990273fe36447e5d7e407261 F test/btree2.test dbce930b549d5ac883a7d8905c976209ea241db3 @@ -109,7 +109,7 @@ F test/capi3.test f50dd4666deba96275f9927fe8ec089a3d8c0efa F test/capi3b.test 5b6a66f9f295f79f443b5d3f33187fa5ef6cf336 F test/collate1.test f79736d2ebf5492167ee4d1f4ab4c09dda776b03 F test/collate2.test 224a632ba04907c049804b08162efd234aa7871f -F test/collate3.test 210fab018450eeb085e4190cd7ca0aabd99b8c11 +F test/collate3.test 51362bdfb43a72bd2b087d90b2623b0695538e7a F test/collate4.test b8668612691c4dcf90f67a8df1eeb1544e7fdaf8 F test/collate5.test 581775b94604b7435dc6a5c6e72fbbf7d69e3830 F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638 @@ -135,7 +135,7 @@ F test/index.test 51e01a0928b4b61228917ddd8c6c0e2466547f6f F test/index2.test 9ad98243fd7fe833795a9cc662f371f0eed4ff4f F test/insert.test f39cb2306199c6f9d8959b843c9199d799217055 F test/insert2.test 420cb5c23912732219aad87420abdd7b994b1cad -F test/insert3.test fa7cb5b01709a1bca3e28c82c80c1d44386b3676 +F test/insert3.test c67f0240b1c17e71fa2ed8bb6de064928f549f95 F test/interrupt.test 5b4d8389e6cf2d01b94f87cfd02d9df1073bfb2d F test/intpkey.test b57cf5236fde1bd8cbc1388fa0c91908f6fd9194 F test/ioerr.test 3155522a4fd73c714a78fc3403cced7252a130f3 @@ -157,7 +157,7 @@ F test/minmax.test 9429a06f1f93acf76fcacafd17160a4392e88526 F test/misc1.test ff817d3740458884fea535b44821ec7e84700457 F test/misc2.test fc052267d5178367f955538ae34aae1b2f696a92 F test/misc3.test 7bd937e2c62bcc6be71939faf068d506467b1e03 -F test/misc4.test 145e301fdf10bd47059132db932523814201dc2a +F test/misc4.test f44ad10982592bb77f1ae6d7876890b3af9605c1 F test/misuse.test 1c7fee3c4c0cb4008717ecccf5c72281fac0008e F test/notnull.test 7a08117a71e74b0321aaa937dbeb41a09d6eb1d0 F test/null.test 69c62daf1630bf54c87bbc7ef2e22012e58d6da8 @@ -180,10 +180,10 @@ F test/select2.test 01b9cbc06e5ed662ce0289aa5f47314d54541e82 F test/select3.test 9de435aa84fc406708cd8dc1b1d60e7f27cea685 F test/select4.test c239f516aa31f42f2ef7c6d7cd01105f08f934ca F test/select5.test 2d414f712bff8e590091e08f9b7287600731be00 -F test/select6.test ba1b4dd18a85bf9070c6df8d933ac4cfcacea6a6 +F test/select6.test 6e5a1a70a788cdbe515d1252dd0917d7e9d1d71e F test/select7.test 8f3362336c10d828ab6fe9c1b8897b484da8b592 F test/sort.test 87882e6c72a75d45e98a1c802c1ded0eac557d85 -F test/subquery.test a3ed9f11a4e576ff31b539ab5d65953dc3d27a81 +F test/subquery.test ecec0780e2c8b101068e8780a012dcf1ef5194f4 F test/subselect.test 3f3f7a940dc3195c3139f4d530385cb54665d614 F test/table.test a2a58cae70ef2511cbf27d40fb8f570106a2677e F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1 @@ -196,7 +196,7 @@ F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b F test/trace.test a54fa8df0d01cf827289a7659d78959e8fd2f955 F test/trans.test 29645b344d2b9b6792793562b12340177ddd8f96 F test/trigger1.test 9db1a7c91930baa2dc60ce72c7e969900bf2ae8a -F test/trigger2.test cf497cd1cecf46774558d2ca80017a7a68f38473 +F test/trigger2.test cbc8fe3775904d5b49ff26888aa39df7341fae7c F test/trigger3.test 9102fd3933db294dc654b5aee9edfe9e94f2b9e2 F test/trigger4.test e7c0812b14750754602468f15495260e8c6625e0 F test/trigger5.test 619391a3e9fc194081d22cefd830d811e7badf83 @@ -208,7 +208,7 @@ F test/update.test 7669ca789d62c258b678e8aa7a22a57eac10f2cf F test/utf16.test 459c2f5ab80c60092c603630a348c32d6e59c558 F test/vacuum.test f18eccdee5b538d46298c64d6a060cfbf97bbc23 F test/varint.test ab7b110089a08b9926ed7390e7e97bdefeb74102 -F test/view.test 5aac4c79eb86e297a53c8c4a2543dc193034e66d +F test/view.test a34c5488932cb9f3daf5797b395da6444e570b98 F test/where.test ffb790dfda75d977bae7a1f5830351623f76861b F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b F tool/lemon.c 4a3b5ccc76d959b8caa5f127d23a7e14d4470b4e @@ -272,7 +272,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl 3e522a06ad41992023c80ca29a048ae2331ca5bd -P 522c094f799220468780acb77731edb715bf5e3c -R 9002e89543be697066b39cd89912bc24 +P 95ecb2745f3fc69d370fc3961800db56297acb68 +R fe4aca512646cc7b9406c540eeb0d8a2 U danielk1977 -Z e3ae9b709b41b90ac9746a34ed6aa9ba +Z 3506c9b8c122ebd01b86eeaa686e6ed6 diff --git a/manifest.uuid b/manifest.uuid index 0e3d6f6784..f927db229c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -95ecb2745f3fc69d370fc3961800db56297acb68 \ No newline at end of file +b1b50f315873a8614920d1e3af4a07fb29a7ff6a \ No newline at end of file diff --git a/src/auth.c b/src/auth.c index 6aa69c8747..aa387cc603 100644 --- a/src/auth.c +++ b/src/auth.c @@ -14,7 +14,7 @@ ** systems that do not need this facility may omit it by recompiling ** the library with -DSQLITE_OMIT_AUTHORIZATION=1 ** -** $Id: auth.c,v 1.20 2005/01/22 03:03:54 drh Exp $ +** $Id: auth.c,v 1.21 2005/01/29 08:32:44 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -115,10 +115,10 @@ void sqlite3AuthRead( if( db->xAuth==0 ) return; assert( pExpr->op==TK_COLUMN ); - for(iSrc=0; iSrcnSrc; iSrc++){ + for(iSrc=0; pTabList && iSrcnSrc; iSrc++){ if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; } - if( iSrc>=0 && iSrcnSrc ){ + if( iSrc>=0 && pTabList && iSrcnSrc ){ pTab = pTabList->a[iSrc].pTab; }else if( (pStack = pParse->trigStack)!=0 ){ /* This must be an attempt to read the NEW or OLD pseudo-tables diff --git a/src/build.c b/src/build.c index 2b2602226a..f43ee301b2 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.300 2005/01/27 00:33:38 danielk1977 Exp $ +** $Id: build.c,v 1.301 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -95,7 +95,7 @@ void sqlite3FinishCoding(Parse *pParse){ FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; sqlite3VdbeTrace(v, trace); sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3, - pParse->nTab+3, pParse->explain); + pParse->nTab+3, pParse->nMaxDepth+1, pParse->explain); pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE; pParse->colNamesSet = 0; }else if( pParse->rc==SQLITE_OK ){ @@ -104,7 +104,6 @@ void sqlite3FinishCoding(Parse *pParse){ pParse->nTab = 0; pParse->nMem = 0; pParse->nSet = 0; - pParse->nAgg = 0; pParse->nVar = 0; pParse->cookieMask = 0; pParse->cookieGoto = 0; @@ -896,7 +895,6 @@ void sqlite3AddDefaultValue(Parse *pParse, Expr *pExpr){ }else{ sqlite3ExprDelete(pCol->pDflt); pCol->pDflt = sqlite3ExprDup(pExpr); - sqlite3ExprResolveNames(pParse,0,0,0,pExpr,0,0); } sqlite3ExprDelete(pExpr); } @@ -1439,7 +1437,7 @@ void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){ sqlite3VdbeAddOp(v, OP_Integer, p->iDb, 0); sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0); pParse->nTab = 2; - sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0, 0); + sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0); sqlite3VdbeAddOp(v, OP_Close, 1, 0); if( pParse->nErr==0 ){ pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect); diff --git a/src/delete.c b/src/delete.c index 50a25ab6fc..b160bdaba2 100644 --- a/src/delete.c +++ b/src/delete.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. ** -** $Id: delete.c,v 1.99 2005/01/20 11:32:24 danielk1977 Exp $ +** $Id: delete.c,v 1.100 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -90,6 +90,7 @@ void sqlite3DeleteFrom( sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ + NameContext sNC; /* Name context to resolve expressions in */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to delete from a view */ @@ -148,11 +149,14 @@ void sqlite3DeleteFrom( oldIdx = pParse->nTab++; } - /* Resolve the column names in all the expressions. + /* Resolve the column names in the WHERE clause. */ assert( pTabList->nSrc==1 ); iCur = pTabList->a[0].iCursor = pParse->nTab++; - if( sqlite3ExprResolveNames(pParse, pTabList, 0, 0, pWhere, 0, 1) ){ + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; + if( sqlite3ExprResolveNames(&sNC, pWhere) ){ goto delete_from_cleanup; } @@ -176,7 +180,7 @@ void sqlite3DeleteFrom( */ if( isView ){ Select *pView = sqlite3SelectDup(pTab->pSelect); - sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0, 0); + sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0); sqlite3SelectDelete(pView); } diff --git a/src/expr.c b/src/expr.c index 2f22842a7f..815d0b064f 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.188 2005/01/23 22:41:37 danielk1977 Exp $ +** $Id: expr.c,v 1.189 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -516,6 +516,8 @@ Select *sqlite3SelectDup(Select *p){ pNew->iOffset = -1; pNew->ppOpenTemp = 0; pNew->pFetch = 0; + pNew->isResolved = 0; + pNew->isAgg = 0; return pNew; } #else @@ -750,40 +752,42 @@ static int lookupName( pNC->nRef++; /* assert( zTab==0 || pEList==0 ); */ - for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ - Table *pTab = pItem->pTab; - Column *pCol; - - if( pTab==0 ) continue; - assert( pTab->nCol>0 ); - if( zTab ){ - if( pItem->zAlias ){ - char *zTabName = pItem->zAlias; - if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; - }else{ - char *zTabName = pTab->zName; - if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue; - if( zDb!=0 && sqlite3StrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ - continue; + if( pSrcList ){ + for(i=0, pItem=pSrcList->a; inSrc; i++, pItem++){ + Table *pTab = pItem->pTab; + Column *pCol; + + if( pTab==0 ) continue; + assert( pTab->nCol>0 ); + if( zTab ){ + if( pItem->zAlias ){ + char *zTabName = pItem->zAlias; + if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue; + }else{ + char *zTabName = pTab->zName; + if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue; + if( zDb!=0 && sqlite3StrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){ + continue; + } } } - } - if( 0==(cntTab++) ){ - pExpr->iTable = pItem->iCursor; - pExpr->iDb = pTab->iDb; - pMatch = pItem; - } - for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ - if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - cnt++; + if( 0==(cntTab++) ){ pExpr->iTable = pItem->iCursor; - pMatch = pItem; pExpr->iDb = pTab->iDb; - /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ - pExpr->iColumn = j==pTab->iPKey ? -1 : j; - pExpr->affinity = pTab->aCol[j].affinity; - pExpr->pColl = pTab->aCol[j].pColl; - break; + pMatch = pItem; + } + for(j=0, pCol=pTab->aCol; jnCol; j++, pCol++){ + if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ + cnt++; + pExpr->iTable = pItem->iCursor; + pMatch = pItem; + pExpr->iDb = pTab->iDb; + /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */ + pExpr->iColumn = j==pTab->iPKey ? -1 : j; + pExpr->affinity = pTab->aCol[j].affinity; + pExpr->pColl = pTab->aCol[j].pColl; + break; + } } } } @@ -926,7 +930,7 @@ static int lookupName( pExpr->pRight = 0; pExpr->op = TK_COLUMN; if( cnt==1 ){ - assert( pNC!=0 && pNC->pSrcList!=0 ); + assert( pNC!=0 ); sqlite3AuthRead(pParse, pExpr, pNC->pSrcList); } return cnt!=1; @@ -991,10 +995,11 @@ static int nameResolverStep(void *pArg, Expr *pExpr){ SrcList *pSrcList; Parse *pParse; + if( pExpr==0 ) return 1; assert( pNC!=0 ); pSrcList = pNC->pSrcList; pParse = pNC->pParse; - if( pExpr==0 ) return 1; + if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return 1; ExprSetProperty(pExpr, EP_Resolved); #ifndef NDEBUG @@ -1017,7 +1022,6 @@ static int nameResolverStep(void *pArg, Expr *pExpr){ /* A lone identifier is the name of a column. */ case TK_ID: { - if( pSrcList==0 ) break; lookupName(pParse, 0, 0, &pExpr->token, pNC, pExpr); return 1; } @@ -1031,7 +1035,7 @@ static int nameResolverStep(void *pArg, Expr *pExpr){ Token *pDb; Expr *pRight; - if( pSrcList==0 ) break; + /* if( pSrcList==0 ) break; */ pRight = pExpr->pRight; if( pRight->op==TK_ID ){ pDb = 0; @@ -1052,7 +1056,6 @@ static int nameResolverStep(void *pArg, Expr *pExpr){ case TK_CTIME: case TK_CTIMESTAMP: case TK_CDATE: - /* Note: The above three were a seperate case in sqlmoto. Reason? */ case TK_GLOB: case TK_LIKE: case TK_FUNCTION: { @@ -1105,13 +1108,24 @@ static int nameResolverStep(void *pArg, Expr *pExpr){ */ return is_agg; } +#ifndef SQLITE_OMIT_SUBQUERY + case TK_SELECT: + case TK_EXISTS: +#endif + case TK_IN: { + if( pExpr->pSelect ){ + int nRef = pNC->nRef; + sqlite3SelectResolve(pParse, pExpr->pSelect, pNC); + assert( pNC->nRef>=nRef ); + if( nRef!=pNC->nRef ){ + ExprSetProperty(pExpr, EP_VarSelect); + } + } + } } return 0; } -/* Forward declaration */ -static int sqlite3ExprCodeSubquery(Parse*, NameContext*, Expr*); - /* ** This routine walks an expression tree and resolves references to ** table columns. Nodes of the form ID.ID or ID resolve into an @@ -1134,31 +1148,13 @@ static int sqlite3ExprCodeSubquery(Parse*, NameContext*, Expr*); ** property on the expression. */ int sqlite3ExprResolveNames( - Parse *pParse, /* The parser context */ - SrcList *pSrcList, /* List of tables used to resolve column names */ - ExprList *pEList, /* List of expressions used to resolve "AS" */ - NameContext *pNC, /* Namespace of enclosing statement */ - Expr *pExpr, /* The expression to be analyzed. */ - int allowAgg, /* True to allow aggregate expressions */ - int codeSubquery /* If true, then generate code for subqueries too */ + NameContext *pNC, /* Namespace to resolve expressions in. */ + Expr *pExpr /* The expression to be analyzed. */ ){ - NameContext sNC; - if( pExpr==0 ) return 0; - memset(&sNC, 0, sizeof(sNC)); - sNC.pSrcList = pSrcList; - sNC.pParse = pParse; - sNC.pEList = pEList; - sNC.allowAgg = allowAgg; - sNC.pNext = pNC; - walkExprTree(pExpr, nameResolverStep, &sNC); - if( sNC.hasAgg ){ - ExprSetProperty(pExpr, EP_Agg); - } - if( sNC.nErr>0 ){ + walkExprTree(pExpr, nameResolverStep, pNC); + if( pNC->nErr>0 ){ ExprSetProperty(pExpr, EP_Error); - }else if( codeSubquery && sqlite3ExprCodeSubquery(pParse, &sNC, pExpr) ){ - return 1; } return ExprHasProperty(pExpr, EP_Error); } @@ -1186,28 +1182,36 @@ struct QueryCoder { ** The first form is handled by creating a set holding the list ** of allowed values. The second form causes the SELECT to generate ** a temporary table. -** -** This routine also looks for scalar SELECTs that are part of an expression. -** If it finds any, it generates code to write the value of that select -** into a memory cell. -** -** This routine is a callback for wallExprTree() used to implement -** sqlite3ExprCodeSubquery(). See comments on those routines for -** additional information. */ #ifndef SQLITE_OMIT_SUBQUERY -static int codeSubqueryStep(void *pArg, Expr *pExpr){ - QueryCoder *pCoder = (QueryCoder*)pArg; - Parse *pParse = pCoder->pParse; +void sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){ + int label = 0; /* Address after sub-select code */ + Vdbe *v = sqlite3GetVdbe(pParse); + if( v==0 ) return; + + /* If this is not a variable (correlated) select, then execute + ** it only once. Unless this is part of a trigger program. In + ** that case re-execute every time (this could be optimized). + */ + if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){ + int mem = pParse->nMem++; + sqlite3VdbeAddOp(v, OP_MemLoad, mem, 0); + label = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp(v, OP_If, 0, label); + sqlite3VdbeAddOp(v, OP_Integer, 1, 0); + sqlite3VdbeAddOp(v, OP_MemStore, mem, 1); + } + + if( pExpr->pSelect ){ + sqlite3VdbeAddOp(v, OP_AggContextPush, 0, 0); + } switch( pExpr->op ){ case TK_IN: { char affinity; - Vdbe *v = sqlite3GetVdbe(pParse); KeyInfo keyInfo; int addr; /* Address of OP_OpenTemp instruction */ - if( v==0 ) return 2; affinity = sqlite3ExprAffinity(pExpr->pLeft); /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' @@ -1238,7 +1242,7 @@ static int codeSubqueryStep(void *pArg, Expr *pExpr){ int iParm = pExpr->iTable + (((int)affinity)<<16); ExprList *pEList; assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); - sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0, 0); + sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0); pEList = pExpr->pSelect->pEList; if( pEList && pEList->nExpr>0 ){ keyInfo.aColl[0] = binaryCompareCollSeq(pParse, pExpr->pLeft, @@ -1266,10 +1270,7 @@ static int codeSubqueryStep(void *pArg, Expr *pExpr){ if( !sqlite3ExprIsConstant(pE2) ){ sqlite3ErrorMsg(pParse, "right-hand side of IN operator must be constant"); - return 2; - } - if( sqlite3ExprResolveNames(pParse, 0, 0, 0, pE2, 0, 0) ){ - return 2; + return; } /* Evaluate the expression and insert it into the temp table */ @@ -1280,7 +1281,7 @@ static int codeSubqueryStep(void *pArg, Expr *pExpr){ } } sqlite3VdbeChangeP3(v, addr, (void *)&keyInfo, P3_KEYINFO); - return 1; + break; } case TK_EXISTS: @@ -1289,18 +1290,9 @@ static int codeSubqueryStep(void *pArg, Expr *pExpr){ ** value of this select in a memory cell and record the number ** of the memory cell in iColumn. */ - NameContext *pNC; - int nRef; - Vdbe *v; - int addr; int sop; Select *pSel; - pNC = pCoder->pNC; - if( pNC ) nRef = pNC->nRef; - sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ - v = sqlite3GetVdbe(pParse); - addr = sqlite3VdbeAddOp(v, OP_Goto, 0, 0); pExpr->iColumn = pParse->nMem++; pSel = pExpr->pSelect; if( pExpr->op==TK_SELECT ){ @@ -1312,42 +1304,21 @@ static int codeSubqueryStep(void *pArg, Expr *pExpr){ pSel->pEList = sqlite3ExprListAppend(0, sqlite3Expr(TK_INTEGER, 0, 0, &one), 0); } - sqlite3Select(pParse, pSel, sop, pExpr->iColumn, 0, 0, 0, 0, pNC); - if( pNC && pNC->nRef>nRef ){ - /* Subquery value changes. Evaluate at each use */ - pExpr->iTable = addr+1; - sqlite3VdbeAddOp(v, OP_Return, 0, 0); - sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v)); - }else{ - /* Subquery value is constant. evaluate only once. */ - pExpr->iTable = -1; - sqlite3VdbeChangeP2(v, addr, addr+1); - } - return 1; + sqlite3Select(pParse, pSel, sop, pExpr->iColumn, 0, 0, 0, 0); + break; } } - return 0; + + if( pExpr->pSelect ){ + sqlite3VdbeAddOp(v, OP_AggContextPop, 0, 0); + } + if( label<0 ){ + sqlite3VdbeResolveLabel(v, label); + } + return; } #endif /* SQLITE_OMIT_SUBQUERY */ -/* -** Generate code to evaluate subqueries and IN operators contained -** in expression pExpr. -*/ -static int sqlite3ExprCodeSubquery( - Parse *pParse, /* Parser */ - NameContext *pNC, /* First enclosing namespace. Often NULL */ - Expr *pExpr /* Subquery to be coded */ -){ -#ifndef SQLITE_OMIT_SUBQUERY - QueryCoder sCoder; - sCoder.pParse = pParse; - sCoder.pNC = pNC; - walkExprTree(pExpr, codeSubqueryStep, &sCoder); -#endif - return 0; -} - /* ** Generate an instruction that will put the integer describe by ** text z[0..n-1] on the stack. @@ -1556,10 +1527,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ #ifndef SQLITE_OMIT_SUBQUERY case TK_EXISTS: case TK_SELECT: { - if( pExpr->iTable>=0 ){ - sqlite3VdbeAddOp(v, OP_Gosub, 0, pExpr->iTable); - VdbeComment((v, "# run subquery")); - } + sqlite3CodeSubselect(pParse, pExpr); sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0); VdbeComment((v, "# load subquery result")); break; @@ -1567,6 +1535,7 @@ void sqlite3ExprCode(Parse *pParse, Expr *pExpr){ case TK_IN: { int addr; char affinity; + sqlite3CodeSubselect(pParse, pExpr); /* Figure out the affinity to use to create a key from the results ** of the expression. affinityStr stores a static string suitable for diff --git a/src/insert.c b/src/insert.c index 5dbc1397da..1795055e25 100644 --- a/src/insert.c +++ b/src/insert.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle INSERT statements in SQLite. ** -** $Id: insert.c,v 1.134 2005/01/20 11:32:24 danielk1977 Exp $ +** $Id: insert.c,v 1.135 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -312,8 +312,11 @@ void sqlite3Insert( iInitCode = sqlite3VdbeAddOp(v, OP_Goto, 0, 0); iSelectLoop = sqlite3VdbeCurrentAddr(v); iInsertBlock = sqlite3VdbeMakeLabel(v); - rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock,0,0,0,0,0); + + /* Resolve the expressions in the SELECT statement and execute it. */ + rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock,0,0,0,0); if( rc || pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup; + iCleanup = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp(v, OP_Goto, 0, iCleanup); assert( pSelect->pEList ); @@ -372,15 +375,16 @@ void sqlite3Insert( /* This is the case if the data for the INSERT is coming from a VALUES ** clause */ - SrcList dummy; + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; assert( pList!=0 ); srcTab = -1; useTempTable = 0; assert( pList ); nColumn = pList->nExpr; - dummy.nSrc = 0; for(i=0; ia[i].pExpr,0,1) ){ + if( sqlite3ExprResolveNames(&sNC, pList->a[i].pExpr) ){ goto insert_cleanup; } } diff --git a/src/main.c b/src/main.c index e993b81617..dfcab944b5 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.275 2005/01/25 04:27:55 danielk1977 Exp $ +** $Id: main.c,v 1.276 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -1266,7 +1266,7 @@ int sqlite3_reset(sqlite3_stmt *pStmt){ rc = SQLITE_OK; }else{ rc = sqlite3VdbeReset((Vdbe*)pStmt); - sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0); + sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0, 0); } return rc; } diff --git a/src/pager.c b/src/pager.c index 2ec7561c38..e8b5a14309 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.186 2005/01/22 03:39:39 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.187 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -1802,7 +1802,19 @@ int sqlite3pager_close(Pager *pPager){ case PAGER_RESERVED: case PAGER_SYNCED: case PAGER_EXCLUSIVE: { + /* We ignore any IO errors that occur during the rollback + ** operation. So disable IO error simulation so that testing + ** works more easily. + */ +#if defined(SQLITE_TEST) && (defined(OS_UNIX) || defined(OS_WIN)) + extern int sqlite3_io_error_pending; + int ioerr_cnt = sqlite3_io_error_pending; + sqlite3_io_error_pending = -1; +#endif sqlite3pager_rollback(pPager); +#if defined(SQLITE_TEST) && (defined(OS_UNIX) || defined(OS_WIN)) + sqlite3_io_error_pending = ioerr_cnt; +#endif if( !MEMDB ){ sqlite3OsUnlock(&pPager->fd, NO_LOCK); } diff --git a/src/parse.y b/src/parse.y index d8482fb694..78875a6790 100644 --- a/src/parse.y +++ b/src/parse.y @@ -14,7 +14,7 @@ ** the parser. Lemon will also generate a header file containing ** numeric codes for all of the tokens. ** -** @(#) $Id: parse.y,v 1.162 2005/01/21 03:12:15 danielk1977 Exp $ +** @(#) $Id: parse.y,v 1.163 2005/01/29 08:32:45 danielk1977 Exp $ */ %token_prefix TK_ %token_type {Token} @@ -317,7 +317,7 @@ cmd ::= DROP VIEW fullname(X). { //////////////////////// The SELECT statement ///////////////////////////////// // cmd ::= select(X). { - sqlite3Select(pParse, X, SRT_Callback, 0, 0, 0, 0, 0, 0); + sqlite3Select(pParse, X, SRT_Callback, 0, 0, 0, 0, 0); sqlite3SelectDelete(X); } @@ -609,7 +609,7 @@ expr(A) ::= VARIABLE(X). { Expr *pExpr = A = sqlite3Expr(TK_VARIABLE, 0, 0, pToken); sqlite3ExprAssignVarNumber(pParse, pExpr); } -term(A) ::= ID(X) LP exprlist(Y) RP(E). { +expr(A) ::= ID(X) LP exprlist(Y) RP(E). { A = sqlite3ExprFunction(Y, &X); sqlite3ExprSpan(A,&X,&E); } diff --git a/src/select.c b/src/select.c index 97c2da0a35..ab42652669 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.234 2005/01/26 03:58:36 danielk1977 Exp $ +** $Id: select.c,v 1.235 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -314,12 +314,14 @@ void sqlite3SelectDelete(Select *p){ /* ** Delete the aggregate information from the parse structure. */ +#if 0 static void sqliteAggregateInfoReset(Parse *pParse){ sqliteFree(pParse->aAgg); pParse->aAgg = 0; pParse->nAgg = 0; pParse->useAgg = 0; } +#endif /* ** Insert code into "v" that will push the record on the top of the @@ -669,12 +671,10 @@ static void generateSortTail( ** The declaration type for an expression is either TEXT, NUMERIC or ANY. ** The declaration type for a ROWID field is INTEGER. */ -static const char *columnType(Parse *pParse, SrcList *pTabList, Expr *pExpr){ +static const char *columnType(NameContext *pNC, Expr *pExpr){ char const *zType; int j; - if( pExpr==0 || pTabList==0 ) return 0; - - sqlite3ExprResolveNames(pParse, pTabList, 0, 0, pExpr, 1, 0); + if( pExpr==0 || pNC->pSrcList==0 ) return 0; /* The TK_AS operator can only occur in ORDER BY, GROUP BY, HAVING, ** and LIMIT clauses. But pExpr originates in the result set of a @@ -684,11 +684,18 @@ static const char *columnType(Parse *pParse, SrcList *pTabList, Expr *pExpr){ switch( pExpr->op ){ case TK_COLUMN: { - Table *pTab; + Table *pTab = 0; int iCol = pExpr->iColumn; - for(j=0; jnSrc && pTabList->a[j].iCursor!=pExpr->iTable; j++){} - assert( jnSrc ); - pTab = pTabList->a[j].pTab; + while( pNC && !pTab ){ + SrcList *pTabList = pNC->pSrcList; + for(j=0;jnSrc && pTabList->a[j].iCursor!=pExpr->iTable;j++); + if( jnSrc ){ + pTab = pTabList->a[j].pTab; + }else{ + pNC = pNC->pNext; + } + } + assert( pTab ); if( iCol<0 ) iCol = pTab->iPKey; assert( iCol==-1 || (iCol>=0 && iColnCol) ); if( iCol<0 ){ @@ -700,8 +707,11 @@ static const char *columnType(Parse *pParse, SrcList *pTabList, Expr *pExpr){ } #ifndef SQLITE_OMIT_SUBQUERY case TK_SELECT: { + NameContext sNC; Select *pS = pExpr->pSelect; - zType = columnType(pParse, pS->pSrc, pS->pEList->a[0].pExpr); + sNC.pSrcList = pExpr->pSelect->pSrc; + sNC.pNext = pNC; + zType = columnType(&sNC, pS->pEList->a[0].pExpr); break; } #endif @@ -723,9 +733,11 @@ static void generateColumnTypes( ){ Vdbe *v = pParse->pVdbe; int i; + NameContext sNC; + sNC.pSrcList = pTabList; for(i=0; inExpr; i++){ Expr *p = pEList->a[i].pExpr; - const char *zType = columnType(pParse, pTabList, p); + const char *zType = columnType(&sNC, p); if( zType==0 ) continue; /* The vdbe must make it's own copy of the column-type, in case the ** schema is reset before this virtual machine is deleted. @@ -860,6 +872,7 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){ char *zName; char *zBasename; int cnt; + NameContext sNC; /* Get an appropriate name for the column */ @@ -899,7 +912,8 @@ Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){ /* Get the typename, type affinity, and collating sequence for the ** column. */ - zType = sqliteStrDup(columnType(pParse, pSelect->pSrc ,p)); + sNC.pSrcList = pSelect->pSrc; + zType = sqliteStrDup(columnType(&sNC, p)); pCol->zType = zType; pCol->affinity = SQLITE_AFF_NUMERIC; if( zType ){ @@ -975,6 +989,7 @@ static int prepSelectStmt(Parse *pParse, Select *p){ pFrom->zAlias = sqlite3MPrintf("sqlite_subquery_%p_", (void*)pFrom->pSelect); } + sqlite3SelectResolve(pParse, pFrom->pSelect, 0); pFrom->pTab = pTab = sqlite3ResultSetOfSelect(pParse, pFrom->zAlias, pFrom->pSelect); if( pTab==0 ){ @@ -1492,7 +1507,7 @@ static int multiSelect( if( p->pOrderBy==0 ){ pPrior->nLimit = p->nLimit; pPrior->nOffset = p->nOffset; - rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff, 0); + rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff); if( rc ){ goto multi_select_end; } @@ -1501,7 +1516,7 @@ static int multiSelect( p->iOffset = pPrior->iOffset; p->nLimit = -1; p->nOffset = 0; - rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff, 0); + rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff); p->pPrior = pPrior; if( rc ){ goto multi_select_end; @@ -1550,7 +1565,8 @@ static int multiSelect( /* Code the SELECT statements to our left */ - rc = sqlite3Select(pParse, pPrior, priorOp, unionTab, 0, 0, 0, aff, 0); + assert( !pPrior->pOrderBy ); + rc = sqlite3Select(pParse, pPrior, priorOp, unionTab, 0, 0, 0, aff); if( rc ){ goto multi_select_end; } @@ -1569,7 +1585,7 @@ static int multiSelect( p->nLimit = -1; nOffset = p->nOffset; p->nOffset = 0; - rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff, 0); + rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff); p->pPrior = pPrior; p->pOrderBy = pOrderBy; p->nLimit = nLimit; @@ -1638,7 +1654,7 @@ static int multiSelect( /* Code the SELECTs to our left into temporary table "tab1". */ - rc = sqlite3Select(pParse, pPrior, SRT_Union, tab1, 0, 0, 0, aff, 0); + rc = sqlite3Select(pParse, pPrior, SRT_Union, tab1, 0, 0, 0, aff); if( rc ){ goto multi_select_end; } @@ -1658,7 +1674,7 @@ static int multiSelect( p->nLimit = -1; nOffset = p->nOffset; p->nOffset = 0; - rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff, 0); + rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff); p->pPrior = pPrior; p->nLimit = nLimit; p->nOffset = nOffset; @@ -1757,7 +1773,7 @@ static int multiSelect( Expr *pExpr = pOrderByTerm->pExpr; char *zName = pOrderByTerm->zName; assert( pExpr->op==TK_COLUMN && pExpr->iColumnpColl ); + /* assert( !pExpr->pColl ); */ if( zName ){ pExpr->pColl = sqlite3LocateCollSeq(pParse, zName, -1); }else{ @@ -1797,6 +1813,7 @@ multi_select_end: ** of the subquery rather the result set of the subquery. */ static void substExprList(ExprList*,int,ExprList*); /* Forward Decl */ +static void substSelect(Select *, int, ExprList *); /* Forward Decl */ static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){ if( pExpr==0 ) return; if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){ @@ -1824,17 +1841,25 @@ static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){ }else{ substExpr(pExpr->pLeft, iTable, pEList); substExpr(pExpr->pRight, iTable, pEList); + substSelect(pExpr->pSelect, iTable, pEList); substExprList(pExpr->pList, iTable, pEList); } } -static void -substExprList(ExprList *pList, int iTable, ExprList *pEList){ +static void substExprList(ExprList *pList, int iTable, ExprList *pEList){ int i; if( pList==0 ) return; for(i=0; inExpr; i++){ substExpr(pList->a[i].pExpr, iTable, pEList); } } +static void substSelect(Select *p, int iTable, ExprList *pEList){ + if( !p ) return; + substExprList(p->pEList, iTable, pEList); + substExprList(p->pGroupBy, iTable, pEList); + substExprList(p->pOrderBy, iTable, pEList); + substExpr(p->pHaving, iTable, pEList); + substExpr(p->pWhere, iTable, pEList); +} #endif /* !defined(SQLITE_OMIT_VIEW) */ #ifndef SQLITE_OMIT_VIEW @@ -2236,42 +2261,184 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ ** corresponding entry in the result set. */ static int processOrderGroupBy( - Parse *pParse, /* Parsing context */ + NameContext *pNC, /* Name context of the SELECT statement. */ ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */ - SrcList *pTabList, /* The FROM clause */ - ExprList *pEList, /* The result set */ - NameContext *pNC, /* Name context for enclosing query */ - int isAgg, /* True if aggregate functions are involved */ const char *zType /* Either "ORDER" or "GROUP", as appropriate */ ){ int i; + ExprList *pEList = pNC->pEList; /* The result set of the SELECT */ + Parse *pParse = pNC->pParse; /* The result set of the SELECT */ + assert( pEList ); + if( pOrderBy==0 ) return 0; for(i=0; inExpr; i++){ int iCol; Expr *pE = pOrderBy->a[i].pExpr; - if( sqlite3ExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){ - sqlite3ExprDelete(pE); - pE = pOrderBy->a[i].pExpr = sqlite3ExprDup(pEList->a[iCol-1].pExpr); - } - if( sqlite3ExprResolveNames(pParse, pTabList, pEList, pNC, pE, isAgg, 1) ){ - return 1; - } - if( sqlite3ExprIsConstant(pE) ){ - if( sqlite3ExprIsInteger(pE, &iCol)==0 ){ - sqlite3ErrorMsg(pParse, - "%s BY terms must not be non-integer constants", zType); - return 1; - }else if( iCol<=0 || iCol>pEList->nExpr ){ + if( sqlite3ExprIsInteger(pE, &iCol) ){ + if( iCol>0 && iCol<=pEList->nExpr ){ + sqlite3ExprDelete(pE); + pE = pOrderBy->a[i].pExpr = sqlite3ExprDup(pEList->a[iCol-1].pExpr); + }else{ sqlite3ErrorMsg(pParse, "%s BY column number %d out of range - should be " "between 1 and %d", zType, iCol, pEList->nExpr); return 1; } } + if( sqlite3ExprResolveNames(pNC, pE) ){ + return 1; + } + if( sqlite3ExprIsConstant(pE) ){ + sqlite3ErrorMsg(pParse, + "%s BY terms must not be non-integer constants", zType); + return 1; + } } return 0; } +/* +** This routine resolves any names used in the result set of the +** supplied SELECT statement. If the SELECT statement being resolved +** is a sub-select, then pOuterNC is a pointer to the NameContext +** of the parent SELECT. +*/ +int sqlite3SelectResolve( + Parse *pParse, /* The parser context */ + Select *p, /* The SELECT statement being coded. */ + NameContext *pOuterNC /* The outer name context. May be NULL. */ +){ + ExprList *pEList; /* Result set. */ + int i; /* For-loop variable used in multiple places */ + NameContext sNC; /* Local name-context */ + + /* If this routine has run before, return immediately. */ + if( p->isResolved ){ + assert( !pOuterNC ); + return SQLITE_OK; + } + p->isResolved = 1; + + /* If there have already been errors, do nothing. */ + if( pParse->nErr>0 ){ + return SQLITE_ERROR; + } + + /* Prepare the select statement. This call will allocate all cursors + ** required to handle the tables and subqueries in the FROM clause. + */ + if( prepSelectStmt(pParse, p) ){ + return SQLITE_ERROR; + } + + /* Set up the local name-context to pass to ExprResolveNames(). */ + sNC.pNext = pOuterNC; + sNC.pParse = pParse; + sNC.pSrcList = p->pSrc; + sNC.allowAgg = 1; + sNC.hasAgg = 0; + sNC.nErr = 0; + sNC.nRef = 0; + sNC.pEList = 0; + + /* NameContext.nDepth stores the depth of recursion for this query. For + ** an outer query (e.g. SELECT * FROM sqlite_master) this is 1. For + ** a subquery it is 2. For a subquery of a subquery, 3. And so on. + ** Parse.nMaxDepth is the maximum depth for any subquery resolved so + ** far. This is used to determine the number of aggregate contexts + ** required at runtime. + */ + sNC.nDepth = (pOuterNC?pOuterNC->nDepth+1:1); + if( sNC.nDepth>pParse->nMaxDepth ){ + pParse->nMaxDepth = sNC.nDepth; + } + + /* Resolve names in the result set. */ + pEList = p->pEList; + if( !pEList ) return SQLITE_ERROR; + for(i=0; inExpr; i++){ + Expr *pX = pEList->a[i].pExpr; + if( sqlite3ExprResolveNames(&sNC, pX) ){ + return SQLITE_ERROR; + } + } + + /* If there are no aggregate functions in the result-set, and no GROUP BY + ** expression, do not allow aggregates in any of the other expressions. + */ + assert( !p->isAgg ); + if( p->pGroupBy || sNC.hasAgg ){ + p->isAgg = 1; + }else{ + sNC.allowAgg = 0; + } + + /* If a HAVING clause is present, then there must be a GROUP BY clause. + */ + if( p->pHaving && !p->pGroupBy ){ + sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); + return SQLITE_ERROR; + } + + /* Add the expression list to the name-context before parsing the + ** other expressions in the SELECT statement. This is so that + ** expressions in the WHERE clause (etc.) can refer to expressions by + ** aliases in the result set. + ** + ** Minor point: If this is the case, then the expression will be + ** re-evaluated for each reference to it. + */ + sNC.pEList = p->pEList; + if( sqlite3ExprResolveNames(&sNC, p->pWhere) || + sqlite3ExprResolveNames(&sNC, p->pHaving) || + processOrderGroupBy(&sNC, p->pOrderBy, "ORDER") || + processOrderGroupBy(&sNC, p->pGroupBy, "GROUP") + ){ + return SQLITE_ERROR; + } + + return SQLITE_OK; +} + +/* +** An instance of the following struct is used by sqlite3Select() +** to save aggregate related information from the Parse object +** at the start of each call and to restore it at the end. See +** saveAggregateInfo() and restoreAggregateInfo(). +*/ +struct AggregateInfo { + u8 useAgg; + int nAgg; + AggExpr *aAgg; +}; +typedef struct AggregateInfo AggregateInfo; + +/* +** Copy aggregate related information from the Parse structure +** into the AggregateInfo structure. Zero the aggregate related +** values in the Parse struct. +*/ +static void saveAggregateInfo(Parse *pParse, AggregateInfo *pInfo){ + pInfo->aAgg = pParse->aAgg; + pInfo->nAgg = pParse->nAgg; + pInfo->useAgg = pParse->useAgg; + pParse->aAgg = 0; + pParse->nAgg = 0; + pParse->useAgg = 0; +} + +/* +** Copy aggregate related information from the AggregateInfo struct +** back into the Parse structure. The aggregate related information +** currently stored in the Parse structure is deleted. +*/ +static void restoreAggregateInfo(Parse *pParse, AggregateInfo *pInfo){ + sqliteFree(pParse->aAgg); + pParse->aAgg = pInfo->aAgg; + pParse->nAgg = pInfo->nAgg; + pParse->useAgg = pInfo->useAgg; +} + /* ** Generate code for the given SELECT statement. ** @@ -2332,13 +2499,12 @@ int sqlite3Select( Select *pParent, /* Another SELECT for which this is a sub-query */ int parentTab, /* Index in pParent->pSrc of this query */ int *pParentAgg, /* True if pParent uses aggregate functions */ - char *aff, /* If eDest is SRT_Union, the affinity string */ - NameContext *pNC /* Namespace of the next outer query */ + char *aff /* If eDest is SRT_Union, the affinity string */ ){ int i; WhereInfo *pWInfo; Vdbe *v; - int isAgg = 0; /* True for select lists like "count(*)" */ + int isAgg; /* True for select lists like "count(*)" */ ExprList *pEList; /* List of columns to extract. */ SrcList *pTabList; /* List of tables to select from */ Expr *pWhere; /* The WHERE clause. May be NULL */ @@ -2348,6 +2514,7 @@ int sqlite3Select( int isDistinct; /* True if the DISTINCT keyword is present */ int distinct; /* Table to use for the distinct set */ int rc = 1; /* Value to return from this function */ + AggregateInfo sAggInfo; if( sqlite3_malloc_failed || pParse->nErr || p==0 ) return 1; if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; @@ -2366,14 +2533,26 @@ int sqlite3Select( } #endif + saveAggregateInfo(pParse, &sAggInfo); + pOrderBy = p->pOrderBy; + if( eDest==SRT_Union || eDest==SRT_Except || eDest==SRT_Discard ){ + p->pOrderBy = 0; + } + if( sqlite3SelectResolve(pParse, p, 0) ){ + goto select_end; + } + p->pOrderBy = pOrderBy; + /* Make local copies of the parameters for this query. */ pTabList = p->pSrc; pWhere = p->pWhere; - pOrderBy = p->pOrderBy; pGroupBy = p->pGroupBy; pHaving = p->pHaving; + isAgg = p->isAgg; isDistinct = p->isDistinct; + pEList = p->pEList; + if( pEList==0 ) goto select_end; /* ** Do not even attempt to generate any code if we have already seen @@ -2381,13 +2560,6 @@ int sqlite3Select( */ if( pParse->nErr>0 ) goto select_end; - if( prepSelectStmt(pParse, p) ){ - goto select_end; - } - pWhere = p->pWhere; - pEList = p->pEList; - if( pEList==0 ) goto select_end; - /* If writing to memory or generating a set ** only a single column may be output. */ @@ -2412,39 +2584,6 @@ int sqlite3Select( break; } - /* At this point, we should have allocated all the cursors that we - ** need to handle subquerys and temporary tables. - ** - ** Resolve the column names and do a semantics check on all the expressions. - */ - for(i=0; inExpr; i++){ - Expr *pX = pEList->a[i].pExpr; - if( sqlite3ExprResolveNames(pParse, pTabList, 0, pNC, pX, 1, 1) ){ - goto select_end; - } - if( ExprHasProperty(pX, EP_Agg) ) isAgg = 1; - } - if( sqlite3ExprResolveNames(pParse, pTabList, pEList, pNC, pWhere, 0, 1) ){ - goto select_end; - } - if( pHaving ){ - if( pGroupBy==0 ){ - sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING"); - goto select_end; - } - if( sqlite3ExprResolveNames(pParse, pTabList, pEList, pNC, pHaving, 1, 1) ){ - goto select_end; - } - } - if( pGroupBy ){ - isAgg = 1; - } - if( processOrderGroupBy(pParse,pOrderBy,pTabList,pEList,pNC,isAgg,"ORDER") - || processOrderGroupBy(pParse,pGroupBy,pTabList,pEList,pNC,isAgg,"GROUP") - ){ - goto select_end; - } - /* We cannot use a SQL cursor on a join or on a DISTINCT query */ #ifndef SQLITE_OMIT_CURSOR @@ -2493,7 +2632,7 @@ int sqlite3Select( needRestoreContext = 0; } sqlite3Select(pParse, pTabList->a[i].pSelect, SRT_TempTable, - pTabList->a[i].iCursor, p, i, &isAgg, 0, 0); + pTabList->a[i].iCursor, p, i, &isAgg, 0); if( needRestoreContext ){ pParse->zAuthContext = zSavedAuthContext; } @@ -2523,7 +2662,7 @@ int sqlite3Select( if( pParent && pParentAgg && flattenSubquery(pParse, pParent, parentTab, *pParentAgg, isAgg) ){ if( isAgg ) *pParentAgg = 1; - return rc; + goto select_end; } #endif @@ -2555,7 +2694,6 @@ int sqlite3Select( /* Do an analysis of aggregate expressions. */ - sqliteAggregateInfoReset(pParse); if( isAgg || pGroupBy ){ assert( pParse->nAgg==0 ); isAgg = 1; @@ -2746,6 +2884,6 @@ int sqlite3Select( ** successful coding of the SELECT. */ select_end: - sqliteAggregateInfoReset(pParse); + restoreAggregateInfo(pParse, &sAggInfo); return rc; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 4f95805a97..524d5a4d8c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.363 2005/01/22 03:03:55 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.364 2005/01/29 08:32:45 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -838,6 +838,7 @@ struct Expr { #define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */ #define EP_Error 0x0008 /* Expression contains one or more errors */ #define EP_Not 0x0010 /* Operator preceeded by NOT */ +#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */ /* ** These macros can be used to test, set, or clear bits in the @@ -983,6 +984,39 @@ struct Fetch { int doRewind; /* True to rewind cursor before starting */ }; +/* +** A NameContext defines a context in which to resolve table and column +** names. The context consists of a list of tables (the pSrcList) field and +** a list of named expression (pEList). The named expression list may +** be NULL. The pSrc corresponds to the FROM clause of a SELECT or +** to the table being operated on by INSERT, UPDATE, or DELETE. The +** pEList corresponds to the result set of a SELECT and is NULL for +** other statements. +** +** NameContexts can be nested. When resolving names, the inner-most +** context is searched first. If no match is found, the next outer +** context is checked. If there is still no match, the next context +** is checked. This process continues until either a match is found +** or all contexts are check. When a match is found, the nRef member of +** the context containing the match is incremented. +** +** Each subquery gets a new NameContext. The pNext field points to the +** NameContext in the parent query. Thus the process of scanning the +** NameContext list corresponds to searching through successively outer +** subqueries looking for a match. +*/ +struct NameContext { + Parse *pParse; /* The parser */ + SrcList *pSrcList; /* One or more tables used to resolve names */ + ExprList *pEList; /* Optional list of named expressions */ + int nRef; /* Number of names resolved by this context */ + int nErr; /* Number of errors encountered while resolving names */ + u8 allowAgg; /* Aggregate functions allowed here */ + u8 hasAgg; + int nDepth; /* Depth of subquery recursion. 1 for no recursion */ + NameContext *pNext; /* Next outer name context. NULL for outermost */ +}; + /* ** An instance of the following structure contains all information ** needed to generate code for a single SELECT statement. @@ -1007,6 +1041,8 @@ struct Select { int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */ IdList **ppOpenTemp; /* OP_OpenTemp addresses used by multi-selects */ Fetch *pFetch; /* If this stmt is part of a FETCH command */ + u8 isResolved; /* True once sqlite3SelectResolve() has run. */ + u8 isAgg; /* True if this is an aggregate query */ }; /* @@ -1076,6 +1112,8 @@ struct Parse { int cookieValue[MAX_ATTACHED+2]; /* Values of cookies to verify */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ u32 writeMask; /* Start a write transaction on these databases */ + u8 useAgg; /* If true, extract field values from the aggregator + ** while generating expressions. Normally false */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ @@ -1085,15 +1123,11 @@ struct Parse { int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ u8 explain; /* True if the EXPLAIN flag is found on the query */ - u8 useAgg; /* If true, extract field values from the aggregator - ** while generating expressions. Normally false */ #ifndef SQLITE_OMIT_CURSOR u8 fetchDir; /* The direction argument to the FETCH command */ int dirArg1; /* First argument to the direction */ int dirArg2; /* Second argument to the direction */ #endif - int nAgg; /* Number of aggregate expressions */ - AggExpr *aAgg; /* An array of aggregate expressions */ Token sErrToken; /* The token at which the error occurred */ Token sNameToken; /* Token with unqualified schema object name */ Token sLastToken; /* The last token parsed */ @@ -1103,6 +1137,9 @@ struct Parse { Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ TriggerStack *trigStack; /* Trigger actions being coded */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ + int nAgg; /* Number of aggregate expressions */ + AggExpr *aAgg; /* An array of aggregate expressions */ + int nMaxDepth; /* Maximum depth of subquery recursion */ }; /* @@ -1275,38 +1312,6 @@ typedef struct { char **pzErrMsg; /* Error message stored here */ } InitData; -/* -** A NameContext defines a context in which to resolve table and column -** names. The context consists of a list of tables (the pSrcList) field and -** a list of named expression (pEList). The named expression list may -** be NULL. The pSrc corresponds to the FROM clause of a SELECT or -** to the table being operated on by INSERT, UPDATE, or DELETE. The -** pEList corresponds to the result set of a SELECT and is NULL for -** other statements. -** -** NameContexts can be nested. When resolving names, the inner-most -** context is searched first. If no match is found, the next outer -** context is checked. If there is still no match, the next context -** is checked. This process continues until either a match is found -** or all contexts are check. When a match is found, the nRef member of -** the context containing the match is incremented. -** -** Each subquery gets a new NameContext. The pNext field points to the -** NameContext in the parent query. Thus the process of scanning the -** NameContext list corresponds to searching through successively outer -** subqueries looking for a match. -*/ -struct NameContext { - Parse *pParse; /* The parser */ - SrcList *pSrcList; /* One or more tables used to resolve names */ - ExprList *pEList; /* Optional list of named expressions */ - int nRef; /* Number of names resolved by this context */ - int nErr; /* Number of errors encountered while resolving names */ - u8 allowAgg; /* Aggregate functions allowed here */ - u8 hasAgg; /* Expression actually contains aggregate functions */ - NameContext *pNext; /* Next outer name context. NULL for outermost */ -}; - /* ** Each SQL cursor (a cursor created by the DECLARE ... CURSOR syntax) ** is represented by an instance of the following structure. @@ -1413,8 +1418,7 @@ void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, void sqlite3DropIndex(Parse*, SrcList*); void sqlite3AddKeyType(Vdbe*, ExprList*); void sqlite3AddIdxKeyType(Vdbe*, Index*); -int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, - char *aff, NameContext*); +int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff); Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*, int,int,int); void sqlite3SelectDelete(Select*); @@ -1444,8 +1448,7 @@ char *sqlite3NameFromToken(Token*); int sqlite3ExprCheck(Parse*, Expr*, int, int*); int sqlite3ExprCompare(Expr*, Expr*); int sqliteFuncId(Token*); -int sqlite3ExprResolveNames(Parse*, SrcList*, ExprList*, NameContext*, - Expr*, int, int); +int sqlite3ExprResolveNames(NameContext *, Expr *); int sqlite3ExprAnalyzeAggregates(Parse*, Expr*); Vdbe *sqlite3GetVdbe(Parse*); void sqlite3Randomness(int, void*); @@ -1573,6 +1576,8 @@ void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); int sqlite3GetToken(const unsigned char *, int *); void sqlite3NestedParse(Parse*, const char*, ...); void sqlite3ExpirePreparedStatements(sqlite3*); +void sqlite3CodeSubselect(Parse *, Expr *); +int sqlite3SelectResolve(Parse *, Select *, NameContext *); #ifndef SQLITE_OMIT_CURSOR void sqlite3CursorDelete(SqlCursor*); diff --git a/src/trigger.c b/src/trigger.c index ef4acefc02..eccc810e9b 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -640,7 +640,8 @@ static int codeTriggerProgram( Select * ss = sqlite3SelectDup(pTriggerStep->pSelect); assert(ss); assert(ss->pSrc); - sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0, 0); + sqlite3SelectResolve(pParse, ss, 0); + sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0); sqlite3SelectDelete(ss); break; } @@ -744,11 +745,12 @@ int sqlite3CodeRowTrigger( if( fire_this ){ int endTrigger; - SrcList dummyTablist; Expr * whenExpr; AuthContext sContext; + NameContext sNC; - dummyTablist.nSrc = 0; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; /* Push an entry on to the trigger stack */ trigStackEntry.pTrigger = pTrigger; @@ -763,7 +765,7 @@ int sqlite3CodeRowTrigger( /* code the WHEN clause */ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); whenExpr = sqlite3ExprDup(pTrigger->pWhen); - if( sqlite3ExprResolveNames(pParse, &dummyTablist, 0, 0, whenExpr, 0,1) ){ + if( sqlite3ExprResolveNames(&sNC, whenExpr) ){ pParse->trigStack = trigStackEntry.pNext; sqlite3ExprDelete(whenExpr); return 1; diff --git a/src/update.c b/src/update.c index 2158938cfb..81e5db951f 100644 --- a/src/update.c +++ b/src/update.c @@ -12,7 +12,7 @@ ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. ** -** $Id: update.c,v 1.103 2005/01/19 23:24:51 drh Exp $ +** $Id: update.c,v 1.104 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -49,6 +49,7 @@ void sqlite3Update( Expr *pRecnoExpr = 0; /* Expression defining the new record number */ int openAll = 0; /* True if all indices need to be opened */ AuthContext sContext; /* The authorization context */ + NameContext sNC; /* The name-context to resolve expressions in */ #ifndef SQLITE_OMIT_TRIGGER int isView; /* Trying to update a view */ @@ -113,6 +114,11 @@ void sqlite3Update( pParse->nTab++; } + /* Initialize the name-context */ + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; + /* Resolve the column names in all the expressions of the ** of the UPDATE statement. Also find the column index ** for each column to be updated in the pChanges array. For each @@ -121,8 +127,7 @@ void sqlite3Update( */ chngRecno = 0; for(i=0; inExpr; i++){ - if( sqlite3ExprResolveNames(pParse, pTabList, 0, 0, - pChanges->a[i].pExpr, 0, 1) ){ + if( sqlite3ExprResolveNames(&sNC, pChanges->a[i].pExpr) ){ goto update_cleanup; } for(j=0; jnCol; j++){ @@ -198,7 +203,7 @@ void sqlite3Update( /* Resolve the column names in all the expressions in the ** WHERE clause. */ - if( sqlite3ExprResolveNames(pParse, pTabList, 0, 0, pWhere, 0, 1) ){ + if( sqlite3ExprResolveNames(&sNC, pWhere) ){ goto update_cleanup; } @@ -221,7 +226,7 @@ void sqlite3Update( if( isView ){ Select *pView; pView = sqlite3SelectDup(pTab->pSelect); - sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0, 0); + sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0); sqlite3SelectDelete(pView); } diff --git a/src/vdbe.c b/src/vdbe.c index 029f4ac786..31218cb64c 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** 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.448 2005/01/27 00:33:21 drh Exp $ +** $Id: vdbe.c,v 1.449 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -3957,6 +3957,31 @@ case OP_ListReset: { break; } +#ifndef SQLITE_OMIT_SUBQUERY +/* Opcode: AggContextPush * * * +** +** Save the state of the current aggregator. It is restored an +** AggContextPop opcode. +** +*/ +case OP_AggContextPush: { + p->pAgg++; + assert( p->pAgg<&p->apAgg[p->nAgg] ); + break; +} + +/* Opcode: AggContextPop * * * +** +** Restore the aggregator to the state it was in when AggContextPush +** was last called. Any data in the current aggregator is deleted. +*/ +case OP_AggContextPop: { + p->pAgg--; + assert( p->pAgg>=p->apAgg ); + break; +} +#endif + #ifndef SQLITE_OMIT_TRIGGER /* Opcode: ContextPush * * * ** @@ -4185,8 +4210,8 @@ case OP_MemIncr: { /* Opcode: AggReset P1 P2 P3 ** -** Reset the aggregator so that it no longer contains any data. -** Future aggregator elements will contain P2 values each and be sorted +** Reset the current aggregator context so that it no longer contains any +** data. Future aggregator elements will contain P2 values each and be sorted ** using the KeyInfo structure pointed to by P3. ** ** If P1 is non-zero, then only a single aggregator row is available (i.e. @@ -4196,18 +4221,18 @@ case OP_MemIncr: { case OP_AggReset: { assert( !pOp->p3 || pOp->p3type==P3_KEYINFO ); if( pOp->p1 ){ - rc = sqlite3VdbeAggReset(0, &p->agg, (KeyInfo *)pOp->p3); - p->agg.nMem = pOp->p2; /* Agg.nMem is used by AggInsert() */ - rc = AggInsert(&p->agg, 0, 0); + rc = sqlite3VdbeAggReset(0, p->pAgg, (KeyInfo *)pOp->p3); + p->pAgg->nMem = pOp->p2; /* Agg.nMem is used by AggInsert() */ + rc = AggInsert(p->pAgg, 0, 0); }else{ - rc = sqlite3VdbeAggReset(db, &p->agg, (KeyInfo *)pOp->p3); - p->agg.nMem = pOp->p2; + rc = sqlite3VdbeAggReset(db, p->pAgg, (KeyInfo *)pOp->p3); + p->pAgg->nMem = pOp->p2; } if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - p->agg.apFunc = sqliteMalloc( p->agg.nMem*sizeof(p->agg.apFunc[0]) ); - if( p->agg.apFunc==0 ) goto no_mem; + p->pAgg->apFunc = sqliteMalloc( p->pAgg->nMem*sizeof(p->pAgg->apFunc[0]) ); + if( p->pAgg->apFunc==0 ) goto no_mem; break; } @@ -4219,8 +4244,8 @@ case OP_AggReset: { */ case OP_AggInit: { int i = pOp->p2; - assert( i>=0 && iagg.nMem ); - p->agg.apFunc[i] = (FuncDef*)pOp->p3; + assert( i>=0 && ipAgg->nMem ); + p->pAgg->apFunc[i] = (FuncDef*)pOp->p3; break; } @@ -4255,9 +4280,9 @@ case OP_AggFunc: { storeTypeInfo(pRec, db->enc); } i = pTos->i; - assert( i>=0 && iagg.nMem ); + assert( i>=0 && ipAgg->nMem ); ctx.pFunc = (FuncDef*)pOp->p3; - pMem = &p->agg.pCurrent->aMem[i]; + pMem = &p->pAgg->pCurrent->aMem[i]; ctx.s.z = pMem->zShort; /* Space used for small aggregate contexts */ ctx.pAgg = pMem->z; ctx.cnt = ++pMem->i; @@ -4301,18 +4326,18 @@ case OP_AggFocus: { Stringify(pTos, db->enc); zKey = pTos->z; nKey = pTos->n; - assert( p->agg.pBtree ); - assert( p->agg.pCsr ); - rc = sqlite3BtreeMoveto(p->agg.pCsr, zKey, nKey, &res); + assert( p->pAgg->pBtree ); + assert( p->pAgg->pCsr ); + rc = sqlite3BtreeMoveto(p->pAgg->pCsr, zKey, nKey, &res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } if( res==0 ){ - rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*), - (char *)&p->agg.pCurrent); + rc = sqlite3BtreeData(p->pAgg->pCsr, 0, sizeof(AggElem*), + (char *)&p->pAgg->pCurrent); pc = pOp->p2 - 1; }else{ - rc = AggInsert(&p->agg, zKey, nKey); + rc = AggInsert(p->pAgg, zKey, nKey); } if( rc!=SQLITE_OK ){ goto abort_due_to_error; @@ -4330,10 +4355,10 @@ case OP_AggFocus: { case OP_AggSet: { AggElem *pFocus; int i = pOp->p2; - pFocus = p->agg.pCurrent; + pFocus = p->pAgg->pCurrent; assert( pTos>=p->aStack ); if( pFocus==0 ) goto no_mem; - assert( i>=0 && iagg.nMem ); + assert( i>=0 && ipAgg->nMem ); rc = sqlite3VdbeMemMove(&pFocus->aMem[i], pTos); pTos--; break; @@ -4348,22 +4373,22 @@ case OP_AggSet: { case OP_AggGet: { AggElem *pFocus; int i = pOp->p2; - pFocus = p->agg.pCurrent; + pFocus = p->pAgg->pCurrent; if( pFocus==0 ){ int res; if( sqlite3_malloc_failed ) goto no_mem; - rc = sqlite3BtreeFirst(p->agg.pCsr, &res); + rc = sqlite3BtreeFirst(p->pAgg->pCsr, &res); if( rc!=SQLITE_OK ){ return rc; } if( res!=0 ){ - rc = AggInsert(&p->agg,"",1); - pFocus = p->agg.pCurrent; + rc = AggInsert(p->pAgg, "", 1); + pFocus = p->pAgg->pCurrent; }else{ - rc = sqlite3BtreeData(p->agg.pCsr, 0, 4, (char *)&pFocus); + rc = sqlite3BtreeData(p->pAgg->pCsr, 0, 4, (char *)&pFocus); } } - assert( i>=0 && iagg.nMem ); + assert( i>=0 && ipAgg->nMem ); pTos++; sqlite3VdbeMemShallowCopy(pTos, &pFocus->aMem[i], MEM_Ephem); if( pTos->flags&MEM_Str ){ @@ -4388,16 +4413,16 @@ case OP_AggNext: { int res; assert( rc==SQLITE_OK ); CHECK_FOR_INTERRUPT; - if( p->agg.searching==0 ){ - p->agg.searching = 1; - if( p->agg.pCsr ){ - rc = sqlite3BtreeFirst(p->agg.pCsr, &res); + if( p->pAgg->searching==0 ){ + p->pAgg->searching = 1; + if( p->pAgg->pCsr ){ + rc = sqlite3BtreeFirst(p->pAgg->pCsr, &res); }else{ res = 0; } }else{ - if( p->agg.pCsr ){ - rc = sqlite3BtreeNext(p->agg.pCsr, &res); + if( p->pAgg->pCsr ){ + rc = sqlite3BtreeNext(p->pAgg->pCsr, &res); }else{ res = 1; } @@ -4410,14 +4435,14 @@ case OP_AggNext: { sqlite3_context ctx; Mem *aMem; - if( p->agg.pCsr ){ - rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*), - (char *)&p->agg.pCurrent); + if( p->pAgg->pCsr ){ + rc = sqlite3BtreeData(p->pAgg->pCsr, 0, sizeof(AggElem*), + (char *)&p->pAgg->pCurrent); if( rc!=SQLITE_OK ) goto abort_due_to_error; } - aMem = p->agg.pCurrent->aMem; - for(i=0; iagg.nMem; i++){ - FuncDef *pFunc = p->agg.apFunc[i]; + aMem = p->pAgg->pCurrent->aMem; + for(i=0; ipAgg->nMem; i++){ + FuncDef *pFunc = p->pAgg->apFunc[i]; Mem *pMem = &aMem[i]; if( pFunc==0 || pFunc->xFinalize==0 ) continue; ctx.s.flags = MEM_Null; @@ -4519,7 +4544,6 @@ case OP_Expire: { } - /* An other opcode is illegal... */ default: { diff --git a/src/vdbe.h b/src/vdbe.h index edef241de0..d0e9fb188a 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.91 2004/09/06 17:24:13 drh Exp $ +** $Id: vdbe.h,v 1.92 2005/01/29 08:32:45 danielk1977 Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -110,7 +110,7 @@ int sqlite3VdbeFindOp(Vdbe*, int, int, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeDelete(Vdbe*); -void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); +void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int); int sqlite3VdbeFinalize(Vdbe*); void sqlite3VdbeResolveLabel(Vdbe*, int); int sqlite3VdbeCurrentAddr(Vdbe*); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index da64f762d0..42682d1e25 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -321,7 +321,9 @@ struct Vdbe { int magic; /* Magic number for sanity checking */ int nMem; /* Number of memory locations currently allocated */ Mem *aMem; /* The memory locations */ - Agg agg; /* Aggregate information */ + int nAgg; /* Number of elements in apAgg */ + Agg *apAgg; /* Array of aggregate contexts */ + Agg *pAgg; /* Current aggregate context */ int nCallback; /* Number of callbacks invoked so far */ Keylist *pList; /* A list of ROWIDs */ int contextStackTop; /* Index of top element in the context stack */ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 28a83f54da..6d77d725f4 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -586,6 +586,7 @@ void sqlite3VdbeMakeReady( int nVar, /* Number of '?' see in the SQL statement */ int nMem, /* Number of memory cells to allocate */ int nCursor, /* Number of cursors to allocate */ + int nAgg, /* Number of aggregate contexts required */ int isExplain /* True if the EXPLAIN keywords is present */ ){ int n; @@ -615,6 +616,7 @@ void sqlite3VdbeMakeReady( + nVar*sizeof(char*) /* azVar */ + nMem*sizeof(Mem) /* aMem */ + nCursor*sizeof(Cursor*) /* apCsr */ + + nAgg*sizeof(Agg) /* Aggregate contexts */ ); if( !sqlite3_malloc_failed ){ p->aMem = &p->aStack[n]; @@ -625,15 +627,20 @@ void sqlite3VdbeMakeReady( p->apArg = (Mem**)&p->aVar[nVar]; p->azVar = (char**)&p->apArg[n]; p->apCsr = (Cursor**)&p->azVar[nVar]; + if( nAgg>0 ){ + p->nAgg = nAgg; + p->apAgg = (Agg*)&p->apCsr[nCursor]; + } p->nCursor = nCursor; for(n=0; naVar[n].flags = MEM_Null; } - for(n=0; naMem[n].flags = MEM_Null; - } } } + p->pAgg = p->apAgg; + for(n=0; nnMem; n++){ + p->aMem[n].flags = MEM_Null; + } #ifdef SQLITE_DEBUG if( (p->db->flags & SQLITE_VdbeListing)!=0 @@ -733,8 +740,10 @@ static void freeAggElem(AggElem *pElem, Agg *pAgg){ */ int sqlite3VdbeAggReset(sqlite3 *db, Agg *pAgg, KeyInfo *pKeyInfo){ int rc = 0; - BtCursor *pCsr = pAgg->pCsr; + BtCursor *pCsr; + if( !pAgg ) return SQLITE_OK; + pCsr = pAgg->pCsr; assert( (pCsr && pAgg->nTab>0) || (!pCsr && pAgg->nTab==0) || sqlite3_malloc_failed ); @@ -886,7 +895,9 @@ static void Cleanup(Vdbe *p){ sqliteFree(p->contextStack); } sqlite3VdbeSorterReset(p); - sqlite3VdbeAggReset(0, &p->agg, 0); + for(i=0; inAgg; i++){ + sqlite3VdbeAggReset(0, &p->apAgg[i], 0); + } p->contextStack = 0; p->contextStackDepth = 0; p->contextStackTop = 0; diff --git a/src/where.c b/src/where.c index 1054bf55ba..86d539a392 100644 --- a/src/where.c +++ b/src/where.c @@ -16,7 +16,7 @@ ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". ** -** $Id: where.c,v 1.131 2005/01/20 22:48:48 drh Exp $ +** $Id: where.c,v 1.132 2005/01/29 08:32:45 danielk1977 Exp $ */ #include "sqliteInt.h" @@ -191,6 +191,7 @@ static void createMask(ExprMaskSet *pMaskSet, int iCursor){ ** sets their opcodes to TK_COLUMN and their Expr.iTable fields to ** the VDBE cursor number of the table. */ +static Bitmask exprListTableUsage(ExprMaskSet *, ExprList *); static Bitmask exprTableUsage(ExprMaskSet *pMaskSet, Expr *p){ Bitmask mask = 0; if( p==0 ) return 0; @@ -198,16 +199,25 @@ static Bitmask exprTableUsage(ExprMaskSet *pMaskSet, Expr *p){ mask = getMask(pMaskSet, p->iTable); return mask; } - if( p->pRight ){ - mask = exprTableUsage(pMaskSet, p->pRight); + mask = exprTableUsage(pMaskSet, p->pRight); + mask |= exprTableUsage(pMaskSet, p->pLeft); + mask |= exprListTableUsage(pMaskSet, p->pList); + if( p->pSelect ){ + Select *pS = p->pSelect; + mask |= exprListTableUsage(pMaskSet, pS->pEList); + mask |= exprListTableUsage(pMaskSet, pS->pGroupBy); + mask |= exprListTableUsage(pMaskSet, pS->pOrderBy); + mask |= exprTableUsage(pMaskSet, pS->pWhere); + mask |= exprTableUsage(pMaskSet, pS->pHaving); } - if( p->pLeft ){ - mask |= exprTableUsage(pMaskSet, p->pLeft); - } - if( p->pList ){ - int i; - for(i=0; ipList->nExpr; i++){ - mask |= exprTableUsage(pMaskSet, p->pList->a[i].pExpr); + return mask; +} +static Bitmask exprListTableUsage(ExprMaskSet *pMaskSet, ExprList *pList){ + int i; + Bitmask mask = 0; + if( pList ){ + for(i=0; inExpr; i++){ + mask |= exprTableUsage(pMaskSet, pList->a[i].pExpr); } } return mask; @@ -479,14 +489,20 @@ static void codeEqualityTerm( if( pX->op!=TK_IN ){ assert( pX->op==TK_EQ ); sqlite3ExprCode(pParse, pX->pRight); +#ifndef SQLITE_OMIT_SUBQUERY }else{ - int iTab = pX->iTable; + int iTab; Vdbe *v = pParse->pVdbe; + + sqlite3CodeSubselect(pParse, pX); + iTab = pX->iTable; sqlite3VdbeAddOp(v, OP_Rewind, iTab, brk); sqlite3VdbeAddOp(v, OP_KeyAsData, iTab, 1); + VdbeComment((v, "# %.*s", pX->span.n, pX->span.z)); pLevel->inP2 = sqlite3VdbeAddOp(v, OP_Column, iTab, 0); pLevel->inOp = OP_Next; pLevel->inP1 = iTab; +#endif } disableTerm(pLevel, &pTerm->p); } diff --git a/test/bind.test b/test/bind.test index 5ac1344031..fa363f47f2 100644 --- a/test/bind.test +++ b/test/bind.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script testing the sqlite_bind API. # -# $Id: bind.test,v 1.27 2005/01/20 02:17:02 danielk1977 Exp $ +# $Id: bind.test,v 1.28 2005/01/29 08:32:46 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -478,16 +478,16 @@ do_test bind-10.15 { do_test bind-10.16 { sqlite3_bind_parameter_name $VM 1 } :abc -do_test bind-10.16 { +do_test bind-10.17 { sqlite3_bind_parameter_name $VM 2 } {} -do_test bind-10.16 { +do_test bind-10.18 { sqlite3_bind_parameter_name $VM 3 } {} -do_test bind-10.16 { +do_test bind-10.19 { sqlite3_bind_parameter_name $VM 4 } {?4} -do_test bind-10.16 { +do_test bind-10.20 { sqlite3_bind_parameter_name $VM 5 } :pqr catch {sqlite3_finalize $VM} diff --git a/test/collate3.test b/test/collate3.test index 02fff218a9..7a471c0dea 100644 --- a/test/collate3.test +++ b/test/collate3.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is page cache subsystem. # -# $Id: collate3.test,v 1.9 2005/01/26 03:58:36 danielk1977 Exp $ +# $Id: collate3.test,v 1.10 2005/01/29 08:32:46 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -131,51 +131,51 @@ do_test collate3-2.8 { } {1 {no such collation sequence: string_compare}} ifcapable compound { -do_test collate3-2.9 { - catchsql { - SELECT c1 FROM collate3t1 UNION SELECT c1 FROM collate3t1; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.10 { - catchsql { - SELECT c1 FROM collate3t1 EXCEPT SELECT c1 FROM collate3t1; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.11 { - catchsql { - SELECT c1 FROM collate3t1 INTERSECT SELECT c1 FROM collate3t1; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.12 { - catchsql { - SELECT c1 FROM collate3t1 UNION ALL SELECT c1 FROM collate3t1; - } -} {0 {}} -do_test collate3-2.13 { - catchsql { - SELECT 10 UNION ALL SELECT 20 ORDER BY 1 COLLATE string_compare; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.14 { - catchsql { - SELECT 10 INTERSECT SELECT 20 ORDER BY 1 COLLATE string_compare; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.15 { - catchsql { - SELECT 10 EXCEPT SELECT 20 ORDER BY 1 COLLATE string_compare; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.16 { - catchsql { - SELECT 10 UNION SELECT 20 ORDER BY 1 COLLATE string_compare; - } -} {1 {no such collation sequence: string_compare}} -do_test collate3-2.17 { - catchsql { - SELECT c1 FROM collate3t1 UNION ALL SELECT c1 FROM collate3t1 ORDER BY 1; - } -} {1 {no such collation sequence: string_compare}} + do_test collate3-2.9 { + catchsql { + SELECT c1 FROM collate3t1 UNION SELECT c1 FROM collate3t1; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.10 { + catchsql { + SELECT c1 FROM collate3t1 EXCEPT SELECT c1 FROM collate3t1; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.11 { + catchsql { + SELECT c1 FROM collate3t1 INTERSECT SELECT c1 FROM collate3t1; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.12 { + catchsql { + SELECT c1 FROM collate3t1 UNION ALL SELECT c1 FROM collate3t1; + } + } {0 {}} + do_test collate3-2.13 { + catchsql { + SELECT 10 UNION ALL SELECT 20 ORDER BY 1 COLLATE string_compare; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.14 { + catchsql { + SELECT 10 INTERSECT SELECT 20 ORDER BY 1 COLLATE string_compare; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.15 { + catchsql { + SELECT 10 EXCEPT SELECT 20 ORDER BY 1 COLLATE string_compare; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.16 { + catchsql { + SELECT 10 UNION SELECT 20 ORDER BY 1 COLLATE string_compare; + } + } {1 {no such collation sequence: string_compare}} + do_test collate3-2.17 { + catchsql { + SELECT c1 FROM collate3t1 UNION ALL SELECT c1 FROM collate3t1 ORDER BY 1; + } + } {1 {no such collation sequence: string_compare}} } ;# ifcapable compound # diff --git a/test/insert3.test b/test/insert3.test index b505f637c2..b2999ff954 100644 --- a/test/insert3.test +++ b/test/insert3.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing corner cases of the INSERT statement. # -# $Id: insert3.test,v 1.2 2005/01/15 00:36:37 drh Exp $ +# $Id: insert3.test,v 1.3 2005/01/29 08:32:46 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -58,18 +58,21 @@ do_test insert3-1.3 { SELECT * FROM log2 ORDER BY x; } } {hi 1} -do_test insert3-1.4 { - execsql { - INSERT INTO t1 SELECT * FROM t1; - SELECT 'a:', x, y FROM log UNION ALL SELECT 'b:', x, y FROM log2 ORDER BY x; - } -} {a: 5 4 b: 10 2 b: 20 1 a: 453 2 a: hello 4 b: hi 2 b: world 1} -do_test insert3-1.5 { - execsql { - INSERT INTO t1(a) VALUES('xyz'); - SELECT * FROM log ORDER BY x; - } -} {5 4 453 2 hello 4 xyz 1} +ifcapable compound { + do_test insert3-1.4 { + execsql { + INSERT INTO t1 SELECT * FROM t1; + SELECT 'a:', x, y FROM log UNION ALL + SELECT 'b:', x, y FROM log2 ORDER BY x; + } + } {a: 5 4 b: 10 2 b: 20 1 a: 453 2 a: hello 4 b: hi 2 b: world 1} + do_test insert3-1.5 { + execsql { + INSERT INTO t1(a) VALUES('xyz'); + SELECT * FROM log ORDER BY x; + } + } {5 4 453 2 hello 4 xyz 1} +} do_test insert3-2.1 { execsql { diff --git a/test/misc4.test b/test/misc4.test index 0ce75a598f..872d587a5d 100644 --- a/test/misc4.test +++ b/test/misc4.test @@ -13,7 +13,7 @@ # This file implements tests for miscellanous features that were # left out of other test files. # -# $Id: misc4.test,v 1.13 2005/01/21 03:12:16 danielk1977 Exp $ +# $Id: misc4.test,v 1.14 2005/01/29 08:32:46 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -119,8 +119,21 @@ ifcapable subquery { where a.key=x.key; } } {01 data01 01 3.0 +1 data+1 +1 7.0} + + # This test case tests the same property as misc4-4.1, but it is + # a bit smaller which makes it easier to work with while debugging. + do_test misc4-4.2 { + execsql { + CREATE TABLE ab(a TEXT, b TEXT); + INSERT INTO ab VALUES('01', '1'); + } + execsql { + select * from ab, (select b from ab) as x where x.b = ab.a; + } + } {} } + # Ticket #1036. When creating tables from a SELECT on a view, use the # short names of columns. # diff --git a/test/select6.test b/test/select6.test index 14e280ae5c..acb9fb92bb 100644 --- a/test/select6.test +++ b/test/select6.test @@ -12,7 +12,7 @@ # focus of this file is testing SELECT statements that contain # subqueries in their FROM clause. # -# $Id: select6.test,v 1.16 2005/01/21 03:12:16 danielk1977 Exp $ +# $Id: select6.test,v 1.17 2005/01/29 08:32:46 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -157,19 +157,19 @@ do_test select6-2.9 { } } {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20} -do_test sqlite6-3.1 { +do_test select6-3.1 { execsql2 { SELECT * FROM (SELECT * FROM (SELECT * FROM t1 WHERE x=3)); } } {x 3 y 2} -do_test sqlite6-3.2 { +do_test select6-3.2 { execsql { SELECT * FROM (SELECT a.q, a.p, b.r FROM (SELECT count(*) as p , b as q FROM t2 GROUP BY q) AS a, (SELECT max(a) as r, b as s FROM t2 GROUP BY s) as b WHERE a.q=b.s ORDER BY a.q) - ORDER BY q + ORDER BY "a.q" } } {1 1 1 2 2 3 3 4 7 4 8 15 5 5 20} do_test select6-3.3 { diff --git a/test/subquery.test b/test/subquery.test index 0e517da435..a5d9bc630d 100644 --- a/test/subquery.test +++ b/test/subquery.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this script is testing correlated subqueries # -# $Id: subquery.test,v 1.2 2005/01/21 11:55:28 danielk1977 Exp $ +# $Id: subquery.test,v 1.3 2005/01/29 08:32:47 danielk1977 Exp $ # set testdir [file dirname $argv0] @@ -59,5 +59,183 @@ do_test subquery-1.4 { } } {13 31 57} +# Simple tests to make sure correlated subqueries in WHERE clauses +# are used by the query optimizer correctly. +do_test subquery-1.5 { + execsql { + SELECT a, x FROM t1, t2 WHERE t1.a = (SELECT x); + } +} {1 1 3 3 5 5 7 7} +do_test subquery-1.6 { + execsql { + CREATE INDEX i1 ON t1(a); + SELECT a, x FROM t1, t2 WHERE t1.a = (SELECT x); + } +} {1 1 3 3 5 5 7 7} +do_test subquery-1.7 { + execsql { + SELECT a, x FROM t2, t1 WHERE t1.a = (SELECT x); + } +} {1 1 3 3 5 5 7 7} + +# Try an aggregate in both the subquery and the parent query. +do_test subquery-1.6 { + execsql { + SELECT count(*) FROM t1 WHERE a > (SELECT count(*) FROM t2); + } +} {2} + + +#------------------------------------------------------------------ +# The following test cases - subquery-2.* - are not logically +# organized. They're here largely because they were failing during +# one stage of development of sub-queries. +# +do_test subquery-2.1 { + execsql { + SELECT (SELECT 10); + } +} {10} +do_test subquery-2.2.1 { + execsql { + CREATE TABLE t3(a PRIMARY KEY, b); + INSERT INTO t3 VALUES(1, 2); + INSERT INTO t3 VALUES(3, 1); + } +} {} +do_test subquery-2.2.2 { + execsql { + SELECT * FROM t3 WHERE a IN (SELECT b FROM t3); + } +} {1 2} +do_test subquery-2.2.3 { + execsql { + DROP TABLE t3; + } +} {} +do_test subquery-2.3.1 { + execsql { + CREATE TABLE t3(a TEXT); + INSERT INTO t3 VALUES('10'); + } +} {} +do_test subquery-2.3.2 { + execsql { + SELECT a IN (10.0, 20) FROM t3; + } +} {0} +do_test subquery-2.3.3 { + execsql { + DROP TABLE t3; + } +} {} +do_test subquery-2.4.1 { + execsql { + CREATE TABLE t3(a TEXT); + INSERT INTO t3 VALUES('XX'); + } +} {} +do_test subquery-2.4.2 { + execsql { + SELECT count(*) FROM t3 WHERE a IN (SELECT 'XX') + } +} {1} +do_test subquery-2.4.3 { + execsql { + DROP TABLE t3; + } +} {} +do_test subquery-2.5.1 { + execsql { + CREATE TABLE t3(a INTEGER); + INSERT INTO t3 VALUES(10); + + CREATE TABLE t4(x TEXT); + INSERT INTO t4 VALUES('10.0'); + } +} {} +do_test subquery-2.5.2 { + execsql { + SELECT * FROM t4 WHERE x IN (SELECT a FROM t3); + } +} {10.0} +do_test subquery-2.5.3 { + execsql { + CREATE INDEX t4i ON t4(x); + SELECT * FROM t4 WHERE x IN (SELECT a FROM t3); + } +} {10.0} +do_test subquery-2.5.4 { + execsql { + DROP TABLE t3; + DROP TABLE t4; + } +} {} + +#------------------------------------------------------------------ +# The following test cases - subquery-3.* - test tickets that +# were raised during development of correlated subqueries. +# + +# Ticket 1083 +ifcapable view { + do_test subquery-3.1 { + catchsql { DROP TABLE t1; } + catchsql { DROP TABLE t2; } + execsql { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2); + CREATE VIEW v1 AS SELECT b FROM t1 WHERE a>0; + CREATE TABLE t2(p,q); + INSERT INTO t2 VALUES(2,9); + SELECT * FROM v1 WHERE EXISTS(SELECT * FROM t2 WHERE p=v1.b); + } + } {2} +} + +# Ticket 1084 +do_test subquery-3.2 { + catchsql { + CREATE TABLE t1(a,b); + INSERT INTO t1 VALUES(1,2); + } + execsql { + SELECT (SELECT t1.a) FROM t1; + } +} {1} + +#------------------------------------------------------------------ +# These tests - subquery-4.* - use the TCL statement cache to try +# and expose bugs to do with re-using statements that have been +# passed to sqlite3_reset(). +# +# One problem was that VDBE memory cells were not being initialised +# to NULL on the second and subsequent executions. +# +do_test subquery-4.1.1 { + execsql { + SELECT (SELECT a FROM t1); + } +} {1} +do_test subquery-4.2 { + execsql { + DELETE FROM t1; + SELECT (SELECT a FROM t1); + } +} {{}} +do_test subquery-4.2.1 { + execsql { + CREATE TABLE t3(a PRIMARY KEY); + INSERT INTO t3 VALUES(10); + } + execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)} +} {} +do_test subquery-4.2.2 { + execsql {INSERT INTO t3 VALUES((SELECT max(a) FROM t3)+1)} +} {} + + finish_test + + diff --git a/test/trigger2.test b/test/trigger2.test index 616b6031ab..9dc03ef44b 100644 --- a/test/trigger2.test +++ b/test/trigger2.test @@ -122,8 +122,8 @@ ifcapable subquery { set r } [list 1 1 2 4 6 10 20 \ 2 1 2 13 24 10 20 \ - 3 3 4 13 24 30 40 \ - 4 3 4 40 60 30 40 \ + 3 3 4 13 24 30 40 \ + 4 3 4 40 60 30 40 \ 1 1 2 13 24 10 20 ] execsql { diff --git a/test/view.test b/test/view.test index 6f30a943a3..77f7057e63 100644 --- a/test/view.test +++ b/test/view.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing VIEW statements. # -# $Id: view.test,v 1.22 2005/01/21 04:25:47 danielk1977 Exp $ +# $Id: view.test,v 1.23 2005/01/29 08:32:47 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -154,7 +154,7 @@ do_test view-3.4 { SELECT * FROM v3 LIMIT 4; } } {b 2 b 3 b 5 b 6} -do_test view-3.5 { +do_test view-3.5 { execsql2 { CREATE VIEW v4 AS SELECT a, b FROM t1