aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2002-02-23 02:32:10 +0000
committerdrh <drh@noemail.net>2002-02-23 02:32:10 +0000
commita76b5dfca6c12611fb31f054d692204adddeda08 (patch)
tree3ed0fe854bca2fa3735bdc0d76f898eec7de99e4 /src
parent17f7193434585e87dbfc96efeb69ae0857969030 (diff)
downloadsqlite-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.c180
-rw-r--r--src/delete.c68
-rw-r--r--src/expr.c178
-rw-r--r--src/insert.c17
-rw-r--r--src/parse.y11
-rw-r--r--src/select.c25
-rw-r--r--src/sqliteInt.h22
-rw-r--r--src/tokenize.c5
-rw-r--r--src/update.c19
-rw-r--r--src/where.c4
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);