diff --git a/manifest b/manifest index 15d3d861dc..b0ea0909d9 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bug\sfix\swhen\s-DMEMORY_DEBUG\sis\soff.\s(CVS\s407) -D 2002-02-28T04:10:30 +C Subquery\sflattening\sis\simplemented\sand\spasses\sall\sregression\stests.\nWe\sstill\sneed\sto\sadd\saddition\stests\sto\sthe\ssuite\sto\sfurther\sexercise\nthe\sflattener,\showever.\s(CVS\s408) +D 2002-03-02T17:04:08 F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -21,27 +21,27 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6 F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3 -F src/build.c 7ada2426caba70cb1072ba268bedb694b5018065 -F src/delete.c 950d8f9097361419f1963875f9943344b469cf02 -F src/expr.c b70bedaffd27ea24c5c2a197a88b07e82dfa4967 +F src/build.c 2f6d3136e6b824b2b446c54db2d2be5703033203 +F src/delete.c bf569eeb66dc851966b5681e5154d5fe2aee92c2 +F src/expr.c 17e3db6f115d60530a55530e3046312196c5eb36 F src/func.c 0db438ba17f3394dc5a3ffcd2ee41ca0c8e80b21 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9 -F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11 +F src/insert.c 4fb3428f591afe106f6e60029a61f87fa0e79920 F src/main.c 5651146585ae613e759fcf372ee064e4940c2463 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6 F src/pager.c 9761c79ccb844bf29ffc5cbed4fa1a32e0740147 F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283 -F src/parse.y fc460cda6f475beae963c7f9c737cf13f44f3420 +F src/parse.y d62960cdee2d2e7821f277d2fe63d823c86602ba F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe -F src/select.c 1851abd794ad81639bb7d8ac4b24c8c70cd0ec17 +F src/select.c b99de04f6f2414bb21a065150e0748ccd8329519 F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in a9b5772604265f98f3120573ef29e37b9d917216 -F src/sqliteInt.h 9cd512d5be2d2838950e5ace7f92e14ab4804e70 +F src/sqliteInt.h df68f09091ab37d904020ac48ca2ea29e4985442 F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f @@ -49,11 +49,11 @@ F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af -F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997 +F src/update.c 943b821901efb796dc82d91653621aeeb48d223b F src/util.c 00a35b421c92ae0d7cfa51bd87f7d4995f464d19 F src/vdbe.c 91311e99efe980459a78e8f5b9e5456d772c9e23 F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc -F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585 +F src/where.c 34d91fd5d822c2663caeb023f72d60df316ebf29 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1 @@ -63,7 +63,7 @@ F test/conflict.test c794c6c8f6e59918107dbab2d201ae454bb47db8 F test/copy.test b3cefcb520c64d7e7dfedbab06b4d4c31fa5b99a F test/delete.test c904a62129fe102b314a96111a8417f10249e4d8 F test/expr.test c8a495050dcec3f9e68538c3ef466726933302c1 -F test/func.test 13572d84cb0f5d4dbd9a51bf767eac047e6f9779 +F test/func.test 4359344586067e79abf4c710c4737d67ed3cf963 F test/in.test c09312672e3f0709fa02c8e2e9cd8fb4bd6269aa F test/index.test c8a471243bbf878974b99baf5badd59407237cf3 F test/insert.test c36d534a4ab58c2cd452a273e51b2b0dd1ede1f9 @@ -82,7 +82,7 @@ F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05 F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435 F test/rowid.test 4c55943300cddf73dd0f88d40a268cab14c83274 -F test/select1.test ec4c20514598ae9b52abbd5e17488c8dbcdc74d1 +F test/select1.test 572d53f7b28c35a1efdce207f7579523358ba637 F test/select2.test ed2c1882857106b85478f54f67000e14966be4c4 F test/select3.test 9469c332250a75a0ef1771fb5da62dc04ec77f18 F test/select4.test 29a2ffb187f3d8b6ca42a0a6b619e9cabe12e228 @@ -112,7 +112,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4 F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b F www/c_interface.tcl 63efc40f09e2f0d8fea43d174103248b160fdf0e -F www/changes.tcl 0c40569ef5c92af1fb211ea1f679113a44c60f19 +F www/changes.tcl 18df8bc8dbcefbfd9538ce476d4fcbc193ac3352 F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2 F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060 F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0 @@ -127,7 +127,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P e9fd9e7b0fed445b48e7024ecde0354fff1478a6 -R 963d95bca45cb4c9e29d8cc361ba95c2 +P e14b0c82f3514f41934a7c0d173b6fdb186aafc8 +R f60c0ec90c7ca3b07c137c446692b495 U drh -Z bd4f48a03082cea631000ad6c4df51a3 +Z 28d7598277d139ff75ed8a6bf05f4a82 diff --git a/manifest.uuid b/manifest.uuid index 07f84c335f..7018ef2a6c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e14b0c82f3514f41934a7c0d173b6fdb186aafc8 \ No newline at end of file +d5d3e79cc58da5bd315cc1fea1f7cbf46274da16 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 6f6acc3f7d..b5b6579f53 100644 --- a/src/build.c +++ b/src/build.c @@ -17,7 +17,7 @@ ** DROP TABLE ** CREATE INDEX ** DROP INDEX -** creating expressions and ID lists +** creating ID lists ** COPY ** VACUUM ** BEGIN TRANSACTION @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.80 2002/02/27 01:47:12 drh Exp $ +** $Id: build.c,v 1.81 2002/03/02 17:04:08 drh Exp $ */ #include "sqliteInt.h" #include @@ -791,7 +791,7 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){ int op = p->isTemp ? OP_OpenWrAux : OP_OpenWrite; sqliteVdbeAddOp(v, op, 1, 0); pParse->nTab = 2; - sqliteSelect(pParse, pSelect, SRT_Table, 1); + sqliteSelect(pParse, pSelect, SRT_Table, 1, 0, 0, 0); } sqliteEndWriteOperation(pParse); } diff --git a/src/delete.c b/src/delete.c index d815a45009..2d5771a4e4 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 ** to handle DELETE FROM statements. ** -** $Id: delete.c,v 1.27 2002/02/23 02:32:10 drh Exp $ +** $Id: delete.c,v 1.28 2002/03/02 17:04:08 drh Exp $ */ #include "sqliteInt.h" @@ -102,9 +102,9 @@ void sqliteDeleteFrom( /* Resolve the column names in all the expressions. */ + base = pParse->nTab++; if( pWhere ){ - sqliteExprResolveInSelect(pParse, pWhere); - if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){ goto delete_from_cleanup; } if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ @@ -135,6 +135,7 @@ void sqliteDeleteFrom( int endOfLoop = sqliteVdbeMakeLabel(v); int addr; openOp = pTab->isTemp ? OP_OpenAux : OP_Open; + assert( base==0 ); sqliteVdbeAddOp(v, openOp, 0, pTab->tnum); sqliteVdbeAddOp(v, OP_Rewind, 0, sqliteVdbeCurrentAddr(v)+2); addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0); @@ -154,7 +155,7 @@ void sqliteDeleteFrom( else{ /* Begin the database scan */ - pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1); + pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1); if( pWInfo==0 ) goto delete_from_cleanup; /* Remember the key of every item to be deleted. @@ -172,7 +173,6 @@ void sqliteDeleteFrom( ** database scan. We have to delete items after the scan is complete ** because deleting an item can change the scan order. */ - base = pParse->nTab; sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; sqliteVdbeAddOp(v, openOp, base, pTab->tnum); diff --git a/src/expr.c b/src/expr.c index f265f0f4a9..46c56b57fb 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.51 2002/02/28 03:04:48 drh Exp $ +** $Id: expr.c,v 1.52 2002/03/02 17:04:08 drh Exp $ */ #include "sqliteInt.h" @@ -172,6 +172,9 @@ Expr *sqliteExprDup(Expr *p){ pNew->pLeft = sqliteExprDup(p->pLeft); pNew->pRight = sqliteExprDup(p->pRight); pNew->pList = sqliteExprListDup(p->pList); + pNew->iTable = p->iTable; + pNew->iColumn = p->iColumn; + pNew->iAgg = p->iAgg; pNew->token = p->token; pNew->span = p->span; pNew->pSelect = sqliteSelectDup(p->pSelect); @@ -310,40 +313,6 @@ int sqliteExprIsConstant(Expr *p){ return 0; } -/* -** Walk the expression tree and process operators of the form: -** -** expr IN (SELECT ...) -** -** These operators have to be processed before column names are -** resolved because each such operator increments pParse->nTab -** to reserve cursor numbers for its own use. But pParse->nTab -** needs to be constant once we begin resolving column names. For -** that reason, this procedure needs to be called on every expression -** before sqliteExprResolveIds() is called on any expression. -** -** Actually, the processing of IN-SELECT is only started by this -** routine. This routine allocates a cursor number to the IN-SELECT -** and then moves on. The code generation is done by -** sqliteExprResolveIds() which must be called afterwards. -*/ -void sqliteExprResolveInSelect(Parse *pParse, Expr *pExpr){ - if( pExpr==0 ) return; - if( pExpr->op==TK_IN && pExpr->pSelect!=0 ){ - pExpr->iTable = pParse->nTab++; - }else{ - if( pExpr->pLeft ) sqliteExprResolveInSelect(pParse, pExpr->pLeft); - if( pExpr->pRight ) sqliteExprResolveInSelect(pParse, pExpr->pRight); - if( pExpr->pList ){ - int i; - ExprList *pList = pExpr->pList; - for(i=0; inExpr; i++){ - sqliteExprResolveInSelect(pParse, pList->a[i].pExpr); - } - } - } -} - /* ** Return TRUE if the given string is a row-id column name. */ @@ -360,7 +329,7 @@ static int sqliteIsRowid(const char *z){ ** index to the table in the table list and a column offset. The ** Expr.opcode for such nodes is changed to TK_COLUMN. The Expr.iTable ** value is changed to the index of the referenced table in pTabList -** plus the pParse->nTab value. This value will ultimately become the +** plus the "base" value. The base value will ultimately become the ** VDBE cursor number for a cursor that is pointing into the referenced ** table. The Expr.iColumn value is changed to the index of the column ** of the referenced table. The Expr.iColumn value for the special @@ -387,11 +356,13 @@ static int sqliteIsRowid(const char *z){ */ int sqliteExprResolveIds( Parse *pParse, /* The parser context */ + int base, /* VDBE cursor number for first entry in pTabList */ IdList *pTabList, /* List of tables used to resolve column names */ ExprList *pEList, /* List of expressions used to resolve "AS" */ Expr *pExpr /* The expression to be analyzed. */ ){ if( pExpr==0 || pTabList==0 ) return 0; + assert( base+pTabList->nId<=pParse->nTab ); switch( pExpr->op ){ /* A lone identifier. Try and match it as follows: ** @@ -418,7 +389,7 @@ int sqliteExprResolveIds( for(j=0; jnCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){ cnt++; - pExpr->iTable = i + pParse->nTab; + pExpr->iTable = i + base; if( j==pTab->iPKey ){ /* Substitute the record number for the INTEGER PRIMARY KEY */ pExpr->iColumn = -1; @@ -444,7 +415,7 @@ int sqliteExprResolveIds( } if( cnt==0 && sqliteIsRowid(z) ){ pExpr->iColumn = -1; - pExpr->iTable = pParse->nTab; + pExpr->iTable = base; cnt = 1 + (pTabList->nId>1); pExpr->op = TK_COLUMN; } @@ -496,11 +467,11 @@ int sqliteExprResolveIds( zTab = pTab->zName; } if( sqliteStrICmp(zTab, zLeft)!=0 ) continue; - if( 0==(cntTab++) ) pExpr->iTable = i + pParse->nTab; + if( 0==(cntTab++) ) pExpr->iTable = i + base; for(j=0; jnCol; j++){ if( sqliteStrICmp(pTab->aCol[j].zName, zRight)==0 ){ cnt++; - pExpr->iTable = i + pParse->nTab; + pExpr->iTable = i + base; if( j==pTab->iPKey ){ /* Substitute the record number for the INTEGER PRIMARY KEY */ pExpr->iColumn = -1; @@ -540,7 +511,7 @@ int sqliteExprResolveIds( case TK_IN: { Vdbe *v = sqliteGetVdbe(pParse); if( v==0 ) return 1; - if( sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pLeft) ){ return 1; } if( pExpr->pSelect ){ @@ -550,8 +521,9 @@ int sqliteExprResolveIds( ** table. The cursor number of the temporary table has already ** been put in iTable by sqliteExprResolveInSelect(). */ + pExpr->iTable = pParse->nTab++; sqliteVdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 1); - if( sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable) ); + sqliteSelect(pParse, pExpr->pSelect, SRT_Set, pExpr->iTable, 0,0,0); }else if( pExpr->pList ){ /* Case 2: expr IN (exprlist) ** @@ -601,7 +573,7 @@ int sqliteExprResolveIds( ** of the memory cell in iColumn. */ pExpr->iColumn = pParse->nMem++; - if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn) ){ + if( sqliteSelect(pParse, pExpr->pSelect, SRT_Mem, pExpr->iColumn,0,0,0) ){ return 1; } break; @@ -610,18 +582,19 @@ int sqliteExprResolveIds( /* For all else, just recursively walk the tree */ default: { if( pExpr->pLeft - && sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pLeft) ){ + && sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pLeft) ){ return 1; } if( pExpr->pRight - && sqliteExprResolveIds(pParse, pTabList, pEList, pExpr->pRight) ){ + && sqliteExprResolveIds(pParse, base, pTabList, pEList, pExpr->pRight) ){ return 1; } if( pExpr->pList ){ int i; ExprList *pList = pExpr->pList; for(i=0; inExpr; i++){ - if( sqliteExprResolveIds(pParse,pTabList,pEList,pList->a[i].pExpr) ){ + Expr *pArg = pList->a[i].pExpr; + if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pArg) ){ return 1; } } diff --git a/src/insert.c b/src/insert.c index c12c32a70c..b7617289e5 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.45 2002/02/23 02:32:10 drh Exp $ +** $Id: insert.c,v 1.46 2002/03/02 17:04:08 drh Exp $ */ #include "sqliteInt.h" @@ -84,7 +84,7 @@ void sqliteInsert( int rc; srcTab = pParse->nTab++; sqliteVdbeAddOp(v, OP_OpenTemp, srcTab, 0); - rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab); + rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab, 0,0,0); if( rc || pParse->nErr || sqlite_malloc_failed ) goto insert_cleanup; assert( pSelect->pEList ); nColumn = pSelect->pEList->nExpr; @@ -94,12 +94,9 @@ void sqliteInsert( srcTab = -1; assert( pList ); nColumn = pList->nExpr; - for(i=0; ia[i].pExpr); - } dummy.nId = 0; for(i=0; ia[i].pExpr) ){ + if( sqliteExprResolveIds(pParse, 0, &dummy, 0, pList->a[i].pExpr) ){ goto insert_cleanup; } } @@ -183,6 +180,7 @@ void sqliteInsert( sqliteVdbeAddOp(v, openOp, idx+base, pIdx->tnum); sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC); } + pParse->nTab += idx; /* If the data source is a SELECT statement, then we have to create ** a loop because there might be multiple rows of data. If the data diff --git a/src/parse.y b/src/parse.y index fee981f632..6e3f45c442 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.53 2002/02/23 02:32:10 drh Exp $ +** @(#) $Id: parse.y,v 1.54 2002/03/02 17:04:08 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -202,7 +202,7 @@ cmd ::= DROP VIEW ids(X). { //////////////////////// The SELECT statement ///////////////////////////////// // cmd ::= select(X). { - sqliteSelect(pParse, X, SRT_Callback, 0); + sqliteSelect(pParse, X, SRT_Callback, 0, 0, 0, 0); sqliteSelectDelete(X); } diff --git a/src/select.c b/src/select.c index f71016524e..94eb2ae0be 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.70 2002/02/28 01:46:13 drh Exp $ +** $Id: select.c,v 1.71 2002/03/02 17:04:08 drh Exp $ */ #include "sqliteInt.h" @@ -231,8 +231,12 @@ static void generateSortTail(Vdbe *v, int nColumn){ ** are in the result and the name for each column. This information ** is used to provide "argc" and "azCol[]" values in the callback. */ -static -void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ +static void generateColumnNames( + Parse *pParse, /* Parser context */ + int base, /* VDBE cursor corresponding to first entry in pTabList */ + IdList *pTabList, /* List of tables */ + ExprList *pEList /* Expressions defining the result set */ +){ Vdbe *v = pParse->pVdbe; int i; if( pParse->colNamesSet || v==0 || sqlite_malloc_failed ) return; @@ -255,7 +259,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ sqliteVdbeChangeP3(v, -1, p->span.z, p->span.n); sqliteVdbeCompressSpace(v, addr); }else if( p->op==TK_COLUMN && pTabList ){ - Table *pTab = pTabList->a[p->iTable - pParse->nTab].pTab; + Table *pTab = pTabList->a[p->iTable - base].pTab; char *zCol; int iCol = p->iColumn; if( iCol<0 ) iCol = pTab->iPKey; @@ -265,7 +269,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ char *zName = 0; char *zTab; - zTab = pTabList->a[p->iTable - pParse->nTab].zAlias; + zTab = pTabList->a[p->iTable - base].zAlias; if( showFullNames || zTab==0 ) zTab = pTab->zName; sqliteSetString(&zName, zTab, ".", zCol, 0); sqliteVdbeAddOp(v, OP_ColumnName, i, 0); @@ -640,7 +644,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ /* Code the SELECT statements to our left */ - rc = sqliteSelect(pParse, pPrior, priorOp, unionTab); + rc = sqliteSelect(pParse, pPrior, priorOp, unionTab, 0, 0, 0); if( rc ) return rc; /* Code the current SELECT statement @@ -651,7 +655,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ case TK_ALL: op = SRT_Table; break; } p->pPrior = 0; - rc = sqliteSelect(pParse, p, op, unionTab); + rc = sqliteSelect(pParse, p, op, unionTab, 0, 0, 0); p->pPrior = pPrior; if( rc ) return rc; @@ -661,7 +665,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ if( eDest!=priorOp ){ int iCont, iBreak, iStart; assert( p->pEList ); - generateColumnNames(pParse, 0, p->pEList); + generateColumnNames(pParse, p->base, 0, p->pEList); iBreak = sqliteVdbeMakeLabel(v); iCont = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, unionTab, iBreak); @@ -698,7 +702,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ /* Code the SELECTs to our left into temporary table "tab1". */ - rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1); + rc = sqliteSelect(pParse, pPrior, SRT_Union, tab1, 0, 0, 0); if( rc ) return rc; /* Code the current SELECT into temporary table "tab2" @@ -706,7 +710,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ sqliteVdbeAddOp(v, OP_OpenTemp, tab2, 1); sqliteVdbeAddOp(v, OP_KeyAsData, tab2, 1); p->pPrior = 0; - rc = sqliteSelect(pParse, p, SRT_Union, tab2); + rc = sqliteSelect(pParse, p, SRT_Union, tab2, 0, 0, 0); p->pPrior = pPrior; if( rc ) return rc; @@ -714,7 +718,7 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ ** tables. */ assert( p->pEList ); - generateColumnNames(pParse, 0, p->pEList); + generateColumnNames(pParse, p->base, 0, p->pEList); iBreak = sqliteVdbeMakeLabel(v); iCont = sqliteVdbeMakeLabel(v); sqliteVdbeAddOp(v, OP_Rewind, tab1, iBreak); @@ -746,6 +750,74 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ return 0; } +/* +** Recursively scan through an expression tree. For every reference +** to a column in table number iFrom, change that reference to the +** same column in table number iTo. +*/ +static void changeTables(Expr *pExpr, int iFrom, int iTo){ + if( pExpr==0 ) return; + if( pExpr->op==TK_COLUMN && pExpr->iTable==iFrom ){ + pExpr->iTable = iTo; + }else{ + changeTables(pExpr->pLeft, iFrom, iTo); + changeTables(pExpr->pRight, iFrom, iTo); + if( pExpr->pList ){ + int i; + for(i=0; ipList->nExpr; i++){ + changeTables(pExpr->pList->a[i].pExpr, iFrom, iTo); + } + } + } +} + +/* +** Scan through the expression pExpr. Replace every reference to +** a column in table number iTable with a copy of the corresponding +** entry in pEList. When make a copy of pEList, change references +** to columns in table iSub into references to table iTable. +** +** This routine is part of the flattening procedure. A subquery +** whose result set is defined by pEList appears as entry in the +** FROM clause of a SELECT such that the VDBE cursor assigned to that +** FORM clause entry is iTable. This routine make the necessary +** changes to pExpr so that it refers directly to the source table +** of the subquery rather the result set of the subquery. +*/ +static void substExpr(Expr *pExpr, int iTable, ExprList *pEList, int iSub){ + if( pExpr==0 ) return; + if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){ + Expr *pNew; + assert( pEList!=0 && pExpr->iColumn>=0 && pExpr->iColumnnExpr ); + assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 ); + pNew = pEList->a[pExpr->iColumn].pExpr; + assert( pNew!=0 ); + pExpr->op = pNew->op; + pExpr->pLeft = sqliteExprDup(pNew->pLeft); + pExpr->pRight = sqliteExprDup(pNew->pRight); + pExpr->pList = sqliteExprListDup(pNew->pList); + pExpr->iTable = pNew->iTable; + pExpr->iColumn = pNew->iColumn; + pExpr->iAgg = pNew->iAgg; + if( iSub!=iTable ){ + changeTables(pExpr, iSub, iTable); + } + }else{ + static void substExprList(ExprList*,int,ExprList*,int); + substExpr(pExpr->pLeft, iTable, pEList, iSub); + substExpr(pExpr->pRight, iTable, pEList, iSub); + substExprList(pExpr->pList, iTable, pEList, iSub); + } +} +static void +substExprList(ExprList *pList, int iTable, ExprList *pEList, int iSub){ + int i; + if( pList==0 ) return; + for(i=0; inExpr; i++){ + substExpr(pList->a[i].pExpr, iTable, pEList, iSub); + } +} + /* ** This routine attempts to flatten subqueries in order to speed ** execution. It returns 1 if it makes changes and 0 if no flattening @@ -761,48 +833,115 @@ static int multiSelect(Parse *pParse, Select *p, int eDest, int iParm){ ** run the outer query on that temporary table. This requires two ** passes over the data. Furthermore, because the temporary table ** has no indices, the WHERE clause on the outer query cannot be -** optimized using indices. +** optimized. ** -** This routine attempts to write queries such as the above into +** This routine attempts to rewrite queries such as the above into ** a single flat select, like this: ** ** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5 ** ** The code generated for this simpification gives the same result -** but only has to scan the data once. +** but only has to scan the data once. And because indices might +** exist on the table t1, a complete scan of the data might be +** avoided. ** -** Generally speaking, flattening is only possible if the subquery -** query is a simple query without a GROUP BY clause or the DISTINCT -** keyword and the outer query is not a join. +** Flattening is only attempted if all of the following are true: ** -** If flattening is not possible, this routine is a no-op and return 0. -** If flattening is possible, this routine rewrites the query into -** the simplified form and return 1. +** (1) The subquery and the outer query do not both use aggregates. ** -** All of the expression analysis must occur before this routine runs. -** This routine depends on the results of the expression analysis. +** (2) The subquery is not an aggregate or the outer query is not a join. +** +** (3) The subquery is not a join. +** +** (4) The subquery is not DISTINCT or the outer query is not a join. +** +** (5) The subquery is not DISTINCT or the outer query does not use +** aggregates. +** +** (6) The subquery does not use aggregates or the outer query is not +** DISTINCT. +** +** In this routine, the "p" parameter is a pointer to the outer query. +** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query +** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates. +** +** If flattening is not attempted, this routine is a no-op and return 0. +** If flattening is attempted this routine returns 1. +** +** All of the expression analysis must occur on both the outer query and +** the subquery before this routine runs. */ -int flattenSubqueries(Select *p){ +int flattenSubquery(Select *p, int iFrom, int isAgg, int subqueryIsAgg){ Select *pSub; - if( p->pSrc->nId>1 ){ - return 0; /* Cannot optimize: The outer query is a join. */ - } - pSub = p->pSrc->a[0].pSelect; - if( pSub==0 ){ - return 0; /* Nothing to optimize: There is no subquery. */ - } - if( pSub->isDistinct ){ - return 0; /* Subquery contains DISTINCT keyword */ - } - if( pSub->pGroupBy ){ - return 0; /* Subquery contains a GROUP BY clause */ - } - if( pSub->pPrior ){ - return 0; /* Subquery is the union of two or more queries */ - } + IdList *pSrc, *pSubSrc; + ExprList *pList; + int i; + int iParent, iSub; + Expr *pWhere; - return 0; -} + /* Check to see if flattening is permitted. Return 0 if not. + */ + if( p==0 ) return 0; + pSrc = p->pSrc; + assert( pSrc && iFrom>=0 && iFromnId ); + pSub = pSrc->a[iFrom].pSelect; + assert( pSub!=0 ); + if( isAgg && subqueryIsAgg ) return 0; + if( subqueryIsAgg && pSrc->nId>1 ) return 0; + pSubSrc = pSub->pSrc; + assert( pSubSrc ); + if( pSubSrc->nId>1 ) return 0; + if( pSub->isDistinct && pSrc->nId>1 ) return 0; + if( pSub->isDistinct && isAgg ) return 0; + if( p->isDistinct && subqueryIsAgg ) return 0; + + /* If we reach this point, it means flatting is permitted for the + ** i-th entry of the FROM clause in the outer query. + */ + iParent = p->base + iFrom; + iSub = pSub->base; + substExprList(p->pEList, iParent, pSub->pEList, iSub); + pList = p->pEList; + for(i=0; inExpr; i++){ + if( pList->a[i].zName==0 ){ + Expr *pExpr = pList->a[i].pExpr; + pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n); + } + } + substExprList(p->pGroupBy, iParent, pSub->pEList, iSub); + substExpr(p->pHaving, iParent, pSub->pEList, iSub); + substExprList(p->pOrderBy, iParent, pSub->pEList, iSub); + if( pSub->pWhere ){ + pWhere = sqliteExprDup(pSub->pWhere); + if( iParent!=iSub ){ + changeTables(pWhere, iSub, iParent); + } + }else{ + pWhere = 0; + } + if( subqueryIsAgg ){ + assert( p->pHaving==0 ); + p->pHaving = pWhere; + substExpr(p->pHaving, iParent, pSub->pEList, iSub); + }else if( p->pWhere==0 ){ + p->pWhere = pWhere; + }else{ + substExpr(p->pWhere, iParent, pSub->pEList, iSub); + if( pWhere ){ + p->pWhere = sqliteExpr(TK_AND, p->pWhere, pWhere, 0); + } + } + p->isDistinct = p->isDistinct || pSub->isDistinct; + if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){ + sqliteDeleteTable(0, pSrc->a[iFrom].pTab); + } + pSrc->a[iFrom].pTab = pSubSrc->a[0].pTab; + pSubSrc->a[0].pTab = 0; + pSrc->a[iFrom].pSelect = pSubSrc->a[0].pSelect; + pSubSrc->a[0].pSelect = 0; + sqliteSelectDelete(pSub); + return 1; +} /* ** Analyze the SELECT statement passed in as an argument to see if it @@ -881,7 +1020,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ v = sqliteGetVdbe(pParse); if( v==0 ) return 0; if( eDest==SRT_Callback ){ - generateColumnNames(pParse, p->pSrc, p->pEList); + generateColumnNames(pParse, p->base, p->pSrc, p->pEList); } /* Generating code to find the min or the max. Basically all we have @@ -894,7 +1033,7 @@ static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){ pParse->schemaVerified = 1; } openOp = pTab->isTemp ? OP_OpenAux : OP_Open; - base = pParse->nTab; + base = p->base; sqliteVdbeAddOp(v, openOp, base, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); if( pIdx==0 ){ @@ -949,7 +1088,10 @@ int sqliteSelect( Parse *pParse, /* The parser context */ Select *p, /* The SELECT statement being coded. */ int eDest, /* One of: SRT_Callback Mem Set Union Except */ - int iParm /* Save result in this memory location, if >=0 */ + int iParm, /* Save result in this memory location, if >=0 */ + Select *pParent, /* Another SELECT for which this is a sub-query */ + int parentTab, /* Index in pParent->pSrc of this query */ + int parentAgg /* True if pParent uses aggregate functions */ ){ int i; WhereInfo *pWInfo; @@ -983,10 +1125,12 @@ int sqliteSelect( pHaving = p->pHaving; isDistinct = p->isDistinct; - /* Save the current value of pParse->nTab. Restore this value before - ** we exit. + /* Allocate a block of VDBE cursors, one for each table in the FROM clause. + ** The WHERE processing requires that the cursors for the tables in the + ** FROM clause be consecutive. */ - base = pParse->nTab; + base = p->base = pParse->nTab; + pParse->nTab += pTabList->nId; /* ** Do not even attempt to generate any code if we have already seen @@ -1004,16 +1148,6 @@ int sqliteSelect( pEList = p->pEList; if( pEList==0 ) goto select_end; - /* Allocate a temporary table to use for the DISTINCT set, if - ** necessary. This must be done early to allocate the cursor before - ** any calls to sqliteExprResolveIds(). - */ - if( isDistinct ){ - distinct = pParse->nTab++; - }else{ - distinct = -1; - } - /* If writing to memory or generating a set ** only a single column may be output. */ @@ -1027,35 +1161,17 @@ int sqliteSelect( /* ORDER BY is ignored if we are not sending the result to a callback. */ if( eDest!=SRT_Callback ){ - pOrderBy = 0; + sqliteExprListDelete(p->pOrderBy); + pOrderBy = p->pOrderBy = 0; } - /* Allocate cursors for "expr IN (SELECT ...)" constructs. - */ - for(i=0; inExpr; i++){ - sqliteExprResolveInSelect(pParse, pEList->a[i].pExpr); - } - if( pWhere ) sqliteExprResolveInSelect(pParse, pWhere); - if( pOrderBy ){ - for(i=0; inExpr; i++){ - sqliteExprResolveInSelect(pParse, pOrderBy->a[i].pExpr); - } - } - if( pGroupBy ){ - for(i=0; inExpr; i++){ - sqliteExprResolveInSelect(pParse, pGroupBy->a[i].pExpr); - } - } - if( pHaving ) sqliteExprResolveInSelect(pParse, pHaving); - /* At this point, we should have allocated all the cursors that we - ** need to handle subquerys and temporary tables. From here on we - ** are committed to keeping the same value for pParse->nTab. + ** 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++){ - if( sqliteExprResolveIds(pParse, pTabList, 0, pEList->a[i].pExpr) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, 0, pEList->a[i].pExpr) ){ goto select_end; } if( sqliteExprCheck(pParse, pEList->a[i].pExpr, 1, &isAgg) ){ @@ -1063,7 +1179,7 @@ int sqliteSelect( } } if( pWhere ){ - if( sqliteExprResolveIds(pParse, pTabList, pEList, pWhere) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pWhere) ){ goto select_end; } if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ @@ -1079,7 +1195,7 @@ int sqliteSelect( pParse->nErr++; goto select_end; } - if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){ goto select_end; } if( sqliteExprCheck(pParse, pE, isAgg, 0) ){ @@ -1096,7 +1212,7 @@ int sqliteSelect( pParse->nErr++; goto select_end; } - if( sqliteExprResolveIds(pParse, pTabList, pEList, pE) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pE) ){ goto select_end; } if( sqliteExprCheck(pParse, pE, isAgg, 0) ){ @@ -1111,7 +1227,7 @@ int sqliteSelect( pParse->nErr++; goto select_end; } - if( sqliteExprResolveIds(pParse, pTabList, pEList, pHaving) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, pEList, pHaving) ){ goto select_end; } if( sqliteExprCheck(pParse, pHaving, isAgg, 0) ){ @@ -1119,14 +1235,6 @@ int sqliteSelect( } } - /* Try to merge subqueries in the FROM clause into the main - ** query. - */ - if( flattenSubqueries(p) ){ - pEList = p->pEList; - pWhere = p->pWhere; - } - /* Check for the special case of a min() or max() function by itself ** in the result set. */ @@ -1143,15 +1251,25 @@ int sqliteSelect( /* Generate code for all sub-queries in the FROM clause */ for(i=0; inId; i++){ - int oldNTab; if( pTabList->a[i].pSelect==0 ) continue; - oldNTab = pParse->nTab; - pParse->nTab += i+1; - sqliteVdbeAddOp(v, OP_OpenTemp, oldNTab+i, 0); - sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_Table, oldNTab+i); - pParse->nTab = oldNTab; + sqliteVdbeAddOp(v, OP_OpenTemp, base+i, 0); + sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_Table, base+i, + p, i, isAgg); } + /* Check to see if this is a subquery that can be "flattened" into its parent. + ** If flattening is a possiblity, do so and return immediately. + */ + if( flattenSubquery(pParent, parentTab, parentAgg, isAgg) ){ + return rc; + } + pTabList = p->pSrc; + pWhere = p->pWhere; + pOrderBy = p->pOrderBy; + pGroupBy = p->pGroupBy; + pHaving = p->pHaving; + isDistinct = p->isDistinct; + /* Do an analysis of aggregate expressions. */ sqliteAggregateInfoReset(pParse); @@ -1195,7 +1313,7 @@ int sqliteSelect( ** step is skipped if the output is going to a table or a memory cell. */ if( eDest==SRT_Callback ){ - generateColumnNames(pParse, pTabList, pEList); + generateColumnNames(pParse, p->base, pTabList, pEList); } /* Reset the aggregator @@ -1222,12 +1340,18 @@ int sqliteSelect( sqliteVdbeAddOp(v, OP_MemStore, iParm, 1); } - /* Begin the database scan + /* Open a temporary table to use for the distinct set. */ if( isDistinct ){ + distinct = pParse->nTab++; sqliteVdbeAddOp(v, OP_OpenTemp, distinct, 1); + }else{ + distinct = -1; } - pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 0); + + /* Begin the database scan + */ + pWInfo = sqliteWhereBegin(pParse, p->base, pTabList, pWhere, 0); if( pWInfo==0 ) goto select_end; /* Use the standard inner loop if we are not dealing with @@ -1309,7 +1433,6 @@ int sqliteSelect( if( pOrderBy ){ generateSortTail(v, pEList->nExpr); } - pParse->nTab = base; /* Issue a null callback if that is what the user wants. @@ -1327,6 +1450,7 @@ int sqliteSelect( ** successful coding of the SELECT. */ select_end: + pParse->nTab = base; sqliteAggregateInfoReset(pParse); return rc; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a39272077a..3dcda0a66a 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.96 2002/02/28 00:41:11 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.97 2002/03/02 17:04:08 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -434,6 +434,8 @@ struct WhereInfo { int iBreak; /* Jump here to break out of the loop */ int base; /* Index of first Open opcode */ int nLevel; /* Number of nested loop */ + int savedNTab; /* Value of pParse->nTab before WhereBegin() */ + int peakNTab; /* Value of pParse->nTab after WhereBegin() */ WhereLevel a[1]; /* Information about each nest loop in the WHERE */ }; @@ -461,6 +463,7 @@ struct Select { Select *pPrior; /* Prior select in a compound select statement */ int nLimit, nOffset; /* LIMIT and OFFSET values. -1 means not used */ char *zSelect; /* Complete text of the SELECT command */ + int base; /* Index of VDBE cursor for left-most FROM table */ }; /* @@ -521,7 +524,7 @@ struct Parse { int nameClash; /* A permanent table name clashes with temp table name */ int newTnum; /* Table number to use when reparsing CREATE TABLEs */ int nErr; /* Number of errors seen */ - int nTab; /* Number of previously allocated cursors */ + int nTab; /* Number of previously allocated VDBE cursors */ int nMem; /* Number of memory cells used so far */ int nSet; /* Number of sets used so far */ int nAgg; /* Number of aggregate expressions */ @@ -586,7 +589,7 @@ void sqliteIdListAddAlias(IdList*, Token*); void sqliteIdListDelete(IdList*); void sqliteCreateIndex(Parse*, Token*, Token*, IdList*, int, Token*, Token*); void sqliteDropIndex(Parse*, Token*); -int sqliteSelect(Parse*, Select*, int, int); +int sqliteSelect(Parse*, Select*, int, int, Select*, int, int); Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*, int,int,int); void sqliteSelectDelete(Select*); @@ -595,7 +598,7 @@ Table *sqliteTableNameToTable(Parse*, const char*); IdList *sqliteTableTokenToIdList(Parse*, Token*); void sqliteDeleteFrom(Parse*, Token*, Expr*); void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int); -WhereInfo *sqliteWhereBegin(Parse*, IdList*, Expr*, int); +WhereInfo *sqliteWhereBegin(Parse*, int, IdList*, Expr*, int); void sqliteWhereEnd(WhereInfo*); void sqliteExprCode(Parse*, Expr*); void sqliteExprIfTrue(Parse*, Expr*, int); @@ -611,8 +614,7 @@ char *sqliteTableNameFromToken(Token*); int sqliteExprCheck(Parse*, Expr*, int, int*); int sqliteExprCompare(Expr*, Expr*); int sqliteFuncId(Token*); -int sqliteExprResolveIds(Parse*, IdList*, ExprList*, Expr*); -void sqliteExprResolveInSelect(Parse*, Expr*); +int sqliteExprResolveIds(Parse*, int, IdList*, ExprList*, Expr*); int sqliteExprAnalyzeAggregates(Parse*, Expr*); Vdbe *sqliteGetVdbe(Parse*); int sqliteRandomByte(void); diff --git a/src/update.c b/src/update.c index a4aa308e40..7e470fed7f 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.34 2002/02/23 02:32:10 drh Exp $ +** $Id: update.c,v 1.35 2002/03/02 17:04:09 drh Exp $ */ #include "sqliteInt.h" @@ -66,14 +66,9 @@ void sqliteUpdate( ** WHERE clause and in the new values. Also find the column index ** for each column to be updated in the pChanges array. */ + base = pParse->nTab++; if( pWhere ){ - sqliteExprResolveInSelect(pParse, pWhere); - } - for(i=0; inExpr; i++){ - sqliteExprResolveInSelect(pParse, pChanges->a[i].pExpr); - } - if( pWhere ){ - if( sqliteExprResolveIds(pParse, pTabList, 0, pWhere) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, 0, pWhere) ){ goto update_cleanup; } if( sqliteExprCheck(pParse, pWhere, 0, 0) ){ @@ -82,7 +77,7 @@ void sqliteUpdate( } chngRecno = 0; for(i=0; inExpr; i++){ - if( sqliteExprResolveIds(pParse, pTabList, 0, pChanges->a[i].pExpr) ){ + if( sqliteExprResolveIds(pParse, base, pTabList, 0, pChanges->a[i].pExpr) ){ goto update_cleanup; } if( sqliteExprCheck(pParse, pChanges->a[i].pExpr, 0, 0) ){ @@ -150,7 +145,7 @@ void sqliteUpdate( /* Begin the database scan */ - pWInfo = sqliteWhereBegin(pParse, pTabList, pWhere, 1); + pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1); if( pWInfo==0 ) goto update_cleanup; /* Remember the index of every item to be updated. @@ -174,7 +169,6 @@ void sqliteUpdate( ** to be deleting some records. */ sqliteVdbeAddOp(v, OP_ListRewind, 0, 0); - base = pParse->nTab; openOp = pTab->isTemp ? OP_OpenWrAux : OP_OpenWrite; sqliteVdbeAddOp(v, openOp, base, pTab->tnum); if( onError==OE_Replace ){ @@ -191,7 +185,9 @@ void sqliteUpdate( for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){ if( openAll || aIdxUsed[i] ){ sqliteVdbeAddOp(v, openOp, base+i+1, pIdx->tnum); + assert( pParse->nTab==base+i+1 ); } + pParse->nTab++; } /* Loop over every record that needs updating. We have to load diff --git a/src/where.c b/src/where.c index e325af807a..32077dc157 100644 --- a/src/where.c +++ b/src/where.c @@ -13,7 +13,7 @@ ** the WHERE clause of SQL statements. Also found here are subroutines ** to generate VDBE code to evaluate expressions. ** -** $Id: where.c,v 1.37 2002/02/23 02:32:10 drh Exp $ +** $Id: where.c,v 1.38 2002/03/02 17:04:09 drh Exp $ */ #include "sqliteInt.h" @@ -76,8 +76,7 @@ static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){ ** the header comment on that routine for additional information. ** ** "base" is the cursor number (the value of the iTable field) that -** corresponds to the first entry in the table list. This is the -** same as pParse->nTab. +** corresponds to the first entry in the table list. */ static int exprTableUsage(int base, Expr *p){ unsigned int mask = 0; @@ -119,8 +118,7 @@ static int allowedOp(int op){ ** structure. ** ** "base" is the cursor number (the value of the iTable field) that -** corresponds to the first entry in the table list. This is the -** same as pParse->nTab. +** corresponds to the first entry in the table list. */ static void exprAnalyze(int base, ExprInfo *pInfo){ Expr *pExpr = pInfo->p; @@ -152,6 +150,7 @@ static void exprAnalyze(int base, ExprInfo *pInfo){ */ WhereInfo *sqliteWhereBegin( Parse *pParse, /* The parser context */ + int base, /* VDBE cursor index for left-most table in pTabList */ IdList *pTabList, /* A list of all tables */ Expr *pWhere, /* The WHERE clause */ int pushKey /* If TRUE, leave the table key on the stack */ @@ -164,8 +163,6 @@ WhereInfo *sqliteWhereBegin( int nExpr; /* Number of subexpressions in the WHERE clause */ int loopMask; /* One bit set for each outer loop */ int haveKey; /* True if KEY is on the stack */ - int base; /* First available index for OP_Open opcodes */ - int nCur; /* Next unused cursor number */ int aDirect[32]; /* If TRUE, then index this table using ROWID */ int iDirectEq[32]; /* Term of the form ROWID==X for the N-th table */ int iDirectLt[32]; /* Term of the form ROWIDpParse = pParse; pWInfo->pTabList = pTabList; - base = pWInfo->base = pParse->nTab; - nCur = base + pTabList->nId; - pParse->nTab += nCur*2; + pWInfo->base = base; + pWInfo->peakNTab = pWInfo->savedNTab = pParse->nTab; /* Split the WHERE clause into as many as 32 separate subexpressions ** where each subexpression is separated by an AND operator. Any additional @@ -388,7 +384,8 @@ WhereInfo *sqliteWhereBegin( pWInfo->a[i].score = bestScore; loopMask |= 1<a[i].iCur = nCur++; + pWInfo->a[i].iCur = pParse->nTab++; + pWInfo->peakNTab = pParse->nTab; } } @@ -794,7 +791,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){ sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0); } } - pWInfo->pParse->nTab = base; + if( pWInfo->pParse->nTab==pWInfo->peakNTab ){ + pWInfo->pParse->nTab = pWInfo->savedNTab; + } sqliteFree(pWInfo); return; } diff --git a/test/func.test b/test/func.test index af3311c640..f37f185a5b 100644 --- a/test/func.test +++ b/test/func.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing built-in functions. # -# $Id: func.test,v 1.8 2002/02/28 03:04:48 drh Exp $ +# $Id: func.test,v 1.9 2002/03/02 17:04:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -25,6 +25,17 @@ do_test func-0.0 { } execsql {SELECT t1 FROM tbl1 ORDER BY t1} } {free is program software this} +do_test func-0.1 { + execsql { + CREATE TABLE t2(a); + INSERT INTO t2 VALUES(1); + INSERT INTO t2 VALUES(NULL); + INSERT INTO t2 VALUES(345); + INSERT INTO t2 VALUES(NULL); + INSERT INTO t2 VALUES(67890); + SELECT * FROM t2; + } +} {1 {} 345 {} 67890} # Check out the length() function # @@ -43,6 +54,9 @@ do_test func-1.3 { execsql {SELECT length(t1), count(*) FROM tbl1 GROUP BY length(t1) ORDER BY length(t1)} } {2 1 4 2 7 1 8 1} +do_test func-1.4 { + execsql {SELECT length(a) FROM t2} +} {1 0 3 0 5} # Check out the substr() function # @@ -73,6 +87,12 @@ do_test func-2.7 { do_test func-2.8 { execsql {SELECT t1 FROM tbl1 ORDER BY substr(t1,2,20)} } {this software free program is} +do_test func-2.9 { + execsql {SELECT substr(a,1,1) FROM t2} +} {1 {} 3 {} 6} +do_test func-2.10 { + execsql {SELECT substr(a,2,2) FROM t2} +} {{} {} 45 {} 78} # Only do the following tests if TCL has UTF-8 capabilities and # the UTF-8 encoding is turned on in the SQLite library. @@ -118,6 +138,13 @@ do_test func-3.9 { do_test func-3.10 { execsql {SELECT substr(t1,-4,3) FROM tbl1 ORDER BY t1} } "ter ain i\u1234h TF-" +do_test func-3.99 { + execsql {DELETE FROM tbl1} + foreach word {this program is free software} { + execsql "INSERT INTO tbl1 VALUES('$word')" + } + execsql {SELECT t1 FROM tbl1} +} {this program is free software} } ;# End [sqlite -encoding]==UTF-8 and \u1234!=u1234 @@ -141,6 +168,12 @@ do_test func-4.3 { do_test func-4.4 { catchsql {SELECT abs(c) FROM t1 ORDER BY a} } {0 {3 12345.67890 5}} +do_test func-4.4.1 { + execsql {SELECT abs(a) FROM t2} +} {1 {} 345 {} 67890} +do_test func-4.4.2 { + execsql {SELECT abs(t1) FROM tbl1} +} {this program is free software} do_test func-4.5 { catchsql {SELECT round(a,b,c) FROM t1} @@ -163,5 +196,39 @@ do_test func-4.10 { do_test func-4.11 { catchsql {SELECT round() FROM t1 ORDER BY a} } {1 {wrong number of arguments to function round()}} +do_test func-4.12 { + execsql {SELECT round(a,2) FROM t2} +} {1.00 0.00 345.00 0.00 67890.00} +do_test func-4.13 { + execsql {SELECT round(t1,2) FROM tbl1} +} {0.00 0.00 0.00 0.00 0.00} + +# Test the upper() and lower() functions +# +do_test func-5.1 { + execsql {SELECT upper(t1) FROM tbl1} +} {THIS PROGRAM IS FREE SOFTWARE} +do_test func-5.2 { + execsql {SELECT lower(upper(t1)) FROM tbl1} +} {this program is free software} +do_test func-5.3 { + execsql {SELECT upper(a), lower(a) FROM t2} +} {1 1 {} {} 345 345 {} {} 67890 67890} +do_test func-5.4 { + catchsql {SELECT upper(a,5) FROM t2} +} {1 {wrong number of arguments to function upper()}} +do_test func-5.5 { + catchsql {SELECT upper(*) FROM t2} +} {1 {wrong number of arguments to function upper()}} + +# Test the coalesce() function +# +do_test func-6.1 { + execsql {SELECT coalesce(a,'xyz') FROM t2} +} {1 xyz 345 xyz 67890} +do_test func-6.2 { + execsql {SELECT coalesce(upper(a),'nil') FROM t2} +} {1 nil 345 nil 67890} + finish_test diff --git a/test/select1.test b/test/select1.test index 7fd6a360f8..4aa36e8a6b 100644 --- a/test/select1.test +++ b/test/select1.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: select1.test,v 1.21 2002/02/28 03:14:18 drh Exp $ +# $Id: select1.test,v 1.22 2002/03/02 17:04:09 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -97,10 +97,22 @@ do_test select1-1.13 { FROM test1, test2} } {1.1 22} -execsql {DROP TABLE test2} -execsql {DELETE FROM test1} -execsql {INSERT INTO test1 VALUES(11,22)} -execsql {INSERT INTO test1 VALUES(33,44)} +set long {This is a string that is too big to fit inside a NBFS buffer} +do_test select1-2.0 { + execsql " + DROP TABLE test2; + DELETE FROM test1; + INSERT INTO test1 VALUES(11,22); + INSERT INTO test1 VALUES(33,44); + CREATE TABLE t3(a,b); + INSERT INTO t3 VALUES('abc',NULL); + INSERT INTO t3 VALUES(NULL,'xyz'); + INSERT INTO t3 SELECT * FROM test1; + CREATE TABLE t4(a,b); + INSERT INTO t4 VALUES(NULL,'$long'); + SELECT * FROM t3; + " +} {abc {} {} xyz 11 22 33 44} # Error messges from sqliteExprCheck # @@ -124,6 +136,15 @@ do_test select1-2.5 { set v [catch {execsql {SELECT COUNT(*)+1 FROM test1}} msg] lappend v $msg } {0 3} +do_test select1-2.5.1 { + execsql {SELECT count(*),count(a),count(b) FROM t3} +} {4 3 3} +do_test select1-2.5.2 { + execsql {SELECT count(*),count(a),count(b) FROM t4} +} {1 0 1} +do_test select1-2.5.3 { + execsql {SELECT count(*),count(a),count(b) FROM t4 WHERE b=5} +} {0 0 0} do_test select1-2.6 { set v [catch {execsql {SELECT min(*) FROM test1}} msg] lappend v $msg @@ -136,6 +157,15 @@ do_test select1-2.8 { set v [catch {execsql {SELECT MIN(f1,f2) FROM test1}} msg] lappend v [lsort $msg] } {0 {11 33}} +do_test select1-2.8.1 { + execsql {SELECT coalesce(min(a),'xyzzy') FROM t3} +} {xyzzy} +do_test select1-2.8.2 { + execsql {SELECT min(coalesce(a,'xyzzy')) FROM t3} +} {11} +do_test select1-2.8.3 { + execsql {SELECT min(b), min(b) FROM t4} +} [list $long $long] do_test select1-2.9 { set v [catch {execsql {SELECT MAX(*) FROM test1}} msg] lappend v $msg @@ -156,6 +186,12 @@ do_test select1-2.13 { set v [catch {execsql {SELECT MAX(f1)+1 FROM test1}} msg] lappend v $msg } {0 34} +do_test select1-2.13.1 { + execsql {SELECT coalesce(max(a),'xyzzy') FROM t3} +} {abc} +do_test select1-2.13.1 { + execsql {SELECT max(coalesce(a,'xyzzy')) FROM t3} +} {xyzzy} do_test select1-2.14 { set v [catch {execsql {SELECT SUM(*) FROM test1}} msg] lappend v $msg @@ -172,6 +208,9 @@ do_test select1-2.17 { set v [catch {execsql {SELECT SUM(f1)+1 FROM test1}} msg] lappend v $msg } {0 45} +do_test select1-2.17.1 { + execsql {SELECT sum(a) FROM t3} +} {44} do_test select1-2.18 { set v [catch {execsql {SELECT XYZZY(f1) FROM test1}} msg] lappend v $msg diff --git a/www/changes.tcl b/www/changes.tcl index 1e22c76dd8..2cbdfcdcd7 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -28,6 +28,8 @@ chng {2002 Feb * (2.3.4)} {
  • Modifications to the "lemon" parser generator so that the parser tables are 4 times smaller.
  • Added support for user-defined functions implemented in C.
  • +
  • Added support for VIEWs.
  • +
  • Added the subquery flattening optimizer.
  • } chng {2002 Feb 18 (2.3.3)} {