]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
VIEWs are bound to tables when they are used, not when they are first
authordrh <drh@noemail.net>
Sun, 3 Mar 2002 18:59:40 +0000 (18:59 +0000)
committerdrh <drh@noemail.net>
Sun, 3 Mar 2002 18:59:40 +0000 (18:59 +0000)
entered.  This works around the problem of what to do if a table is deleted
that a view refers to. (CVS 415)

FossilOrigin-Name: 6121e5ab9328c90c64d40ade3de73ad11d4aaf4e

manifest
manifest.uuid
src/build.c
src/delete.c
src/expr.c
src/insert.c
src/select.c
src/sqliteInt.h
src/update.c
test/view.test

index a7dd36dd4ff61ea2e9dca279ccc47f68d1049f52..fb765b93327f51743b4fab707a1fa4047a92cbc1 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\smemory\sleak\sin\sexpression\sprocessing.\s(CVS\s414)
-D 2002-03-03T03:42:31
+C VIEWs\sare\sbound\sto\stables\swhen\sthey\sare\sused,\snot\swhen\sthey\sare\sfirst\nentered.\s\sThis\sworks\saround\sthe\sproblem\sof\swhat\sto\sdo\sif\sa\stable\sis\sdeleted\nthat\sa\sview\srefers\sto.\s(CVS\s415)
+D 2002-03-03T18:59:40
 F Makefile.in 50f1b3351df109b5774771350d8c1b8d3640130d
 F Makefile.template 89e373b2dad0321df00400fa968dc14b61a03296
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -21,13 +21,13 @@ F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
 F src/btree.c e732bb03715f326a25e0b6fea2e778e063ec3893
 F src/btree.h 8abeabfe6e0b1a990b64fa457592a6482f6674f3
-F src/build.c 2f6d3136e6b824b2b446c54db2d2be5703033203
-F src/delete.c bf569eeb66dc851966b5681e5154d5fe2aee92c2
-F src/expr.c 178248fd4815bbd46c266f87ff0a52a96dff23a3
+F src/build.c 7c65eeab9644c6699cc20a4e93fadd24513e5d72
+F src/delete.c 577da499162291c1855f0b304b211bffcf9da945
+F src/expr.c e12ca550536c0bae7a3acef49dfa3068fe5f0900
 F src/func.c 5b4d9707b0c8f463824c1f04547b524cba24bf7b
 F src/hash.c cc259475e358baaf299b00a2c7370f2b03dda892
 F src/hash.h dca065dda89d4575f3176e75e9a3dc0f4b4fb8b9
-F src/insert.c 4fb3428f591afe106f6e60029a61f87fa0e79920
+F src/insert.c 42bfd145efd428d7e5f200dd49ea0b816fc30d79
 F src/main.c 5651146585ae613e759fcf372ee064e4940c2463
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c f6bc9b7ab530346bb7fef2ed39f2f1f214bc14ea
@@ -37,11 +37,11 @@ F src/pager.h feb18aab2f6dea439393f23a382699b9b1053c32
 F src/parse.y d62960cdee2d2e7821f277d2fe63d823c86602ba
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 68d4ee84e6b6202a15fff60e0eaaf20983525d54
+F src/select.c 49c78aa0c96dda036846937b516658536db98b56
 F src/shell.c 9f8249ca5b8f8aad40becd778c151b58c0d6109e
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in a9b5772604265f98f3120573ef29e37b9d917216
-F src/sqliteInt.h cd2c2fcc50950ab384de8e09fa818fd329288148
+F src/sqliteInt.h 9018cf8cd1e469d7b3fc0c6a7afaeaf765043172
 F src/table.c 203a09d5d0009eeeb1f670370d52b4ce163a3b52
 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
 F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
@@ -49,7 +49,7 @@ F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
 F src/test3.c 4e52fff8b01f08bd202f7633feda5639b7ba2b5e
 F src/threadtest.c 81f0598e0f031c1bd506af337fdc1b7e8dff263f
 F src/tokenize.c 4b5d30590a744b9bb5605a92d1f620ab2e7e75af
