]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix some issues with INSTEAD OF triggers. (CVS 930)
authordrh <drh@noemail.net>
Thu, 24 Apr 2003 01:45:04 +0000 (01:45 +0000)
committerdrh <drh@noemail.net>
Thu, 24 Apr 2003 01:45:04 +0000 (01:45 +0000)
FossilOrigin-Name: 206b17397b1d2b55179c935927ff1d8215728c32

17 files changed:
manifest
manifest.uuid
src/auth.c
src/btree_rb.c
src/copy.c
src/delete.c
src/insert.c
src/os.h
src/select.c
src/shell.c
src/sqlite.h.in
src/sqliteInt.h
src/trigger.c
src/update.c
src/where.c
test/trigger2.test
test/view.test

index 37c39bc388839fabb4356cc651a2c3feca59a318..46e2059505e5a45ff5a48d4683a913c48cc76785 100644 (file)
--- a/manifest
+++ b/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)
-D 2003-04-23T12:25:24
+C Fix\ssome\sissues\swith\sINSTEAD\sOF\striggers.\s(CVS\s930)
+D 2003-04-24T01:45:04
 F Makefile.in 004acec253ecdde985c8ecd5b7c9accdb210378f
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -20,34 +20,34 @@ F spec.template 238f7db425a78dc1bb7682e56e3834c7270a3f5e
 F sqlite.1 83f4a9d37bdf2b7ef079a82d54eaf2e3509ee6ea
 F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
 F src/attach.c 7ebc7487de43e357a64226f8abef81f2669f2183
-F src/auth.c 7b0a72a649989461d36eced6ff1214f32af436c5
+F src/auth.c a4afd27964fb9f661147115790c8ae2ee230ebcc
 F src/btree.c b9487cceb9ea78af9cbae9def34114902f511736
 F src/btree.h 529c98cb0715c62214544fbbe50b946f99a85540
-F src/btree_rb.c 97375d44bc2cf93b6312acd0f3276177c20e77bb
+F src/btree_rb.c 85cbf6720db54b844747ce4dba4c3fcc8fd6f0a8
 F src/build.c d5a26baeffa5bc49b4b7009a7723c6ab7e1b02d9
-F src/copy.c 6bafc19598daef79d80d16214260611d758a53a1
-F src/delete.c c5c26039cfdf1eadabff698eb329e3880189795e
+F src/copy.c 44b13fd4d2444fb53bff8a5ecee1c5f6f86a8263
+F src/delete.c 23d33fd8967c6cc492943bbecea93be6491edc6a
 F src/encode.c faf03741efe921755ec371cf4a6984536de00042
 F src/expr.c 46e2bb93abd6c70e67c8cdc5d92fdcd0b95498f3
 F src/func.c 882c3ed5a02be18cd904715c7ec62947a34a3605
 F src/hash.c 4fc39feb7b7711f6495ee9f2159559bedb043e1f
 F src/hash.h cd0433998bc1a3759d244e1637fe5a3c13b53bf8
-F src/insert.c 350167db53b779a8d402d00ec5153410a8003931
+F src/insert.c 19882be1edc4b1629b8f3097e2615164f2c9cecb
 F src/main.c 5e4d4d081d82840a743c57269ca3c32640cefc06
 F src/md5.c fe4f9c9c6f71dfc26af8da63e4d04489b1430565
 F src/os.c 7274951ed6894f383cb889342267ded07caf339b
-F src/os.h aa52f0c9da321ff6134d19f2ca959e18e33615d0
+F src/os.h 9e5bbddff123187295e3d00d49af06192cd1cd49
 F src/pager.c df4c81350cbd80c1ab48341ae0768ba78d99ad49
 F src/pager.h e3702f7d384921f6cd5ce0b3ed589185433e9f6c
 F src/parse.y 15ae47e7dd84304c1c6ae9205558405127977541
 F src/pragma.c 3b1e8da84304d5efa1db5802c67261335b663327
 F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 92a66f0122f321688569e108feceaf74f5f4e63a
