/ Check-in [3391f413]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix the code generation for UPDATE and DELETE so that BEFORE triggers that use RAISE(IGNORE) do not leave extra values on the stack. Ticket #2767 (CVS 4535)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3391f4139ccfe62cc27739a06567c422ddae9c69
User & Date: drh 2007-11-11 18:36:34
Context
2007-11-12
08:09
Modify the order of assert() macros in vdbe.c as per the suggestion in #2768. (CVS 4536) check-in: af5af5dc user: danielk1977 tags: trunk
2007-11-11
18:36
Fix the code generation for UPDATE and DELETE so that BEFORE triggers that use RAISE(IGNORE) do not leave extra values on the stack. Ticket #2767 (CVS 4535) check-in: 3391f413 user: drh tags: trunk
2007-11-07
15:13
Add asserts on sqlite3_mutex_held() to the zero-malloc memory allocator, in order to prove that the mutex is held when it is needed. (CVS 4534) check-in: 31eb4abc user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/delete.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
273
274
275
276
277
278
279

280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
295



296
297
298
299
300
301
302
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
** $Id: delete.c,v 1.130 2007/08/16 04:30:40 drh Exp $
*/
#include "sqliteInt.h"

/*
** Look up every table that is named in pSrc.  If any table is not found,
** add an error message to pParse->zErrMsg and return NULL.  If all tables
** are found, return a pointer to the last table.
................................................................................
    */
    end = sqlite3VdbeMakeLabel(v);

    /* This is the beginning of the delete loop when there are
    ** row triggers.
    */
    if( triggers_exist ){

      addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);

      if( !isView ){
        sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
        sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
      }
      sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
      sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
      sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
      sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0);
      if( !isView ){
        sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
      }

      (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
          -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
          addr);



    }

    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_FifoRead loop because the cursor have to all be closed
      ** before the trigger fires.  If there are no row triggers, the







|







 







>

>

<













>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
273
274
275
276
277
278
279
280
281
282
283

284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
**
** $Id: delete.c,v 1.131 2007/11/11 18:36:34 drh Exp $
*/
#include "sqliteInt.h"

/*
** Look up every table that is named in pSrc.  If any table is not found,
** add an error message to pParse->zErrMsg and return NULL.  If all tables
** are found, return a pointer to the last table.
................................................................................
    */
    end = sqlite3VdbeMakeLabel(v);

    /* This is the beginning of the delete loop when there are
    ** row triggers.
    */
    if( triggers_exist ){
      int mem1 = pParse->nMem++;
      addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
      sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
      if( !isView ){

        sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
      }
      sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
      sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
      sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
      sqlite3VdbeAddOp(v, OP_Insert, oldIdx, 0);
      if( !isView ){
        sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
      }

      (void)sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TRIGGER_BEFORE, pTab,
          -1, oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
          addr);
      if( !isView ){
        sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);
      }
    }

    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_FifoRead loop because the cursor have to all be closed
      ** before the trigger fires.  If there are no row triggers, the

Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
314
315
316
317
318
319
320


321
322
323
324
325
326
327
328
329
330


331
332
333
334
335
336
337
338
339
340
341
...
377
378
379
380
381
382
383





384
385
386
387
388
389
390
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.140 2007/08/16 10:09:03 danielk1977 Exp $
*/
#include "sqliteInt.h"

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
................................................................................
  */
  if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
    memCnt = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
  }

  if( triggers_exist ){


    /* Create pseudo-tables for NEW and OLD
    */
    sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
    sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);

    /* The top of the update loop for when there are triggers.
    */
    addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);



    if( !isView ){
      sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
      sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
      /* Open a cursor and make it point to the record that is
      ** being updated.
      */
      sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
    }
    sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);

................................................................................

    /* Fire the BEFORE and INSTEAD OF triggers
    */
    if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }





  }

  if( !isView && !IsVirtual(pTab) ){
    /* 
    ** 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







|







 







>
>










>
>
|

<
<







 







>
>
>
>
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336


337
338
339
340
341
342
343
...
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
**
** $Id: update.c,v 1.141 2007/11/11 18:36:34 drh Exp $
*/
#include "sqliteInt.h"

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
................................................................................
  */
  if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
    memCnt = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
  }

  if( triggers_exist ){
    int mem1;      /* Memory address storing the rowid for next row to update */
    
    /* Create pseudo-tables for NEW and OLD
    */
    sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
    sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);

    /* The top of the update loop for when there are triggers.
    */
    addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
    mem1 = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
    
    if( !isView ){


      /* Open a cursor and make it point to the record that is
      ** being updated.
      */
      sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
    }
    sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);

................................................................................

    /* Fire the BEFORE and INSTEAD OF triggers
    */
    if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
          newIdx, oldIdx, onError, addr) ){
      goto update_cleanup;
    }
    
    if( !isView ){
      sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);
      sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
    }
  }

  if( !isView && !IsVirtual(pTab) ){
    /* 
    ** 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

Added test/tkt2767.test.

















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# 2007 Oct 3
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file is to test that ticket #2767 has been fixed.
# Ticket #2767 is for a VDBE stack overflow on BEFORE
# triggers that run RAISE(IGNORE).
#
# $Id: tkt2767.test,v 1.1 2007/11/11 18:36:35 drh Exp $
#

set testdir [file dirname $argv0]
source $testdir/tester.tcl

do_test tkt2767-1.1 {
  execsql {
    -- Construct a table with many rows of data
    CREATE TABLE t1(x);
    INSERT INTO t1 VALUES(1);
    INSERT INTO t1 VALUES(2);
    INSERT INTO t1 SELECT x+2 FROM t1;
    INSERT INTO t1 SELECT x+4 FROM t1;
    INSERT INTO t1 SELECT x+8 FROM t1;
    INSERT INTO t1 SELECT x+16 FROM t1;

    -- BEFORE triggers that invoke raise(ignore).  The effect of
    -- these triggers should be to make INSERTs, UPDATEs, and DELETEs
    -- into no-ops.
    CREATE TRIGGER r1 BEFORE UPDATE ON t1 BEGIN
      SELECT raise(ignore);
    END;
    CREATE TRIGGER r2 BEFORE DELETE ON t1 BEGIN
      SELECT raise(ignore);
    END;
    CREATE TRIGGER r3 BEFORE INSERT ON t1 BEGIN
      SELECT raise(ignore);
    END;

    -- Verify the table content
    SELECT count(*), sum(x) FROM t1;
  }
} {32 528}

# Try to delete all elements of the table.  This will invoke the
# DELETE trigger 32 times, which should overflow the VDBE stack if
# the problem of #2767 is not fixed.  If the problem is fixed, all
# the deletes should be no-ops so the table should remain unchanged.
#
do_test tkt2767-1.2 {
  execsql {
    DELETE FROM t1 WHERE x>0;
    SELECT count(*), sum(x) FROM t1;
  }
} {32 528}

# Try to update all elements of the table.  This will invoke the
# UPDATE trigger 32 times, which should overflow the VDBE stack if
# the problem of #2767 is not fixed.  If the problem is fixed, all
# the updates should be no-ops so the table should remain unchanged.
#
do_test tkt2767-1.3 {
  execsql {
    UPDATE t1 SET x=x+1;
    SELECT count(*), sum(x) FROM t1;
  }
} {32 528}

# Invoke the insert trigger.  The insert trigger was working
# even prior to the fix of #2767.  But it seems good to go ahead
# and verify that it works.
#
do_test tkt2767-1.4 {
  execsql {
    INSERT INTO t1 SELECT x+32 FROM t1;
    SELECT count(*), sum(x) FROM t1;
  }
} {32 528}



finish_test