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)} {