Code to implement CREATE VIEW is in place.  A quick smoke test shows that
it works, but there are probably still many bugs. (CVS 387)

FossilOrigin-Name: 39fed2df11382b9855d518502a6c2ca200fa66b8
diff --git a/src/build.c b/src/build.c
index 3b4599c..1914f07 100644
--- a/src/build.c
+++ b/src/build.c
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.78 2002/02/21 12:01:27 drh Exp $
+** $Id: build.c,v 1.79 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -65,74 +65,11 @@
 }
 
 /*
-** Construct a new expression node and return a pointer to it.  Memory
-** for this node is obtained from sqliteMalloc().  The calling function
-** is responsible for making sure the node eventually gets freed.
-*/
-Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
-  Expr *pNew;
-  pNew = sqliteMalloc( sizeof(Expr) );
-  if( pNew==0 ){
-    sqliteExprDelete(pLeft);
-    sqliteExprDelete(pRight);
-    return 0;
-  }
-  pNew->op = op;
-  pNew->pLeft = pLeft;
-  pNew->pRight = pRight;
-  if( pToken ){
-    pNew->token = *pToken;
-  }else{
-    pNew->token.z = "";
-    pNew->token.n = 0;
-  }
-  if( pLeft && pRight ){
-    sqliteExprSpan(pNew, &pLeft->span, &pRight->span);
-  }else{
-    pNew->span = pNew->token;
-  }
-  return pNew;
-}
-
-/*
-** Set the Expr.token field of the given expression to span all
-** text between the two given tokens.
-*/
-void sqliteExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
-  if( pExpr ){
-    pExpr->span.z = pLeft->z;
-    pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z);
-  }
-}
-
-/*
-** Construct a new expression node for a function with multiple
-** arguments.
-*/
-Expr *sqliteExprFunction(ExprList *pList, Token *pToken){
-  Expr *pNew;
-  pNew = sqliteMalloc( sizeof(Expr) );
-  if( pNew==0 ){
-    sqliteExprListDelete(pList);
-    return 0;
-  }
-  pNew->op = TK_FUNCTION;
-  pNew->pList = pList;
-  if( pToken ){
-    pNew->token = *pToken;
-  }else{
-    pNew->token.z = "";
-    pNew->token.n = 0;
-  }
-  return pNew;
-}
-
-/*
 ** Locate the in-memory structure that describes 
 ** a particular database table given the name
 ** of that table.  Return NULL if not found.
 */
-Table *sqliteFindTable(sqlite *db, char *zName){
+Table *sqliteFindTable(sqlite *db, const char *zName){
   Table *p = sqliteHashFind(&db->tblHash, zName, strlen(zName)+1);
   return p;
 }
@@ -142,7 +79,7 @@
 ** a particular index given the name of that index.
 ** Return NULL if not found.
 */
-Index *sqliteFindIndex(sqlite *db, char *zName){
+Index *sqliteFindIndex(sqlite *db, const char *zName){
   Index *p = sqliteHashFind(&db->idxHash, zName, strlen(zName)+1);
   return p;
 }
@@ -235,6 +172,7 @@
   }
   sqliteFree(pTable->zName);
   sqliteFree(pTable->aCol);
+  sqliteSelectDelete(pTable->pSelect);
   sqliteFree(pTable);
 }
 