-F src/update.c 943b821901efb796dc82d91653621aeeb48d223b
+F src/update.c 7dd714a6a7fa47f849ebb36b6d915974d6c6accb
 F src/util.c 00a35b421c92ae0d7cfa51bd87f7d4995f464d19
 F src/vdbe.c 4989cd3e6f4f699c9b08cd1980d1b588cede5c1f
 F src/vdbe.h f9be1f6e9a336c3ff4d14ea7489ee976e07460cc
@@ -99,7 +99,7 @@ F test/trans.test 9e49495c06b1c41f889bf4f0fb195a015b126de0
 F test/unique.test 07776624b82221a80c8b4138ce0dd8b0853bb3ea
 F test/update.test 3cf1ca0565f678063c2dfa9a7948d2d66ae1a778
 F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
-F test/view.test 4619ebede587102e577e19ce52e9852ec8293cbc
+F test/view.test 56802271e3098cc719b0b2ef7fe41c5275080287
 F test/where.test 032d581c3de4893eba33b569e581c46b941bb02a
 F tool/lemon.c a26214e008a7351c0c9fc57c5aab44b403c36c42
 F tool/lempar.c 2ff255186fffb38a43a9f7b010e87eee6308edcc
@@ -127,7 +127,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P e1d93c5923195507642d882fff8cd85c454f69ee
-R ff7bf4f99d51069c40ce44488417e03c
+P dfe431c9b70bc3a1bf5148826edce0846737e66b
+R d212405b74c1101eea18c187f0e9c551
 U drh
-Z 29e2db61809076cbb2296b019a75fdb3
+Z 14456ce632fb768365261ac0c9859518
index ef63c1610c061413ed673a576b1145642fc3aaed..9874b7d6311ce97e38ea1a0a8487f50eb86f9eaa 100644 (file)
@@ -1 +1 @@
-dfe431c9b70bc3a1bf5148826edce0846737e66b
\ No newline at end of file
+6121e5ab9328c90c64d40ade3de73ad11d4aaf4e
\ No newline at end of file
index b5b6579f53d989cd142763c897b5fb3212480b66..250872fb374b76ddd1918e33baa271d41e86fd2a 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.81 2002/03/02 17:04:08 drh Exp $
+** $Id: build.c,v 1.82 2002/03/03 18:59:40 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -807,24 +807,22 @@ void sqliteCreateView(
   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;
+  if( p==0 ){
+    sqliteSelectDelete(pSelect);
+    return;
+  }
   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);
-  sqliteSelectUnbind(pSelect);
+  if( !pParse->initFlag ){
+    if( sqliteViewGetColumnNames(pParse, p) ){
+      return;
+    }
+  }
   sEnd = pParse->sLastToken;
   if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){
     sEnd.z += sEnd.n;
@@ -832,15 +830,117 @@ void sqliteCreateView(
   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);
+  if( z ){
+    offset = ((int)z) - (int)pBegin->z;
+    sqliteSelectMoveStrings(p->pSelect, offset);
+    sqliteEndTable(pParse, &sEnd, 0);
+  }
   return;
+}
 
