Record the database name in addition to the table name for DELETE, INSERT,

and UPDATE statements. (CVS 879)

FossilOrigin-Name: a5d8fc95ee58dc3205a0bbbcadaa3b9c902a941b
This commit is contained in:
drh 2003-03-20 01:16:58 +00:00
parent 001bbcbb8f
commit 113088ec68
13 changed files with 169 additions and 138 deletions

View File

@ -1,5 +1,5 @@
C Modifications\sto\sthe\sVDBE\sto\ssupport\smore\sthan\sone\sdatabase\sfile.\s(CVS\s878) C Record\sthe\sdatabase\sname\sin\saddition\sto\sthe\stable\sname\sfor\sDELETE,\sINSERT,\nand\sUPDATE\sstatements.\s(CVS\s879)
D 2003-03-19T03:14:01 D 2003-03-20T01:16:58
F Makefile.in 6606854b1512f185b8e8c779b8d7fc2750463d64 F Makefile.in 6606854b1512f185b8e8c779b8d7fc2750463d64
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@ -21,39 +21,39 @@ F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
F src/auth.c f37bfc9451b8c1fa52f34adff474560018892729 F src/auth.c f37bfc9451b8c1fa52f34adff474560018892729
F src/btree.c 327819bb858d534072f5004973f8bcdd50f133d6 F src/btree.c 327819bb858d534072f5004973f8bcdd50f133d6
F src/btree.h 8209bfadf5845d4fdaa60f471bb360f894cd4095 F src/btree.h 8209bfadf5845d4fdaa60f471bb360f894cd4095
F src/build.c be6db117e97d8c47596b09480b1aa2626f083ab3 F src/build.c a965338bee81ce20fb0ce38419e9a9d159e720f0
F src/delete.c d76f767696b0ee3661e937ccf4c6c45857c1b78e F src/delete.c 96a0ae021f960a7f2dbb3d1456802624deacfd3c
F src/encode.c faf03741efe921755ec371cf4a6984536de00042 F src/encode.c faf03741efe921755ec371cf4a6984536de00042
F src/expr.c bd690b3a6174e97a0f16800e78c8aeae749a4e71 F src/expr.c 8af430cdbcb6122dd0320c8860602bd4cc778486
F src/func.c 90c583f0b91220f7cd411a2407deaf9327245d63 F src/func.c 90c583f0b91220f7cd411a2407deaf9327245d63
F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
F src/insert.c 02ac6147cb360385be94b0e6f572db131505d1c9 F src/insert.c 1f31bdec48bd3915615e7251b74c27dcfaee9b88
F src/main.c d0418850385895202f9b28e0bd7d0b0fdfd868df F src/main.c 66cd7ff4fc9f43719aaa2bc22db5babd8f437a9f
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
F src/os.c dfed46091f69cd2d1e601f8a214d41344f2b00b6 F src/os.c dfed46091f69cd2d1e601f8a214d41344f2b00b6
F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0 F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0
F src/pager.c dd1dfa4d929a58b44175f3117360ff1553671173 F src/pager.c dd1dfa4d929a58b44175f3117360ff1553671173
F src/pager.h 97d9a8cc5103750efd8037d71ebfb41849ef2f2f F src/pager.h 97d9a8cc5103750efd8037d71ebfb41849ef2f2f
F src/parse.y 4c4b2ff3d20d4a2afb51f05ac18edde20a173abe F src/parse.y 7a9f333e7b09b0584fb4cc3799f8828f612e282e
F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
F src/select.c 06ddc007c20862b3beb8c1c2504db664335d6706 F src/select.c 06ddc007c20862b3beb8c1c2504db664335d6706
F src/shell.c 0d260a007e0668fc7dda2b0c89bd597ef2966ec6 F src/shell.c 0d260a007e0668fc7dda2b0c89bd597ef2966ec6
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
F src/sqlite.h.in 6f648803f2ffb9beb35cb1cfa42b323d55519171 F src/sqlite.h.in 6f648803f2ffb9beb35cb1cfa42b323d55519171
F src/sqliteInt.h 888faaa05195bcdb24a9aa22108894b778132cbc F src/sqliteInt.h ad95c947582d0584240ed413c5f1e9df71749ebe
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
F src/tclsqlite.c 8167d40fd34036701e07492d07a6f9e5c4015241 F src/tclsqlite.c 8167d40fd34036701e07492d07a6f9e5c4015241
F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8 F src/test1.c 7ad4e6308dde0bf5a0f0775ce20cb2ec37a328f8
F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700 F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
F src/test3.c c12ea7f1c3fbbd58904e81e6cb10ad424e6fc728 F src/test3.c c12ea7f1c3fbbd58904e81e6cb10ad424e6fc728
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
F src/tokenize.c bc40937d6666f188037aa3e54f0a2661a6fef6d1 F src/tokenize.c 675b4718d17c69fe7609dc8e85e426ef002be811
F src/trigger.c da142decd2808bc39e801f3bb1f161dbc2bd4005 F src/trigger.c aafc83ea108ec6a1b501b31b7fb6cebcd4725fd1
F src/update.c c523bf6ef4106ca45761a3e54fc6d09ee273804b F src/update.c 5c644629cc73993ba762bf186944816581213bd1
F src/util.c 73b668d1ed468df650dc00685a5e4ffa6887feb4 F src/util.c 73b668d1ed468df650dc00685a5e4ffa6887feb4
F src/vdbe.c e2313377c463fdeba2e8eb3d9e3336292c60f51e F src/vdbe.c 7171dbe873760f403b2501e96fd3d1bd852b3ce8
F src/vdbe.h ed43771f1dc2b994d5c484fdf2eab357c6ef0ee3 F src/vdbe.h ed43771f1dc2b994d5c484fdf2eab357c6ef0ee3
F src/where.c 3111c1c209023e4f6b7b7eb0df48cef0010967c3 F src/where.c 3111c1c209023e4f6b7b7eb0df48cef0010967c3
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
@ -155,7 +155,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
P 2aba40bea5fc1c4aef8cfd4c790d40808821ca14 P 875da9eed981bfa27b98e95025f9fdbed74b4098
R 5e37bd21e0d92e541d7a5b6e1113c76c R 08491389f4b4be3f29552d1284957d26
U drh U drh
Z 586a66fe460de3c14ba71e3472471b48 Z 9eb95d01444da0b85f74b43fda15e074

