diff --git a/manifest b/manifest index a7dd36dd4f..fb765b9332 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index ef63c1610c..9874b7d631 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dfe431c9b70bc3a1bf5148826edce0846737e66b \ No newline at end of file +6121e5ab9328c90c64d40ade3de73ad11d4aaf4e \ No newline at end of file diff --git a/src/build.c b/src/build.c index b5b6579f53..250872fb37 100644 --- a/src/build.c +++ b/src/build.c @@ -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 @@ -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; inCol; 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; inCol; 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); } diff --git a/src/delete.c b/src/delete.c index 2d5771a4e4..03df82ea2e 100644 --- a/src/delete.c +++ b/src/delete.c @@ -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. */ diff --git a/src/expr.c b/src/expr.c index d487188232..9c16889dbd 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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; jnCol; 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{ diff --git a/src/insert.c b/src/insert.c index b7617289e5..0c5aaba0a2 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.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; diff --git a/src/select.c b/src/select.c index 305c3e39a2..280d99ebc8 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.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; inCol; 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); } } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 28b106e84e..cdc7a0ce08 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/update.c b/src/update.c index 7e470fed7f..8bf5e7c371 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.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; inCol; i++) aXRef[i] = -1; diff --git a/test/view.test b/test/view.test index 84a7f45ace..2421aaebf6 100644 --- a/test/view.test +++ b/test/view.test @@ -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