From a76b5dfca6c12611fb31f054d692204adddeda08 Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 23 Feb 2002 02:32:10 +0000 Subject: [PATCH] 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 --- manifest | 30 ++++---- manifest.uuid | 2 +- src/build.c | 180 +++++++++++++++++------------------------------- src/delete.c | 68 +++++++++++++----- src/expr.c | 178 +++++++++++++++++++++++++++++++++++++++++++++-- src/insert.c | 17 +---- src/parse.y | 11 ++- src/select.c | 25 ++++--- src/sqliteInt.h | 22 +++++- src/tokenize.c | 5 +- src/update.c | 19 +---- src/where.c | 4 +- 12 files changed, 357 insertions(+), 204 deletions(-) diff --git a/manifest b/manifest index 92008934ab..2c7f3f9678 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index c3c6ccb035..fa8fb5e212 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b2a9807fed544e83002366149b9a363759338c5d \ No newline at end of file +39fed2df11382b9855d518502a6c2ca200fa66b8 \ No newline at end of file diff --git a/src/build.c b/src/build.c index 3b4599c2d3..1914f07b06 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 @@ -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; inExpr; 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; inId; 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 3898fdfcf1..d815a45009 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 @@ 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; inId; 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 29c9a8b1c3..40a4a388eb 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,11 +12,74 @@ ** 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; inExpr; 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; inExpr; 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; inId; 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; diff --git a/src/insert.c b/src/insert.c index 2efabfd02a..c12c32a70c 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 @@ 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 */ diff --git a/src/parse.y b/src/parse.y index 7aec242923..fee981f632 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 @@ 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). { diff --git a/src/select.c b/src/select.c index 6183275793..ac2fc5803b 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 @@ 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; jnExpr; 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; inId; 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 2eece76f9d..59d17221b3 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 @@ 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); diff --git a/src/tokenize.c b/src/tokenize.c index cd44b1517e..a9650d38c9 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 @@ 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, diff --git a/src/update.c b/src/update.c index c7f12888d2..a4aa308e40 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 @@ 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; inId; 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 6c9ca7e78d..e325af807a 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 @@ 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);