View File

@ -1 +1 @@
875da9eed981bfa27b98e95025f9fdbed74b4098 a5d8fc95ee58dc3205a0bbbcadaa3b9c902a941b

View File

@ -25,7 +25,7 @@
** ROLLBACK ** ROLLBACK
** PRAGMA ** PRAGMA
** **
** $Id: build.c,v 1.132 2003/03/19 03:14:01 drh Exp $ ** $Id: build.c,v 1.133 2003/03/20 01:16:58 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@ -1805,25 +1805,63 @@ IdList *sqliteIdListAppend(IdList *pList, Token *pToken){
** need be. A new entry is created in the SrcList even if pToken is NULL. ** need be. A new entry is created in the SrcList even if pToken is NULL.
** **
** A new SrcList is returned, or NULL if malloc() fails. ** A new SrcList is returned, or NULL if malloc() fails.
**
** If pDatabase is not null, it means that the table has an optional
** database name prefix. Like this: "database.table". The pDatabase
** points to the table name and the pTable points to the database name.
** The SrcList.a[].zName field is filled with the table name which might
** come from pTable (if pDatabase is NULL) or from pDatabase.
** SrcList.a[].zDatabase is filled with the database name from pTable,
** or with NULL if no database is specified.
**
** In other words, if call like this:
**
** sqliteSrcListAppend(A,B,0);
**
** Then B is a table name and the database name is unspecified. If called
** like this:
**
** sqliteSrcListAppend(A,B,C);
**
** Then C is the table name and B is the database name.
*/ */
SrcList *sqliteSrcListAppend(SrcList *pList, Token *pToken){ SrcList *sqliteSrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){
if( pList==0 ){ if( pList==0 ){
pList = sqliteMalloc( sizeof(IdList) ); pList = sqliteMalloc( sizeof(SrcList) );
if( pList==0 ) return 0; if( pList==0 ) return 0;
} }
if( (pList->nSrc & 7)==0 ){ if( (pList->nSrc & 7)==1 ){
struct SrcList_item *a; SrcList *pNew;
a = sqliteRealloc(pList->a, (pList->nSrc+8)*sizeof(pList->a[0]) ); pNew = sqliteRealloc(pList,
if( a==0 ){ sizeof(*pList) + (pList->nSrc+8)*sizeof(pList->a[0]) );
if( pNew==0 ){
sqliteSrcListDelete(pList); sqliteSrcListDelete(pList);
return 0; return 0;
} }
pList->a = a; pList = pNew;
} }
memset(&pList->a[pList->nSrc], 0, sizeof(pList->a[0])); memset(&pList->a[pList->nSrc], 0, sizeof(pList->a[0]));
if( pToken ){ if( pDatabase && pDatabase->z==0 ){
pDatabase = 0;
}
if( pDatabase && pTable ){
Token *pTemp = pDatabase;
pDatabase = pTable;
pTable = pTemp;
}
if( pTable ){
char **pz = &pList->a[pList->nSrc].zName; char **pz = &pList->a[pList->nSrc].zName;
sqliteSetNString(pz, pToken->z, pToken->n, 0); sqliteSetNString(pz, pTable->z, pTable->n, 0);
if( *pz==0 ){
sqliteSrcListDelete(pList);
return 0;
}else{
sqliteDequote(*pz);
}
}
if( pDatabase ){
char **pz = &pList->a[pList->nSrc].zDatabase;
sqliteSetNString(pz, pDatabase->z, pDatabase->n, 0);
if( *pz==0 ){ if( *pz==0 ){
sqliteSrcListDelete(pList); sqliteSrcListDelete(pList);
return 0; return 0;
@ -1879,6 +1917,7 @@ void sqliteSrcListDelete(SrcList *pList){
int i; int i;
if( pList==0 ) return; if( pList==0 ) return;
for(i=0; i<pList->nSrc; i++){ for(i=0; i<pList->nSrc; i++){
sqliteFree(pList->a[i].zDatabase);
sqliteFree(pList->a[i].zName); sqliteFree(pList->a[i].zName);
sqliteFree(pList->a[i].zAlias); sqliteFree(pList->a[i].zAlias);
if( pList->a[i].pTab && pList->a[i].pTab->isTransient ){ if( pList->a[i].pTab && pList->a[i].pTab->isTransient ){
@ -1888,7 +1927,6 @@ void sqliteSrcListDelete(SrcList *pList){
sqliteExprDelete(pList->a[i].pOn); sqliteExprDelete(pList->a[i].pOn);
sqliteIdListDelete(pList->a[i].pUsing); sqliteIdListDelete(pList->a[i].pUsing);
} }
sqliteFree(pList->a);
sqliteFree(pList); sqliteFree(pList);
} }
@ -2079,7 +2117,7 @@ void sqliteCodeVerifySchema(Parse *pParse){
sqlite *db = pParse->db; sqlite *db = pParse->db;
Vdbe *v = sqliteGetVdbe(pParse); Vdbe *v = sqliteGetVdbe(pParse);
for(i=0; i<db->nDb; i++){ for(i=0; i<db->nDb; i++){
if( db->aDb[i].zName==0 || db->aDb[i].pBt==0 ) continue; if( i==1 || db->aDb[i].pBt==0 ) continue;
sqliteVdbeAddOp(v, OP_VerifyCookie, 0, db->aDb[i].schema_cookie); sqliteVdbeAddOp(v, OP_VerifyCookie, 0, db->aDb[i].schema_cookie);
} }
pParse->schemaVerified = 1; pParse->schemaVerified = 1;

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle DELETE FROM statements. ** to handle DELETE FROM statements.
** **
** $Id: delete.c,v 1.46 2003/03/19 03:14:01 drh Exp $ ** $Id: delete.c,v 1.47 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -41,42 +41,17 @@ Table *sqliteTableNameToTable(Parse *pParse, const char *zTab){
return pTab; 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 SrcList holding
** the table and return a pointer to the SrcList. The calling function
** is responsible for freeing the SrcList when it has finished with it.
** If there is an error, leave a message on pParse->zErrMsg and return
** NULL.
*/
SrcList *sqliteTableTokenToSrcList(Parse *pParse, Token *pTableName){
Table *pTab;
SrcList *pTabList;
pTabList = sqliteSrcListAppend(0, pTableName);
if( pTabList==0 ) return 0;
assert( pTabList->nSrc==1 );
pTab = sqliteTableNameToTable(pParse, pTabList->a[0].zName);
if( pTab==0 ){
sqliteSrcListDelete(pTabList);
return 0;
}
pTabList->a[0].pTab = pTab;
return pTabList;
}
/* /*
** Process a DELETE FROM statement. ** Process a DELETE FROM statement.
*/ */
void sqliteDeleteFrom( void sqliteDeleteFrom(
Parse *pParse, /* The parser context */ Parse *pParse, /* The parser context */
Token *pTableName, /* The table from which we should delete things */ SrcList *pTabList, /* The table from which we should delete things */
Expr *pWhere /* The WHERE clause. May be null */ Expr *pWhere /* The WHERE clause. May be null */
){ ){
Vdbe *v; /* The virtual database engine */ Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */ Table *pTab; /* The table from which records will be deleted */
char *zTab; /* Name of the table from which we are deleting */ char *zTab; /* Name of the table from which we are deleting */
SrcList *pTabList; /* A fake FROM clause holding just pTab */
int end, addr; /* A couple addresses of generated code */ int end, addr; /* A couple addresses of generated code */
int i; /* Loop counter */ int i; /* Loop counter */
WhereInfo *pWInfo; /* Information about the WHERE clause */ WhereInfo *pWInfo; /* Information about the WHERE clause */
@ -92,11 +67,12 @@ void sqliteDeleteFrom(
goto delete_from_cleanup; goto delete_from_cleanup;
} }
db = pParse->db; db = pParse->db;
assert( pTabList->nSrc==1 );
/* Check for the special case of a VIEW with one or more ON DELETE triggers /* Check for the special case of a VIEW with one or more ON DELETE triggers
** defined ** defined
*/ */
zTab = sqliteTableNameFromToken(pTableName); zTab = pTabList->a[0].zName;
if( zTab != 0 ){ if( zTab != 0 ){
pTab = sqliteFindTable(pParse->db, zTab); pTab = sqliteFindTable(pParse->db, zTab);
if( pTab ){ if( pTab ){
@ -106,9 +82,9 @@ void sqliteDeleteFrom(
sqliteTriggersExist(pParse, pTab->pTrigger, sqliteTriggersExist(pParse, pTab->pTrigger,
TK_DELETE, TK_AFTER, TK_ROW, 0); TK_DELETE, TK_AFTER, TK_ROW, 0);
} }
sqliteFree(zTab);
if( row_triggers_exist && pTab->pSelect ){ if( row_triggers_exist && pTab->pSelect ){
/* Just fire VIEW triggers */ /* Just fire VIEW triggers */
sqliteSrcListDelete(pTabList);
sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0); sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
return; return;
} }
@ -119,10 +95,10 @@ void sqliteDeleteFrom(
** will be calling are designed to work with multiple tables and expect ** will be calling are designed to work with multiple tables and expect
** an SrcList* parameter instead of just a Table* parameter. ** an SrcList* parameter instead of just a Table* parameter.
*/ */
pTabList = sqliteTableTokenToSrcList(pParse, pTableName); pTab = pTabList->a[0].pTab = sqliteTableNameToTable(pParse, zTab);
if( pTabList==0 ) goto delete_from_cleanup; if( pTab==0 ){
assert( pTabList->nSrc==1 ); goto delete_from_cleanup;
pTab = pTabList->a[0].pTab; }
assert( pTab->pSelect==0 ); /* This table is not a view */ assert( pTab->pSelect==0 ); /* This table is not a view */
if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0) ){ if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0) ){
goto delete_from_cleanup; goto delete_from_cleanup;

View File

@ -12,7 +12,7 @@
** This file contains routines used for analyzing expressions and ** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite. ** for generating VDBE code that evaluates expressions in SQLite.
** **
** $Id: expr.c,v 1.88 2003/01/31 17:16:37 drh Exp $ ** $Id: expr.c,v 1.89 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@ -184,12 +184,12 @@ ExprList *sqliteExprListDup(ExprList *p){
SrcList *sqliteSrcListDup(SrcList *p){ SrcList *sqliteSrcListDup(SrcList *p){
SrcList *pNew; SrcList *pNew;
int i; int i;
int nByte;
if( p==0 ) return 0; if( p==0 ) return 0;
pNew = sqliteMalloc( sizeof(*pNew) ); nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
pNew = sqliteMalloc( nByte );
if( pNew==0 ) return 0; if( pNew==0 ) return 0;
pNew->nSrc = p->nSrc; pNew->nSrc = p->nSrc;
pNew->a = sqliteMalloc( p->nSrc*sizeof(p->a[0]) );
if( pNew->a==0 && p->nSrc != 0 ) return 0;
for(i=0; i<p->nSrc; i++){ for(i=0; i<p->nSrc; i++){
pNew->a[i].zName = sqliteStrDup(p->a[i].zName); pNew->a[i].zName = sqliteStrDup(p->a[i].zName);
pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias); pNew->a[i].zAlias = sqliteStrDup(p->a[i].zAlias);

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite. ** to handle INSERT statements in SQLite.
** **
** $Id: insert.c,v 1.73 2003/03/19 03:14:01 drh Exp $ ** $Id: insert.c,v 1.74 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -85,14 +85,14 @@
*/ */
void sqliteInsert( void sqliteInsert(
Parse *pParse, /* Parser context */ Parse *pParse, /* Parser context */
Token *pTableName, /* Name of table into which we are inserting */ SrcList *pTabList, /* Name of table into which we are inserting */
ExprList *pList, /* List of values to be inserted */ ExprList *pList, /* List of values to be inserted */
Select *pSelect, /* A SELECT statement to use as the data source */ Select *pSelect, /* A SELECT statement to use as the data source */
IdList *pColumn, /* Column names corresponding to IDLIST. */ IdList *pColumn, /* Column names corresponding to IDLIST. */
int onError /* How to handle constraint errors */ int onError /* How to handle constraint errors */
){ ){
Table *pTab; /* The table to insert into */ Table *pTab; /* The table to insert into */
char *zTab = 0; /* Name of the table into which we are inserting */ char *zTab; /* Name of the table into which we are inserting */
int i, j, idx; /* Loop counters */ int i, j, idx; /* Loop counters */
Vdbe *v; /* Generate code into this virtual machine */ Vdbe *v; /* Generate code into this virtual machine */
Index *pIdx; /* For looping over indices of the table */ Index *pIdx; /* For looping over indices of the table */
@ -117,7 +117,8 @@ void sqliteInsert(
/* Locate the table into which we will be inserting new information. /* Locate the table into which we will be inserting new information.
*/ */
zTab = sqliteTableNameFromToken(pTableName); assert( pTabList->nSrc==1 );
zTab = pTabList->a[0].zName;
if( zTab==0 ) goto insert_cleanup; if( zTab==0 ) goto insert_cleanup;
pTab = sqliteFindTable(pParse->db, zTab); pTab = sqliteFindTable(pParse->db, zTab);
if( pTab==0 ){ if( pTab==0 ){
@ -145,8 +146,6 @@ void sqliteInsert(
pParse->nErr++; pParse->nErr++;
goto insert_cleanup; goto insert_cleanup;
} }
sqliteFree(zTab);
zTab = 0;
if( pTab==0 ) goto insert_cleanup; if( pTab==0 ) goto insert_cleanup;
@ -521,9 +520,9 @@ void sqliteInsert(
} }
insert_cleanup: insert_cleanup:
sqliteSrcListDelete(pTabList);
if( pList ) sqliteExprListDelete(pList); if( pList ) sqliteExprListDelete(pList);
if( pSelect ) sqliteSelectDelete(pSelect); if( pSelect ) sqliteSelectDelete(pSelect);
if ( zTab ) sqliteFree(zTab);
sqliteIdListDelete(pColumn); sqliteIdListDelete(pColumn);
} }

View File

@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be ** other files are for internal use by SQLite and should not be
** accessed by users of the library. ** accessed by users of the library.
** **
** $Id: main.c,v 1.115 2003/03/19 03:14:02 drh Exp $ ** $Id: main.c,v 1.116 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "os.h" #include "os.h"
@ -377,6 +377,7 @@ sqlite *sqlite_open(const char *zFilename, int mode, char **pzErrMsg){
return 0; return 0;
} }
db->aDb[0].zName = "main"; db->aDb[0].zName = "main";
db->aDb[1].zName = "temp";
/* Attempt to read the schema */ /* Attempt to read the schema */
sqliteRegisterBuiltinFunctions(db); sqliteRegisterBuiltinFunctions(db);
@ -469,6 +470,9 @@ void sqlite_close(sqlite *db){
if( db->aDb[j].pBt ){ if( db->aDb[j].pBt ){
sqliteBtreeClose(db->aDb[j].pBt); sqliteBtreeClose(db->aDb[j].pBt);
} }
if( j>=2 ){
sqliteFree(db->aDb[j].zName);
}
} }
if( db->aDb!=db->aDbStatic ){ if( db->aDb!=db->aDbStatic ){
sqliteFree(db->aDb); sqliteFree(db->aDb);

View File

@ -14,7 +14,7 @@
** the parser. Lemon will also generate a header file containing ** the parser. Lemon will also generate a header file containing
** numeric codes for all of the tokens. ** numeric codes for all of the tokens.
** **
** @(#) $Id: parse.y,v 1.91 2003/02/20 00:44:52 drh Exp $ ** @(#) $Id: parse.y,v 1.92 2003/03/20 01:16:59 drh Exp $
*/ */
%token_prefix TK_ %token_prefix TK_
%token_type {Token} %token_type {Token}
@ -126,8 +126,8 @@ id(A) ::= ID(X). {A = X;}
// This obviates the need for the "id" nonterminal. // This obviates the need for the "id" nonterminal.
// //
%fallback ID %fallback ID
ABORT AFTER ASC BEFORE BEGIN CASCADE CLUSTER CONFLICT ABORT AFTER ASC ATTACH BEFORE BEGIN CASCADE CLUSTER CONFLICT
COPY DEFERRED DELIMITERS DESC EACH END EXPLAIN FAIL FOR COPY DATABASE DEFERRED DELIMITERS DESC DETACH EACH END EXPLAIN FAIL FOR
IGNORE IMMEDIATE INITIALLY INSTEAD MATCH KEY IGNORE IMMEDIATE INITIALLY INSTEAD MATCH KEY
OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT OF OFFSET PRAGMA RAISE REPLACE RESTRICT ROW STATEMENT
TEMP TRIGGER VACUUM VIEW. TEMP TRIGGER VACUUM VIEW.
@ -353,8 +353,8 @@ stl_prefix(A) ::= seltablist(X) joinop(Y). {
if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y; if( A && A->nSrc>0 ) A->a[A->nSrc-1].jointype = Y;
} }
stl_prefix(A) ::= . {A = 0;} stl_prefix(A) ::= . {A = 0;}
seltablist(A) ::= stl_prefix(X) nm(Y) as(Z) on_opt(N) using_opt(U). { seltablist(A) ::= stl_prefix(X) nm(Y) dbnm(D) as(Z) on_opt(N) using_opt(U). {
A = sqliteSrcListAppend(X,&Y); A = sqliteSrcListAppend(X,&Y,&D);
if( Z.n ) sqliteSrcListAddAlias(A,&Z); if( Z.n ) sqliteSrcListAddAlias(A,&Z);
if( N ){ if( N ){
if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; } if( A && A->nSrc>1 ){ A->a[A->nSrc-2].pOn = N; }
@ -366,7 +366,7 @@ seltablist(A) ::= stl_prefix(X) nm(Y) as(Z) on_opt(N) using_opt(U). {
} }
} }
seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). { seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). {
A = sqliteSrcListAppend(X,0); A = sqliteSrcListAppend(X,0,0);
A->a[A->nSrc-1].pSelect = S; A->a[A->nSrc-1].pSelect = S;
if( Z.n ) sqliteSrcListAddAlias(A,&Z); if( Z.n ) sqliteSrcListAddAlias(A,&Z);
if( N ){ if( N ){
@ -379,6 +379,10 @@ seltablist(A) ::= stl_prefix(X) LP select(S) RP as(Z) on_opt(N) using_opt(U). {
} }
} }
%type dbnm {Token}
dbnm(A) ::= . {A.z=0; A.n=0;}
dbnm(A) ::= DOT nm(X). {A = X;}
%type joinop {int} %type joinop {int}
%type joinop2 {int} %type joinop2 {int}
joinop(X) ::= COMMA. { X = JT_INNER; } joinop(X) ::= COMMA. { X = JT_INNER; }
@ -447,8 +451,9 @@ limit_opt(A) ::= LIMIT INTEGER(X) COMMA INTEGER(Y).
/////////////////////////// The DELETE statement ///////////////////////////// /////////////////////////// The DELETE statement /////////////////////////////
// //
cmd ::= DELETE FROM nm(X) where_opt(Y). cmd ::= DELETE FROM nm(X) dbnm(D) where_opt(Y). {
{sqliteDeleteFrom(pParse, &X, Y);} sqliteDeleteFrom(pParse, sqliteSrcListAppend(0,&X,&D), Y);
}
%type where_opt {Expr*} %type where_opt {Expr*}
%destructor where_opt {sqliteExprDelete($$);} %destructor where_opt {sqliteExprDelete($$);}
@ -461,8 +466,8 @@ where_opt(A) ::= WHERE expr(X). {A = X;}
////////////////////////// The UPDATE command //////////////////////////////// ////////////////////////// The UPDATE command ////////////////////////////////
// //
cmd ::= UPDATE orconf(R) nm(X) SET setlist(Y) where_opt(Z). cmd ::= UPDATE orconf(R) nm(X) dbnm(D) SET setlist(Y) where_opt(Z).
{sqliteUpdate(pParse,&X,Y,Z,R);} {sqliteUpdate(pParse,sqliteSrcListAppend(0,&X,&D),Y,Z,R);}
setlist(A) ::= setlist(Z) COMMA nm(X) EQ expr(Y). setlist(A) ::= setlist(Z) COMMA nm(X) EQ expr(Y).
{A = sqliteExprListAppend(Z,Y,&X);} {A = sqliteExprListAppend(Z,Y,&X);}
@ -470,10 +475,11 @@ setlist(A) ::= nm(X) EQ expr(Y). {A = sqliteExprListAppend(0,Y,&X);}
////////////////////////// The INSERT command ///////////////////////////////// ////////////////////////// The INSERT command /////////////////////////////////
// //
cmd ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) VALUES LP itemlist(Y) RP. cmd ::= insert_cmd(R) INTO nm(X) dbnm(D) inscollist_opt(F)
{sqliteInsert(pParse, &X, Y, 0, F, R);} VALUES LP itemlist(Y) RP.
cmd ::= insert_cmd(R) INTO nm(X) inscollist_opt(F) select(S). {sqliteInsert(pParse, sqliteSrcListAppend(0,&X,&D), Y, 0, F, R);}
{sqliteInsert(pParse, &X, 0, S, F, R);} cmd ::= insert_cmd(R) INTO nm(X) dbnm(D) inscollist_opt(F) select(S).
{sqliteInsert(pParse, sqliteSrcListAppend(0,&X,&D), 0, S, F, R);}
%type insert_cmd {int} %type insert_cmd {int}
insert_cmd(A) ::= INSERT orconf(R). {A = R;} insert_cmd(A) ::= INSERT orconf(R). {A = R;}
@ -825,3 +831,12 @@ expr(A) ::= RAISE(X) LP FAIL COMMA nm(Z) RP(Y). {
cmd ::= DROP TRIGGER nm(X). { cmd ::= DROP TRIGGER nm(X). {
sqliteDropTrigger(pParse,&X,0); sqliteDropTrigger(pParse,&X,0);
} }
//////////////////////// ATTACH DATABASE file AS name /////////////////////////
cmd ::= ATTACH database_kw_opt ids AS nm.
database_kw_opt ::= DATABASE.
database_kw_opt ::= .
//////////////////////// DETACH DATABASE name /////////////////////////////////
cmd ::= DETACH database_kw_opt nm.

View File

@ -11,7 +11,7 @@
************************************************************************* *************************************************************************
** Internal interface definitions for SQLite. ** Internal interface definitions for SQLite.
** **
** @(#) $Id: sqliteInt.h,v 1.163 2003/03/19 03:14:02 drh Exp $ ** @(#) $Id: sqliteInt.h,v 1.164 2003/03/20 01:16:59 drh Exp $
*/ */
#include "config.h" #include "config.h"
#include "sqlite.h" #include "sqlite.h"
@ -608,6 +608,7 @@ struct IdList {
struct SrcList { struct SrcList {
int nSrc; /* Number of tables or subqueries in the FROM clause */ int nSrc; /* Number of tables or subqueries in the FROM clause */
struct SrcList_item { struct SrcList_item {
char *zDatabase; /* Name of database holding this table */
char *zName; /* Name of the table */ char *zName; /* Name of the table */
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
Table *pTab; /* An SQL table corresponding to zName */ Table *pTab; /* An SQL table corresponding to zName */
@ -615,7 +616,7 @@ struct SrcList {
int jointype; /* Type of join between this table and the next */ int jointype; /* Type of join between this table and the next */
Expr *pOn; /* The ON clause of a join */ Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */ IdList *pUsing; /* The USING clause of a join */
} *a; /* One entry for each identifier on the list */ } a[1]; /* One entry for each identifier on the list */
}; };
/* /*
@ -975,10 +976,10 @@ int sqliteViewGetColumnNames(Parse*,Table*);
void sqliteViewResetAll(sqlite*); void sqliteViewResetAll(sqlite*);
void sqliteDropTable(Parse*, Token*, int); void sqliteDropTable(Parse*, Token*, int);
void sqliteDeleteTable(sqlite*, Table*); void sqliteDeleteTable(sqlite*, Table*);
void sqliteInsert(Parse*, Token*, ExprList*, Select*, IdList*, int); void sqliteInsert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
IdList *sqliteIdListAppend(IdList*, Token*); IdList *sqliteIdListAppend(IdList*, Token*);
int sqliteIdListIndex(IdList*,const char*); int sqliteIdListIndex(IdList*,const char*);
SrcList *sqliteSrcListAppend(SrcList*, Token*); SrcList *sqliteSrcListAppend(SrcList*, Token*, Token*);
void sqliteSrcListAddAlias(SrcList*, Token*); void sqliteSrcListAddAlias(SrcList*, Token*);
void sqliteIdListDelete(IdList*); void sqliteIdListDelete(IdList*);
void sqliteSrcListDelete(SrcList*); void sqliteSrcListDelete(SrcList*);
@ -992,9 +993,8 @@ Select *sqliteSelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
void sqliteSelectDelete(Select*); void sqliteSelectDelete(Select*);
void sqliteSelectUnbind(Select*); void sqliteSelectUnbind(Select*);
Table *sqliteTableNameToTable(Parse*, const char*); Table *sqliteTableNameToTable(Parse*, const char*);
SrcList *sqliteTableTokenToSrcList(Parse*, Token*); void sqliteDeleteFrom(Parse*, SrcList*, Expr*);
void sqliteDeleteFrom(Parse*, Token*, Expr*); void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int);
void sqliteUpdate(Parse*, Token*, ExprList*, Expr*, int);
WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**); WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**);
void sqliteWhereEnd(WhereInfo*); void sqliteWhereEnd(WhereInfo*);
void sqliteExprCode(Parse*, Expr*); void sqliteExprCode(Parse*, Expr*);

View File

@ -15,7 +15,7 @@
** individual tokens and sends those tokens one-by-one over to the ** individual tokens and sends those tokens one-by-one over to the
** parser for analysis. ** parser for analysis.
** **
** $Id: tokenize.c,v 1.55 2003/01/29 14:06:09 drh Exp $ ** $Id: tokenize.c,v 1.56 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include "os.h" #include "os.h"
@ -44,6 +44,7 @@ static Keyword aKeywordTable[] = {
{ "AND", 0, TK_AND, 0 }, { "AND", 0, TK_AND, 0 },
{ "AS", 0, TK_AS, 0 }, { "AS", 0, TK_AS, 0 },
{ "ASC", 0, TK_ASC, 0 }, { "ASC", 0, TK_ASC, 0 },
{ "ATTACH", 0, TK_ATTACH, 0 },
{ "BEFORE", 0, TK_BEFORE, 0 }, { "BEFORE", 0, TK_BEFORE, 0 },
{ "BEGIN", 0, TK_BEGIN, 0 }, { "BEGIN", 0, TK_BEGIN, 0 },
{ "BETWEEN", 0, TK_BETWEEN, 0 }, { "BETWEEN", 0, TK_BETWEEN, 0 },
@ -59,12 +60,14 @@ static Keyword aKeywordTable[] = {
{ "COPY", 0, TK_COPY, 0 }, { "COPY", 0, TK_COPY, 0 },
{ "CREATE", 0, TK_CREATE, 0 }, { "CREATE", 0, TK_CREATE, 0 },
{ "CROSS", 0, TK_JOIN_KW, 0 }, { "CROSS", 0, TK_JOIN_KW, 0 },
{ "DATABASE", 0, TK_DATABASE, 0 },
{ "DEFAULT", 0, TK_DEFAULT, 0 }, { "DEFAULT", 0, TK_DEFAULT, 0 },
{ "DEFERRED", 0, TK_DEFERRED, 0 }, { "DEFERRED", 0, TK_DEFERRED, 0 },
{ "DEFERRABLE", 0, TK_DEFERRABLE, 0 }, { "DEFERRABLE", 0, TK_DEFERRABLE, 0 },
{ "DELETE", 0, TK_DELETE, 0 }, { "DELETE", 0, TK_DELETE, 0 },
{ "DELIMITERS", 0, TK_DELIMITERS, 0 }, { "DELIMITERS", 0, TK_DELIMITERS, 0 },
{ "DESC", 0, TK_DESC, 0 }, { "DESC", 0, TK_DESC, 0 },
{ "DETACH", 0, TK_DETACH, 0 },
{ "DISTINCT", 0, TK_DISTINCT, 0 }, { "DISTINCT", 0, TK_DISTINCT, 0 },
{ "DROP", 0, TK_DROP, 0 }, { "DROP", 0, TK_DROP, 0 },
{ "END", 0, TK_END, 0 }, { "END", 0, TK_END, 0 },

View File

@ -514,24 +514,29 @@ static int codeTriggerProgram(
break; break;
} }
case TK_UPDATE: { case TK_UPDATE: {
SrcList *pSrc;
pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
sqliteUpdate(pParse, &pTriggerStep->target, sqliteUpdate(pParse, pSrc,
sqliteExprListDup(pTriggerStep->pExprList), sqliteExprListDup(pTriggerStep->pExprList),
sqliteExprDup(pTriggerStep->pWhere), orconf); sqliteExprDup(pTriggerStep->pWhere), orconf);
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
break; break;
} }
case TK_INSERT: { case TK_INSERT: {
sqliteInsert(pParse, &pTriggerStep->target, SrcList *pSrc;
sqliteExprListDup(pTriggerStep->pExprList), pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
sqliteSelectDup(pTriggerStep->pSelect), sqliteInsert(pParse, pSrc,
sqliteIdListDup(pTriggerStep->pIdList), orconf); sqliteExprListDup(pTriggerStep->pExprList),
sqliteSelectDup(pTriggerStep->pSelect),
sqliteIdListDup(pTriggerStep->pIdList), orconf);
break; break;
} }
case TK_DELETE: { case TK_DELETE: {
SrcList *pSrc;
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPush, 0, 0);
sqliteDeleteFrom(pParse, &pTriggerStep->target, pSrc = sqliteSrcListAppend(0, &pTriggerStep->target, 0);
sqliteExprDup(pTriggerStep->pWhere)); sqliteDeleteFrom(pParse, pSrc, sqliteExprDup(pTriggerStep->pWhere));
sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0); sqliteVdbeAddOp(pParse->pVdbe, OP_ListPop, 0, 0);
break; break;
} }
@ -611,7 +616,6 @@ int sqliteCodeRowTrigger(
Expr * whenExpr; Expr * whenExpr;
dummyTablist.nSrc = 0; dummyTablist.nSrc = 0;
dummyTablist.a = 0;
/* Push an entry on to the trigger stack */ /* Push an entry on to the trigger stack */
pTriggerStack->pTrigger = pTrigger; pTriggerStack->pTrigger = pTrigger;
@ -682,7 +686,7 @@ void sqliteViewTriggers(
theSelect.isDistinct = 0; theSelect.isDistinct = 0;
theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0); theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
theSelect.pSrc = sqliteSrcListAppend(0, &tblNameToken); theSelect.pSrc = sqliteSrcListAppend(0, &tblNameToken, 0);
theSelect.pWhere = pWhere; pWhere = 0; theSelect.pWhere = pWhere; pWhere = 0;
theSelect.pGroupBy = 0; theSelect.pGroupBy = 0;
theSelect.pHaving = 0; theSelect.pHaving = 0;

View File

@ -12,7 +12,7 @@
** This file contains C code routines that are called by the parser ** This file contains C code routines that are called by the parser
** to handle UPDATE statements. ** to handle UPDATE statements.
** **
** $Id: update.c,v 1.54 2003/03/19 03:14:02 drh Exp $ ** $Id: update.c,v 1.55 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
@ -21,14 +21,14 @@
*/ */
void sqliteUpdate( void sqliteUpdate(
Parse *pParse, /* The parser context */ Parse *pParse, /* The parser context */
Token *pTableName, /* The table in which we should change things */ SrcList *pTabList, /* The table in which we should change things */
ExprList *pChanges, /* Things to be changed */ ExprList *pChanges, /* Things to be changed */
Expr *pWhere, /* The WHERE clause. May be null */ Expr *pWhere, /* The WHERE clause. May be null */
int onError /* How to handle constraint errors */ int onError /* How to handle constraint errors */
){ ){
int i, j; /* Loop counters */ int i, j; /* Loop counters */
char *zTab; /* Name of the table to be updated */
Table *pTab; /* The table to be updated */ Table *pTab; /* The table to be updated */
SrcList *pTabList = 0; /* Fake FROM clause containing only pTab */
int addr; /* VDBE instruction address of the start of the loop */ int addr; /* VDBE instruction address of the start of the loop */
WhereInfo *pWInfo; /* Information about the WHERE clause */ WhereInfo *pWInfo; /* Information about the WHERE clause */
Vdbe *v; /* The virtual database engine */ Vdbe *v; /* The virtual database engine */
@ -53,28 +53,27 @@ void sqliteUpdate(
if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup; if( pParse->nErr || sqlite_malloc_failed ) goto update_cleanup;
db = pParse->db; db = pParse->db;
assert( pTabList->nSrc==1 );
/* Check for the special case of a VIEW with one or more ON UPDATE triggers /* Check for the special case of a VIEW with one or more ON UPDATE triggers
* defined * defined
*/ */
{ zTab = pTabList->a[0].zName;
char *zTab = sqliteTableNameFromToken(pTableName); if( zTab != 0 ){
pTab = sqliteFindTable(pParse->db, zTab);
if( pTab ){
row_triggers_exist =
sqliteTriggersExist(pParse, pTab->pTrigger,
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges) ||
sqliteTriggersExist(pParse, pTab->pTrigger,
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
}
if( zTab != 0 ){ if( row_triggers_exist && pTab->pSelect ){
pTab = sqliteFindTable(pParse->db, zTab); /* Just fire VIEW triggers */
if( pTab ){ sqliteSrcListDelete(pTabList);
row_triggers_exist = sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
sqliteTriggersExist(pParse, pTab->pTrigger, return;
TK_UPDATE, TK_BEFORE, TK_ROW, pChanges) ||
sqliteTriggersExist(pParse, pTab->pTrigger,
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
}
sqliteFree(zTab);
if( row_triggers_exist && pTab->pSelect ){
/* Just fire VIEW triggers */
sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
return;
}
} }
} }
@ -83,9 +82,8 @@ void sqliteUpdate(
** will be calling are designed to work with multiple tables and expect ** will be calling are designed to work with multiple tables and expect
** an SrcList* parameter instead of just a Table* parameter. ** an SrcList* parameter instead of just a Table* parameter.
*/ */
pTabList = sqliteTableTokenToSrcList(pParse, pTableName); pTab = pTabList->a[0].pTab = sqliteTableNameToTable(pParse, zTab);
if( pTabList==0 ) goto update_cleanup; if( pTab==0 ) goto update_cleanup;
pTab = pTabList->a[0].pTab;
assert( pTab->pSelect==0 ); /* This table is not a VIEW */ assert( pTab->pSelect==0 ); /* This table is not a VIEW */
aXRef = sqliteMalloc( sizeof(int) * pTab->nCol ); aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
if( aXRef==0 ) goto update_cleanup; if( aXRef==0 ) goto update_cleanup;

View File

@ -36,7 +36,7 @@
** in this file for details. If in doubt, do not deviate from existing ** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code. ** commenting and indentation practices when changing or adding code.
** **
** $Id: vdbe.c,v 1.208 2003/03/19 03:14:02 drh Exp $ ** $Id: vdbe.c,v 1.209 2003/03/20 01:16:59 drh Exp $
*/ */
#include "sqliteInt.h" #include "sqliteInt.h"
#include <ctype.h> #include <ctype.h>
@ -1210,7 +1210,6 @@ static void Cleanup(Vdbe *p){
*/ */
void sqliteVdbeDelete(Vdbe *p){ void sqliteVdbeDelete(Vdbe *p){
int i; int i;
sqlite *db = p->db;
if( p==0 ) return; if( p==0 ) return;
Cleanup(p); Cleanup(p);
if( p->pPrev ){ if( p->pPrev ){
@ -1232,13 +1231,6 @@ void sqliteVdbeDelete(Vdbe *p){
sqliteFree(p->aOp[i].p3); sqliteFree(p->aOp[i].p3);
} }
} }
for(i=2; i<db->nDb; i++){
if( db->aDb[i].pBt && db->aDb[i].zName==0 ){
sqliteBtreeClose(db->aDb[i].pBt);
db->aDb[i].pBt = 0;
db->aDb[i].inTrans = 0;
}
}
sqliteFree(p->aOp); sqliteFree(p->aOp);
sqliteFree(p->aLabel); sqliteFree(p->aLabel);
sqliteFree(p->aStack); sqliteFree(p->aStack);
@ -1505,6 +1497,9 @@ void sqliteVdbeMakeReady(
int isExplain /* True if the EXPLAIN keywords is present */ int isExplain /* True if the EXPLAIN keywords is present */
){ ){
int n; int n;
#ifdef MEMORY_DEBUG
extern int access(const char*,int);
#endif
assert( p!=0 ); assert( p!=0 );
assert( p->aStack==0 ); assert( p->aStack==0 );
@ -3345,7 +3340,6 @@ case OP_SetCookie: {
case OP_VerifyCookie: { case OP_VerifyCookie: {
int aMeta[SQLITE_N_BTREE_META]; int aMeta[SQLITE_N_BTREE_META];
assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( db->aDb[pOp->p1].zName!=0 );
rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta); rc = sqliteBtreeGetMeta(db->aDb[pOp->p1].pBt, aMeta);
if( rc==SQLITE_OK && aMeta[1]!=pOp->p2 ){ if( rc==SQLITE_OK && aMeta[1]!=pOp->p2 ){
sqliteSetString(&p->zErrMsg, "database schema has changed", 0); sqliteSetString(&p->zErrMsg, "database schema has changed", 0);