]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Code to implement CREATE VIEW is in place. A quick smoke test shows that
authordrh <drh@noemail.net>
Sat, 23 Feb 2002 02:32:10 +0000 (02:32 +0000)
committerdrh <drh@noemail.net>
Sat, 23 Feb 2002 02:32:10 +0000 (02:32 +0000)
it works, but there are probably still many bugs. (CVS 387)

FossilOrigin-Name: 39fed2df11382b9855d518502a6c2ca200fa66b8

12 files changed:
manifest
manifest.uuid
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/parse.y
src/select.c
src/sqliteInt.h
src/tokenize.c
src/update.c
src/where.c

index 92008934ab4bcf0cf3c9dd4dce721f1c3e469fc2..2c7f3f96789ac1bcb83d689ad6fd6c7818dfb11a 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Change\sthe\sSQLITE_MASTER\sformat\sto\sversion\s2\sin\spreparation\sfor\sadding\sviews.\s(CVS\s386)
-D 2002-02-21T12:01:27
+C Code\sto\simplement\sCREATE\sVIEW\sis\sin\splace.\s\sA\squick\ssmoke\stest\sshows\sthat\nit\sworks,\sbut\sthere\sare\sprobably\sstill\smany\sbugs.\s(CVS\s387)
+D 2002-02-23T02:32:10
 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
 F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -21,38 +21,38 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c 495275fe14f3b718cf2f691dce979d4c0e1f8e5d
 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
-F src/build.c 4e10d8e61971fe900317d00a98f49dd7ceb27c20
-F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c
-F src/expr.c 7aff65ea0732b07d36925087ad611019103ad69a
+F src/build.c 1da051784be0155ae579d47890db74f0186f9b9f
+F src/delete.c 950d8f9097361419f1963875f9943344b469cf02
+F src/expr.c 6b641c43941094a5d1f7a96657d8a34d07188856
 F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
 F src/hash.h d1ce47900c7325af5e41c4feb4855c4bf2b841e7
-F src/insert.c eae5aa2e9ac68c4d465e71b2ad34bcbb882979cf
+F src/insert.c 164d2d5e943268a8ff0594e1947599e04df0ce11
 F src/main.c abc0732d4caa676ff8337f278b01f1f1b57538f5
 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 b82278917959eefd05bd08c90e07a4fa5917ea51
+F src/parse.y fc460cda6f475beae963c7f9c737cf13f44f3420
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c f0cbfd2d9059e0f33837797ee326fbe964a35f09
+F src/select.c 61d4a739956aaeb124cdf12c34c66e99ae34212c
 F src/shell.c cbf48bf0ca35c4e0d8a7d2a86f7724f52c525cd7
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
-F src/sqliteInt.h 338cd586b9322105080a2a31122446e504ac1fc4
+F src/sqliteInt.h 81dcdf77391471443d53e4b96ac5e78a10e9df4b
 F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
 F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
 F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
 F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
-F src/tokenize.c 777b734f9d06f5a9055b6808e5baec18936a4fd9
-F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103
+F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
+F src/update.c 18971d265b0341574b7e3f73116e7947ddab0997
 F src/util.c f31f3d6198a0d1296a16f5a6ceec423a932cbbf6
 F src/vdbe.c 44832d804e109248e9e2abd40daee5f8ac735450
 F src/vdbe.h 002bb8cf884034bea25a9fe901a9c5e9d29bc045
-F src/where.c f79bc3179379b46b131a67ab10713779368dceee
+F src/where.c 664be01b0ce9ffaecbde609afbd4d8d3e5ed1585
 F test/all.test 7a8a8a7a579ed2bb4d8976d55402f21eacd58049
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
 F test/btree.test bf326f546a666617367a7033fa2c07451bd4f8e1
@@ -125,7 +125,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P ffb00bf36a9a5851ea4a69f9c7dd7ce412553e3b
-R 827e990a5d306ede002baf12f96964e4
+P b2a9807fed544e83002366149b9a363759338c5d
+R e0b567ef590e442398fe19c801c19dbc
 U drh
-Z 93dfb9234ed5554b3f3a0a3a7b0ddf3a
+Z 84f7f5c8e0889cae09918887440ad98b
index c3c6ccb035c6e524e34b09af3d1f13397b858b6c..fa8fb5e21293c472a1a0e245dffe3a078be5ab62 100644 (file)
@@ -1 +1 @@
-b2a9807fed544e83002366149b9a363759338c5d
\ No newline at end of file
+39fed2df11382b9855d518502a6c2ca200fa66b8
\ No newline at end of file
index 3b4599c2d3d782e875b7ac274da30a2fe9321145..1914f07b064a2a7f0ae8831bf6db61d7f2351039 100644 (file)
@@ -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>
@@ -64,75 +64,12 @@ void sqliteExec(Parse *pParse){
   }
 }
 
