diff --git a/manifest b/manifest index 9547ff6eba..ec4280f03c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Make\sthe\ssqliteParseInfoReset()\sfunction\slocale\sto\sthe\sselect.c\sfile.\s(CVS\s371) -D 2002-02-17T00:30:36 +C Add\ssupport\sfor\ssubqueries\sin\sthe\sFROM\sclause\sof\sa\sSELECT.\s\sStill\sneed\nto\sadd\stests\sfor\sthis\sfeature.\s(CVS\s372) +D 2002-02-18T01:17:00 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0 @@ -33,14 +33,14 @@ F src/os.c 1953080d14098cd45e5bde88941567688efb72b1 F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6 F src/pager.c d261a3a0b4e96a400ef5432297edec09b041e9c7 F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283 -F src/parse.y 734ba1e0dce9882345e65de5a14d9fe02fb757c2 +F src/parse.y f7a6d8122b2d65ae8ded78721d0995f47ad277e1 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d F src/random.c f6b36bec5ebd3edb3440224bf5bf811fe4ac9a1b -F src/select.c 631fe7c1e06c70e4e78e9536e69f4eb57fdae07b +F src/select.c 48c14a44da6bc04a6e19f2c5da15b5ea984ede13 F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c -F src/sqliteInt.h 58d6311edad0d52eae1631c47356b329da24f3af +F src/sqliteInt.h 6c5d0415a6ff54e14fab74d5686016cc53f0be5c F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f @@ -51,8 +51,8 @@ F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103 F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6 F src/vdbe.c 94704a5733db95b78cc902208c5e8e26a784e7f8 F src/vdbe.h 3d49d22ba9ad14ea0e380bc582ff57347eaddb59 -F src/where.c fd4d817dedd2a29e7f118cac3517c4c9d9ff199c -F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe +F src/where.c f4bc12e172823ff9dc55e0c86031460a8043886a +F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578 F test/btree.test 6ab4dc5f595905a276ef588fad3c9236dc07a47b F test/btree2.test 08e9485619265cbaf5d11bd71f357cdc26bb87e0 @@ -107,7 +107,7 @@ F www/arch.fig d5f9752a4dbf242e9cfffffd3f5762b6c63b3bcf F www/arch.png 82ef36db1143828a7abc88b1e308a5f55d4336f4 F www/arch.tcl 72a0c80e9054cc7025a50928d28d9c75c02c2b8b F www/c_interface.tcl 82a026b1681757f13b3f62e035f3a31407c1d353 -F www/changes.tcl 0ed7e3af0a72b228157beb4a14f19f12737828d8 +F www/changes.tcl f10f6552731c9b11a8f73d6d900320b29b56b656 F www/conflict.tcl 81dd21f9a679e60aae049e9dd8ab53d59570cda2 F www/crosscompile.tcl 3622ebbe518927a3854a12de51344673eb2dd060 F www/download.tcl a6d75b8b117cd33dcb090bef7e80d7556d28ebe0 @@ -122,7 +122,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 -P e17a858c9eeb70c62f54c88e6be5897e58d67301 -R b50edb4b34a53c4baa439c4b19149a7d +P 2336b1eadaedf2556a1988acc7bdf133135154dc +R b61f3cbe24d03942a9f4eed0442e350a U drh -Z adc815cb558970a8bb42b33bc644f373 +Z 5a9cc96ed308ce034a9e31eeae708204 diff --git a/manifest.uuid b/manifest.uuid index 591ad6ac00..3b0496643f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2336b1eadaedf2556a1988acc7bdf133135154dc \ No newline at end of file +89ffa9ff132858b62a91df1fb7fe49b2d58c01e7 \ No newline at end of file diff --git a/src/parse.y b/src/parse.y index 7959e0b6fe..fc5180c737 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.48 2002/02/03 17:37:36 drh Exp $ +** @(#) $Id: parse.y,v 1.49 2002/02/18 01:17:00 drh Exp $ */ %token_prefix TK_ %token_type {Token} @@ -257,6 +257,15 @@ seltablist(A) ::= stl_prefix(X) ids(Y) as ids(Z). { A = sqliteIdListAppend(X,&Y); sqliteIdListAddAlias(A,&Z); } +seltablist(A) ::= stl_prefix(X) LP select(S) RP. { + A = sqliteIdListAppend(X,0); + A->a[A->nId-1].pSelect = S; +} +seltablist(A) ::= stl_prefix(X) LP select(S) RP as ids(Z). { + A = sqliteIdListAppend(X,0); + A->a[A->nId-1].pSelect = S; + sqliteIdListAddAlias(A,&Z); +} %type orderby_opt {ExprList*} %destructor orderby_opt {sqliteExprListDelete($$);} diff --git a/src/select.c b/src/select.c index 611d70ea02..29f1497519 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.59 2002/02/17 00:30:36 drh Exp $ +** $Id: select.c,v 1.60 2002/02/18 01:17:00 drh Exp $ */ #include "sqliteInt.h" @@ -273,7 +273,7 @@ void generateColumnNames(Parse *pParse, IdList *pTabList, ExprList *pEList){ sqliteFree(zName); }else{ sqliteVdbeAddOp(v, OP_ColumnName, i, 0); - sqliteVdbeChangeP3(v, -1, zCol, P3_STATIC); + sqliteVdbeChangeP3(v, -1, zCol, 0); } }else if( p->span.z && p->span.z[0] ){ int addr = sqliteVdbeAddOp(v,OP_ColumnName, i, 0); @@ -303,11 +303,48 @@ static const char *selectOpName(int id){ return z; } +/* +** Given a SELECT statement, generate a Table structure that describes +** the result set of that SELECT. +*/ +Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){ + Table *pTab; + int i; + ExprList *pEList; + static int fillInColumnList(Parse*, Select*); + + if( fillInColumnList(pParse, pSelect) ){ + return 0; + } + pTab = sqliteMalloc( sizeof(Table) ); + if( pTab==0 ){ + return 0; + } + pTab->zName = zTabName ? sqliteStrDup(zTabName) : 0; + pEList = pSelect->pEList; + pTab->nCol = pEList->nExpr; + pTab->aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol ); + for(i=0; i<pTab->nCol; i++){ + Expr *p; + if( pEList->a[i].zName ){ + pTab->aCol[i].zName = sqliteStrDup(pEList->a[i].zName); + }else if( (p=pEList->a[i].pExpr)->span.z && p->span.z[0] ){ + sqliteSetNString(&pTab->aCol[i].zName, p->span.z, p->span.n, 0); + }else{ + char zBuf[30]; + sprintf(zBuf, "column%d", i+1); + pTab->aCol[i].zName = sqliteStrDup(zBuf); + } + } + pTab->iPKey = -1; + return pTab; +} + /* ** For the given SELECT statement, do two things. ** ** (1) Fill in the pTabList->a[].pTab fields in the IdList that -** defines the set of tables that should be scanned. +** defines the set of tables that should be scanned. ** ** (2) If the columns to be extracted variable (pEList) is NULL ** (meaning that a "*" was used in the SQL statement) then @@ -334,22 +371,25 @@ static int fillInColumnList(Parse *pParse, Select *p){ return 0; } if( pTabList->a[i].zName==0 ){ - /* No table name is given. Instead, there is a (SELECT ...) statement - ** the results of which should be used in place of the table. The - ** way this is implemented is that the (SELECT ...) writes its results - ** into a temporary table which is then scanned like any other table. - */ - sqliteSetString(&pParse->zErrMsg, - "(SELECT...) in a FROM clause is not yet implemented.", 0); - pParse->nErr++; - return 1; - } - pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); - if( pTabList->a[i].pTab==0 ){ - sqliteSetString(&pParse->zErrMsg, "no such table: ", - pTabList->a[i].zName, 0); - pParse->nErr++; - return 1; + /* A sub-query in the FROM clause of a SELECT */ + Table *pTab; + assert( pTabList->a[i].pSelect!=0 ); + pTabList->a[i].pTab = pTab = + sqliteResultSetOfSelect(pParse, pTabList->a[i].zAlias, + pTabList->a[i].pSelect); + if( pTab==0 ){ + return 1; + } + pTab->isTransient = 1; + }else{ + /* An ordinary table name in the FROM clause */ + pTabList->a[i].pTab = sqliteFindTable(pParse->db, pTabList->a[i].zName); + if( pTabList->a[i].pTab==0 ){ + sqliteSetString(&pParse->zErrMsg, "no such table: ", + pTabList->a[i].zName, 0); + pParse->nErr++; + return 1; + } } } @@ -375,23 +415,27 @@ static int fillInColumnList(Parse *pParse, Select *p){ for(i=0; i<pTabList->nId; i++){ Table *pTab = pTabList->a[i].pTab; for(j=0; j<pTab->nCol; j++){ - Expr *pExpr = sqliteExpr(TK_DOT, 0, 0, 0); - if( pExpr==0 ) break; - pExpr->pLeft = sqliteExpr(TK_ID, 0, 0, 0); - if( pExpr->pLeft==0 ){ sqliteExprDelete(pExpr); break; } - if( pTabList->a[i].zAlias && pTabList->a[i].zAlias[0] ){ - pExpr->pLeft->token.z = pTabList->a[i].zAlias; - pExpr->pLeft->token.n = strlen(pTabList->a[i].zAlias); + Expr *pExpr, *pLeft, *pRight; + pRight = sqliteExpr(TK_ID, 0, 0, 0); + if( pRight==0 ) break; + pRight->token.z = pTab->aCol[j].zName; + pRight->token.n = strlen(pTab->aCol[j].zName); + if( pTab->zName ){ + pLeft = sqliteExpr(TK_ID, 0, 0, 0); + if( pLeft==0 ) break; + if( pTabList->a[i].zAlias && pTabList->a[i].zAlias[0] ){ + pLeft->token.z = pTabList->a[i].zAlias; + pLeft->token.n = strlen(pTabList->a[i].zAlias); + }else{ + pLeft->token.z = pTab->zName; + pLeft->token.n = strlen(pTab->zName); + } + pExpr = sqliteExpr(TK_DOT, pLeft, pRight, 0); + if( pExpr==0 ) break; }else{ - pExpr->pLeft->token.z = pTab->zName; - pExpr->pLeft->token.n = strlen(pTab->zName); + pExpr = pRight; + pExpr->span = pExpr->token; } - pExpr->pRight = sqliteExpr(TK_ID, 0, 0, 0); - if( pExpr->pRight==0 ){ sqliteExprDelete(pExpr); break; } - pExpr->pRight->token.z = pTab->aCol[j].zName; - pExpr->pRight->token.n = strlen(pTab->aCol[j].zName); - pExpr->span.z = ""; - pExpr->span.n = 0; pNew = sqliteExprListAppend(pNew, pExpr, 0); } } @@ -897,6 +941,20 @@ int sqliteSelect( v = sqliteGetVdbe(pParse); if( v==0 ) goto select_end; + /* Generate code for all sub-queries in the FROM clause + */ + for(i=0; i<pTabList->nId; i++){ + int oldNTab; + Table *pTab = pTabList->a[i].pTab; + if( !pTab->isTransient ) continue; + assert( pTabList->a[i].pSelect!=0 ); + 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; + } + /* Set the limiter */ if( p->nLimit<=0 ){ diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 70be7c4308..03da707bb2 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.85 2002/02/17 00:30:36 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.86 2002/02/18 01:17:00 drh Exp $ */ #include "sqlite.h" #include "hash.h" @@ -220,8 +220,33 @@ struct Column { }; /* -** Each SQL table is represented in memory by -** an instance of the following structure. +** Each SQL table is represented in memory by an instance of the +** following structure. +** +** Expr.zName is the name of the table. The case of the original +** CREATE TABLE statement is stored, but case is not significant for +** comparisons. +** +** Expr.nCol is the number of columns in this table. Expr.aCol is a +** pointer to an array of Column structures, one for each column. +** +** If the table has an INTEGER PRIMARY KEY, then Expr.iPKey is the index of +** the column that is that key. Otherwise Expr.iPKey is negative. Note +** that the datatype of the PRIMARY KEY must be INTEGER for this field to +** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of +** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid +** is generated for each row of the table. Expr.hasPrimKey is true if +** the table has any PRIMARY KEY, INTEGER or otherwise. +** +** Expr.tnum is the page number for the root BTree page of the table in the +** database file. If Expr.isTemp is true, then this page occurs in the +** auxiliary database file, not the main database file. If Expr.isTransient +** is true, then the table is stored in a file that is automatically deleted +** when the VDBE cursor to the table is closed. In this case Expr.tnum +** refers VDBE cursor number that holds the table open, not to the root +** page number. Transient tables are used to hold the results of a +** sub-query that appears instead of a real table name in the FROM clause +** of a SELECT statement. */ struct Table { char *zName; /* Name of the table */ @@ -229,18 +254,18 @@ struct Table { Column *aCol; /* Information about each column */ int iPKey; /* If not less then 0, use aCol[iPKey] as the primary key */ Index *pIndex; /* List of SQL indexes on this table. */ - int tnum; /* Page containing root for this table */ + int tnum; /* Root BTree node for this table (see note above) */ u8 readOnly; /* True if this table should not be written by the user */ u8 isCommit; /* True if creation of this table has been committed */ u8 isTemp; /* True if stored in db->pBeTemp instead of db->pBe */ + u8 isTransient; /* True if automatically deleted when VDBE finishes */ u8 hasPrimKey; /* True if there exists a primary key */ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */ }; /* -** SQLite supports 4 or 5 different ways to resolve a contraint -** error. (Only 4 are implemented as of this writing. The fifth method -** "ABORT" is planned.) ROLLBACK processing means that a constraint violation +** SQLite supports 5 different ways to resolve a contraint +** error. ROLLBACK processing means that a constraint violation ** causes the operation in proces to fail and for the current transaction ** to be rolled back. ABORT processing means the operation in process ** fails and any prior changes from that one operation are backed out, @@ -307,7 +332,37 @@ struct Token { /* ** Each node of an expression in the parse tree is an instance -** of this structure +** of this structure. +** +** Expr.op is the opcode. The integer parser token codes are reused +** as opcodes here. For example, the parser defines TK_GE to be an integer +** code representing the ">=" operator. This same integer code is reused +** to represent the greater-than-or-equal-to operator in the expression +** tree. +** +** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list +** of argument if the expression is a function. +** +** Expr.token is the operator token for this node. Expr.span is the complete +** subexpression represented by this node and all its decendents. These +** fields are used for error reporting and for reconstructing the text of +** an expression to use as the column name in a SELECT statement. +** +** An expression of the form ID or ID.ID refers to a column in a table. +** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is +** the integer cursor number of a VDBE cursor pointing to that table and +** Expr.iColumn is the column number for the specific column. If the +** expression is used as a result in an aggregate SELECT, then the +** value is also stored in the Expr.iAgg column in the aggregate so that +** it can be accessed after all aggregates are computed. +** +** If the expression is a function, the Expr.iTable is an integer code +** representing which function. +** +** The Expr.pSelect field points to a SELECT statement. The SELECT might +** be the right operand of an IN operator. Or, if a scalar SELECT appears +** in an expression the opcode is TK_SELECT and Expr.pSelect is the only +** operand. */ struct Expr { int op; /* Operation performed by this node */ diff --git a/src/where.c b/src/where.c index dd11202391..48cf4b3026 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.34 2002/02/13 23:22:54 drh Exp $ +** $Id: where.c,v 1.35 2002/02/18 01:17:00 drh Exp $ */ #include "sqliteInt.h" @@ -188,6 +188,7 @@ WhereInfo *sqliteWhereBegin( pWInfo->pTabList = pTabList; base = pWInfo->base = pParse->nTab; nCur = base + pTabList->nId; + pParse->nTab += nCur*2; /* Split the WHERE clause into as many as 32 separate subexpressions ** where each subexpression is separated by an AND operator. Any additional @@ -202,7 +203,7 @@ WhereInfo *sqliteWhereBegin( /* Analyze all of the subexpressions. */ for(i=0; i<nExpr; i++){ - exprAnalyze(pParse->nTab, &aExpr[i]); + exprAnalyze(base, &aExpr[i]); } /* Figure out a good nesting order for the tables. aOrder[0] will @@ -396,6 +397,7 @@ WhereInfo *sqliteWhereBegin( Table *pTab; pTab = pTabList->a[i].pTab; + if( pTab->isTransient ) continue; openOp = pTab->isTemp ? OP_OpenAux : OP_Open; sqliteVdbeAddOp(v, openOp, base+i, pTab->tnum); sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC); @@ -771,8 +773,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){ int i; int base = pWInfo->base; WhereLevel *pLevel; + IdList *pTabList = pWInfo->pTabList; - for(i=pWInfo->pTabList->nId-1; i>=0; i--){ + for(i=pTabList->nId-1; i>=0; i--){ pLevel = &pWInfo->a[i]; sqliteVdbeResolveLabel(v, pLevel->cont); if( pLevel->op!=OP_Noop ){ @@ -781,13 +784,15 @@ void sqliteWhereEnd(WhereInfo *pWInfo){ sqliteVdbeResolveLabel(v, pLevel->brk); } sqliteVdbeResolveLabel(v, pWInfo->iBreak); - for(i=0; i<pWInfo->pTabList->nId; i++){ + for(i=0; i<pTabList->nId; i++){ + if( pTabList->a[i].pTab->isTransient ) continue; pLevel = &pWInfo->a[i]; sqliteVdbeAddOp(v, OP_Close, base+i, 0); if( pLevel->pIdx!=0 ){ sqliteVdbeAddOp(v, OP_Close, pLevel->iCur, 0); } } + pWInfo->pParse->nTab = base; sqliteFree(pWInfo); return; } diff --git a/test/all.test b/test/all.test index 3f05ebc234..3139bbab3d 100644 --- a/test/all.test +++ b/test/all.test @@ -10,7 +10,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: all.test,v 1.11 2001/10/22 02:58:11 drh Exp $ +# $Id: all.test,v 1.12 2002/02/18 01:17:00 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -33,6 +33,7 @@ set EXCLUDE { all.test quick.test malloc.test + btree2.test } for {set Counter 0} {$Counter<$COUNT && $nErr==0} {incr Counter} { diff --git a/www/changes.tcl b/www/changes.tcl index f5ea31756d..1032aef323 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -20,6 +20,7 @@ proc chng {date desc} { chng {2002 Feb * (2.3.3)} { <li>Allow identifiers to be quoted in square brackets, for compatibility with MS-Access.</li> +<li>Added support for sub-queries in the FROM clause of a SELECT</li> } chng {2002 Feb 14 (2.3.2)} {