@@ -860,6 +798,51 @@
 }
 
 /*
+** The parser calls this routine in order to create a new VIEW
+*/
+void sqliteCreateView(
+  Parse *pParse,     /* The parsing context */
+  Token *pBegin,     /* The CREATE token that begins the statement */
+  Token *pName,      /* The token that holds the name of the view */
+  Select *pSelect    /* A SELECT statement that will become the new view */
+){
+  Token sEnd;
+  Table *pSelTab;
+  Table *p;
+  char *z;
+  int n, offset;
+
+  sqliteStartTable(pParse, pBegin, pName, 0);
+  p = pParse->pNewTable;
+  if( p==0 ) goto create_view_failed;
+  p->pSelect = pSelect;
+  pSelTab = sqliteResultSetOfSelect(pParse, 0, pSelect);
+  if( pSelTab==0 ) goto create_view_failed;
+  assert( p->aCol==0 );
+  p->nCol = pSelTab->nCol;
+  p->aCol = pSelTab->aCol;
+  pSelTab->nCol = 0;
+  pSelTab->aCol = 0;
+  sqliteDeleteTable(0, pSelTab);
+  sEnd = pParse->sLastToken;
+  if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){
+    sEnd.z += sEnd.n;
+  }
+  sEnd.n = 0;
+  n = ((int)sEnd.z) - (int)pBegin->z;
+  z = p->pSelect->zSelect = sqliteStrNDup(pBegin->z, n+1);
+  if( z==0 ) goto create_view_failed;
+  offset = ((int)z) - (int)pBegin->z;
+  sqliteSelectMoveStrings(p->pSelect, offset);
+  sqliteEndTable(pParse, &sEnd, 0);
+  return;
+
+create_view_failed:
+  sqliteSelectDelete(pSelect);
+  return;
+}
+
+/*
 ** Given a token, look up a table with that name.  If not found, leave
 ** an error for the parser to find and return NULL.
 */
@@ -993,6 +976,11 @@
     pParse->nErr++;
     goto exit_create_index;
   }