-F src/shell.c a0b7043713713ff45f666ce6b3c03a64109a8bb5
+F src/select.c dfc13cb62ba658c4463179713c40ee25a062b2ba
+F src/shell.c e0b3da1f44a2cc72daf41a4559b1c5f0545944a5
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
-F src/sqlite.h.in b4799af223dfc82f47d27c34b14f43b27509a49a
-F src/sqliteInt.h cd2952587a8d3bff5d6cc7a0cfaf0e89c3bef0ad
+F src/sqlite.h.in eec06462cba262c0ee03f38462a18a4bc66dda4e
+F src/sqliteInt.h 5d15d1dea3f0c497a78c6a123eec5b4b92811c1c
 F src/table.c eed2098c9b577aa17f8abe89313a9c4413f57d63
 F src/tclsqlite.c 9e25f98f1765afa0716144ef57abda75c88f688d
 F src/test1.c 4484806861a3099670188a09e12f858ec65aa56c
@@ -55,13 +55,13 @@ F src/test2.c 5014337d8576b731cce5b5a14bec4f0daf432700
 F src/test3.c 30985ebdfaf3ee1462a9b0652d3efbdc8d9798f5
 F src/threadtest.c d641a5219e718e18a1a80a50eb9bb549f451f42e
 F src/tokenize.c 067d1a477a94af7712ca74e09aaa6bd0f7299527
-F src/trigger.c 21ad1677bb0f0625348a01e92d1e0c6d794185a1
-F src/update.c 3301448786205a7ec2d035c7cb7bd8ae5128c2b0
+F src/trigger.c e763f4015c96e06b694184ead5754985c1dfdae0
+F src/update.c b7fa7c427b74aee6db56ecfa09e5e151e6f9fa6a
 F src/util.c 87635cfdfffa056a8d3147719357aa442374f78c
 F src/vacuum.c e24781e38db36d1c9f578b6b3613bf0989ebd63c
 F src/vdbe.c f0868ac926d98395d28c2a29119364ff11b77852
 F src/vdbe.h 985c24f312d10f9ef8f9a8b8ea62fcdf68e82f21
-F src/where.c c0709e5cf402f30026b597dce9dc3e74f1d07f8e
+F src/where.c f632cd30f013163484a4d60c249d36fe31f5be12
 F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
 F test/attach.test b311c83e370e6b22b79a8279317039440ce64862
 F test/auth.test d25a76f21494b61483787caa7b28c713bc7c7c7f
@@ -120,14 +120,14 @@ F test/temptable.test 6feff1960c707e924d5462356c5303943dac4a8e
 F test/tester.tcl d7a5835edaf118539241145d8188f0822b673488
 F test/trans.test 75e7a171b5d2d94ee56766459113e2ad0e5f809d
 F test/trigger1.test 61ef41666f066ac417730dc26056053a7c36cd11
-F test/trigger2.test ab4c743bb96cee96ab5a17c5edfd57a9134329d6
+F test/trigger2.test adf6a9cfd735bd4be4f7be19da629b0968703744
 F test/trigger3.test 870afef7997a5b86bf3ea893ce0c2e85d6356c72
 F test/trigger4.test 9a5c1406344d743020c2753ae8d6dfe6eb75f818
 F test/unique.test 22a46df72a3e0a3fd1a2d39e96fb59f18448dd5f
 F test/update.test 198360dfa14e65354dbcc66d5b98d8070780e42b
 F test/vacuum.test 059871b312eb910bbe49dafde1d01490cc2c6bbe
 F test/version.test 605fd0d7e7d571370c32b12dbf395b58953de246
-F test/view.test c64fa39ea57f3c2066c854290f032ad13b23b83d
+F test/view.test d356f445d481c04ffa6036a4c61cb8ba70289f69
 F test/where.test d719129a052280fe245a2ddcbd09bcc0b8c17ce4
 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
 F tool/lemon.c 14fedcde9cf70aa6040b89de164cf8f56f92a4b9
@@ -165,7 +165,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P c675a5504138f34cae6def782b5d3add2c67d2bc
-R e42e1c0805bb96ad9734d493fd4b89bd
+P 6289b863590ecc5de3d1efaaa60aa6f3f64fefb3
+R 566c0486d0b4bc8e9732c5c29c3b5dcc
 U drh