-create_view_failed:
-  sqliteSelectDelete(pSelect);
-  return;
+/*
+** The Table structure pTable is really a VIEW.  Fill in the names of
+** the columns of the view in the pTable structure.  Return the number
+** of errors.  If an error is seen leave an error message in pPare->zErrMsg.
+*/
+int sqliteViewGetColumnNames(Parse *pParse, Table *pTable){
+  ExprList *pEList;
+  Select *pSel;
+  Table *pSelTab;
+  int nErr = 0;
+
+  assert( pTable );
+
+  /* A positive nCol means the columns names for this view are
+  ** already known.
+  */
+  if( pTable->nCol>0 ) return 0;
+
+  /* A negative nCol is a special marker meaning that we are currently
+  ** trying to compute the column names.  If we enter this routine with
+  ** a negative nCol, it means two or more views form a loop, like this:
+  **
+  **     CREATE VIEW one AS SELECT * FROM two;
+  **     CREATE VIEW two AS SELECT * FROM one;
+  */
+  if( pTable->nCol<0 ){
+    sqliteSetString(&pParse->zErrMsg, "view ", pTable->zName,
+         " is circularly defined", 0);
+    pParse->nErr++;
+    return 1;
+  }
+
+  /* If we get this far, it means we need to compute the table names.
+  */
+  assert( pTable->pSelect ); /* If nCol==0, then pTable must be a VIEW */
+  pSel = pTable->pSelect;
+
+  /* Note that the call to sqliteResultSetOfSelect() will expand any
+  ** "*" elements in this list.  But we will need to restore the list
+  ** back to its original configuration afterwards, so we save a copy of
+  ** the original in pEList.
+  */
+  pEList = pSel->pEList;
+  pSel->pEList = sqliteExprListDup(pEList);
+  if( pSel->pEList==0 ){
+    pSel->pEList = pEList;
+    return 1;  /* Malloc failed */
+  }
+  pTable->nCol = -1;
+  pSelTab = sqliteResultSetOfSelect(pParse, 0, pSel);
+  if( pSelTab ){
+    assert( pTable->aCol==0 );
+    pTable->nCol = pSelTab->nCol;
+    pTable->aCol = pSelTab->aCol;
+    pSelTab->nCol = 0;
+    pSelTab->aCol = 0;
+    sqliteDeleteTable(0, pSelTab);
+    pParse->db->flags |= SQLITE_UnresetViews;
+  }else{
+    pTable->nCol = 0;
+    nErr++;
+  }
+  sqliteSelectUnbind(pSel);
+  sqliteExprListDelete(pSel->pEList);
+  pSel->pEList = pEList;
+  return nErr;  
+}
+
+/*
+** Clear the column names from the VIEW pTable.
+**
+** This routine is called whenever any other table or view is modified.
+** The view passed into this routine might depend directly or indirectly
+** on the modified or deleted table so we need to clear the old column
+** names so that they will be recomputed.
+*/
+static void sqliteViewResetColumnNames(Table *pTable){
+  int i;
+  if( pTable==0 || pTable->pSelect==0 ) return;
+  if( pTable->nCol==0 ) return;
+  for(i=0; i<pTable->nCol; i++){
+    sqliteFree(pTable->aCol[i].zName);
+    sqliteFree(pTable->aCol[i].zDflt);
+    sqliteFree(pTable->aCol[i].zType);
+  }
+  sqliteFree(pTable->aCol);
+  pTable->aCol = 0;
+  pTable->nCol = 0;
+}
+
+/*
+** Clear the column names from every VIEW.
+*/
+void sqliteViewResetAll(sqlite *db){
+  HashElem *i;
+  if( (db->flags & SQLITE_UnresetViews)==0 ) return;
+  for(i=sqliteHashFirst(&db->tblHash); i; i=sqliteHashNext(i)){
+    Table *pTab = sqliteHashData(i);
+    if( pTab->pSelect ){
+      sqliteViewResetColumnNames(pTab);
+    }
+  }
+  db->flags &= ~SQLITE_UnresetViews;
 }
 
 /*
@@ -927,6 +1027,7 @@ void sqliteDropTable(Parse *pParse, Token *pName){
     sqlitePendingDropTable(db, pTable);
     db->flags |= SQLITE_InternChanges;
   }
+  sqliteViewResetAll(db);
 }
 
 /*
@@ -1674,6 +1775,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
       };
       int i;
       sqliteVdbeAddOpList(v, ArraySize(tableInfoPreface), tableInfoPreface);
+      sqliteViewGetColumnNames(pParse, pTab);
       for(i=0; i<pTab->nCol; i++){
         sqliteVdbeAddOp(v, OP_Integer, i, 0);
         sqliteVdbeAddOp(v, OP_String, 0, 0);
@@ -1710,6 +1812,7 @@ void sqlitePragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
         sqliteVdbeAddOp(v, OP_Integer, i, 0);
         sqliteVdbeAddOp(v, OP_Integer, cnum, 0);
         sqliteVdbeAddOp(v, OP_String, 0, 0);
+        assert( pTab->nCol>cnum );
         sqliteVdbeChangeP3(v, -1, pTab->aCol[cnum].zName, P3_STATIC);
         sqliteVdbeAddOp(v, OP_Callback, 3, 0);
       }
index 2d5771a4e4aeabfa689027b0b505ecdb71361465..03df82ea2e3d323e8d91a2cec6d27a4be9ddbfbe 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle DELETE FROM statements.
 **
-** $Id: delete.c,v 1.28 2002/03/02 17:04:08 drh Exp $
+** $Id: delete.c,v 1.29 2002/03/03 18:59:40 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -98,7 +98,9 @@ void sqliteDeleteFrom(
   */
   pTabList = sqliteTableTokenToIdList(pParse, pTableName);
   if( pTabList==0 ) goto delete_from_cleanup;
