diff options
author | drh <drh@noemail.net> | 2002-02-23 02:32:10 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2002-02-23 02:32:10 +0000 |
commit | a76b5dfca6c12611fb31f054d692204adddeda08 (patch) | |
tree | 3ed0fe854bca2fa3735bdc0d76f898eec7de99e4 /src | |
parent | 17f7193434585e87dbfc96efeb69ae0857969030 (diff) | |
download | sqlite-a76b5dfca6c12611fb31f054d692204adddeda08.tar.gz sqlite-a76b5dfca6c12611fb31f054d692204adddeda08.zip |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 180 | ||||
-rw-r--r-- | src/delete.c | 68 | ||||
-rw-r--r-- | src/expr.c | 178 | ||||
-rw-r--r-- | src/insert.c | 17 | ||||
-rw-r--r-- | src/parse.y | 11 | ||||
-rw-r--r-- | src/select.c | 25 | ||||
-rw-r--r-- | src/sqliteInt.h | 22 | ||||
-rw-r--r-- | src/tokenize.c | 5 | ||||
-rw-r--r-- | src/update.c | 19 | ||||
-rw-r--r-- | src/where.c | 4 |
10 files changed, 341 insertions, 188 deletions
diff --git a/src/build.c b/src/build.c index 3b4599c2d..1914f07b0 100644 --- a/src/build.c +++ b/src/build.c @@ -25,7 +25,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.78 2002/02/21 12:01:27 drh Exp $ +** $Id: build.c,v 1.79 2002/02/23 02:32:10 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -65,74 +65,11 @@ 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); } @@ -860,6 +798,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 @@ -1299,55 +1287,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); } diff --git a/src/delete.c b/src/delete.c index 3898fdfcf..d815a4500 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; i<pTabList->nId; i++){ - pTabList->a[i].pTab = sqliteFindTable(db, pTabList->a[i].zName); - if( pTabList->a[i].pTab==0 ){ - sqliteSetString(&pParse->zErrMsg, "no such table: ", - pTabList->a[i].zName, 0); - pParse->nErr++; - goto delete_from_cleanup; - } - if( pTabList->a[i].pTab->readOnly ){ - sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName, - " may not be modified", 0); - pParse->nErr++; - goto delete_from_cleanup; - } - } pTab = pTabList->a[0].pTab; /* Resolve the column names in all the expressions. diff --git a/src/expr.c b/src/expr.c index 29c9a8b1c..40a4a388e 100644 --- a/src/expr.c +++ b/src/expr.c @@ -12,12 +12,75 @@ ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. ** -** $Id: expr.c,v 1.41 2002/02/14 21:42:51 drh Exp $ +** $Id: expr.c,v 1.42 2002/02/23 02:32:10 drh Exp $ */ #include "sqliteInt.h" /* +** Construct a new expression node and return a pointer to it. Memory +** for this node is obtained from sqliteMalloc(). The calling function +** is responsible for making sure the node eventually gets freed. +*/ +Expr *sqliteExpr(int op, Expr *pLeft, Expr *pRight, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ){ + sqliteExprDelete(pLeft); + sqliteExprDelete(pRight); + return 0; + } + pNew->op = op; + pNew->pLeft = pLeft; + pNew->pRight = pRight; + if( pToken ){ + pNew->token = *pToken; + }else{ + pNew->token.z = 0; + pNew->token.n = 0; + } + if( pLeft && pRight ){ + sqliteExprSpan(pNew, &pLeft->span, &pRight->span); + }else{ + pNew->span = pNew->token; + } + return pNew; +} + +/* +** Set the Expr.token field of the given expression to span all +** text between the two given tokens. +*/ +void sqliteExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){ + if( pExpr ){ + pExpr->span.z = pLeft->z; + pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z); + } +} + +/* +** Construct a new expression node for a function with multiple +** arguments. +*/ +Expr *sqliteExprFunction(ExprList *pList, Token *pToken){ + Expr *pNew; + pNew = sqliteMalloc( sizeof(Expr) ); + if( pNew==0 ){ + sqliteExprListDelete(pList); + return 0; + } + pNew->op = TK_FUNCTION; + pNew->pList = pList; + if( pToken ){ + pNew->token = *pToken; + }else{ + pNew->token.z = 0; + pNew->token.n = 0; + } + return pNew; +} + +/* ** Recursively delete an expression tree. */ void sqliteExprDelete(Expr *p){ @@ -32,6 +95,108 @@ void sqliteExprDelete(Expr *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; diff --git a/src/insert.c b/src/insert.c index 2efabfd02..c12c32a70 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 7aec24292..fee981f63 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 618327579..ac2fc5803 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; 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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2eece76f9..59d17221b 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 cd44b1517..a9650d38c 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 c7f12888d..a4aa308e4 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; i<pTabList->nId; i++){ - pTabList->a[i].pTab = sqliteFindTable(db, pTabList->a[i].zName); - if( pTabList->a[i].pTab==0 ){ - sqliteSetString(&pParse->zErrMsg, "no such table: ", - pTabList->a[i].zName, 0); - pParse->nErr++; - goto update_cleanup; - } - if( pTabList->a[i].pTab->readOnly ){ - sqliteSetString(&pParse->zErrMsg, "table ", pTabList->a[i].zName, - " may not be modified", 0); - pParse->nErr++; - goto update_cleanup; - } - } pTab = pTabList->a[0].pTab; aXRef = sqliteMalloc( sizeof(int) * pTab->nCol ); if( aXRef==0 ) goto update_cleanup; diff --git a/src/where.c b/src/where.c index 6c9ca7e78..e325af807 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); |