/ Check-in [5ab71c3a]
Login

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

Overview
Comment:When materializing a view for an UPDATE or DELETE make use of the WHERE clause to limit the number of rows materialized. Ticket #2938. (CVS 4782)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 5ab71c3a79cac04cb2c576f83a62218d05571006
User & Date: drh 2008-02-12 16:52:14
Context
2008-02-13
18:25
Where possible, avoid freeing buffers allocated for vdbe memory cells in case they can be reused. (CVS 4783) check-in: 990237e2 user: danielk1977 tags: trunk
2008-02-12
16:52
When materializing a view for an UPDATE or DELETE make use of the WHERE clause to limit the number of rows materialized. Ticket #2938. (CVS 4782) check-in: 5ab71c3a user: drh tags: trunk
2008-02-09
14:30
ALTER TABLE uses double-quotes for quoting table names. (CVS 4781) check-in: 607247c2 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Show Whitespace Changes Patch

Changes to src/delete.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains C code routines that are called by the parser
    13     13   ** in order to generate code for DELETE FROM statements.
    14     14   **
    15         -** $Id: delete.c,v 1.160 2008/01/25 15:04:50 drh Exp $
           15  +** $Id: delete.c,v 1.161 2008/02/12 16:52:14 drh Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   
    19     19   /*
    20     20   ** Look up every table that is named in pSrc.  If any table is not found,
    21     21   ** add an error message to pParse->zErrMsg and return NULL.  If all tables
    22     22   ** are found, return a pointer to the last table.
................................................................................
    76     76     assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
    77     77     sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite), pTab->zName);
    78     78     sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
    79     79     VdbeComment((v, "%s", pTab->zName));
    80     80     sqlite3VdbeAddOp2(v, OP_SetNumColumns, iCur, pTab->nCol);
    81     81   }
    82     82   
           83  +
           84  +#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
           85  +/*
           86  +** Evaluate a view and store its result in an ephemeral table.  The
           87  +** pWhere argument is an optional WHERE clause that restricts the
           88  +** set of rows in the view that are to be added to the ephemeral table.
           89  +*/
           90  +void sqlite3MaterializeView(
           91  +  Parse *pParse,       /* Parsing context */
           92  +  Select *pView,       /* View definition */
           93  +  Expr *pWhere,        /* Optional WHERE clause to be added */
           94  +  u32 col_mask,        /* Render only the columns in this mask. */
           95  +  int iCur             /* Cursor number for ephemerial table */
           96  +){
           97  +  SelectDest dest;
           98  +  Select *pDup;
           99  +  sqlite3 *db = pParse->db;
          100  +
          101  +  pDup = sqlite3SelectDup(db, pView);
          102  +  if( pWhere ){
          103  +    SrcList *pFrom;
          104  +    
          105  +    pWhere = sqlite3ExprDup(db, pWhere);
          106  +    pFrom = sqlite3SrcListAppendFromTerm(pParse, 0, 0, 0, 0, pDup, 0, 0);
          107  +    pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
          108  +  }
          109  +  sqlite3SelectMask(pParse, pDup, col_mask);
          110  +  sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
          111  +  sqlite3Select(pParse, pDup, &dest, 0, 0, 0, 0);
          112  +  sqlite3SelectDelete(pDup);
          113  +}
          114  +#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */
          115  +
    83    116   
    84    117   /*
    85    118   ** Generate code for a DELETE FROM statement.
    86    119   **
    87    120   **     DELETE FROM table_wxyz WHERE a<5 AND b NOT NULL;
    88    121   **                 \________/       \________________/
    89    122   **                  pTabList              pWhere
................................................................................
   166    199   
   167    200     /* Allocate a cursor used to store the old.* data for a trigger.
   168    201     */
   169    202     if( triggers_exist ){ 
   170    203       oldIdx = pParse->nTab++;
   171    204     }
   172    205   
   173         -  /* Resolve the column names in the WHERE clause.
          206  +  /* Assign  cursor number to the table and all its indices.
   174    207     */
   175    208     assert( pTabList->nSrc==1 );
   176    209     iCur = pTabList->a[0].iCursor = pParse->nTab++;
   177    210     for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
   178    211       pParse->nTab++;
   179    212     }
   180         -  memset(&sNC, 0, sizeof(sNC));
   181         -  sNC.pParse = pParse;
   182         -  sNC.pSrcList = pTabList;
   183         -  if( sqlite3ExprResolveNames(&sNC, pWhere) ){
   184         -    goto delete_from_cleanup;
   185         -  }
   186    213   
   187    214     /* Start the view context
   188    215     */
   189    216     if( isView ){
   190    217       sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
   191    218     }
   192    219   
