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
This commit is contained in:
parent
17f7193434
commit
a76b5dfca6
30
manifest
30
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
|
||||
|
@ -1 +1 @@
|
||||
b2a9807fed544e83002366149b9a363759338c5d
|
||||
39fed2df11382b9855d518502a6c2ca200fa66b8
|
180
src/build.c
180
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>
|
||||
@ -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; 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);
|
||||
}
|
||||
|
68
src/delete.c
68
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.
|
||||
|
178
src/expr.c
178
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; 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;
|
||||
|
17
src/insert.c
17
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
|
||||
*/
|
||||
|
11
src/parse.y
11
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). {
|
||||
|
25
src/select.c
25
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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
19
src/update.c
19
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;
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user