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
diff --git a/src/expr.c b/src/expr.c
index 29c9a8b..40a4a38 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 @@
}
/*
+** 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 @@
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 @@
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 @@
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 @@
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;