+  if( pTab->pSelect ){
+    sqliteSetString(&pParse->zErrMsg, "views may not be indexed", 0);
+    pParse->nErr++;
+    goto exit_create_index;
+  }
 
   /* If this index is created while re-reading the schema from sqlite_master
   ** but the table associated with this index is a temporary table, it can
@@ -1299,55 +1287,6 @@
 }
 
 /*
-** Add a new element to the end of an expression list.  If pList is
-** initially NULL, then create a new expression list.
-*/
-ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
-  int i;
-  if( pList==0 ){
-    pList = sqliteMalloc( sizeof(ExprList) );
-    if( pList==0 ){
-      sqliteExprDelete(pExpr);
-      return 0;
-    }
-  }
-  if( (pList->nExpr & 7)==0 ){
-    int n = pList->nExpr + 8;
-    struct ExprList_item *a;
-    a = sqliteRealloc(pList->a, n*sizeof(pList->a[0]));
-    if( a==0 ){
-      sqliteExprDelete(pExpr);
-      return pList;
-    }
-    pList->a = a;
-  }
-  if( pExpr || pName ){
-    i = pList->nExpr++;
-    pList->a[i].pExpr = pExpr;
-    pList->a[i].zName = 0;
-    if( pName ){
-      sqliteSetNString(&pList->a[i].zName, pName->z, pName->n, 0);
-      sqliteDequote(pList->a[i].zName);
-    }
-  }
-  return pList;
-}
-
-/*
-** Delete an entire expression list.
-*/
-void sqliteExprListDelete(ExprList *pList){
-  int i;
-  if( pList==0 ) return;
-  for(i=0; i<pList->nExpr; i++){
-    sqliteExprDelete(pList->a[i].pExpr);
-    sqliteFree(pList->a[i].zName);
-  }
-  sqliteFree(pList->a);
-  sqliteFree(pList);
-}
-
-/*
 ** Append a new element to the given IdList.  Create a new IdList if
 ** need be.
 **
@@ -1394,7 +1333,7 @@
 }
 
 /*
-** Delete an entire IdList
+** Delete an entire IdList.
 */
 void sqliteIdListDelete(IdList *pList){
   int i;
@@ -1402,8 +1341,13 @@
   for(i=0; i<pList->nId; i++){
     sqliteFree(pList->a[i].zName);
     sqliteFree(pList->a[i].zAlias);
-    if( pList->a[i].pSelect ){
-      sqliteFree(pList->a[i].zName);
+
+    /* If the pSelect field is set and is not pointing to the Select
+    ** structure that defines a VIEW, then the Select is for a subquery
+    ** and should be deleted.  Do not delete VIEWs, however.
+    */
+    if( pList->a[i].pSelect && 
+         (pList->a[i].pTab==0 || pList->a[i].pTab->pSelect==0) ){
       sqliteSelectDelete(pList->a[i].pSelect);
       sqliteDeleteTable(0, pList->a[i].pTab);
     }
diff --git a/src/delete.c b/src/delete.c
index 3898fdf..d815a45 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -12,10 +12,59 @@
 ** This file contains C code routines that are called by the parser
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.26 2002/01/31 15:54:22 drh Exp $
+** $Id: delete.c,v 1.27 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 
+
+/*
+** Given a table name, find the corresponding table and make sure the
+** table is writeable.  Generate an error and return NULL if not.  If
+** everything checks out, return a pointer to the Table structure.
+*/
+Table *sqliteTableNameToTable(Parse *pParse, const char *zTab){
+  Table *pTab;
+  pTab = sqliteFindTable(pParse->db, zTab);
+  if( pTab==0 ){
+    sqliteSetString(&pParse->zErrMsg, "no such table: ", zTab, 0);
+    pParse->nErr++;
+    return 0;
+  }
+  if( pTab->readOnly || pTab->pSelect ){
+    sqliteSetString(&pParse->zErrMsg, 
+      pTab->pSelect ? "view " : "table ",
+      zTab,
+      " may not be modified", 0);
+    pParse->nErr++;
+    return 0;      
+  }
+  return pTab;
+}
+
+/*
+** Given a table name, check to make sure the table exists, is writable
+** and is not a view.  If everything is OK, construct an IdList holding
+** the table and return a pointer to the IdList.  The calling function
+** is responsible for freeing the IdList when it has finished with it.
+** If there is an error, leave a message on pParse->zErrMsg and return
+** NULL.
+*/
+IdList *sqliteTableTokenToIdList(Parse *pParse, Token *pTableName){
+  Table *pTab;
+  IdList *pTabList;
+
+  pTabList = sqliteIdListAppend(0, pTableName);
+  if( pTabList==0 ) return 0;
+  assert( pTabList->nId==1 );
+  pTab = sqliteTableNameToTable(pParse, pTabList->a[0].zName);
+  if( pTab==0 ){
+    sqliteIdListDelete(pTabList);
+    return 0;
+  }
+  pTabList->a[0].pTab = pTab;
+  return pTabList;
+}
+
 /*
 ** Process a DELETE FROM statement.
 */
@@ -47,23 +96,8 @@
   ** will be calling are designed to work with multiple tables and expect
   ** an IdList* parameter instead of just a Table* parameger.
   */
-  pTabList = sqliteIdListAppend(0, pTableName);
+  pTabList = sqliteTableTokenToIdList(pParse, pTableName);
   if( pTabList==0 ) goto delete_from_cleanup;
-  for(i=0; i<pTabList->nId; i++){
-    pTabList->a[i].pTab = sqliteFindTable(db, pTabList->a[i].zName);
-    if( pTabList->a[i].pTab==0 ){
-      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-         pTabList->a[i].zName, 0);
-      pParse->nErr++;
-      goto delete_from_cleanup;
-    }
-    if( pTabList->a[i].pTab->readOnly ){
-      sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName,
-        " may not be modified", 0);
-      pParse->nErr++;
-      goto delete_from_cleanup;
-    }
-  }
   pTab = pTabList->a[0].pTab;
 
   /* Resolve the column names in all the expressions.
diff --git a/src/expr.c b/src/expr.c
index 29c9a8b..40a4a38 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -12,12 +12,75 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.41 2002/02/14 21:42:51 drh Exp $
+** $Id: expr.c,v 1.42 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 
 
 /*
+** Construct a new expression node and return a pointer to it.  Memory
+** for this node is obtained from sqliteMalloc().  The calling function
+** is responsible for making sure the node eventually gets freed.
+*/
+Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
+  Expr *pNew;
+  pNew = sqliteMalloc( sizeof(Expr) );
+  if( pNew==0 ){
+    sqliteExprDelete(pLeft);
+    sqliteExprDelete(pRight);
+    return 0;
+  }
+  pNew->op = op;
+  pNew->pLeft = pLeft;
+  pNew->pRight = pRight;
+  if( pToken ){
+    pNew->token = *pToken;
+  }else{
+    pNew->token.z = 0;
+    pNew->token.n = 0;
+  }
+  if( pLeft && pRight ){
+    sqliteExprSpan(pNew, &pLeft->span, &pRight->span);
+  }else{
+    pNew->span = pNew->token;
+  }
+  return pNew;
+}
+
+/*
+** Set the Expr.token field of the given expression to span all
+** text between the two given tokens.
+*/
+void sqliteExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
+  if( pExpr ){
+    pExpr->span.z = pLeft->z;
+    pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z);
+  }
+}
+
+/*
+** Construct a new expression node for a function with multiple
+** arguments.
+*/
+Expr *sqliteExprFunction(ExprList *pList, Token *pToken){
+  Expr *pNew;
+  pNew = sqliteMalloc( sizeof(Expr) );
+  if( pNew==0 ){
+    sqliteExprListDelete(pList);
+    return 0;
+  }
+  pNew->op = TK_FUNCTION;
+  pNew->pList = pList;
+  if( pToken ){
+    pNew->token = *pToken;
+  }else{
+    pNew->token.z = 0;
+    pNew->token.n = 0;
+  }
+  return pNew;
+}
+
+/*
 ** Recursively delete an expression tree.
 */
 void sqliteExprDelete(Expr *p){
@@ -32,6 +95,108 @@
 }
 
 /*
+** The following group of functions are used to translate the string
+** pointers of tokens in expression from one buffer to another.
+**
+** Normally, the Expr.token.z and Expr.span.z fields point into the
+** original input buffer of an SQL statement.  This is usually OK
+** since the SQL statement is executed and the expression is deleted
+** before the input buffer is freed.  Making the tokens point to the
+** original input buffer saves many calls to malloc() and thus helps
+** the library to run faster. 
+**
+** But sometimes we need an expression to persist past the time when
+** the input buffer is freed.  (Example: The SELECT clause of a
+** CREATE VIEW statement contains expressions that must persist for
+** the life of the view.)  When that happens we have to make a
+** persistent copy of the input buffer and translate the Expr.token.z
+** and Expr.span.z fields to point to the copy rather than the 
+** original input buffer.  The following group of routines to that
+** translation.
+**
+** The "offset" parameter is the distance from the original input buffer
+** to the persistent copy.  These routines recursively walk the entire
+** expression tree and shift all tokens by "offset" amount.
+**
+** The work of figuring out the appropriate "offset" and making the
+** presistent copy of the input buffer is done by the calling routine.
+*/
+void sqliteExprMoveStrings(Expr *p, int offset){
+  if( p==0 ) return;
+  if( p->token.z ) p->token.z += offset;
+  if( p->span.z ) p->span.z += offset;
+  if( p->pLeft ) sqliteExprMoveStrings(p->pLeft, offset);
+  if( p->pRight ) sqliteExprMoveStrings(p->pRight, offset);
+  if( p->pList ) sqliteExprListMoveStrings(p->pList, offset);
+  if( p->pSelect ) sqliteSelectMoveStrings(p->pSelect, offset);
+}
+void sqliteExprListMoveStrings(ExprList *pList, int offset){
+  int i;
+  if( pList==0 ) return;
+  for(i=0; i<pList->nExpr; i++){
+    sqliteExprMoveStrings(pList->a[i].pExpr, offset);
+  }
+}
+void sqliteSelectMoveStrings(Select *pSelect, int offset){
+  if( pSelect==0 ) return;
+  sqliteExprListMoveStrings(pSelect->pEList, offset);
+  sqliteExprMoveStrings(pSelect->pWhere, offset);
+  sqliteExprListMoveStrings(pSelect->pGroupBy, offset);
+  sqliteExprMoveStrings(pSelect->pHaving, offset);
+  sqliteExprListMoveStrings(pSelect->pOrderBy, offset);
+  sqliteSelectMoveStrings(pSelect->pPrior, offset);
+}
+
+/*
+** Add a new element to the end of an expression list.  If pList is
+** initially NULL, then create a new expression list.
+*/
+ExprList *sqliteExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
+  int i;
+  if( pList==0 ){
+    pList = sqliteMalloc( sizeof(ExprList) );
+    if( pList==0 ){
+      sqliteExprDelete(pExpr);
+      return 0;
+    }
+  }
+  if( (pList->nExpr & 7)==0 ){
+    int n = pList->nExpr + 8;
+    struct ExprList_item *a;
+    a = sqliteRealloc(pList->a, n*sizeof(pList->a[0]));
+    if( a==0 ){
+      sqliteExprDelete(pExpr);
+      return pList;
+    }
+    pList->a = a;
+  }
+  if( pExpr || pName ){
+    i = pList->nExpr++;
+    pList->a[i].pExpr = pExpr;
+    pList->a[i].zName = 0;
+    if( pName ){
+      sqliteSetNString(&pList->a[i].zName, pName->z, pName->n, 0);
+      sqliteDequote(pList->a[i].zName);
+    }
+  }
+  return pList;
+}
+
+/*
+** Delete an entire expression list.
+*/
+void sqliteExprListDelete(ExprList *pList){
+  int i;
+  if( pList==0 ) return;
+  for(i=0; i<pList->nExpr; i++){
+    sqliteExprDelete(pList->a[i].pExpr);
+    sqliteFree(pList->a[i].zName);
+  }
+  sqliteFree(pList->a);
+  sqliteFree(pList);
+}
+
+/*
 ** Walk an expression tree.  Return 1 if the expression is constant
 ** and 0 if it involves variables.
 */