-/*
-** 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 @@ Table *sqliteFindTable(sqlite *db, char *zName){
 ** 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 @@ void sqliteDeleteTable(sqlite *db, Table *pTable){
   }
   sqliteFree(pTable->zName);
   sqliteFree(pTable->aCol);
+  sqliteSelectDelete(pTable->pSelect);
   sqliteFree(pTable);
 }
 
@@ -859,6 +797,51 @@ void sqliteEndTable(Parse *pParse, Token *pEnd, Select *pSelect){
   }
 }
 
+/*
+** 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 @@ void sqliteCreateIndex(
     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
@@ -1298,55 +1286,6 @@ void sqliteDropIndex(Parse *pParse, Token *pName){
   }
 }
 
-/*
-** 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 @@ void sqliteIdListAddAlias(IdList *pList, Token *pToken){
 }
 
 /*
-** Delete an entire IdList
+** Delete an entire IdList.
 */
 void sqliteIdListDelete(IdList *pList){
   int i;
@@ -1402,8 +1341,13 @@ void sqliteIdListDelete(IdList *pList){
   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);
     }
index 3898fdfcf1b5936dd14ca284382c2fa0c5b680cf..d815a45009979d9c9e9d1e37d666d1f5c21114f9 100644 (file)
 ** 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 @@ void sqliteDeleteFrom(
   ** 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.
index 29c9a8b1c32963dbfe60c2d89350dbc1f462c6d2..40a4a388eb2df0080038dac778e8b335f649c93f 100644 (file)
 ** 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.
 */
@@ -31,6 +94,108 @@ void sqliteExprDelete(Expr *p){
   sqliteFree(p);
 }
 
+/*
+** 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 @@ int sqliteExprResolveIds(
     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 @@ int sqliteExprResolveIds(
 
       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 @@ int sqliteExprResolveIds(
             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 @@ void sqliteExprCode(Parse *pParse, Expr *pExpr){
     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;
index 2efabfd02ad3366714ec4d8533433ff1e5a74fe4..c12c32a70c8274891a1a52aa204b20cfbd904d1e 100644 (file)
@@ -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 @@ void sqliteInsert(
   */
   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
   */
index 7aec2429235a6d101604ff219ca628d305d6d18a..fee981f632318734c2267a76d588fbbb610345f7 100644 (file)
@@ -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 @@ resolvetype(A) ::= REPLACE.                  { A = OE_Replace; }
 //
 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).  {
index 6183275793734792df917dfdb129c9bb30c07ab3..ac2fc5803b0791259b9bbaa084e50458d8c80690 100644 (file)
@@ -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 @@ void sqliteSelectDelete(Select *p){
   sqliteExprDelete(p->pHaving);
   sqliteExprListDelete(p->pOrderBy);
   sqliteSelectDelete(p->pPrior);
+  sqliteFree(p->zSelect);
   sqliteFree(p);
 }
 
@@ -362,6 +363,7 @@ static int fillInColumnList(Parse *pParse, Select *p){
   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 @@ static int fillInColumnList(Parse *pParse, Select *p){
     }
     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 @@ static int fillInColumnList(Parse *pParse, Select *p){
       }
       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 @@ static int matchOrderbyToColumn(
     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 @@ int sqliteSelect(
   */
   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);
index 2eece76f9d032c703f0d5aaf95414b8077c0127b..59d17221b3e1d5d98db82b90fd7d46a627918ccb 100644 (file)
@@ -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 @@ struct Table {
   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 @@ struct WhereInfo {
 /*
 ** 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 @@ struct Select {
   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 sqliteAddPrimaryKey(Parse*, IdList*, int);
 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 @@ int sqliteSelect(Parse*, Select*, int, int);
 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 sqliteWhereEnd(WhereInfo*);
 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 sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
 void sqliteBeginWriteOperation(Parse*);
 void sqliteBeginMultiWriteOperation(Parse*);
 void sqliteEndWriteOperation(Parse*);
+void sqliteExprMoveStrings(Expr*, int);
+void sqliteExprListMoveStrings(ExprList*, int);
+void sqliteSelectMoveStrings(Select*, int);
index cd44b1517e482ce3d202b40fd7613b0c78f0e3e2..a9650d38c9929a0cc6fafbfb70922820b27e850a 100644 (file)
@@ -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 @@ static Keyword aKeywordTable[] = {
   { "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 @@ int sqliteRunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
         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, 
index c7f12888d22b88c61ce3357655e8f21dc77df8fa..a4aa308e40ad77cf06215144437f11b3ec8cc3bb 100644 (file)
@@ -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 @@ void sqliteUpdate(
   ** 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;
index 6c9ca7e78d7fd84242353bfaaffd81bebe280069..e325af807ac194bfda646d3100df897b6abe4ccb 100644 (file)
@@ -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 @@ WhereInfo *sqliteWhereBegin(
     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);