mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Fix some issues with INSTEAD OF triggers. (CVS 930)
FossilOrigin-Name: 206b17397b1d2b55179c935927ff1d8215728c32
This commit is contained in:
40
manifest
40
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Remove\sthe\sbegin_hook\sand\scommit_hook\sAPIs.\s\sThey\swere\sa\sbad\sidea.\s\sAdd\sa\n"trace"\smethod\sto\sthe\sTCL\sinterface.\s(CVS\s929)
|
C Fix\ssome\sissues\swith\sINSTEAD\sOF\striggers.\s(CVS\s930)
|
||||||
D 2003-04-23T12:25:24
|
D 2003-04-24T01:45:04
|
||||||
F Makefile.in 004acec253ecdde985c8ecd5b7c9accdb210378f
|
F Makefile.in 004acec253ecdde985c8ecd5b7c9accdb210378f
|
||||||
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
||||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||||
@@ -20,34 +20,34 @@ F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
|
|||||||
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
|
F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
|
||||||
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
||||||
F src/attach.c 7ebc7487de43e357a64226f8abef81f2669f2183
|
F src/attach.c 7ebc7487de43e357a64226f8abef81f2669f2183
|
||||||
F src/auth.c 7b0a72a649989461d36eced6ff1214f32af436c5
|
F src/auth.c a4afd27964fb9f661147115790c8ae2ee230ebcc
|
||||||
F src/btree.c b9487cceb9ea78af9cbae9def34114902f511736
|
F src/btree.c b9487cceb9ea78af9cbae9def34114902f511736
|
||||||
F src/btree.h 529c98cb0715c62214544fbbe50b946f99a85540
|
F src/btree.h 529c98cb0715c62214544fbbe50b946f99a85540
|
||||||
F src/btree_rb.c 97375d44bc2cf93b6312acd0f3276177c20e77bb
|
F src/btree_rb.c 85cbf6720db54b844747ce4dba4c3fcc8fd6f0a8
|
||||||
F src/build.c d5a26baeffa5bc49b4b7009a7723c6ab7e1b02d9
|
F src/build.c d5a26baeffa5bc49b4b7009a7723c6ab7e1b02d9
|
||||||
F src/copy.c 6bafc19598daef79d80d16214260611d758a53a1
|
F src/copy.c 44b13fd4d2444fb53bff8a5ecee1c5f6f86a8263
|
||||||
F src/delete.c c5c26039cfdf1eadabff698eb329e3880189795e
|
F src/delete.c 23d33fd8967c6cc492943bbecea93be6491edc6a
|
||||||
F src/encode.c faf03741efe921755ec371cf4a6984536de00042
|
F src/encode.c faf03741efe921755ec371cf4a6984536de00042
|
||||||
F src/expr.c 46e2bb93abd6c70e67c8cdc5d92fdcd0b95498f3
|
F src/expr.c 46e2bb93abd6c70e67c8cdc5d92fdcd0b95498f3
|
||||||
F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
|
F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
|
||||||
F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
|
F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
|
||||||
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
|
F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
|
||||||
F src/insert.c 350167db53b779a8d402d00ec5153410a8003931
|
F src/insert.c 19882be1edc4b1629b8f3097e2615164f2c9cecb
|
||||||
F src/main.c 5e4d4d081d82840a743c57269ca3c32640cefc06
|
F src/main.c 5e4d4d081d82840a743c57269ca3c32640cefc06
|
||||||
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
|
||||||
F src/os.c 7274951ed6894f383cb889342267ded07caf339b
|
F src/os.c 7274951ed6894f383cb889342267ded07caf339b
|
||||||
F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0
|
F src/os.h 9e5bbddff123187295e3d00d49af06192cd1cd49
|
||||||
F src/pager.c df4c81350cbd80c1ab48341ae0768ba78d99ad49
|
F src/pager.c df4c81350cbd80c1ab48341ae0768ba78d99ad49
|
||||||
F src/pager.h e3702f7d384921f6cd5ce0b3ed589185433e9f6c
|
F src/pager.h e3702f7d384921f6cd5ce0b3ed589185433e9f6c
|
||||||
F src/parse.y 15ae47e7dd84304c1c6ae9205558405127977541
|
F src/parse.y 15ae47e7dd84304c1c6ae9205558405127977541
|
||||||
F src/pragma.c 3b1e8da84304d5efa1db5802c67261335b663327
|
F src/pragma.c 3b1e8da84304d5efa1db5802c67261335b663327
|
||||||
F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
|
F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
|
||||||
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
|
||||||
F src/select.c 92a66f0122f321688569e108feceaf74f5f4e63a
|
F src/select.c dfc13cb62ba658c4463179713c40ee25a062b2ba
|
||||||
F src/shell.c a0b7043713713ff45f666ce6b3c03a64109a8bb5
|
F src/shell.c e0b3da1f44a2cc72daf41a4559b1c5f0545944a5
|
||||||
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
|
||||||
F src/sqlite.h.in b4799af223dfc82f47d27c34b14f43b27509a49a
|
F src/sqlite.h.in eec06462cba262c0ee03f38462a18a4bc66dda4e
|
||||||
F src/sqliteInt.h cd2952587a8d3bff5d6cc7a0cfaf0e89c3bef0ad
|
F src/sqliteInt.h 5d15d1dea3f0c497a78c6a123eec5b4b92811c1c
|
||||||
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
|
||||||
F src/tclsqlite.c 9e25f98f1765afa0716144ef57abda75c88f688d
|
F src/tclsqlite.c 9e25f98f1765afa0716144ef57abda75c88f688d
|
||||||
F src/test1.c 4484806861a3099670188a09e12f858ec65aa56c
|
F src/test1.c 4484806861a3099670188a09e12f858ec65aa56c
|
||||||
@@ -55,13 +55,13 @@ F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
|
|||||||
F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
|
F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
|
||||||
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
|
F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
|
||||||
F src/tokenize.c 067d1a477a94af7712ca74e09aaa6bd0f7299527
|
F src/tokenize.c 067d1a477a94af7712ca74e09aaa6bd0f7299527
|
||||||
F src/trigger.c 21ad1677bb0f0625348a01e92d1e0c6d794185a1
|
F src/trigger.c e763f4015c96e06b694184ead5754985c1dfdae0
|
||||||
F src/update.c 3301448786205a7ec2d035c7cb7bd8ae5128c2b0
|
F src/update.c b7fa7c427b74aee6db56ecfa09e5e151e6f9fa6a
|
||||||
F src/util.c 87635cfdfffa056a8d3147719357aa442374f78c
|
F src/util.c 87635cfdfffa056a8d3147719357aa442374f78c
|
||||||
F src/vacuum.c e24781e38db36d1c9f578b6b3613bf0989ebd63c
|
F src/vacuum.c e24781e38db36d1c9f578b6b3613bf0989ebd63c
|
||||||
F src/vdbe.c f0868ac926d98395d28c2a29119364ff11b77852
|
F src/vdbe.c f0868ac926d98395d28c2a29119364ff11b77852
|
||||||
F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
|
F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
|
||||||
F src/where.c c0709e5cf402f30026b597dce9dc3e74f1d07f8e
|
F src/where.c f632cd30f013163484a4d60c249d36fe31f5be12
|
||||||
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
|
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
|
||||||
F test/attach.test b311c83e370e6b22b79a8279317039440ce64862
|
F test/attach.test b311c83e370e6b22b79a8279317039440ce64862
|
||||||
F test/auth.test d25a76f21494b61483787caa7b28c713bc7c7c7f
|
F test/auth.test d25a76f21494b61483787caa7b28c713bc7c7c7f
|
||||||
@@ -120,14 +120,14 @@ F test/temptable.test 6feff1960c707e924d5462356c5303943dac4a8e
|
|||||||
F test/tester.tcl d7a5835edaf118539241145d8188f0822b673488
|
F test/tester.tcl d7a5835edaf118539241145d8188f0822b673488
|
||||||
F test/trans.test 75e7a171b5d2d94ee56766459113e2ad0e5f809d
|
F test/trans.test 75e7a171b5d2d94ee56766459113e2ad0e5f809d
|
||||||
F test/trigger1.test 61ef41666f066ac417730dc26056053a7c36cd11
|
F test/trigger1.test 61ef41666f066ac417730dc26056053a7c36cd11
|
||||||
F test/trigger2.test ab4c743bb96cee96ab5a17c5edfd57a9134329d6
|
F test/trigger2.test adf6a9cfd735bd4be4f7be19da629b0968703744
|
||||||
F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
|
F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
|
||||||
F test/trigger4.test 9a5c1406344d743020c2753ae8d6dfe6eb75f818
|
F test/trigger4.test 9a5c1406344d743020c2753ae8d6dfe6eb75f818
|
||||||
F test/unique.test 22a46df72a3e0a3fd1a2d39e96fb59f18448dd5f
|
F test/unique.test 22a46df72a3e0a3fd1a2d39e96fb59f18448dd5f
|
||||||
F test/update.test 198360dfa14e65354dbcc66d5b98d8070780e42b
|
F test/update.test 198360dfa14e65354dbcc66d5b98d8070780e42b
|
||||||
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
|
F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
|
||||||
F test/version.test 605fd0d7e7d571370c32b12dbf395b58953de246
|
F test/version.test 605fd0d7e7d571370c32b12dbf395b58953de246
|
||||||
F test/view.test c64fa39ea57f3c2066c854290f032ad13b23b83d
|
F test/view.test d356f445d481c04ffa6036a4c61cb8ba70289f69
|
||||||
F test/where.test d719129a052280fe245a2ddcbd09bcc0b8c17ce4
|
F test/where.test d719129a052280fe245a2ddcbd09bcc0b8c17ce4
|
||||||
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
|
||||||
F tool/lemon.c 14fedcde9cf70aa6040b89de164cf8f56f92a4b9
|
F tool/lemon.c 14fedcde9cf70aa6040b89de164cf8f56f92a4b9
|
||||||
@@ -165,7 +165,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 c675a5504138f34cae6def782b5d3add2c67d2bc
|
P 6289b863590ecc5de3d1efaaa60aa6f3f64fefb3
|
||||||
R e42e1c0805bb96ad9734d493fd4b89bd
|
R 566c0486d0b4bc8e9732c5c29c3b5dcc
|
||||||
U drh
|
U drh
|
||||||
Z 6cfe8cf5cfb533faee03d9b06ef89d27
|
Z d31c46a8e175a3b83b9efc55c7de92d9
|
||||||
|
@@ -1 +1 @@
|
|||||||
6289b863590ecc5de3d1efaaa60aa6f3f64fefb3
|
206b17397b1d2b55179c935927ff1d8215728c32
|
17
src/auth.c
17
src/auth.c
@@ -14,7 +14,7 @@
|
|||||||
** systems that do not need this facility may omit it by recompiling
|
** systems that do not need this facility may omit it by recompiling
|
||||||
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
|
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
|
||||||
**
|
**
|
||||||
** $Id: auth.c,v 1.6 2003/04/22 20:30:38 drh Exp $
|
** $Id: auth.c,v 1.7 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -95,11 +95,7 @@ void sqliteAuthRead(
|
|||||||
const char *zCol; /* Name of the column of the table */
|
const char *zCol; /* Name of the column of the table */
|
||||||
int iSrc; /* Index in pTabList->a[] of table being read */
|
int iSrc; /* Index in pTabList->a[] of table being read */
|
||||||
const char *zDBase; /* Name of database being accessed */
|
const char *zDBase; /* Name of database being accessed */
|
||||||
const char *zTrig; /* Name of the trigger doing the accessing */
|
|
||||||
TriggerStack *pStack; /* The stack of current triggers */
|
|
||||||
|
|
||||||
pStack = pParse->trigStack;
|
|
||||||
zTrig = pStack ? pStack->pTrigger->name : 0;
|
|
||||||
if( db->xAuth==0 ) return;
|
if( db->xAuth==0 ) return;
|
||||||
assert( pExpr->op==TK_COLUMN );
|
assert( pExpr->op==TK_COLUMN );
|
||||||
iSrc = pExpr->iTable - base;
|
iSrc = pExpr->iTable - base;
|
||||||
@@ -109,6 +105,8 @@ void sqliteAuthRead(
|
|||||||
/* This must be an attempt to read the NEW or OLD pseudo-tables
|
/* This must be an attempt to read the NEW or OLD pseudo-tables
|
||||||
** of a trigger.
|
** of a trigger.
|
||||||
*/
|
*/
|
||||||
|
TriggerStack *pStack; /* The stack of current triggers */
|
||||||
|
pStack = pParse->trigStack;
|
||||||
assert( pStack!=0 );
|
assert( pStack!=0 );
|
||||||
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
|
assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
|
||||||
pTab = pStack->pTab;
|
pTab = pStack->pTab;
|
||||||
@@ -123,9 +121,10 @@ void sqliteAuthRead(
|
|||||||
}else{
|
}else{
|
||||||
zCol = "ROWID";
|
zCol = "ROWID";
|
||||||
}
|
}
|
||||||
assert( pExpr->iDb>=0 && pExpr->iDb<db->nDb );
|
assert( pExpr->iDb<db->nDb );
|
||||||
zDBase = db->aDb[pExpr->iDb].zName;
|
zDBase = db->aDb[pExpr->iDb].zName;
|
||||||
rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, zTrig);
|
rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase,
|
||||||
|
pParse->zAuthContext);
|
||||||
if( rc==SQLITE_IGNORE ){
|
if( rc==SQLITE_IGNORE ){
|
||||||
pExpr->op = TK_NULL;
|
pExpr->op = TK_NULL;
|
||||||
}else if( rc==SQLITE_DENY ){
|
}else if( rc==SQLITE_DENY ){
|
||||||
@@ -158,13 +157,11 @@ int sqliteAuthCheck(
|
|||||||
){
|
){
|
||||||
sqlite *db = pParse->db;
|
sqlite *db = pParse->db;
|
||||||
int rc;
|
int rc;
|
||||||
const char *zTrigName;
|
|
||||||
|
|
||||||
if( db->xAuth==0 ){
|
if( db->xAuth==0 ){
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
zTrigName = pParse->trigStack ? pParse->trigStack->pTrigger->name : 0;
|
rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
|
||||||
rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, zTrigName);
|
|
||||||
if( rc==SQLITE_DENY ){
|
if( rc==SQLITE_DENY ){
|
||||||
sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
|
sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
|
||||||
pParse->rc = SQLITE_AUTH;
|
pParse->rc = SQLITE_AUTH;
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree_rb.c,v 1.6 2003/04/20 17:29:24 drh Exp $
|
** $Id: btree_rb.c,v 1.7 2003/04/24 01:45:04 drh Exp $
|
||||||
**
|
**
|
||||||
** This file implements an in-core database using Red-Black balanced
|
** This file implements an in-core database using Red-Black balanced
|
||||||
** binary trees.
|
** binary trees.
|
||||||
@@ -1082,10 +1082,10 @@ static int memBtreeKey(BtCursor* pCur, int offset, int amt, char *zBuf)
|
|||||||
{
|
{
|
||||||
if( !pCur->pNode ) return 0;
|
if( !pCur->pNode ) return 0;
|
||||||
if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
|
if( !pCur->pNode->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
|
||||||
memcpy(zBuf, pCur->pNode->pKey+offset, amt);
|
memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt);
|
||||||
return amt;
|
return amt;
|
||||||
}else{
|
}else{
|
||||||
memcpy(zBuf, pCur->pNode->pKey+offset ,pCur->pNode->nKey-offset);
|
memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, pCur->pNode->nKey-offset);
|
||||||
return pCur->pNode->nKey-offset;
|
return pCur->pNode->nKey-offset;
|
||||||
}
|
}
|
||||||
assert(0);
|
assert(0);
|
||||||
@@ -1105,10 +1105,10 @@ static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
|
|||||||
{
|
{
|
||||||
if( !pCur->pNode ) return 0;
|
if( !pCur->pNode ) return 0;
|
||||||
if( (amt + offset) <= pCur->pNode->nData ){
|
if( (amt + offset) <= pCur->pNode->nData ){
|
||||||
memcpy(zBuf, pCur->pNode->pData+offset, amt);
|
memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt);
|
||||||
return amt;
|
return amt;
|
||||||
}else{
|
}else{
|
||||||
memcpy(zBuf, pCur->pNode->pData+offset ,pCur->pNode->nData-offset);
|
memcpy(zBuf, ((char*)pCur->pNode->pData)+offset ,pCur->pNode->nData-offset);
|
||||||
return pCur->pNode->nData-offset;
|
return pCur->pNode->nData-offset;
|
||||||
}
|
}
|
||||||
assert(0);
|
assert(0);
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** This file contains code used to implement the COPY command.
|
** This file contains code used to implement the COPY command.
|
||||||
**
|
**
|
||||||
** $Id: copy.c,v 1.3 2003/04/22 20:30:39 drh Exp $
|
** $Id: copy.c,v 1.4 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ void sqliteCopy(
|
|||||||
if( sqlite_malloc_failed ) goto copy_cleanup;
|
if( sqlite_malloc_failed ) goto copy_cleanup;
|
||||||
assert( pTableName->nSrc==1 );
|
assert( pTableName->nSrc==1 );
|
||||||
pTab = sqliteSrcListLookup(pParse, pTableName);
|
pTab = sqliteSrcListLookup(pParse, pTableName);
|
||||||
if( pTab==0 || sqliteIsReadOnly(pParse, pTab) ) goto copy_cleanup;
|
if( pTab==0 || sqliteIsReadOnly(pParse, pTab, 0) ) goto copy_cleanup;
|
||||||
zFile = sqliteStrNDup(pFilename->z, pFilename->n);
|
zFile = sqliteStrNDup(pFilename->z, pFilename->n);
|
||||||
sqliteDequote(zFile);
|
sqliteDequote(zFile);
|
||||||
assert( pTab->iDb>=0 && pTab->iDb<db->nDb );
|
assert( pTab->iDb<db->nDb );
|
||||||
zDb = db->aDb[pTab->iDb].zName;
|
zDb = db->aDb[pTab->iDb].zName;
|
||||||
if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
|
if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
|
||||||
|| sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
|
|| sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
|
||||||
|
114
src/delete.c
114
src/delete.c
@@ -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.53 2003/04/22 20:30:39 drh Exp $
|
** $Id: delete.c,v 1.54 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -38,11 +38,13 @@ Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){
|
|||||||
** writable, generate an error message and return 1. If it is
|
** writable, generate an error message and return 1. If it is
|
||||||
** writable return 0;
|
** writable return 0;
|
||||||
*/
|
*/
|
||||||
int sqliteIsReadOnly(Parse *pParse, Table *pTab){
|
int sqliteIsReadOnly(Parse *pParse, Table *pTab, int viewOk){
|
||||||
if( pTab->readOnly || pTab->pSelect ){
|
if( pTab->readOnly ){
|
||||||
sqliteErrorMsg(pParse, "%s %s may not be modified",
|
sqliteErrorMsg(pParse, "table %s may not be modified", pTab->zName);
|
||||||
pTab->pSelect ? "view" : "table",
|
return 1;
|
||||||
pTab->zName);
|
}
|
||||||
|
if( !viewOk && pTab->pSelect ){
|
||||||
|
sqliteErrorMsg(pParse, "cannot modify %s because it is a view",pTab->zName);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -65,6 +67,7 @@ void sqliteDeleteFrom(
|
|||||||
Index *pIdx; /* For looping over indices of the table */
|
Index *pIdx; /* For looping over indices of the table */
|
||||||
int base; /* Index of the first available table cursor */
|
int base; /* Index of the first available table cursor */
|
||||||
sqlite *db; /* Main database structure */
|
sqlite *db; /* Main database structure */
|
||||||
|
int isView; /* True if attempting to delete from a view */
|
||||||
|
|
||||||
int row_triggers_exist = 0; /* True if any triggers exist */
|
int row_triggers_exist = 0; /* True if any triggers exist */
|
||||||
int before_triggers; /* True if there are BEFORE triggers */
|
int before_triggers; /* True if there are BEFORE triggers */
|
||||||
@@ -90,20 +93,22 @@ void sqliteDeleteFrom(
|
|||||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||||
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
TK_DELETE, TK_AFTER, TK_ROW, 0);
|
||||||
row_triggers_exist = before_triggers || after_triggers;
|
row_triggers_exist = before_triggers || after_triggers;
|
||||||
if( row_triggers_exist && pTab->pSelect ){
|
isView = pTab->pSelect!=0;
|
||||||
/* Just fire VIEW triggers */
|
if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
|
||||||
sqliteSrcListDelete(pTabList);
|
goto delete_from_cleanup;
|
||||||
sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if( sqliteIsReadOnly(pParse, pTab) ) goto delete_from_cleanup;
|
|
||||||
assert( pTab->pSelect==0 ); /* This table is not a view */
|
|
||||||
assert( pTab->iDb<db->nDb );
|
assert( pTab->iDb<db->nDb );
|
||||||
zDb = db->aDb[pTab->iDb].zName;
|
zDb = db->aDb[pTab->iDb].zName;
|
||||||
if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
|
if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
|
||||||
goto delete_from_cleanup;
|
goto delete_from_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If pTab is really a view, make sure it has been initialized.
|
||||||
|
*/
|
||||||
|
if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
|
||||||
|
goto delete_from_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate a cursor used to store the old.* data for a trigger.
|
/* Allocate a cursor used to store the old.* data for a trigger.
|
||||||
*/
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
@@ -131,6 +136,15 @@ void sqliteDeleteFrom(
|
|||||||
sqliteBeginWriteOperation(pParse, row_triggers_exist,
|
sqliteBeginWriteOperation(pParse, row_triggers_exist,
|
||||||
!row_triggers_exist && pTab->iDb==1);
|
!row_triggers_exist && pTab->iDb==1);
|
||||||
|
|
||||||
|
/* If we are trying to delete from a view, construct that view into
|
||||||
|
** a temporary table.
|
||||||
|
*/
|
||||||
|
if( isView ){
|
||||||
|
Select *pView = sqliteSelectDup(pTab->pSelect);
|
||||||
|
sqliteSelect(pParse, pView, SRT_TempTable, base, 0, 0, 0);
|
||||||
|
sqliteSelectDelete(pView);
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the counter of the number of rows deleted, if
|
/* Initialize the counter of the number of rows deleted, if
|
||||||
** we are counting rows.
|
** we are counting rows.
|
||||||
*/
|
*/
|
||||||
@@ -148,17 +162,21 @@ void sqliteDeleteFrom(
|
|||||||
** entries in the table. */
|
** entries in the table. */
|
||||||
int endOfLoop = sqliteVdbeMakeLabel(v);
|
int endOfLoop = sqliteVdbeMakeLabel(v);
|
||||||
int addr;
|
int addr;
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
if( !isView ){
|
||||||
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
||||||
|
}
|
||||||
sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2);
|
sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2);
|
||||||
addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Next, base, addr);
|
sqliteVdbeAddOp(v, OP_Next, base, addr);
|
||||||
sqliteVdbeResolveLabel(v, endOfLoop);
|
sqliteVdbeResolveLabel(v, endOfLoop);
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
|
if( !isView ){
|
||||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
|
||||||
sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
|
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||||
|
sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,51 +219,59 @@ void sqliteDeleteFrom(
|
|||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
|
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
|
||||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
if( !isView ){
|
||||||
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
||||||
|
}
|
||||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
sqliteVdbeAddOp(v, OP_Recno, base, 0);
|
||||||
sqliteVdbeAddOp(v, OP_RowData, base, 0);
|
sqliteVdbeAddOp(v, OP_RowData, base, 0);
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
sqliteVdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
if( !isView ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
|
}
|
||||||
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
|
||||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||||
addr);
|
addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open cursors for the table we are deleting from and all its
|
if( !isView ){
|
||||||
** indices. If there are row triggers, this happens inside the
|
/* Open cursors for the table we are deleting from and all its
|
||||||
** OP_ListRead loop because the cursor have to all be closed
|
** indices. If there are row triggers, this happens inside the
|
||||||
** before the trigger fires. If there are no row triggers, the
|
** OP_ListRead loop because the cursor have to all be closed
|
||||||
** cursors are opened only once on the outside the loop.
|
** before the trigger fires. If there are no row triggers, the
|
||||||
*/
|
** cursors are opened only once on the outside the loop.
|
||||||
pParse->nTab = base + 1;
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
pParse->nTab = base + 1;
|
||||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||||
sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
|
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||||
}
|
sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
|
||||||
|
}
|
||||||
|
|
||||||
/* This is the beginning of the delete loop when there are no
|
/* This is the beginning of the delete loop when there are no
|
||||||
** row triggers */
|
** row triggers */
|
||||||
if( !row_triggers_exist ){
|
if( !row_triggers_exist ){
|
||||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
|
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete the row */
|
/* Delete the row */
|
||||||
sqliteGenerateRowDelete(db, v, pTab, base, pParse->trigStack==0);
|
sqliteGenerateRowDelete(db, v, pTab, base, pParse->trigStack==0);
|
||||||
|
}
|
||||||
|
|
||||||
/* If there are row triggers, close all cursors then invoke
|
/* If there are row triggers, close all cursors then invoke
|
||||||
** the AFTER triggers
|
** the AFTER triggers
|
||||||
*/
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
if( !isView ){
|
||||||
sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
|
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
|
||||||
|
}
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
|
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
|
||||||
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
|
||||||
addr);
|
addr);
|
||||||
|
56
src/insert.c
56
src/insert.c
@@ -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.81 2003/04/22 20:30:39 drh Exp $
|
** $Id: insert.c,v 1.82 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -109,6 +109,7 @@ void sqliteInsert(
|
|||||||
int iCleanup; /* Address of the cleanup code */
|
int iCleanup; /* Address of the cleanup code */
|
||||||
int iInsertBlock; /* Address of the subroutine used to insert data */
|
int iInsertBlock; /* Address of the subroutine used to insert data */
|
||||||
int iCntMem; /* Memory cell used for the row counter */
|
int iCntMem; /* Memory cell used for the row counter */
|
||||||
|
int isView; /* True if attempting to insert into a view */
|
||||||
|
|
||||||
int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
|
int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
|
||||||
int before_triggers; /* True if there are BEFORE triggers */
|
int before_triggers; /* True if there are BEFORE triggers */
|
||||||
@@ -142,21 +143,16 @@ void sqliteInsert(
|
|||||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
|
||||||
TK_AFTER, TK_ROW, 0);
|
TK_AFTER, TK_ROW, 0);
|
||||||
row_triggers_exist = before_triggers || after_triggers;
|
row_triggers_exist = before_triggers || after_triggers;
|
||||||
if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){
|
isView = pTab->pSelect!=0;
|
||||||
sqliteErrorMsg(pParse, "%s %s may not be modified",
|
if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
|
||||||
pTab->pSelect ? "view" : "table",
|
|
||||||
zTab);
|
|
||||||
goto insert_cleanup;
|
goto insert_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( pTab==0 ) goto insert_cleanup;
|
if( pTab==0 ) goto insert_cleanup;
|
||||||
|
|
||||||
/* If pTab is really a view, make sure it has been initialized.
|
/* If pTab is really a view, make sure it has been initialized.
|
||||||
*/
|
*/
|
||||||
if( pTab->pSelect ){
|
if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
|
||||||
if( sqliteViewGetColumnNames(pParse, pTab) ){
|
goto insert_cleanup;
|
||||||
goto insert_cleanup;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a VDBE
|
/* Allocate a VDBE
|
||||||
@@ -356,7 +352,7 @@ void sqliteInsert(
|
|||||||
sqliteVdbeResolveLabel(v, iInsertBlock);
|
sqliteVdbeResolveLabel(v, iInsertBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run the BEFORE triggers, if there are any
|
/* Run the BEFORE and INSTEAD OF triggers, if there are any
|
||||||
*/
|
*/
|
||||||
endOfLoop = sqliteVdbeMakeLabel(v);
|
endOfLoop = sqliteVdbeMakeLabel(v);
|
||||||
if( before_triggers ){
|
if( before_triggers ){
|
||||||
@@ -405,7 +401,7 @@ void sqliteInsert(
|
|||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||||
|
|
||||||
/* Fire BEFORE triggers */
|
/* Fire BEFORE or INSTEAD OF triggers */
|
||||||
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab,
|
if( sqliteCodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab,
|
||||||
newIdx, -1, onError, endOfLoop) ){
|
newIdx, -1, onError, endOfLoop) ){
|
||||||
goto insert_cleanup;
|
goto insert_cleanup;
|
||||||
@@ -415,19 +411,17 @@ void sqliteInsert(
|
|||||||
/* If any triggers exists, the opening of tables and indices is deferred
|
/* If any triggers exists, the opening of tables and indices is deferred
|
||||||
** until now.
|
** until now.
|
||||||
*/
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist && !isView ){
|
||||||
if( !pTab->pSelect ){
|
base = pParse->nTab;
|
||||||
base = pParse->nTab;
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
||||||
sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
|
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
|
||||||
sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
|
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
||||||
sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
|
|
||||||
}
|
|
||||||
pParse->nTab += idx;
|
|
||||||
}
|
}
|
||||||
|
pParse->nTab += idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push the record number for the new entry onto the stack. The
|
/* Push the record number for the new entry onto the stack. The
|
||||||
@@ -435,7 +429,7 @@ void sqliteInsert(
|
|||||||
** except when the table has an INTEGER PRIMARY KEY column, in which
|
** except when the table has an INTEGER PRIMARY KEY column, in which
|
||||||
** case the record number is the same as that column.
|
** case the record number is the same as that column.
|
||||||
*/
|
*/
|
||||||
if( !pTab->pSelect ){
|
if( !isView ){
|
||||||
if( keyColumn>=0 ){
|
if( keyColumn>=0 ){
|
||||||
if( useTempTable ){
|
if( useTempTable ){
|
||||||
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
|
sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
|
||||||
@@ -492,17 +486,17 @@ void sqliteInsert(
|
|||||||
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
|
sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
|
||||||
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
|
sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
|
||||||
after_triggers ? newIdx : -1);
|
after_triggers ? newIdx : -1);
|
||||||
|
}
|
||||||
|
|
||||||
/* Update the count of rows that are inserted
|
/* Update the count of rows that are inserted
|
||||||
*/
|
*/
|
||||||
if( (db->flags & SQLITE_CountRows)!=0 ){
|
if( (db->flags & SQLITE_CountRows)!=0 ){
|
||||||
sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
|
sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
/* Close all tables opened */
|
/* Close all tables opened */
|
||||||
if( !pTab->pSelect ){
|
if( !isView ){
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
|
||||||
sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
|
sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
|
||||||
|
5
src/os.h
5
src/os.h
@@ -17,6 +17,11 @@
|
|||||||
#ifndef _SQLITE_OS_H_
|
#ifndef _SQLITE_OS_H_
|
||||||
#define _SQLITE_OS_H_
|
#define _SQLITE_OS_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Helpful hint: To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE
|
||||||
|
** to the compiler command line.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** These #defines should enable >2GB file support on Posix if the
|
** These #defines should enable >2GB file support on Posix if the
|
||||||
** underlying operating system supports it. If the OS lacks
|
** underlying operating system supports it. If the OS lacks
|
||||||
|
21
src/select.c
21
src/select.c
@@ -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 SELECT statements in SQLite.
|
** to handle SELECT statements in SQLite.
|
||||||
**
|
**
|
||||||
** $Id: select.c,v 1.133 2003/04/22 20:30:39 drh Exp $
|
** $Id: select.c,v 1.134 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -904,6 +904,10 @@ static int fillInColumnList(Parse *pParse, Select *p){
|
|||||||
if( pTab==0 ){
|
if( pTab==0 ){
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
/* The isTransient flag indicates that the Table structure has been
|
||||||
|
** dynamically allocated and may be freed at any time. In other words,
|
||||||
|
** pTab is not pointing to a persistent table structure that defines
|
||||||
|
** part of the schema. */
|
||||||
pTab->isTransient = 1;
|
pTab->isTransient = 1;
|
||||||
}else{
|
}else{
|
||||||
/* An ordinary table or view name in the FROM clause */
|
/* An ordinary table or view name in the FROM clause */
|
||||||
@@ -1048,6 +1052,9 @@ static int fillInColumnList(Parse *pParse, Select *p){
|
|||||||
** This routine is called on the Select structure that defines a
|
** This routine is called on the Select structure that defines a
|
||||||
** VIEW in order to undo any bindings to tables. This is necessary
|
** VIEW in order to undo any bindings to tables. This is necessary
|
||||||
** because those tables might be DROPed by a subsequent SQL command.
|
** because those tables might be DROPed by a subsequent SQL command.
|
||||||
|
** If the bindings are not removed, then the Select.pSrc->a[].pTab field
|
||||||
|
** will be left pointing to a deallocated Table structure after the
|
||||||
|
** DROP and a coredump will occur the next time the VIEW is used.
|
||||||
*/
|
*/
|
||||||
void sqliteSelectUnbind(Select *p){
|
void sqliteSelectUnbind(Select *p){
|
||||||
int i;
|
int i;
|
||||||
@@ -1058,10 +1065,6 @@ void sqliteSelectUnbind(Select *p){
|
|||||||
if( (pTab = pSrc->a[i].pTab)!=0 ){
|
if( (pTab = pSrc->a[i].pTab)!=0 ){
|
||||||
if( pTab->isTransient ){
|
if( pTab->isTransient ){
|
||||||
sqliteDeleteTable(0, pTab);
|
sqliteDeleteTable(0, pTab);
|
||||||
#if 0
|
|
||||||
sqliteSelectDelete(pSrc->a[i].pSelect);
|
|
||||||
pSrc->a[i].pSelect = 0;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
pSrc->a[i].pTab = 0;
|
pSrc->a[i].pTab = 0;
|
||||||
if( pSrc->a[i].pSelect ){
|
if( pSrc->a[i].pSelect ){
|
||||||
@@ -2113,9 +2116,17 @@ int sqliteSelect(
|
|||||||
/* Generate code for all sub-queries in the FROM clause
|
/* Generate code for all sub-queries in the FROM clause
|
||||||
*/
|
*/
|
||||||
for(i=0; i<pTabList->nSrc; i++){
|
for(i=0; i<pTabList->nSrc; i++){
|
||||||
|
const char *zSavedAuthContext;
|
||||||
if( pTabList->a[i].pSelect==0 ) continue;
|
if( pTabList->a[i].pSelect==0 ) continue;
|
||||||
|
if( pTabList->a[i].zName!=0 ){
|
||||||
|
zSavedAuthContext = pParse->zAuthContext;
|
||||||
|
pParse->zAuthContext = pTabList->a[i].zName;
|
||||||
|
}
|
||||||
sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_TempTable, base+i,
|
sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_TempTable, base+i,
|
||||||
p, i, &isAgg);
|
p, i, &isAgg);
|
||||||
|
if( pTabList->a[i].zName!=0 ){
|
||||||
|
pParse->zAuthContext = zSavedAuthContext;
|
||||||
|
}
|
||||||
pTabList = p->pSrc;
|
pTabList = p->pSrc;
|
||||||
pWhere = p->pWhere;
|
pWhere = p->pWhere;
|
||||||
if( eDest==SRT_Callback ){
|
if( eDest==SRT_Callback ){
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
** This file contains code to implement the "sqlite" command line
|
** This file contains code to implement the "sqlite" command line
|
||||||
** utility for accessing SQLite databases.
|
** utility for accessing SQLite databases.
|
||||||
**
|
**
|
||||||
** $Id: shell.c,v 1.70 2003/04/17 22:57:54 drh Exp $
|
** $Id: shell.c,v 1.71 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -1183,7 +1183,7 @@ int main(int argc, char **argv){
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
extern int isatty();
|
extern int isatty();
|
||||||
if( isatty(fileno(stdout)) ){
|
if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){
|
||||||
char *zHome;
|
char *zHome;
|
||||||
char *zHistory = 0;
|
char *zHistory = 0;
|
||||||
printf(
|
printf(
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
** This header file defines the interface that the SQLite library
|
** This header file defines the interface that the SQLite library
|
||||||
** presents to client programs.
|
** presents to client programs.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqlite.h.in,v 1.46 2003/04/23 12:25:24 drh Exp $
|
** @(#) $Id: sqlite.h.in,v 1.47 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITE_H_
|
#ifndef _SQLITE_H_
|
||||||
#define _SQLITE_H_
|
#define _SQLITE_H_
|
||||||
@@ -524,8 +524,9 @@ int sqlite_set_authorizer(
|
|||||||
** function will be parameters or NULL depending on which of the following
|
** function will be parameters or NULL depending on which of the following
|
||||||
** codes is used as the second parameter. The 5th parameter is the name
|
** codes is used as the second parameter. The 5th parameter is the name
|
||||||
** of the database ("main", "temp", etc.) if applicable. The 6th parameter
|
** of the database ("main", "temp", etc.) if applicable. The 6th parameter
|
||||||
** is the name of the trigger that is responsible for the access attempt,
|
** is the name of the inner-most trigger or view that is responsible for
|
||||||
** or NULL if this access attempt is directly from input SQL code.
|
** the access attempt or NULL if this access attempt is directly from
|
||||||
|
** input SQL code.
|
||||||
**
|
**
|
||||||
** Arg-3 Arg-4
|
** Arg-3 Arg-4
|
||||||
*/
|
*/
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.179 2003/04/23 12:25:24 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.180 2003/04/24 01:45:04 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "sqlite.h"
|
#include "sqlite.h"
|
||||||
@@ -829,6 +829,7 @@ struct Parse {
|
|||||||
int nSet; /* Number of sets used so far */
|
int nSet; /* Number of sets used so far */
|
||||||
int nAgg; /* Number of aggregate expressions */
|
int nAgg; /* Number of aggregate expressions */
|
||||||
AggExpr *aAgg; /* An array of aggregate expressions */
|
AggExpr *aAgg; /* An array of aggregate expressions */
|
||||||
|
const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
|
||||||
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
|
Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
|
||||||
TriggerStack *trigStack; /* Trigger actions being coded */
|
TriggerStack *trigStack; /* Trigger actions being coded */
|
||||||
};
|
};
|
||||||
@@ -1039,7 +1040,7 @@ Select *sqliteSelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
|
|||||||
void sqliteSelectDelete(Select*);
|
void sqliteSelectDelete(Select*);
|
||||||
void sqliteSelectUnbind(Select*);
|
void sqliteSelectUnbind(Select*);
|
||||||
Table *sqliteSrcListLookup(Parse*, SrcList*);
|
Table *sqliteSrcListLookup(Parse*, SrcList*);
|
||||||
int sqliteIsReadOnly(Parse*, Table*);
|
int sqliteIsReadOnly(Parse*, Table*, int);
|
||||||
void sqliteDeleteFrom(Parse*, SrcList*, Expr*);
|
void sqliteDeleteFrom(Parse*, SrcList*, Expr*);
|
||||||
void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int);
|
void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int);
|
||||||
WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**);
|
WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**);
|
||||||
|
155
src/trigger.c
155
src/trigger.c
@@ -41,7 +41,7 @@ void sqliteDeleteTriggerStep(TriggerStep *pTriggerStep){
|
|||||||
void sqliteBeginTrigger(
|
void sqliteBeginTrigger(
|
||||||
Parse *pParse, /* The parse context of the CREATE TRIGGER statement */
|
Parse *pParse, /* The parse context of the CREATE TRIGGER statement */
|
||||||
Token *pName, /* The name of the trigger */
|
Token *pName, /* The name of the trigger */
|
||||||
int tr_tm, /* One of TK_BEFORE, TK_AFTER , TK_INSTEAD */
|
int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
|
||||||
int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
|
int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
|
||||||
IdList *pColumns, /* column list if this is an UPDATE OF trigger */
|
IdList *pColumns, /* column list if this is an UPDATE OF trigger */
|
||||||
SrcList *pTableName,/* The name of the table/view the trigger applies to */
|
SrcList *pTableName,/* The name of the table/view the trigger applies to */
|
||||||
@@ -110,6 +110,11 @@ void sqliteBeginTrigger(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* INSTEAD OF triggers can only appear on views and BEGIN triggers
|
||||||
|
** cannot appear on views. So we might as well translate every
|
||||||
|
** INSTEAD OF trigger into a BEFORE trigger. It simplifies code
|
||||||
|
** elsewhere.
|
||||||
|
*/
|
||||||
if (tr_tm == TK_INSTEAD){
|
if (tr_tm == TK_INSTEAD){
|
||||||
tr_tm = TK_BEFORE;
|
tr_tm = TK_BEFORE;
|
||||||
}
|
}
|
||||||
@@ -627,7 +632,7 @@ int sqliteCodeRowTrigger(
|
|||||||
TriggerStack * pTriggerStack;
|
TriggerStack * pTriggerStack;
|
||||||
|
|
||||||
assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
|
assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
|
||||||
assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER);
|
assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );
|
||||||
|
|
||||||
assert(newIdx != -1 || oldIdx != -1);
|
assert(newIdx != -1 || oldIdx != -1);
|
||||||
|
|
||||||
@@ -656,6 +661,7 @@ int sqliteCodeRowTrigger(
|
|||||||
int endTrigger;
|
int endTrigger;
|
||||||
SrcList dummyTablist;
|
SrcList dummyTablist;
|
||||||
Expr * whenExpr;
|
Expr * whenExpr;
|
||||||
|
const char *zSavedAuthContext;
|
||||||
|
|
||||||
dummyTablist.nSrc = 0;
|
dummyTablist.nSrc = 0;
|
||||||
|
|
||||||
@@ -667,6 +673,8 @@ int sqliteCodeRowTrigger(
|
|||||||
pTriggerStack->pNext = pParse->trigStack;
|
pTriggerStack->pNext = pParse->trigStack;
|
||||||
pTriggerStack->ignoreJump = ignoreJump;
|
pTriggerStack->ignoreJump = ignoreJump;
|
||||||
pParse->trigStack = pTriggerStack;
|
pParse->trigStack = pTriggerStack;
|
||||||
|
zSavedAuthContext = pParse->zAuthContext;
|
||||||
|
pParse->zAuthContext = pTrigger->name;
|
||||||
|
|
||||||
/* code the WHEN clause */
|
/* code the WHEN clause */
|
||||||
endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
|
endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
|
||||||
@@ -684,6 +692,7 @@ int sqliteCodeRowTrigger(
|
|||||||
|
|
||||||
/* Pop the entry off the trigger stack */
|
/* Pop the entry off the trigger stack */
|
||||||
pParse->trigStack = pParse->trigStack->pNext;
|
pParse->trigStack = pParse->trigStack->pNext;
|
||||||
|
pParse->zAuthContext = zSavedAuthContext;
|
||||||
sqliteFree(pTriggerStack);
|
sqliteFree(pTriggerStack);
|
||||||
|
|
||||||
sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
|
sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
|
||||||
@@ -693,145 +702,3 @@ int sqliteCodeRowTrigger(
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This function is called to code ON UPDATE and ON DELETE triggers on
|
|
||||||
* views.
|
|
||||||
*
|
|
||||||
* This function deletes the data pointed at by the pWhere and pChanges
|
|
||||||
* arguments before it completes.
|
|
||||||
*/
|
|
||||||
void sqliteViewTriggers(
|
|
||||||
Parse *pParse,
|
|
||||||
Table *pTab, /* The view to code triggers on */
|
|
||||||
Expr *pWhere, /* The WHERE clause of the statement causing triggers*/
|
|
||||||
int orconf, /* The ON CONFLICT policy specified as part of the
|
|
||||||
statement causing these triggers */
|
|
||||||
ExprList *pChanges /* If this is an statement causing triggers to fire
|
|
||||||
is an UPDATE, then this list holds the columns
|
|
||||||
to update and the expressions to update them to.
|
|
||||||
See comments for sqliteUpdate(). */
|
|
||||||
){
|
|
||||||
int oldIdx = -1;
|
|
||||||
int newIdx = -1;
|
|
||||||
int *aXRef = 0;
|
|
||||||
Vdbe *v;
|
|
||||||
int endOfLoop;
|
|
||||||
int startOfLoop;
|
|
||||||
Select theSelect;
|
|
||||||
Token tblNameToken;
|
|
||||||
|
|
||||||
assert(pTab->pSelect);
|
|
||||||
|
|
||||||
tblNameToken.z = pTab->zName;
|
|
||||||
tblNameToken.n = strlen(pTab->zName);
|
|
||||||
|
|
||||||
theSelect.isDistinct = 0;
|
|
||||||
theSelect.pEList = sqliteExprListAppend(0, sqliteExpr(TK_ALL, 0, 0, 0), 0);
|
|
||||||
theSelect.pSrc = sqliteSrcListAppend(0, &tblNameToken, 0);
|
|
||||||
theSelect.pWhere = pWhere; pWhere = 0;
|
|
||||||
theSelect.pGroupBy = 0;
|
|
||||||
theSelect.pHaving = 0;
|
|
||||||
theSelect.pOrderBy = 0;
|
|
||||||
theSelect.op = TK_SELECT; /* ?? */
|
|
||||||
theSelect.pPrior = 0;
|
|
||||||
theSelect.nLimit = -1;
|
|
||||||
theSelect.nOffset = -1;
|
|
||||||
theSelect.zSelect = 0;
|
|
||||||
theSelect.base = 0;
|
|
||||||
|
|
||||||
v = sqliteGetVdbe(pParse);
|
|
||||||
assert(v);
|
|
||||||
sqliteBeginWriteOperation(pParse, 1, 0);
|
|
||||||
|
|
||||||
/* Allocate temp tables */
|
|
||||||
oldIdx = pParse->nTab++;
|
|
||||||
sqliteVdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
|
|
||||||
if( pChanges ){
|
|
||||||
newIdx = pParse->nTab++;
|
|
||||||
sqliteVdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Snapshot the view */
|
|
||||||
if( sqliteSelect(pParse, &theSelect, SRT_Table, oldIdx, 0, 0, 0) ){
|
|
||||||
goto trigger_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loop thru the view snapshot, executing triggers for each row */
|
|
||||||
endOfLoop = sqliteVdbeMakeLabel(v);
|
|
||||||
sqliteVdbeAddOp(v, OP_Rewind, oldIdx, endOfLoop);
|
|
||||||
|
|
||||||
/* Loop thru the view snapshot, executing triggers for each row */
|
|
||||||
startOfLoop = sqliteVdbeCurrentAddr(v);
|
|
||||||
|
|
||||||
/* Build the updated row if required */
|
|
||||||
if( pChanges ){
|
|
||||||
int ii;
|
|
||||||
|
|
||||||
aXRef = sqliteMalloc( sizeof(int) * pTab->nCol );
|
|
||||||
if( aXRef==0 ) goto trigger_cleanup;
|
|
||||||
for(ii = 0; ii < pTab->nCol; ii++){
|
|
||||||
aXRef[ii] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(ii=0; ii<pChanges->nExpr; ii++){
|
|
||||||
int jj;
|
|
||||||
if( sqliteExprResolveIds(pParse, oldIdx, theSelect.pSrc , 0,
|
|
||||||
pChanges->a[ii].pExpr) ){
|
|
||||||
goto trigger_cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( sqliteExprCheck(pParse, pChanges->a[ii].pExpr, 0, 0) )
|
|
||||||
goto trigger_cleanup;
|
|
||||||
|
|
||||||
for(jj=0; jj<pTab->nCol; jj++){
|
|
||||||
if( sqliteStrICmp(pTab->aCol[jj].zName, pChanges->a[ii].zName)==0 ){
|
|
||||||
aXRef[jj] = ii;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( jj>=pTab->nCol ){
|
|
||||||
sqliteErrorMsg(pParse, "no such column: %s", pChanges->a[ii].zName);
|
|
||||||
goto trigger_cleanup;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Integer, 13, 0);
|
|
||||||
|
|
||||||
for(ii = 0; ii<pTab->nCol; ii++){
|
|
||||||
if( aXRef[ii] < 0 ){
|
|
||||||
sqliteVdbeAddOp(v, OP_Column, oldIdx, ii);
|
|
||||||
}else{
|
|
||||||
sqliteExprCode(pParse, pChanges->a[aXRef[ii]].pExpr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
|
||||||
sqliteVdbeAddOp(v, OP_Rewind, newIdx, 0);
|
|
||||||
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE,
|
|
||||||
pTab, newIdx, oldIdx, orconf, endOfLoop);
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER,
|
|
||||||
pTab, newIdx, oldIdx, orconf, endOfLoop);
|
|
||||||
}else{
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1, oldIdx,
|
|
||||||
orconf, endOfLoop);
|
|
||||||
sqliteCodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1, oldIdx,
|
|
||||||
orconf, endOfLoop);
|
|
||||||
}
|
|
||||||
|
|
||||||
sqliteVdbeAddOp(v, OP_Next, oldIdx, startOfLoop);
|
|
||||||
|
|
||||||
sqliteVdbeResolveLabel(v, endOfLoop);
|
|
||||||
sqliteEndWriteOperation(pParse);
|
|
||||||
|
|
||||||
trigger_cleanup:
|
|
||||||
sqliteFree(aXRef);
|
|
||||||
sqliteExprListDelete(pChanges);
|
|
||||||
sqliteExprDelete(pWhere);
|
|
||||||
sqliteExprListDelete(theSelect.pEList);
|
|
||||||
sqliteSrcListDelete(theSelect.pSrc);
|
|
||||||
sqliteExprDelete(theSelect.pWhere);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
205
src/update.c
205
src/update.c
@@ -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.62 2003/04/22 20:30:40 drh Exp $
|
** $Id: update.c,v 1.63 2003/04/24 01:45:05 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -48,6 +48,7 @@ void sqliteUpdate(
|
|||||||
int chngRecno; /* True if the record number is being changed */
|
int chngRecno; /* True if the record number is being changed */
|
||||||
Expr *pRecnoExpr; /* Expression defining the new record number */
|
Expr *pRecnoExpr; /* Expression defining the new record number */
|
||||||
int openAll; /* True if all indices need to be opened */
|
int openAll; /* True if all indices need to be opened */
|
||||||
|
int isView; /* Trying to update a view */
|
||||||
|
|
||||||
int before_triggers; /* True if there are any BEFORE triggers */
|
int before_triggers; /* True if there are any BEFORE triggers */
|
||||||
int after_triggers; /* True if there are any AFTER triggers */
|
int after_triggers; /* True if there are any AFTER triggers */
|
||||||
@@ -69,14 +70,13 @@ void sqliteUpdate(
|
|||||||
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger,
|
||||||
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
|
||||||
row_triggers_exist = before_triggers || after_triggers;
|
row_triggers_exist = before_triggers || after_triggers;
|
||||||
if( row_triggers_exist && pTab->pSelect ){
|
isView = pTab->pSelect!=0;
|
||||||
/* Just fire VIEW triggers */
|
if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
|
||||||
sqliteSrcListDelete(pTabList);
|
goto update_cleanup;
|
||||||
sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
|
}
|
||||||
return;
|
if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
|
||||||
|
goto update_cleanup;
|
||||||
}
|
}
|
||||||
if( sqliteIsReadOnly(pParse, pTab) ) goto update_cleanup;
|
|
||||||
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;
|
||||||
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
|
||||||
@@ -191,6 +191,15 @@ void sqliteUpdate(
|
|||||||
if( v==0 ) goto update_cleanup;
|
if( v==0 ) goto update_cleanup;
|
||||||
sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1);
|
sqliteBeginWriteOperation(pParse, 1, !row_triggers_exist && pTab->iDb==1);
|
||||||
|
|
||||||
|
/* If we are trying to update a view, construct that view into
|
||||||
|
** a temporary table.
|
||||||
|
*/
|
||||||
|
if( isView ){
|
||||||
|
Select *pView = sqliteSelectDup(pTab->pSelect);
|
||||||
|
sqliteSelect(pParse, pView, SRT_TempTable, base, 0, 0, 0);
|
||||||
|
sqliteSelectDelete(pView);
|
||||||
|
}
|
||||||
|
|
||||||
/* Begin the database scan
|
/* Begin the database scan
|
||||||
*/
|
*/
|
||||||
pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
|
pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
|
||||||
@@ -226,8 +235,10 @@ void sqliteUpdate(
|
|||||||
** being updated.
|
** being updated.
|
||||||
*/
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
if( !isView ){
|
||||||
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
|
sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
|
||||||
|
}
|
||||||
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
|
||||||
|
|
||||||
/* Generate the OLD table
|
/* Generate the OLD table
|
||||||
@@ -257,9 +268,11 @@ void sqliteUpdate(
|
|||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
|
||||||
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
sqliteVdbeAddOp(v, OP_PutIntKey, newIdx, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
if( !isView ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Fire the BEFORE triggers
|
/* Fire the BEFORE and INSTEAD OF triggers
|
||||||
*/
|
*/
|
||||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
|
||||||
newIdx, oldIdx, onError, addr) ){
|
newIdx, oldIdx, onError, addr) ){
|
||||||
@@ -267,103 +280,109 @@ void sqliteUpdate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rewind the list of records that need to be updated and
|
if( !isView ){
|
||||||
** open every index that needs updating. Note that if any
|
/*
|
||||||
** index could potentially invoke a REPLACE conflict resolution
|
** Open every index that needs updating. Note that if any
|
||||||
** action, then we need to open all indices because we might need
|
** index could potentially invoke a REPLACE conflict resolution
|
||||||
** to be deleting some records.
|
** action, then we need to open all indices because we might need
|
||||||
*/
|
** to be deleting some records.
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
*/
|
||||||
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
|
||||||
if( onError==OE_Replace ){
|
sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
|
||||||
openAll = 1;
|
if( onError==OE_Replace ){
|
||||||
}else{
|
openAll = 1;
|
||||||
openAll = 0;
|
}else{
|
||||||
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
openAll = 0;
|
||||||
if( pIdx->onError==OE_Replace ){
|
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
|
||||||
openAll = 1;
|
if( pIdx->onError==OE_Replace ){
|
||||||
break;
|
openAll = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
if( openAll || aIdxUsed[i] ){
|
||||||
if( openAll || aIdxUsed[i] ){
|
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
|
sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum);
|
||||||
sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum);
|
assert( pParse->nTab>base+i+1 );
|
||||||
assert( pParse->nTab>base+i+1 );
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Loop over every record that needs updating. We have to load
|
/* Loop over every record that needs updating. We have to load
|
||||||
** the old data for each record to be updated because some columns
|
** the old data for each record to be updated because some columns
|
||||||
** might not change and we will need to copy the old value.
|
** might not change and we will need to copy the old value.
|
||||||
** Also, the old data is needed to delete the old index entires.
|
** Also, the old data is needed to delete the old index entires.
|
||||||
** So make the cursor point at the old record.
|
** So make the cursor point at the old record.
|
||||||
*/
|
*/
|
||||||
if( !row_triggers_exist ){
|
if( !row_triggers_exist ){
|
||||||
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
|
||||||
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
|
||||||
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
sqliteVdbeAddOp(v, OP_Dup, 0, 0);
|
||||||
}
|
|
||||||
sqliteVdbeAddOp(v, OP_NotExists, base, addr);
|
|
||||||
|
|
||||||
/* If the record number will change, push the record number as it
|
|
||||||
** will be after the update. (The old record number is currently
|
|
||||||
** on top of the stack.)
|
|
||||||
*/
|
|
||||||
if( chngRecno ){
|
|
||||||
sqliteExprCode(pParse, pRecnoExpr);
|
|
||||||
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute new data for this record.
|
|
||||||
*/
|
|
||||||
for(i=0; i<pTab->nCol; i++){
|
|
||||||
if( i==pTab->iPKey ){
|
|
||||||
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
j = aXRef[i];
|
sqliteVdbeAddOp(v, OP_NotExists, base, addr);
|
||||||
if( j<0 ){
|
|
||||||
sqliteVdbeAddOp(v, OP_Column, base, i);
|
/* If the record number will change, push the record number as it
|
||||||
}else{
|
** will be after the update. (The old record number is currently
|
||||||
sqliteExprCode(pParse, pChanges->a[j].pExpr);
|
** on top of the stack.)
|
||||||
|
*/
|
||||||
|
if( chngRecno ){
|
||||||
|
sqliteExprCode(pParse, pRecnoExpr);
|
||||||
|
sqliteVdbeAddOp(v, OP_MustBeInt, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Compute new data for this record.
|
||||||
|
*/
|
||||||
|
for(i=0; i<pTab->nCol; i++){
|
||||||
|
if( i==pTab->iPKey ){
|
||||||
|
sqliteVdbeAddOp(v, OP_String, 0, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
j = aXRef[i];
|
||||||
|
if( j<0 ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Column, base, i);
|
||||||
|
}else{
|
||||||
|
sqliteExprCode(pParse, pChanges->a[j].pExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do constraint checks
|
||||||
|
*/
|
||||||
|
sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno, 1,
|
||||||
|
onError, addr);
|
||||||
|
|
||||||
|
/* Delete the old indices for the current record.
|
||||||
|
*/
|
||||||
|
sqliteGenerateRowIndexDelete(db, v, pTab, base, aIdxUsed);
|
||||||
|
|
||||||
|
/* If changing the record number, delete the old record.
|
||||||
|
*/
|
||||||
|
if( chngRecno ){
|
||||||
|
sqliteVdbeAddOp(v, OP_Delete, base, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create the new index entries and the new record.
|
||||||
|
*/
|
||||||
|
sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do constraint checks
|
|
||||||
*/
|
|
||||||
sqliteGenerateConstraintChecks(pParse, pTab, base, aIdxUsed, chngRecno, 1,
|
|
||||||
onError, addr);
|
|
||||||
|
|
||||||
/* Delete the old indices for the current record.
|
|
||||||
*/
|
|
||||||
sqliteGenerateRowIndexDelete(db, v, pTab, base, aIdxUsed);
|
|
||||||
|
|
||||||
/* If changing the record number, delete the old record.
|
|
||||||
*/
|
|
||||||
if( chngRecno ){
|
|
||||||
sqliteVdbeAddOp(v, OP_Delete, base, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the new index entries and the new record.
|
|
||||||
*/
|
|
||||||
sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1, -1);
|
|
||||||
|
|
||||||
/* Increment the row counter
|
/* Increment the row counter
|
||||||
*/
|
*/
|
||||||
if( db->flags & SQLITE_CountRows && !pParse->trigStack){
|
if( db->flags & SQLITE_CountRows && !pParse->trigStack){
|
||||||
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If there are triggers, close all the cursors after each iteration
|
||||||
|
** through the loop. The fire the after triggers.
|
||||||
|
*/
|
||||||
if( row_triggers_exist ){
|
if( row_triggers_exist ){
|
||||||
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
if( !isView ){
|
||||||
if( openAll || aIdxUsed[i] )
|
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
|
||||||
sqliteVdbeAddOp(v, OP_Close, base+i+1, 0);
|
if( openAll || aIdxUsed[i] )
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base+i+1, 0);
|
||||||
|
}
|
||||||
|
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
||||||
|
pParse->nTab = base;
|
||||||
}
|
}
|
||||||
sqliteVdbeAddOp(v, OP_Close, base, 0);
|
|
||||||
pParse->nTab = base;
|
|
||||||
|
|
||||||
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
|
if( sqliteCodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
|
||||||
newIdx, oldIdx, onError, addr) ){
|
newIdx, oldIdx, onError, addr) ){
|
||||||
goto update_cleanup;
|
goto update_cleanup;
|
||||||
|
@@ -12,7 +12,7 @@
|
|||||||
** This module contains C code that generates VDBE code used to process
|
** This module contains C code that generates VDBE code used to process
|
||||||
** the WHERE clause of SQL statements.
|
** the WHERE clause of SQL statements.
|
||||||
**
|
**
|
||||||
** $Id: where.c,v 1.76 2003/04/20 17:29:24 drh Exp $
|
** $Id: where.c,v 1.77 2003/04/24 01:45:05 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -1144,7 +1144,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
|
|||||||
}
|
}
|
||||||
sqliteVdbeResolveLabel(v, pWInfo->iBreak);
|
sqliteVdbeResolveLabel(v, pWInfo->iBreak);
|
||||||
for(i=0; i<pTabList->nSrc; i++){
|
for(i=0; i<pTabList->nSrc; i++){
|
||||||
if( pTabList->a[i].pTab->isTransient ) continue;
|
Table *pTab = pTabList->a[i].pTab;
|
||||||
|
assert( pTab!=0 );
|
||||||
|
if( pTab->isTransient || pTab->pSelect ) continue;
|
||||||
pLevel = &pWInfo->a[i];
|
pLevel = &pWInfo->a[i];
|
||||||
sqliteVdbeAddOp(v, OP_Close, base+i, 0);
|
sqliteVdbeAddOp(v, OP_Close, base+i, 0);
|
||||||
if( pLevel->pIdx!=0 ){
|
if( pLevel->pIdx!=0 ){
|
||||||
|
@@ -587,6 +587,7 @@ do_test trigger2-7.1 {
|
|||||||
}
|
}
|
||||||
} {}
|
} {}
|
||||||
|
|
||||||
|
#explain {delete from abcd where a=1;}
|
||||||
do_test trigger2-7.2 {
|
do_test trigger2-7.2 {
|
||||||
execsql {
|
execsql {
|
||||||
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
||||||
@@ -601,4 +602,92 @@ do_test trigger2-7.2 {
|
|||||||
5 0 0 0 0 10 20 30 40 \
|
5 0 0 0 0 10 20 30 40 \
|
||||||
6 0 0 0 0 10 20 30 40 ]
|
6 0 0 0 0 10 20 30 40 ]
|
||||||
|
|
||||||
|
do_test trigger2-7.3 {
|
||||||
|
execsql {
|
||||||
|
DELETE FROM tlog;
|
||||||
|
INSERT INTO abcd VALUES(10, 20, 30, 40);
|
||||||
|
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
||||||
|
DELETE FROM abcd WHERE a = 1;
|
||||||
|
SELECT * FROM tlog;
|
||||||
|
}
|
||||||
|
} [ list \
|
||||||
|
1 0 0 0 0 10 20 30 40 \
|
||||||
|
2 0 0 0 0 10 20 30 40 \
|
||||||
|
3 1 2 3 4 100 25 3 4 \
|
||||||
|
4 1 2 3 4 100 25 3 4 \
|
||||||
|
5 1 2 3 4 0 0 0 0 \
|
||||||
|
6 1 2 3 4 0 0 0 0 \
|
||||||
|
]
|
||||||
|
do_test trigger2-7.4 {
|
||||||
|
execsql {
|
||||||
|
DELETE FROM tlog;
|
||||||
|
DELETE FROM abcd WHERE a = 1;
|
||||||
|
INSERT INTO abcd VALUES(10, 20, 30, 40);
|
||||||
|
UPDATE abcd SET a = 100, b = 5*5 WHERE a = 1;
|
||||||
|
SELECT * FROM tlog;
|
||||||
|
}
|
||||||
|
} [ list \
|
||||||
|
1 1 2 3 4 0 0 0 0 \
|
||||||
|
2 1 2 3 4 0 0 0 0 \
|
||||||
|
3 0 0 0 0 10 20 30 40 \
|
||||||
|
4 0 0 0 0 10 20 30 40 \
|
||||||
|
5 1 2 3 4 100 25 3 4 \
|
||||||
|
6 1 2 3 4 100 25 3 4 \
|
||||||
|
]
|
||||||
|
|
||||||
|
do_test trigger2-8.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a,b,c);
|
||||||
|
INSERT INTO t1 VALUES(1,2,3);
|
||||||
|
CREATE VIEW v1 AS
|
||||||
|
SELECT a+b AS x, b+c AS y, a+c AS z FROM t1;
|
||||||
|
SELECT * FROM v1;
|
||||||
|
}
|
||||||
|
} {3 5 4}
|
||||||
|
do_test trigger2-8.2 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE v1log(a,b,c,d,e,f);
|
||||||
|
CREATE TRIGGER r1 INSTEAD OF DELETE ON v1 BEGIN
|
||||||
|
INSERT INTO v1log VALUES(OLD.x,NULL,OLD.y,NULL,OLD.z,NULL);
|
||||||
|
END;
|
||||||
|
DELETE FROM v1 WHERE x=1;
|
||||||
|
SELECT * FROM v1log;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
do_test trigger2-8.3 {
|
||||||
|
execsql {
|
||||||
|
DELETE FROM v1 WHERE x=3;
|
||||||
|
SELECT * FROM v1log;
|
||||||
|
}
|
||||||
|
} {3 {} 5 {} 4 {}}
|
||||||
|
do_test trigger2-8.4 {
|
||||||
|
execsql {
|
||||||
|
INSERT INTO t1 VALUES(4,5,6);
|
||||||
|
DELETE FROM v1log;
|
||||||
|
DELETE FROM v1 WHERE y=11;
|
||||||
|
SELECT * FROM v1log;
|
||||||
|
}
|
||||||
|
} {9 {} 11 {} 10 {}}
|
||||||
|
do_test trigger2-8.5 {
|
||||||
|
execsql {
|
||||||
|
CREATE TRIGGER r2 INSTEAD OF INSERT ON v1 BEGIN
|
||||||
|
INSERT INTO v1log VALUES(NULL,NEW.x,NULL,NEW.y,NULL,NEW.z);
|
||||||
|
END;
|
||||||
|
DELETE FROM v1log;
|
||||||
|
INSERT INTO v1 VALUES(1,2,3);
|
||||||
|
SELECT * FROM v1log;
|
||||||
|
}
|
||||||
|
} {{} 1 {} 2 {} 3}
|
||||||
|
do_test trigger2-8.6 {
|
||||||
|
execsql {
|
||||||
|
CREATE TRIGGER r3 INSTEAD OF UPDATE ON v1 BEGIN
|
||||||
|
INSERT INTO v1log VALUES(OLD.x,NEW.x,OLD.y,NEW.y,OLD.z,NEW.z);
|
||||||
|
END;
|
||||||
|
DELETE FROM v1log;
|
||||||
|
UPDATE v1 SET x=x+100, y=y+200, z=z+300;
|
||||||
|
SELECT * FROM v1log;
|
||||||
|
}
|
||||||
|
} {3 103 5 205 4 304 9 109 11 211 10 310}
|
||||||
|
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
# This file implements regression tests for SQLite library. The
|
# This file implements regression tests for SQLite library. The
|
||||||
# focus of this file is testing VIEW statements.
|
# focus of this file is testing VIEW statements.
|
||||||
#
|
#
|
||||||
# $Id: view.test,v 1.12 2002/12/03 02:22:53 drh Exp $
|
# $Id: view.test,v 1.13 2003/04/24 01:45:05 drh Exp $
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
|
|
||||||
@@ -98,17 +98,17 @@ do_test view-2.2 {
|
|||||||
catchsql {
|
catchsql {
|
||||||
INSERT INTO v2 VALUES(1,2,3,4);
|
INSERT INTO v2 VALUES(1,2,3,4);
|
||||||
}
|
}
|
||||||
} {1 {view v2 may not be modified}}
|
} {1 {cannot modify v2 because it is a view}}
|
||||||
do_test view-2.3 {
|
do_test view-2.3 {
|
||||||
catchsql {
|
catchsql {
|
||||||
UPDATE v2 SET a=10 WHERE a=5;
|
UPDATE v2 SET a=10 WHERE a=5;
|
||||||
}
|
}
|
||||||
} {1 {view v2 may not be modified}}
|
} {1 {cannot modify v2 because it is a view}}
|
||||||
do_test view-2.4 {
|
do_test view-2.4 {
|
||||||
catchsql {
|
catchsql {
|
||||||
DELETE FROM v2;
|
DELETE FROM v2;
|
||||||
}
|
}
|
||||||
} {1 {view v2 may not be modified}}
|
} {1 {cannot modify v2 because it is a view}}
|
||||||
do_test view-2.5 {
|
do_test view-2.5 {
|
||||||
execsql {
|
execsql {
|
||||||
INSERT INTO t1 VALUES(11,12,13,14);
|
INSERT INTO t1 VALUES(11,12,13,14);
|
||||||
|
Reference in New Issue
Block a user