+  assert( pTabList->nId==1 );
   pTab = pTabList->a[0].pTab;
+  assert( pTab->pSelect==0 );  /* This table is not a view */
 
   /* Resolve the column names in all the expressions.
   */
index d487188232251ab76f7f1bd34a19182ce02d44c2..9c16889dbd0648e9e70eca6d85f794c3c54fe042 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains routines used for analyzing expressions and
 ** for generating VDBE code that evaluates expressions in SQLite.
 **
-** $Id: expr.c,v 1.53 2002/03/03 03:42:31 drh Exp $
+** $Id: expr.c,v 1.54 2002/03/03 18:59:41 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -384,6 +384,7 @@ int sqliteExprResolveIds(
         int j;
         Table *pTab = pTabList->a[i].pTab;
         if( pTab==0 ) continue;
+        assert( pTab->nCol>0 );
         for(j=0; j<pTab->nCol; j++){
           if( sqliteStrICmp(pTab->aCol[j].zName, z)==0 ){
             cnt++;
@@ -459,6 +460,7 @@ int sqliteExprResolveIds(
         char *zTab;
         Table *pTab = pTabList->a[i].pTab;
         if( pTab==0 ) continue;
+        assert( pTab->nCol>0 );
         if( pTabList->a[i].zAlias ){
           zTab = pTabList->a[i].zAlias;
         }else{
index b7617289e5ca480734d8ce6e12651c83c03f8063..0c5aaba0a2d8767f97c12db21ec1b9de064bc8ac 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.46 2002/03/02 17:04:08 drh Exp $
+** $Id: insert.c,v 1.47 2002/03/03 18:59:41 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -63,6 +63,7 @@ void sqliteInsert(
   pTab = sqliteTableNameToTable(pParse, zTab);
   sqliteFree(zTab);
   if( pTab==0 ) goto insert_cleanup;
+  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
 
   /* Allocate a VDBE
   */
@@ -392,6 +393,7 @@ void sqliteGenerateConstraintChecks(
 
   v = sqliteGetVdbe(pParse);
   assert( v!=0 );
+  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
   nCol = pTab->nCol;
 
   /* Test all NOT NULL constraints.
@@ -553,6 +555,7 @@ void sqliteCompleteInsertion(
 
   v = sqliteGetVdbe(pParse);
   assert( v!=0 );
+  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
   for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
   for(i=nIdx-1; i>=0; i--){
     if( aIdxUsed && aIdxUsed[i]==0 ) continue;
index 305c3e39a2f1b990668dbbe71754527e1c988b41..280d99ebc8a157482c17c8a2c30052c08f651550 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.73 2002/03/03 03:03:53 drh Exp $
+** $Id: select.c,v 1.74 2002/03/03 18:59:41 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -327,6 +327,7 @@ Table *sqliteResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
   pTab->zName = zTabName ? sqliteStrDup(zTabName) : 0;
   pEList = pSelect->pEList;
   pTab->nCol = pEList->nExpr;
+  assert( pTab->nCol>0 );
   pTab->aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol );
   for(i=0; i<pTab->nCol; i++){
     Expr *p;
@@ -400,6 +401,9 @@ static int fillInColumnList(Parse *pParse, Select *p){
         return 1;
       }
       if( pTab->pSelect ){
+        if( sqliteViewGetColumnNames(pParse, pTab) ){
+          return 1;
+        }
         pTabList->a[i].pSelect = sqliteSelectDup(pTab->pSelect);
       }
     }
index 28b106e84e330fbed85d7a4a60ec5bf61a9694b8..cdc7a0ce0836a87ad634124e75cce2b6c428660c 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.99 2002/03/03 03:03:53 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.100 2002/03/03 18:59:41 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -183,6 +183,8 @@ struct sqlite {
 #define SQLITE_NullCallback   0x00000080  /* Invoke the callback once if the */
                                           /*   result set is empty */
 #define SQLITE_ResultDetails  0x00000100  /* Details added to result set */
+#define SQLITE_UnresetViews   0x00000200  /* True if one or more views have */
+                                          /*   defined column names */
 
 /*
 ** Each SQL function is defined by an instance of the following
@@ -582,6 +584,8 @@ void sqliteAddColumnType(Parse*,Token*,Token*);
 void sqliteAddDefaultValue(Parse*,Token*,int);
 void sqliteEndTable(Parse*,Token*,Select*);
 void sqliteCreateView(Parse*,Token*,Token*,Select*);
+int sqliteViewGetColumnNames(Parse*,Table*);
+void sqliteViewResetAll(sqlite*);
 void sqliteDropTable(Parse*, Token*);
 void sqliteDeleteTable(sqlite*, Table*);
 void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*, int);
index 7e470fed7f4a12f48009c23d01f2a556672a6539..8bf5e7c371060a032b1da04e822c1310475e9e57 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.35 2002/03/02 17:04:09 drh Exp $
+** $Id: update.c,v 1.36 2002/03/03 18:59:41 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -58,6 +58,7 @@ void sqliteUpdate(
   pTabList = sqliteTableTokenToIdList(pParse, pTableName);
   if( pTabList==0 ) goto update_cleanup;
   pTab = pTabList->a[0].pTab;
+  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
   aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
   if( aXRef==0 ) goto update_cleanup;
   for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
index 84a7f45ace556e747bb5636ec0a7bfe02a52f846..2421aaebf6992d4a8235e2653b8c6f5332f7f2c8 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing VIEW statements.
 #
-# $Id: view.test,v 1.1 2002/02/27 01:47:12 drh Exp $
+# $Id: view.test,v 1.2 2002/03/03 18:59:41 drh Exp $
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
 
@@ -44,6 +44,13 @@ do_test view-1.3 {
     SELECT * FROM v1 ORDER BY a;
   }
 } {1 2 4 5 7 8}
+do_test view-1.3.1 {
+  db close
+  sqlite db test.db
+  execsql {
+    SELECT * FROM v1 ORDER BY a;
+  }
+} {1 2 4 5 7 8}
 do_test view-1.4 {
   catchsql {
     DROP VIEW v1;
@@ -71,5 +78,13 @@ do_test view-1.7 {
     SELECT * FROM v1 ORDER BY a;
   }
 } {2 3 5 6 8 9}
+do_test view-1.8 {
+  db close
+  sqlite db test.db
+  execsql {
+    SELECT * FROM v1 ORDER BY a;
+  }
+} {2 3 5 6 8 9}
+
 
 finish_test