@@ -156,7 +321,9 @@
     case TK_ID: {
       int cnt = 0;      /* Number of matches */
       int i;            /* Loop counter */
-      char *z = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
+      char *z;
+      assert( pExpr->token.z );
+      z = sqliteStrNDup(pExpr->token.z, pExpr->token.n);
       sqliteDequote(z);
       if( z==0 ) return 1;
       for(i=0; i<pTabList->nId; i++){
@@ -221,8 +388,8 @@
 
       pLeft = pExpr->pLeft;
       pRight = pExpr->pRight;
-      assert( pLeft && pLeft->op==TK_ID );
-      assert( pRight && pRight->op==TK_ID );
+      assert( pLeft && pLeft->op==TK_ID && pLeft->token.z );
+      assert( pRight && pRight->op==TK_ID && pRight->token.z );
       zLeft = sqliteStrNDup(pLeft->token.z, pLeft->token.n);
       zRight = sqliteStrNDup(pRight->token.z, pRight->token.n);
       if( zLeft==0 || zRight==0 ){
@@ -327,6 +494,7 @@
             case TK_INTEGER:
             case TK_STRING: {
               int addr = sqliteVdbeAddOp(v, OP_SetInsert, iSet, 0);
+              assert( pE2->token.z );
               sqliteVdbeChangeP3(v, addr, pE2->token.z, pE2->token.n);
               sqliteVdbeDequoteP3(v, addr);
               break;
@@ -577,11 +745,13 @@
     case TK_FLOAT:
     case TK_INTEGER: {
       sqliteVdbeAddOp(v, OP_String, 0, 0);
+      assert( pExpr->token.z );
       sqliteVdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n);
       break;
     }
     case TK_STRING: {
       int addr = sqliteVdbeAddOp(v, OP_String, 0, 0);
+      assert( pExpr->token.z );
       sqliteVdbeChangeP3(v, addr, pExpr->token.z, pExpr->token.n);
       sqliteVdbeDequoteP3(v, addr);
       break;
diff --git a/src/insert.c b/src/insert.c
index 2efabfd..c12c32a 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.44 2002/02/19 13:39:22 drh Exp $
+** $Id: insert.c,v 1.45 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -60,20 +60,9 @@
   */
   zTab = sqliteTableNameFromToken(pTableName);
   if( zTab==0 ) goto insert_cleanup;
-  pTab = sqliteFindTable(db, zTab);
+  pTab = sqliteTableNameToTable(pParse, zTab);
   sqliteFree(zTab);
-  if( pTab==0 ){
-    sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0, 
-        pTableName->z, pTableName->n, 0);
-    pParse->nErr++;
-    goto insert_cleanup;
-  }
-  if( pTab->readOnly ){
-    sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
-        " may not be modified", 0);
-    pParse->nErr++;
-    goto insert_cleanup;
-  }
+  if( pTab==0 ) goto insert_cleanup;
 
   /* Allocate a VDBE
   */
diff --git a/src/parse.y b/src/parse.y
index 7aec242..fee981f 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.52 2002/02/18 22:49:59 drh Exp $
+** @(#) $Id: parse.y,v 1.53 2002/02/23 02:32:10 drh Exp $
 */
 %token_prefix TK_
 %token_type {Token}
@@ -190,6 +190,15 @@
 //
 cmd ::= DROP TABLE ids(X).          {sqliteDropTable(pParse,&X);}
 
+///////////////////// The CREATE VIEW statement /////////////////////////////
+//
+cmd ::= CREATE(X) VIEW ids(Y) AS select(S). {
+  sqliteCreateView(pParse, &X, &Y, S);
+}
+cmd ::= DROP VIEW ids(X). {
+  sqliteDropTable(pParse, &X);
+}
+
 //////////////////////// The SELECT statement /////////////////////////////////
 //
 cmd ::= select(X).  {
diff --git a/src/select.c b/src/select.c
index 6183275..ac2fc58 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.64 2002/02/21 12:01:27 drh Exp $
+** $Id: select.c,v 1.65 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -67,6 +67,7 @@
   sqliteExprDelete(p->pHaving);
   sqliteExprListDelete(p->pOrderBy);
   sqliteSelectDelete(p->pPrior);
+  sqliteFree(p->zSelect);
   sqliteFree(p);
 }
 
@@ -362,6 +363,7 @@
   int i, j, k;
   IdList *pTabList;
   ExprList *pEList;
+  Table *pTab;
 
   if( p==0 || p->pSrc==0 ) return 1;
   pTabList = p->pSrc;
@@ -376,7 +378,6 @@
     }
     if( pTabList->a[i].zName==0 ){
       /* 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,
@@ -386,14 +387,18 @@
       }
       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 ){
+      /* An ordinary table or view name in the FROM clause */
+      pTabList->a[i].pTab = pTab = 
+        sqliteFindTable(pParse->db, pTabList->a[i].zName);
+      if( pTab==0 ){
         sqliteSetString(&pParse->zErrMsg, "no such table: ", 
            pTabList->a[i].zName, 0);
         pParse->nErr++;
         return 1;
       }
+      if( pTab->pSelect ){
+        pTabList->a[i].pSelect = pTab->pSelect;
+      }
     }
   }
 
@@ -494,8 +499,10 @@
     if( pOrderBy->a[i].done ) continue;
     for(j=0; j<pEList->nExpr; j++){
       if( pEList->a[j].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){
-        char *zName = pEList->a[j].zName;
-        char *zLabel = sqliteStrNDup(pE->token.z, pE->token.n);
+        char *zName, *zLabel;
+        zName = pEList->a[j].zName;
+        assert( pE->token.z );
+        zLabel = sqliteStrNDup(pE->token.z, pE->token.n);
         sqliteDequote(zLabel);
         if( sqliteStrICmp(zName, zLabel)==0 ){ 
           match = 1; 
@@ -1036,9 +1043,7 @@
   */
   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 );
+    if( pTabList->a[i].pSelect==0 ) continue;
     oldNTab = pParse->nTab;
     pParse->nTab += i+1;
     sqliteVdbeAddOp(v, OP_OpenTemp, oldNTab+i, 0);
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 2eece76..59d1722 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.89 2002/02/21 12:01:27 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.90 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -250,6 +250,7 @@
   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;        /* Root BTree node for this table (see note above) */
+  Select *pSelect; /* NULL for tables.  Points to definition if a view. */
   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 */
@@ -442,6 +443,14 @@
 /*
 ** An instance of the following structure contains all information
 ** needed to generate code for a single SELECT statement.
+**
+** The zSelect field is used when the Select structure must be persistent.
+** Normally, the expression tree points to tokens in the original input
+** string that encodes the select.  But if the Select structure must live
+** longer than its input string (for example when it is used to describe
+** a VIEW) we have to make a copy of the input string so that the nodes
+** of the expression tree will have something to point to.  zSelect is used
+** to hold that copy.
 */
 struct Select {
   int isDistinct;        /* True if the DISTINCT keyword is present */
@@ -454,6 +463,7 @@
   int op;                /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
   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 */
 };
 
 /*
@@ -570,6 +580,7 @@
 void sqliteAddColumnType(Parse*,Token*,Token*);
 void sqliteAddDefaultValue(Parse*,Token*,int);
 void sqliteEndTable(Parse*,Token*,Select*);
+void sqliteCreateView(Parse*,Token*,Token*,Select*);
 void sqliteDropTable(Parse*, Token*);
 void sqliteDeleteTable(sqlite*, Table*);
 void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*, int);
@@ -582,6 +593,8 @@
 Select *sqliteSelectNew(ExprList*,IdList*,Expr*,ExprList*,Expr*,ExprList*,
                         int,int,int);
 void sqliteSelectDelete(Select*);
+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);
@@ -589,8 +602,8 @@
 void sqliteExprCode(Parse*, Expr*);
 void sqliteExprIfTrue(Parse*, Expr*, int);
 void sqliteExprIfFalse(Parse*, Expr*, int);
-Table *sqliteFindTable(sqlite*,char*);
-Index *sqliteFindIndex(sqlite*,char*);
+Table *sqliteFindTable(sqlite*,const char*);
+Index *sqliteFindIndex(sqlite*,const char*);
 void sqliteUnlinkAndDeleteIndex(sqlite*,Index*);
 void sqliteCopy(Parse*, Token*, Token*, Token*, int);
 void sqliteVacuum(Parse*, Token*);
@@ -618,3 +631,6 @@
 void sqliteBeginWriteOperation(Parse*);
 void sqliteBeginMultiWriteOperation(Parse*);
 void sqliteEndWriteOperation(Parse*);
+void sqliteExprMoveStrings(Expr*, int);
+void sqliteExprListMoveStrings(ExprList*, int);
+void sqliteSelectMoveStrings(Select*, int);
diff --git a/src/tokenize.c b/src/tokenize.c
index cd44b15..a9650d3 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -15,7 +15,7 @@
 ** individual tokens and sends those tokens one-by-one over to the
 ** parser for analysis.
 **
-** $Id: tokenize.c,v 1.37 2002/02/21 12:01:27 drh Exp $
+** $Id: tokenize.c,v 1.38 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -101,6 +101,7 @@
   { "USING",             0, TK_USING,            0 },
   { "VACUUM",            0, TK_VACUUM,           0 },
   { "VALUES",            0, TK_VALUES,           0 },
+  { "VIEW",              0, TK_VIEW,             0 },
   { "WHERE",             0, TK_WHERE,            0 },
 };
 
@@ -419,7 +420,7 @@
         break;
     }
   }
-  if( nErr==0 && (db->flags & SQLITE_Interrupt)==0 ){
+  if( zSql[i]==0 ){
     sqliteParser(pEngine, 0, pParse->sLastToken, pParse);
     if( pParse->zErrMsg && pParse->sErrToken.z ){
        sqliteSetNString(pzErrMsg, "near \"", -1, 
diff --git a/src/update.c b/src/update.c
index c7f1288..a4aa308 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.33 2002/02/02 18:49:21 drh Exp $
+** $Id: update.c,v 1.34 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -55,23 +55,8 @@
   ** will be calling are designed to work with multiple tables and expect
   ** an IdList* parameter instead of just a Table* parameter.
   */
-  pTabList = sqliteIdListAppend(0, pTableName);
+  pTabList = sqliteTableTokenToIdList(pParse, pTableName);
   if( pTabList==0 ) goto update_cleanup;
-  for(i=0; i<pTabList->nId; i++){
-    pTabList->a[i].pTab = sqliteFindTable(db, pTabList->a[i].zName);
-    if( pTabList->a[i].pTab==0 ){
-      sqliteSetString(&pParse->zErrMsg, "no such table: ", 
-         pTabList->a[i].zName, 0);
-      pParse->nErr++;
-      goto update_cleanup;
-    }
-    if( pTabList->a[i].pTab->readOnly ){
-      sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName,
-        " may not be modified", 0);
-      pParse->nErr++;
-      goto update_cleanup;
-    }
-  }
   pTab = pTabList->a[0].pTab;
   aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
   if( aXRef==0 ) goto update_cleanup;
diff --git a/src/where.c b/src/where.c
index 6c9ca7e..e325af8 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.36 2002/02/18 22:49:59 drh Exp $
+** $Id: where.c,v 1.37 2002/02/23 02:32:10 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -399,7 +399,7 @@
     Table *pTab;
 
     pTab = pTabList->a[i].pTab;
-    if( pTab->isTransient ) continue;
+    if( pTab->isTransient || pTab->pSelect ) continue;
     openOp = pTab->isTemp ? OP_OpenAux : OP_Open;
     sqliteVdbeAddOp(v, openOp, base+i, pTab->tnum);
     sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);