................................................................................
   217    244       sqlite3VdbeJumpHere(v, iGoto);
   218    245     }
   219    246   
   220    247     /* If we are trying to delete from a view, realize that view into
   221    248     ** a ephemeral table.
   222    249     */
   223    250     if( isView ){
   224         -    SelectDest dest;
   225         -    Select *pView;
          251  +    sqlite3MaterializeView(pParse, pTab->pSelect, pWhere, old_col_mask, iCur);
          252  +  }
   226    253   
   227         -    pView = sqlite3SelectDup(db, pTab->pSelect);
   228         -    sqlite3SelectMask(pParse, pView, old_col_mask);
   229         -    sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
   230         -    sqlite3Select(pParse, pView, &dest, 0, 0, 0, 0);
   231         -    sqlite3SelectDelete(pView);
          254  +  /* Resolve the column names in the WHERE clause.
          255  +  */
          256  +  memset(&sNC, 0, sizeof(sNC));
          257  +  sNC.pParse = pParse;
          258  +  sNC.pSrcList = pTabList;
          259  +  if( sqlite3ExprResolveNames(&sNC, pWhere) ){
          260  +    goto delete_from_cleanup;
   232    261     }
   233    262   
   234    263     /* Initialize the counter of the number of rows deleted, if
   235    264     ** we are counting rows.
   236    265     */
   237    266     if( db->flags & SQLITE_CountRows ){
   238    267       memCnt = ++pParse->nMem;

Changes to src/sqliteInt.h.

     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** Internal interface definitions for SQLite.
    13     13   **
    14         -** @(#) $Id: sqliteInt.h,v 1.659 2008/02/02 04:47:09 danielk1977 Exp $
           14  +** @(#) $Id: sqliteInt.h,v 1.660 2008/02/12 16:52:14 drh Exp $
    15     15   */
    16     16   #ifndef _SQLITEINT_H_
    17     17   #define _SQLITEINT_H_
    18     18   
    19     19   /*
    20     20   ** The macro unlikely() is a hint that surrounds a boolean
    21     21   ** expression that is usually false.  Macro likely() surrounds
................................................................................
  1822   1822   #else
  1823   1823   # define sqlite3SafetyOn(A) 0
  1824   1824   # define sqlite3SafetyOff(A) 0
  1825   1825   #endif
  1826   1826   int sqlite3SafetyCheckOk(sqlite3*);
  1827   1827   int sqlite3SafetyCheckSickOrOk(sqlite3*);
  1828   1828   void sqlite3ChangeCookie(Parse*, int);
         1829  +void sqlite3MaterializeView(Parse*, Select*, Expr*, u32, int);
  1829   1830   
  1830   1831   #ifndef SQLITE_OMIT_TRIGGER
  1831   1832     void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*,
  1832   1833                              Expr*,int, int);
  1833   1834     void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
  1834   1835     void sqlite3DropTrigger(Parse*, SrcList*, int);
  1835   1836     void sqlite3DropTriggerPtr(Parse*, Trigger*);

Changes to src/update.c.

     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This file contains C code routines that are called by the parser
    13     13   ** to handle UPDATE statements.
    14     14   **
    15         -** $Id: update.c,v 1.170 2008/01/19 03:35:59 drh Exp $
           15  +** $Id: update.c,v 1.171 2008/02/12 16:52:14 drh Exp $
    16     16   */
    17     17   #include "sqliteInt.h"
    18     18   
    19     19   #ifndef SQLITE_OMIT_VIRTUALTABLE
    20     20   /* Forward declaration */
    21     21   static void updateVirtualTable(
    22     22     Parse *pParse,       /* The parsing context */
................................................................................
   285    285                          pWhere);
   286    286       pWhere = 0;
   287    287       pTabList = 0;
   288    288       goto update_cleanup;
   289    289     }
   290    290   #endif
   291    291   
   292         -  /* Resolve the column names in all the expressions in the
   293         -  ** WHERE clause.
   294         -  */
   295         -  if( sqlite3ExprResolveNames(&sNC, pWhere) ){
   296         -    goto update_cleanup;
   297         -  }
   298         -
   299    292     /* Start the view context
   300    293     */
   301    294     if( isView ){
   302    295       sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
   303    296     }
   304    297   
   305    298     /* Generate the code for triggers.
................................................................................
   331    324       sqlite3VdbeJumpHere(v, iGoto);
   332    325     }
   333    326   
   334    327     /* If we are trying to update a view, realize that view into
   335    328     ** a ephemeral table.
   336    329     */
   337    330     if( isView ){
   338         -    Select *pView;
   339         -    SelectDest dest;
          331  +    sqlite3MaterializeView(pParse, pTab->pSelect, pWhere,
          332  +                           old_col_mask|new_col_mask, iCur);
          333  +  }
   340    334   
   341         -    pView = sqlite3SelectDup(db, pTab->pSelect);
   342         -    sqlite3SelectMask(pParse, pView, old_col_mask|new_col_mask);
   343         -    sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
   344         -    sqlite3Select(pParse, pView, &dest, 0, 0, 0, 0);
   345         -    sqlite3SelectDelete(pView);
          335  +  /* Resolve the column names in all the expressions in the
          336  +  ** WHERE clause.
          337  +  */
          338  +  if( sqlite3ExprResolveNames(&sNC, pWhere) ){
          339  +    goto update_cleanup;
   346    340     }
   347    341   
   348    342     /* Begin the database scan
   349    343     */
   350    344     pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0);
   351    345     if( pWInfo==0 ) goto update_cleanup;
   352    346   

Changes to test/auth.test.

     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this script is testing the sqlite3_set_authorizer() API
    13     13   # and related functionality.
    14     14   #
    15         -# $Id: auth.test,v 1.40 2008/01/01 19:02:09 danielk1977 Exp $
           15  +# $Id: auth.test,v 1.41 2008/02/12 16:52:14 drh Exp $
    16     16   #
    17     17   
    18     18   set testdir [file dirname $argv0]
    19     19   source $testdir/tester.tcl
    20     20   
    21     21   # disable this test if the SQLITE_OMIT_AUTHORIZATION macro is
    22     22   # defined during compilation.
................................................................................
  2241   2241     set authargs {}
  2242   2242     execsql {
  2243   2243       UPDATE v1 SET x=1 WHERE x=117
  2244   2244     }
  2245   2245     set authargs
  2246   2246   } [list \
  2247   2247     SQLITE_UPDATE v1     x  main {} \
  2248         -  SQLITE_READ   v1     x  main {} \
  2249   2248     SQLITE_INSERT v1chng {} main r2 \
  2250   2249     SQLITE_READ   v1     x  main r2 \
  2251   2250     SQLITE_READ   v1     x  main r2 \
  2252   2251     SQLITE_READ   t2     a  main v1 \
  2253   2252     SQLITE_READ   t2     b  main v1 \
  2254         -  SQLITE_SELECT {}     {} {}   v1]
         2253  +  SQLITE_SELECT {}     {} {}   v1 \
         2254  +  SQLITE_SELECT {}     {} {}   v1 \
         2255  +  SQLITE_READ   v1     x  main v1 \
         2256  +]
  2255   2257   do_test auth-4.4 {
  2256   2258     execsql {
  2257   2259       CREATE TRIGGER r3 INSTEAD OF DELETE ON v1 BEGIN
  2258   2260         INSERT INTO v1chng VALUES(OLD.x,NULL);
  2259   2261       END;
  2260   2262       SELECT * FROM v1;
  2261   2263     }
................................................................................
  2264   2266     set authargs {}
  2265   2267     execsql {
  2266   2268       DELETE FROM v1 WHERE x=117
  2267   2269     }
  2268   2270     set authargs
  2269   2271   } [list \
  2270   2272     SQLITE_DELETE v1     {} main {} \
  2271         -  SQLITE_READ   v1     x  main {} \
  2272   2273     SQLITE_INSERT v1chng {} main r3 \
  2273   2274     SQLITE_READ   v1     x  main r3 \
  2274   2275     SQLITE_READ   t2     a  main v1 \
  2275   2276     SQLITE_READ   t2     b  main v1 \
  2276         -  SQLITE_SELECT {}     {} {}   v1]
         2277  +  SQLITE_SELECT {}     {} {}   v1 \
         2278  +  SQLITE_SELECT {}     {} {}   v1 \
         2279  +  SQLITE_READ   v1     x  main v1 \
         2280  +]
  2277   2281   
  2278   2282   } ;# ifcapable view && trigger
  2279   2283   
  2280   2284   # Ticket #1338:  Make sure authentication works in the presence of an AS
  2281   2285   # clause.
  2282   2286   #
  2283   2287   do_test auth-5.1 {

Added test/triggerA.test.

            1  +# 2008 February 12
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library. Specifically,
           12  +# it tests issues relating to firing an INSTEAD OF trigger on a VIEW
           13  +# when one tries to UPDATE or DELETE from the view.  Does the WHERE
           14  +# clause of the UPDATE or DELETE statement get passed down correctly 
           15  +# into the query that manifests the view?
           16  +#
           17  +# Ticket #2938
           18  +#
           19  +
           20  +set testdir [file dirname $argv0]
           21  +source $testdir/tester.tcl
           22  +ifcapable {!trigger} {
           23  +  finish_test
           24  +  return
           25  +}
           26  +
           27  +# Create two table containing some sample data
           28  +#
           29  +do_test triggerA-1.1 {
           30  +  db eval {
           31  +    CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT UNIQUE);
           32  +    CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER UNIQUE, c TEXT);
           33  +  }
           34  +  set i 1
           35  +  foreach word {one two three four five six seven eight nine ten} {
           36  +    set j [expr {$i*100 + [string length $word]}]
           37  +    db eval {
           38  +       INSERT INTO t1 VALUES($i,$word);
           39  +       INSERT INTO t2 VALUES(20-$i,$j,$word);
           40  +    }
           41  +    incr i
           42  +  }
           43  +  db eval {
           44  +    SELECT count(*) FROM t1 UNION ALL SELECT count(*) FROM t2;
           45  +  }
           46  +} {10 10}
           47  +
           48  +# Create views of various forms against one or both of the two tables.
           49  +#
           50  +do_test triggerA-1.2 {
           51  +  db eval {
           52  +     CREATE VIEW v1 AS SELECT y, x FROM t1;
           53  +     SELECT * FROM v1 ORDER BY 1;
           54  +  }
           55  +} {eight 8 five 5 four 4 nine 9 one 1 seven 7 six 6 ten 10 three 3 two 2}
           56  +do_test triggerA-1.3 {
           57  +  db eval {
           58  +     CREATE VIEW v2 AS SELECT x, y FROM t1 WHERE y GLOB '*e*';
           59  +     SELECT * FROM v2 ORDER BY 1;
           60  +  }
           61  +} {1 one 3 three 5 five 7 seven 8 eight 9 nine 10 ten}
           62  +do_test triggerA-1.4 {
           63  +  db eval {
           64  +     CREATE VIEW v3 AS
           65  +       SELECT CAST(x AS TEXT) AS c1 FROM t1 UNION SELECT y FROM t1;
           66  +     SELECT * FROM v3 ORDER BY c1;
           67  +  }
           68  +} {1 10 2 3 4 5 6 7 8 9 eight five four nine one seven six ten three two}
           69  +do_test triggerA-1.5 {
           70  +  db eval {
           71  +     CREATE VIEW v4 AS
           72  +        SELECT CAST(x AS TEXT) AS c1 FROM t1
           73  +        UNION SELECT y FROM t1 WHERE x BETWEEN 3 and 5;
           74  +     SELECT * FROM v4 ORDER BY 1;
           75  +  }
           76  +} {1 10 2 3 4 5 6 7 8 9 five four three}
           77  +do_test triggerA-1.6 {
           78  +  db eval {
           79  +     CREATE VIEW v5 AS SELECT x, b FROM t1, t2 WHERE y=c;
           80  +     SELECT * FROM v5;
           81  +  }
           82  +} {1 103 2 203 3 305 4 404 5 504 6 603 7 705 8 805 9 904 10 1003}
           83  +
           84  +# Create INSTEAD OF triggers on the views.  Run UPDATE and DELETE statements
           85  +# using those triggers.  Verify correct operation.
           86  +#
           87  +do_test triggerA-2.1 {
           88  +  db eval {
           89  +     CREATE TABLE result2(a,b);
           90  +     CREATE TRIGGER r1d INSTEAD OF DELETE ON v1 BEGIN
           91  +       INSERT INTO result2(a,b) VALUES(old.y, old.x);
           92  +     END;
           93  +     DELETE FROM v1 WHERE x=5;
           94  +     SELECT * FROM result2;
           95  +  }
           96  +} {five 5}
           97  +do_test triggerA-2.2 {
           98  +  db eval {
           99  +     CREATE TABLE result4(a,b,c,d);
          100  +     CREATE TRIGGER r1u INSTEAD OF UPDATE ON v1 BEGIN
          101  +       INSERT INTO result4(a,b,c,d) VALUES(old.y, old.x, new.y, new.x);
          102  +     END;
          103  +     UPDATE v1 SET y=y||'-extra' WHERE x BETWEEN 3 AND 5;
          104  +     SELECT * FROM result4 ORDER BY a;
          105  +  }
          106  +} {five 5 five-extra 5 four 4 four-extra 4 three 3 three-extra 3}
          107  +
          108  +
          109  +do_test triggerA-2.3 {
          110  +  db eval {
          111  +     DELETE FROM result2;
          112  +     CREATE TRIGGER r2d INSTEAD OF DELETE ON v2 BEGIN
          113  +       INSERT INTO result2(a,b) VALUES(old.y, old.x);
          114  +     END;
          115  +     DELETE FROM v2 WHERE x=5;
          116  +     SELECT * FROM result2;
          117  +  }
          118  +} {five 5}
          119  +do_test triggerA-2.4 {
          120  +  db eval {
          121  +     DELETE FROM result4;
          122  +     CREATE TRIGGER r2u INSTEAD OF UPDATE ON v2 BEGIN
          123  +       INSERT INTO result4(a,b,c,d) VALUES(old.y, old.x, new.y, new.x);
          124  +     END;
          125  +     UPDATE v2 SET y=y||'-extra' WHERE x BETWEEN 3 AND 5;
          126  +     SELECT * FROM result4 ORDER BY a;
          127  +  }
          128  +} {five 5 five-extra 5 three 3 three-extra 3}
          129  +
          130  +
          131  +do_test triggerA-2.5 {
          132  +  db eval {
          133  +     CREATE TABLE result1(a);
          134  +     CREATE TRIGGER r3d INSTEAD OF DELETE ON v3 BEGIN
          135  +       INSERT INTO result1(a) VALUES(old.c1);
          136  +     END;
          137  +     DELETE FROM v3 WHERE c1 BETWEEN '8' AND 'eight';
          138  +     SELECT * FROM result1 ORDER BY a;
          139  +  }
          140  +} {8 9 eight}
          141  +do_test triggerA-2.6 {
          142  +  db eval {
          143  +     DELETE FROM result2;
          144  +     CREATE TRIGGER r3u INSTEAD OF UPDATE ON v3 BEGIN
          145  +       INSERT INTO result2(a,b) VALUES(old.c1, new.c1);
          146  +     END;
          147  +     UPDATE v3 SET c1 = c1 || '-extra' WHERE c1 BETWEEN '8' and 'eight';
          148  +     SELECT * FROM result2 ORDER BY a;
          149  +  }
          150  +} {8 8-extra 9 9-extra eight eight-extra}
          151  +
          152  +
          153  +do_test triggerA-2.7 {
          154  +  db eval {
          155  +     DELETE FROM result1;
          156  +     CREATE TRIGGER r4d INSTEAD OF DELETE ON v4 BEGIN
          157  +       INSERT INTO result1(a) VALUES(old.c1);
          158  +     END;
          159  +     DELETE FROM v4 WHERE c1 BETWEEN '8' AND 'eight';
          160  +     SELECT * FROM result1 ORDER BY a;
          161  +  }
          162  +} {8 9}
          163  +do_test triggerA-2.8 {
          164  +  db eval {
          165  +     DELETE FROM result2;
          166  +     CREATE TRIGGER r4u INSTEAD OF UPDATE ON v4 BEGIN
          167  +       INSERT INTO result2(a,b) VALUES(old.c1, new.c1);
          168  +     END;
          169  +     UPDATE v4 SET c1 = c1 || '-extra' WHERE c1 BETWEEN '8' and 'eight';
          170  +     SELECT * FROM result2 ORDER BY a;
          171  +  }
          172  +} {8 8-extra 9 9-extra}
          173  +
          174  +
          175  +do_test triggerA-2.9 {
          176  +  db eval {
          177  +     DELETE FROM result2;
          178  +     CREATE TRIGGER r5d INSTEAD OF DELETE ON v5 BEGIN
          179  +       INSERT INTO result2(a,b) VALUES(old.x, old.b);
          180  +     END;
          181  +     DELETE FROM v5 WHERE x=5;
          182  +     SELECT * FROM result2;
          183  +  }
          184  +} {5 504}
          185  +do_test triggerA-2.10 {
          186  +  db eval {
          187  +     DELETE FROM result4;
          188  +     CREATE TRIGGER r5u INSTEAD OF UPDATE ON v5 BEGIN
          189  +       INSERT INTO result4(a,b,c,d) VALUES(old.x, old.b, new.x, new.b);
          190  +     END;
          191  +     UPDATE v5 SET b = b+9900000 WHERE x BETWEEN 3 AND 5;
          192  +     SELECT * FROM result4 ORDER BY a;
          193  +  }
          194  +} {3 305 3 9900305 4 404 4 9900404 5 504 5 9900504}
          195  +
          196  +# Only run the reamining tests if memory debugging is turned on.
          197  +#
          198  +ifcapable !memdebug {
          199  +   puts "Skipping triggerA malloc tests: not compiled with -DSQLITE_MEMDEBUG..."
          200  +   finish_test
          201  +   return
          202  +}
          203  +source $testdir/malloc_common.tcl
          204  +
          205  +# Save a copy of the current database configuration.
          206  +#
          207  +db close
          208  +file delete -force test.db-triggerA
          209  +file copy test.db test.db-triggerA
          210  +sqlite3 db test.db
          211  +
          212  +# Run malloc tests on the INSTEAD OF trigger firing.
          213  +#
          214  +do_malloc_test triggerA-3 -tclprep {
          215  +  db close
          216  +  file delete -force test.db test.db-journal
          217  +  file copy -force test.db-triggerA test.db
          218  +  sqlite3 db test.db
          219  +  sqlite3_extended_result_codes db 1  
          220  +  db eval {SELECT * FROM v5; -- warm up the cache}
          221  +} -sqlbody {
          222  +   DELETE FROM v5 WHERE x=5;
          223  +   UPDATE v5 SET b=b+9900000 WHERE x BETWEEN 3 AND 5;
          224  +}
          225  +
          226  +# Clean up the saved database copy.
          227  +#
          228  +file delete -force test.db-triggerA
          229  +
          230  +finish_test