-Z 6cfe8cf5cfb533faee03d9b06ef89d27
+Z d31c46a8e175a3b83b9efc55c7de92d9
index 465ce41a0b946ea4349936eb45e011863526135f..a442ea316dd5dd06a85d629aa1a7c2d923c7c6b4 100644 (file)
@@ -1 +1 @@
-6289b863590ecc5de3d1efaaa60aa6f3f64fefb3
\ No newline at end of file
+206b17397b1d2b55179c935927ff1d8215728c32
\ No newline at end of file
index 47b6600a5e0e18f37f0dd95b32016ec90fbea240..4703ba0132921c53a515c9d575ad20aae657c1a4 100644 (file)
@@ -14,7 +14,7 @@
 ** systems that do not need this facility may omit it by recompiling
 ** 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"
 
@@ -95,11 +95,7 @@ void sqliteAuthRead(
   const char *zCol;     /* Name of the column of the table */
   int iSrc;             /* Index in pTabList->a[] of table being read */
   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;
   assert( pExpr->op==TK_COLUMN );
   iSrc = pExpr->iTable - base;
@@ -109,6 +105,8 @@ void sqliteAuthRead(
     /* This must be an attempt to read the NEW or OLD pseudo-tables
     ** of a trigger.
     */
+    TriggerStack *pStack; /* The stack of current triggers */
+    pStack = pParse->trigStack;
     assert( pStack!=0 );
     assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
     pTab = pStack->pTab;
@@ -123,9 +121,10 @@ void sqliteAuthRead(
   }else{
     zCol = "ROWID";
   }
-  assert( pExpr->iDb>=0 && pExpr->iDb<db->nDb );
+  assert( pExpr->iDb<db->nDb );
   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 ){
     pExpr->op = TK_NULL;
   }else if( rc==SQLITE_DENY ){
@@ -158,13 +157,11 @@ int sqliteAuthCheck(
 ){
   sqlite *db = pParse->db;
   int rc;
-  const char *zTrigName;
 
   if( db->xAuth==0 ){
     return SQLITE_OK;
   }
-  zTrigName = pParse->trigStack ? pParse->trigStack->pTrigger->name : 0;
-  rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, zTrigName);
+  rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
   if( rc==SQLITE_DENY ){
     sqliteSetString(&pParse->zErrMsg, "not authorized", 0);
     pParse->rc = SQLITE_AUTH;
index 1a0db6d7e47c27b9919ff3b68c089e59b29e32bd..35c09e3aff9862597a8c9f7fa364149649a21163 100644 (file)
@@ -9,7 +9,7 @@
 **    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
 ** 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->pKey || ((amt + offset) <= pCur->pNode->nKey) ){
-    memcpy(zBuf, pCur->pNode->pKey+offset, amt);
+    memcpy(zBuf, ((char*)pCur->pNode->pKey)+offset, amt);
     return amt;
   }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;
   }
   assert(0);
@@ -1105,10 +1105,10 @@ static int memBtreeData(BtCursor *pCur, int offset, int amt, char *zBuf)
 {
   if( !pCur->pNode ) return 0;
   if( (amt + offset) <= pCur->pNode->nData ){
-    memcpy(zBuf, pCur->pNode->pData+offset, amt);
+    memcpy(zBuf, ((char*)pCur->pNode->pData)+offset, amt);
     return amt;
   }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;
   }
   assert(0);
index 69711dca6dce807af3825ca312d4f2aeb28fe7bb..44fd471421f2d494db114be07c2cb32542c74850 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** 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"
 
@@ -46,10 +46,10 @@ void sqliteCopy(
   if( sqlite_malloc_failed  ) goto copy_cleanup;
   assert( pTableName->nSrc==1 );
   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);
   sqliteDequote(zFile);
-  assert( pTab->iDb>=0 && pTab->iDb<db->nDb );
+  assert( pTab->iDb<db->nDb );
   zDb = db->aDb[pTab->iDb].zName;
   if( sqliteAuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb)
       || sqliteAuthCheck(pParse, SQLITE_COPY, pTab->zName, zFile, zDb) ){
index 8899e717cfe4ff16598aa3ae14fd70858cb0cce7..7d0003578e0033d713e02380b0d0a816b6b09159 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** 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"
 
@@ -38,11 +38,13 @@ Table *sqliteSrcListLookup(Parse *pParse, SrcList *pSrc){
 ** writable, generate an error message and return 1.  If it is
 ** writable return 0;
 */
-int sqliteIsReadOnly(Parse *pParse, Table *pTab){
-  if( pTab->readOnly || pTab->pSelect ){
-    sqliteErrorMsg(pParse, "%s %s may not be modified",
-      pTab->pSelect ? "view" : "table",
-      pTab->zName);
+int sqliteIsReadOnly(Parse *pParse, Table *pTab, int viewOk){
+  if( pTab->readOnly ){
+    sqliteErrorMsg(pParse, "table %s may not be modified", pTab->zName);
+    return 1;
+  }
+  if( !viewOk && pTab->pSelect ){
+    sqliteErrorMsg(pParse, "cannot modify %s because it is a view",pTab->zName);
     return 1;
   }
   return 0;
@@ -65,6 +67,7 @@ void sqliteDeleteFrom(
   Index *pIdx;           /* For looping over indices of the table */
   int base;              /* Index of the first available table cursor */
   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 before_triggers;         /* True if there are BEFORE triggers */
@@ -90,20 +93,22 @@ void sqliteDeleteFrom(
   after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, 
                          TK_DELETE, TK_AFTER, TK_ROW, 0);
   row_triggers_exist = before_triggers || after_triggers;
-  if( row_triggers_exist &&  pTab->pSelect ){
-    /* Just fire VIEW triggers */
-    sqliteSrcListDelete(pTabList);
-    sqliteViewTriggers(pParse, pTab, pWhere, OE_Replace, 0);
-    return;
+  isView = pTab->pSelect!=0;
+  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
+    goto delete_from_cleanup;
   }
-  if( sqliteIsReadOnly(pParse, pTab) ) goto delete_from_cleanup;
-  assert( pTab->pSelect==0 );  /* This table is not a view */
   assert( pTab->iDb<db->nDb );
   zDb = db->aDb[pTab->iDb].zName;
   if( sqliteAuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
     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.
   */
   if( row_triggers_exist ){ 
@@ -131,6 +136,15 @@ void sqliteDeleteFrom(
   sqliteBeginWriteOperation(pParse, row_triggers_exist,
        !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
   ** we are counting rows.
   */
@@ -148,17 +162,21 @@ void sqliteDeleteFrom(
       ** entries in the table. */
       int endOfLoop = sqliteVdbeMakeLabel(v);
       int addr;
-      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
-      sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
+      if( !isView ){
+        sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+        sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
+      }
       sqliteVdbeAddOp(v, OP_Rewind, base, sqliteVdbeCurrentAddr(v)+2);
       addr = sqliteVdbeAddOp(v, OP_AddImm, 1, 0);
       sqliteVdbeAddOp(v, OP_Next, base, addr);
       sqliteVdbeResolveLabel(v, endOfLoop);
       sqliteVdbeAddOp(v, OP_Close, base, 0);
     }
-    sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
-    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-      sqliteVdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
+    if( !isView ){
+      sqliteVdbeAddOp(v, OP_Clear, pTab->tnum, pTab->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 ){
       addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
       sqliteVdbeAddOp(v, OP_Dup, 0, 0);
-      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
-      sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
+      if( !isView ){
+        sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+        sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
+      }
       sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
 
       sqliteVdbeAddOp(v, OP_Recno, base, 0);
       sqliteVdbeAddOp(v, OP_RowData, base, 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, 
           oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
          addr);
     }
 
-    /* Open cursors for the table we are deleting from and all its
-    ** indices.  If there are row triggers, this happens inside the
-    ** OP_ListRead loop because the cursor have to all be closed
-    ** 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);
-    sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
-    for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
-      sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
-      sqliteVdbeAddOp(v, OP_OpenWrite, pParse->nTab++, pIdx->tnum);
-    }
+    if( !isView ){
+      /* Open cursors for the table we are deleting from and all its
+      ** indices.  If there are row triggers, this happens inside the
+      ** OP_ListRead loop because the cursor have to all be closed
+      ** 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);
+      sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
+      for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+        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
-    ** row triggers */
-    if( !row_triggers_exist ){ 
-      addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
-    }
+      /* This is the beginning of the delete loop when there are no
+      ** row triggers */
+      if( !row_triggers_exist ){ 
+        addr = sqliteVdbeAddOp(v, OP_ListRead, 0, end);
+      }
 
-    /* Delete the row */
-    sqliteGenerateRowDelete(db, v, pTab, base, pParse->trigStack==0);
+      /* Delete the row */
+      sqliteGenerateRowDelete(db, v, pTab, base, pParse->trigStack==0);
+    }
 
     /* If there are row triggers, close all cursors then invoke
     ** the AFTER triggers
     */
     if( row_triggers_exist ){
-      for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
-        sqliteVdbeAddOp(v, OP_Close, base + i, pIdx->tnum);
+      if( !isView ){
+        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, 
           oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
          addr);
index 940536f56ff3814b08fa747d60f25ad3ebd8ed86..e83224a2f839a137190063246d00821b0cdccb90 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.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"
 
@@ -109,6 +109,7 @@ void sqliteInsert(
   int iCleanup;         /* Address of the cleanup code */
   int iInsertBlock;     /* Address of the subroutine used to insert data */
   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 before_triggers;        /* True if there are BEFORE triggers */
@@ -142,21 +143,16 @@ void sqliteInsert(
   after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, TK_INSERT,
                                        TK_AFTER, TK_ROW, 0);
   row_triggers_exist = before_triggers || after_triggers;
-  if( pTab->readOnly || (pTab->pSelect && !row_triggers_exist) ){
-    sqliteErrorMsg(pParse, "%s %s may not be modified",
-      pTab->pSelect ? "view" : "table",
-      zTab);
+  isView = pTab->pSelect!=0;
+  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
     goto insert_cleanup;
   }
-
   if( pTab==0 ) goto insert_cleanup;
 
   /* If pTab is really a view, make sure it has been initialized.
   */
-  if( pTab->pSelect ){
-    if( sqliteViewGetColumnNames(pParse, pTab) ){
-      goto insert_cleanup;
-    }
+  if( isView && sqliteViewGetColumnNames(pParse, pTab) ){
+    goto insert_cleanup;
   }
 
   /* Allocate a VDBE
@@ -356,7 +352,7 @@ void sqliteInsert(
     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);
   if( before_triggers ){
@@ -405,7 +401,7 @@ void sqliteInsert(
     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 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, 
         newIdx, -1, onError, endOfLoop) ){
       goto insert_cleanup;
@@ -415,19 +411,17 @@ void sqliteInsert(
   /* If any triggers exists, the opening of tables and indices is deferred
   ** until now.
   */
-  if( row_triggers_exist ){
-    if( !pTab->pSelect ){
-      base = pParse->nTab;
-      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
-      sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
-      sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
-      for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
-        sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
-        sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
-        sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
-      }
-      pParse->nTab += idx;
+  if( row_triggers_exist && !isView ){
+    base = pParse->nTab;
+    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+    sqliteVdbeAddOp(v, OP_OpenWrite, base, pTab->tnum);
+    sqliteVdbeChangeP3(v, -1, pTab->zName, P3_STATIC);
+    for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
+      sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+      sqliteVdbeAddOp(v, OP_OpenWrite, idx+base, pIdx->tnum);
+      sqliteVdbeChangeP3(v, -1, pIdx->zName, P3_STATIC);
     }
+    pParse->nTab += idx;
   }
 
   /* 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
   ** case the record number is the same as that column. 
   */
-  if( !pTab->pSelect ){
+  if( !isView ){
     if( keyColumn>=0 ){
       if( useTempTable ){
         sqliteVdbeAddOp(v, OP_Column, srcTab, keyColumn);
@@ -492,17 +486,17 @@ void sqliteInsert(
     sqliteGenerateConstraintChecks(pParse, pTab, base, 0,0,0,onError,endOfLoop);
     sqliteCompleteInsertion(pParse, pTab, base, 0,0,0,
                             after_triggers ? newIdx : -1);
+  }
 
-    /* Update the count of rows that are inserted
-    */
-    if( (db->flags & SQLITE_CountRows)!=0 ){
-      sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
-    }
+  /* Update the count of rows that are inserted
+  */
+  if( (db->flags & SQLITE_CountRows)!=0 ){
+    sqliteVdbeAddOp(v, OP_MemIncr, iCntMem, 0);
   }
 
   if( row_triggers_exist ){
     /* Close all tables opened */
-    if( !pTab->pSelect ){
+    if( !isView ){
       sqliteVdbeAddOp(v, OP_Close, base, 0);
       for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
         sqliteVdbeAddOp(v, OP_Close, idx+base, 0);
index f9784ef52536b482f54548b1984c0b0e5dfb0739..d7674267d7c7b5237cb64bad36a2108c1f61acce 100644 (file)
--- a/src/os.h
+++ b/src/os.h
 #ifndef _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
 ** underlying operating system supports it.  If the OS lacks
index f311fa76547db3afc6ad97b53d6d9d3aebb7755b..52a10d8da957673901d62e30cba6f9a93c7abc6e 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.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"
 
@@ -904,6 +904,10 @@ static int fillInColumnList(Parse *pParse, Select *p){
       if( pTab==0 ){
         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;
     }else{
       /* 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
 ** VIEW in order to undo any bindings to tables.  This is necessary
 ** 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){
   int i;
@@ -1058,10 +1065,6 @@ void sqliteSelectUnbind(Select *p){
     if( (pTab = pSrc->a[i].pTab)!=0 ){
       if( pTab->isTransient ){
         sqliteDeleteTable(0, pTab);
-#if 0
-        sqliteSelectDelete(pSrc->a[i].pSelect);
-        pSrc->a[i].pSelect = 0;
-#endif
       }
       pSrc->a[i].pTab = 0;
       if( pSrc->a[i].pSelect ){
@@ -2113,9 +2116,17 @@ int sqliteSelect(
   /* Generate code for all sub-queries in the FROM clause
   */
   for(i=0; i<pTabList->nSrc; i++){
+    const char *zSavedAuthContext;
     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,
                  p, i, &isAgg);
+    if( pTabList->a[i].zName!=0 ){
+      pParse->zAuthContext = zSavedAuthContext;
+    }
     pTabList = p->pSrc;
     pWhere = p->pWhere;
     if( eDest==SRT_Callback ){
index 4873d35df4e631c44295b7d8a60b178cacf4b549..e8efbe3b45c8333415ae7f02b9e63ae19f1cab03 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains code to implement the "sqlite" command line
 ** 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 <string.h>
@@ -1183,7 +1183,7 @@ int main(int argc, char **argv){
     }
   }else{
     extern int isatty();
-    if( isatty(fileno(stdout)) ){
+    if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){
       char *zHome;
       char *zHistory = 0;
       printf(
index cae7814dd22fee9d82cb816233ccfebfd194eebb..6ebbc3dd734bceddf328261565c1d681236f3e6c 100644 (file)
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** 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_
 #define _SQLITE_H_
@@ -524,8 +524,9 @@ int sqlite_set_authorizer(
 ** 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
 ** of the database ("main", "temp", etc.) if applicable.  The 6th parameter
-** is the name of the trigger that is responsible for the access attempt,
-** or NULL if this access attempt is directly from input SQL code.
+** is the name of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from 
+** input SQL code.
 **
 **                                          Arg-3           Arg-4
 */
index 09209c2c8bbe9f82b248f33ba0c8ac65a49999b7..7027b30a96c4fe8e452b0705705cfa1ccbdcfbef 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** 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 "sqlite.h"
@@ -829,6 +829,7 @@ struct Parse {
   int nSet;            /* Number of sets used so far */
   int nAgg;            /* Number 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 */
   TriggerStack *trigStack;  /* Trigger actions being coded */
 };
@@ -1039,7 +1040,7 @@ Select *sqliteSelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
 void sqliteSelectDelete(Select*);
 void sqliteSelectUnbind(Select*);
 Table *sqliteSrcListLookup(Parse*, SrcList*);
-int sqliteIsReadOnly(Parse*, Table*);
+int sqliteIsReadOnly(Parse*, Table*, int);
 void sqliteDeleteFrom(Parse*, SrcList*, Expr*);
 void sqliteUpdate(Parse*, SrcList*, ExprList*, Expr*, int);
 WhereInfo *sqliteWhereBegin(Parse*, int, SrcList*, Expr*, int, ExprList**);
index a2fc91f518aa70c89e368cdc35e2431cf8bbe2b3..0abe7ee0d6912850dd0eab78062f0721d0750656 100644 (file)
@@ -41,7 +41,7 @@ void sqliteDeleteTriggerStep(TriggerStep *pTriggerStep){
 void sqliteBeginTrigger(
   Parse *pParse,      /* The parse context of the CREATE TRIGGER statement */
   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 */
   IdList *pColumns,   /* column list if this is an UPDATE OF trigger */
   SrcList *pTableName,/* The name of the table/view the trigger applies to */
@@ -110,6 +110,11 @@ void sqliteBeginTrigger(
   }
 #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){
     tr_tm = TK_BEFORE;
   }
@@ -627,7 +632,7 @@ int sqliteCodeRowTrigger(
   TriggerStack * pTriggerStack;
 
   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);
 
@@ -656,6 +661,7 @@ int sqliteCodeRowTrigger(
       int endTrigger;
       SrcList dummyTablist;
       Expr * whenExpr;
+      const char *zSavedAuthContext;
 
       dummyTablist.nSrc = 0;
 
@@ -667,6 +673,8 @@ int sqliteCodeRowTrigger(
       pTriggerStack->pNext = pParse->trigStack;
       pTriggerStack->ignoreJump = ignoreJump;
       pParse->trigStack = pTriggerStack;
+      zSavedAuthContext = pParse->zAuthContext;
+      pParse->zAuthContext = pTrigger->name;
 
       /* code the WHEN clause */
       endTrigger = sqliteVdbeMakeLabel(pParse->pVdbe);
@@ -684,6 +692,7 @@ int sqliteCodeRowTrigger(
 
       /* Pop the entry off the trigger stack */
       pParse->trigStack = pParse->trigStack->pNext;
+      pParse->zAuthContext = zSavedAuthContext;
       sqliteFree(pTriggerStack);
 
       sqliteVdbeResolveLabel(pParse->pVdbe, endTrigger);
@@ -693,145 +702,3 @@ int sqliteCodeRowTrigger(
 
   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;
-}
index d320033f67e70f20d254aae29c043143eedb7c08..c1961426cfc9bdbd25ee459732bed5c2d9136080 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.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"
 
@@ -48,6 +48,7 @@ void sqliteUpdate(
   int chngRecno;         /* True if the record number is being changed */
   Expr *pRecnoExpr;      /* Expression defining the new record number */
   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 after_triggers;          /* True if there are any AFTER triggers */
@@ -69,14 +70,13 @@ void sqliteUpdate(
   after_triggers = sqliteTriggersExist(pParse, pTab->pTrigger, 
             TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
   row_triggers_exist = before_triggers || after_triggers;
-  if( row_triggers_exist &&  pTab->pSelect ){
-    /* Just fire VIEW triggers */
-    sqliteSrcListDelete(pTabList);
-    sqliteViewTriggers(pParse, pTab, pWhere, onError, pChanges);
-    return;
+  isView = pTab->pSelect!=0;
+  if( sqliteIsReadOnly(pParse, pTab, before_triggers) ){
+    goto update_cleanup;
+  }
+  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 );
   if( aXRef==0 ) goto update_cleanup;
   for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
@@ -191,6 +191,15 @@ void sqliteUpdate(
   if( v==0 ) goto update_cleanup;
   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
   */
   pWInfo = sqliteWhereBegin(pParse, base, pTabList, pWhere, 1, 0);
@@ -226,8 +235,10 @@ void sqliteUpdate(
     ** being updated.
     */
     sqliteVdbeAddOp(v, OP_Dup, 0, 0);
-    sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
-    sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
+    if( !isView ){
+      sqliteVdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+      sqliteVdbeAddOp(v, OP_OpenRead, base, pTab->tnum);
+    }
     sqliteVdbeAddOp(v, OP_MoveTo, base, 0);
 
     /* Generate the OLD table
@@ -257,9 +268,11 @@ void sqliteUpdate(
     }
     sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 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, 
           newIdx, oldIdx, onError, addr) ){
@@ -267,88 +280,90 @@ void sqliteUpdate(
     }
   }
 
-  /* Rewind the list of records that need to be updated and
-  ** open every index that needs updating.  Note that if any
-  ** index could potentially invoke a REPLACE conflict resolution 
-  ** 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);
-  if( onError==OE_Replace ){
-    openAll = 1;
-  }else{
-    openAll = 0;
-    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
-      if( pIdx->onError==OE_Replace ){
-        openAll = 1;
-        break;
+  if( !isView ){
+    /* 
+    ** Open every index that needs updating.  Note that if any
+    ** index could potentially invoke a REPLACE conflict resolution 
+    ** 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);
+    if( onError==OE_Replace ){
+      openAll = 1;
+    }else{
+      openAll = 0;
+      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+        if( pIdx->onError==OE_Replace ){
+          openAll = 1;
+          break;
+        }
       }
     }
-  }
-  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
-    if( openAll || aIdxUsed[i] ){
-      sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
-      sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum);
-      assert( pParse->nTab>base+i+1 );
+    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+      if( openAll || aIdxUsed[i] ){
+        sqliteVdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+        sqliteVdbeAddOp(v, OP_OpenWrite, base+i+1, pIdx->tnum);
+        assert( pParse->nTab>base+i+1 );
+      }
     }
-  }
-
-  /* Loop over every record that needs updating.  We have to load
-  ** the old data for each record to be updated because some columns
-  ** might not change and we will need to copy the old value.
-  ** Also, the old data is needed to delete the old index entires.
-  ** So make the cursor point at the old record.
-  */
-  if( !row_triggers_exist ){
-    sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
-    addr = sqliteVdbeAddOp(v, OP_ListRead, 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);
-  }
+    /* Loop over every record that needs updating.  We have to load
+    ** the old data for each record to be updated because some columns
+    ** might not change and we will need to copy the old value.
+    ** Also, the old data is needed to delete the old index entires.
+    ** So make the cursor point at the old record.
+    */
+    if( !row_triggers_exist ){
+      sqliteVdbeAddOp(v, OP_ListRewind, 0, 0);
+      addr = sqliteVdbeAddOp(v, OP_ListRead, 0, 0);
+      sqliteVdbeAddOp(v, OP_Dup, 0, 0);
+    }
+    sqliteVdbeAddOp(v, OP_NotExists, base, addr);
 
-  /* Compute new data for this record.  
-  */
-  for(i=0; i<pTab->nCol; i++){
-    if( i==pTab->iPKey ){
-      sqliteVdbeAddOp(v, OP_String, 0, 0);
-      continue;
+    /* 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);
     }
-    j = aXRef[i];
-    if( j<0 ){
-      sqliteVdbeAddOp(v, OP_Column, base, i);
-    }else{
-      sqliteExprCode(pParse, pChanges->a[j].pExpr);
+
+    /* 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);
+    /* 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);
+    /* 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);
-  }
+    /* 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);
+    /* Create the new index entries and the new record.
+    */
+    sqliteCompleteInsertion(pParse, pTab, base, aIdxUsed, chngRecno, 1, -1);
+  }
 
   /* Increment the row counter 
   */
@@ -356,14 +371,18 @@ void sqliteUpdate(
     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 ){
-    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
-      if( openAll || aIdxUsed[i] )
-        sqliteVdbeAddOp(v, OP_Close, base+i+1, 0);
+    if( !isView ){
+      for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+        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, 
           newIdx, oldIdx, onError, addr) ){
       goto update_cleanup;
index 6e6fc289f83bd252b2a9897b919343b23443ea0f..82903047fe289079a4e9af08c315b7901340ebe1 100644 (file)
@@ -12,7 +12,7 @@
 ** This module contains C code that generates VDBE code used to process
 ** 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"
 
@@ -1144,7 +1144,9 @@ void sqliteWhereEnd(WhereInfo *pWInfo){
   }
   sqliteVdbeResolveLabel(v, pWInfo->iBreak);
   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];
     sqliteVdbeAddOp(v, OP_Close, base+i, 0);
     if( pLevel->pIdx!=0 ){
index 1709bf9d1f18f33075f6db9c98faaba9831cf9cb..8cefb756746d295a163ddccd9d646cd06d7693d6 100644 (file)
@@ -587,6 +587,7 @@ do_test trigger2-7.1 {
   }
 } {}
 
+#explain {delete from abcd where a=1;}
 do_test trigger2-7.2 {
   execsql {
     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 \
         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
index 2d6952d2ea8b440a5bd1d70ce50f62f7cea39617..3e088d169c1cfa2633dcb9e59f12faa2171ded74 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # 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]
 source $testdir/tester.tcl
 
@@ -98,17 +98,17 @@ do_test view-2.2 {
   catchsql {
     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 {
   catchsql {
     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 {
   catchsql {
     DELETE FROM v2;
   }
-} {1 {view v2 may not be modified}}
+} {1 {cannot modify v2 because it is a view}}
 do_test view-2.5 {
   execsql {
     INSERT INTO t1 VALUES(11,12,13,14);