/ Check-in [f1966a8a]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Take advantage of the fact that b-tree cursors need not be closed while other cursors modify their tables to simplify trigger program generation code. (CVS 4654)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: f1966a8a47fca85f7862c0797a527ab01ac8b0c1
User & Date: danielk1977 2008-01-02 11:50:51
Context
2008-01-02
13:05
Remove a surplus OP_Close from delete.c. Fixes a problem with (4654). (CVS 4655) check-in: 03cc91b3 user: drh tags: trunk
11:50
Take advantage of the fact that b-tree cursors need not be closed while other cursors modify their tables to simplify trigger program generation code. (CVS 4654) check-in: f1966a8a user: danielk1977 tags: trunk
04:41
Add some extra tests for the recent trigger compilation optimization. (CVS 4653) check-in: de54dad8 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
6225
6226
6227
6228
6229
6230
6231


6232
6233
6234
6235
6236
6237
6238
6239
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.435 2008/01/01 06:19:02 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
** Return the flag byte at the beginning of the page that the cursor
** is currently pointing to.
*/
int sqlite3BtreeFlags(BtCursor *pCur){
  /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
  ** restoreOrClearCursorPosition() here.
  */


  MemPage *pPage = pCur->pPage;
  assert( cursorHoldsMutex(pCur) );
  assert( pPage->pBt==pCur->pBt );
  return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}


/*







|







 







>
>
|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
6225
6226
6227
6228
6229
6230
6231
6232
6233
6234
6235
6236
6237
6238
6239
6240
6241
** 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.
**
*************************************************************************
** $Id: btree.c,v 1.436 2008/01/02 11:50:51 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
** Return the flag byte at the beginning of the page that the cursor
** is currently pointing to.
*/
int sqlite3BtreeFlags(BtCursor *pCur){
  /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call
  ** restoreOrClearCursorPosition() here.
  */
  MemPage *pPage;
  restoreOrClearCursorPosition(pCur);
  pPage = pCur->pPage;
  assert( cursorHoldsMutex(pCur) );
  assert( pPage->pBt==pCur->pBt );
  return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}


/*

Changes to src/delete.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
171
172
173
174
175
176
177



178
179
180
181
182
183
184
...
289
290
291
292
293
294
295







296
297

298
299
300
301
302
303

304
305
306



307
308

309
310
311
312
313
314
315
...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
...
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
**    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.136 2008/01/02 00:34:37 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.
................................................................................
    oldIdx = pParse->nTab++;
  }

  /* Resolve the column names in the WHERE clause.
  */
  assert( pTabList->nSrc==1 );
  iCur = pTabList->a[0].iCursor = pParse->nTab++;



  memset(&sNC, 0, sizeof(sNC));
  sNC.pParse = pParse;
  sNC.pSrcList = pTabList;
  if( sqlite3ExprResolveNames(&sNC, pWhere) ){
    goto delete_from_cleanup;
  }

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

    /* Delete every item whose key was written to the list during the
    ** database scan.  We have to delete items after the scan is complete
    ** because deleting an item can change the scan order.
    */
    end = sqlite3VdbeMakeLabel(v);








    /* This is the beginning of the delete loop when there are
    ** row triggers.

    */
    if( triggers_exist ){
      int mem1 = pParse->nMem++;
      int addr_rowdata;
      u32 mask;
      sqlite3VdbeResolveLabel(v, addr);

      addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
      sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
      sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);



      if( !isView ){
        sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);

      }
      sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
      sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
      if( old_col_mask ){
        sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
      }else{
        sqlite3VdbeAddOp(v, OP_Null, 0, 0);
................................................................................

      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
      ** cursors are opened only once on the outside the loop.
      */
      sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);

      /* This is the beginning of the delete loop when there are no
      ** row triggers */
      if( !triggers_exist ){ 
        addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
        sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
      }

      /* Delete the row */
#ifndef SQLITE_OMIT_VIRTUALTABLE
      if( IsVirtual(pTab) ){
        pParse->pVirtualLock = pTab;
        sqlite3VdbeOp3(v, OP_VUpdate, 0, 1, (const char*)pTab->pVtab, P3_VTAB);
      }else
#endif
................................................................................
      }
    }

    /* If there are row triggers, close all cursors then invoke
    ** the AFTER triggers
    */
    if( triggers_exist ){
      if( !isView ){
        for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
          sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
        }
        sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
      }

      /* Jump back and run the AFTER triggers */
      sqlite3VdbeAddOp(v, OP_Goto, 0, iBeginAfterTrigger);
      sqlite3VdbeJumpHere(v, iEndAfterTrigger);
    }

    /* End of the delete loop */
    sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
    sqlite3VdbeResolveLabel(v, end);

    /* Close the cursors after the loop if there are no row triggers */
    if( !triggers_exist && !IsVirtual(pTab) ){
      for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
        sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
      }
      sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
    }
  }








|







 







>
>
>







 







>
>
>
>
>
>
>
|
<
>


<
<
<

>
|
|
<
>
>
>

<
>







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<










|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
...
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306

307
308
309



310
311
312
313

314
315
316
317

318
319
320
321
322
323
324
325
...
335
336
337
338
339
340
341















342
343
344
345
346
347
348
...
351
352
353
354
355
356
357







358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
**    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.137 2008/01/02 11:50:51 danielk1977 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.
................................................................................
    oldIdx = pParse->nTab++;
  }

  /* Resolve the column names in the WHERE clause.
  */
  assert( pTabList->nSrc==1 );
  iCur = pTabList->a[0].iCursor = pParse->nTab++;
  for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
    pParse->nTab++;
  }
  memset(&sNC, 0, sizeof(sNC));
  sNC.pParse = pParse;
  sNC.pSrcList = pTabList;
  if( sqlite3ExprResolveNames(&sNC, pWhere) ){
    goto delete_from_cleanup;
  }

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

    /* Delete every item whose key was written to the list during the
    ** database scan.  We have to delete items after the scan is complete
    ** because deleting an item can change the scan order.
    */
    end = sqlite3VdbeMakeLabel(v);

    if( !isView ){
      /* Open cursors for the table we are deleting from and 
      ** all its indices.
      */
      sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
    }

    /* This is the beginning of the delete loop. If a trigger encounters

    ** an IGNORE constraint, it jumps back to here.
    */
    if( triggers_exist ){



      sqlite3VdbeResolveLabel(v, addr);
    }
    addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, end);
    sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);


    if( triggers_exist ){
      int mem1 = pParse->nMem++;
      if( !isView ){

        sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
      }
      sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
      sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
      if( old_col_mask ){
        sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
      }else{
        sqlite3VdbeAddOp(v, OP_Null, 0, 0);
................................................................................

      if( !isView ){
        sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);
      }
    }

    if( !isView ){















      /* Delete the row */
#ifndef SQLITE_OMIT_VIRTUALTABLE
      if( IsVirtual(pTab) ){
        pParse->pVirtualLock = pTab;
        sqlite3VdbeOp3(v, OP_VUpdate, 0, 1, (const char*)pTab->pVtab, P3_VTAB);
      }else
#endif
................................................................................
      }
    }

    /* If there are row triggers, close all cursors then invoke
    ** the AFTER triggers
    */
    if( triggers_exist ){







      /* Jump back and run the AFTER triggers */
      sqlite3VdbeAddOp(v, OP_Goto, 0, iBeginAfterTrigger);
      sqlite3VdbeJumpHere(v, iEndAfterTrigger);
    }

    /* End of the delete loop */
    sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
    sqlite3VdbeResolveLabel(v, end);

    /* Close the cursors after the loop if there are no row triggers */
    if( !isView  && !IsVirtual(pTab) ){
      for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
        sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
      }
      sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
    }
  }

Changes to src/insert.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
...
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
...
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
...
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
**    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 INSERT statements in SQLite.
**
** $Id: insert.c,v 1.199 2008/01/02 00:34:37 drh Exp $
*/
#include "sqliteInt.h"

/*
** Set P3 of the most recently inserted opcode to a column affinity
** string for index pIdx. A column affinity string has one character
** for each column in the table, according to the affinity of the column:
................................................................................
  /* Initialize the count of rows to be inserted
  */
  if( db->flags & SQLITE_CountRows ){
    iCntMem = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, iCntMem);
  }

  /* Open tables and indices if there are no row triggers */
  if( !triggers_exist ){
    base = pParse->nTab;
    sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
  }

  /* If the data source is a temporary table, then we have to create
  ** a loop because there might be multiple rows of data.  If the data
  ** source is a subroutine call from the SELECT statement, then we need
................................................................................
    /* Fire BEFORE or INSTEAD OF triggers */
    if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab, 
        newIdx, -1, onError, endOfLoop, 0, 0) ){
      goto insert_cleanup;
    }
  }

  /* If any triggers exists, the opening of tables and indices is deferred
  ** until now.
  */
  if( triggers_exist && !isView ){
    base = pParse->nTab;
    sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
  }

  /* Push the record number for the new entry onto the stack.  The
  ** record number is a randomly generate integer created by NewRowid
  ** except when the table has an INTEGER PRIMARY KEY column, in which
  ** case the record number is the same as that column. 
  */
  if( !isView ){
    if( IsVirtual(pTab) ){
................................................................................
  /* Update the count of rows that are inserted
  */
  if( (db->flags & SQLITE_CountRows)!=0 ){
    sqlite3VdbeAddOp(v, OP_MemIncr, 1, iCntMem);
  }

  if( triggers_exist ){
    /* Close all tables opened */
    if( !isView ){
      sqlite3VdbeAddOp(v, OP_Close, base, 0);
      for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
        sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
      }
    }

    /* Code AFTER triggers */
    if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab,
          newIdx, -1, onError, endOfLoop, 0, 0) ){
      goto insert_cleanup;
    }
  }

................................................................................
    sqlite3VdbeAddOp(v, OP_Close, srcTab, 0);
  }else if( pSelect ){
    sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
    sqlite3VdbeAddOp(v, OP_Return, 0, 0);
    sqlite3VdbeResolveLabel(v, iCleanup);
  }

  if( !triggers_exist && !IsVirtual(pTab) ){
    /* Close all tables opened */
    sqlite3VdbeAddOp(v, OP_Close, base, 0);
    for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
      sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
    }
  }








|







 







|
|







 







<
<
<
<
<
<
<
<







 







<
<
<
<
<
<
<
<







 







|







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
...
709
710
711
712
713
714
715








716
717
718
719
720
721
722
...
815
816
817
818
819
820
821








822
823
824
825
826
827
828
...
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
**    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 INSERT statements in SQLite.
**
** $Id: insert.c,v 1.200 2008/01/02 11:50:51 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** Set P3 of the most recently inserted opcode to a column affinity
** string for index pIdx. A column affinity string has one character
** for each column in the table, according to the affinity of the column:
................................................................................
  /* Initialize the count of rows to be inserted
  */
  if( db->flags & SQLITE_CountRows ){
    iCntMem = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, iCntMem);
  }

  /* If this is not a view, open the table and and all indices */
  if( !isView ){
    base = pParse->nTab;
    sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
  }

  /* If the data source is a temporary table, then we have to create
  ** a loop because there might be multiple rows of data.  If the data
  ** source is a subroutine call from the SELECT statement, then we need
................................................................................
    /* Fire BEFORE or INSTEAD OF triggers */
    if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_BEFORE, pTab, 
        newIdx, -1, onError, endOfLoop, 0, 0) ){
      goto insert_cleanup;
    }
  }









  /* Push the record number for the new entry onto the stack.  The
  ** record number is a randomly generate integer created by NewRowid
  ** except when the table has an INTEGER PRIMARY KEY column, in which
  ** case the record number is the same as that column. 
  */
  if( !isView ){
    if( IsVirtual(pTab) ){
................................................................................
  /* Update the count of rows that are inserted
  */
  if( (db->flags & SQLITE_CountRows)!=0 ){
    sqlite3VdbeAddOp(v, OP_MemIncr, 1, iCntMem);
  }

  if( triggers_exist ){








    /* Code AFTER triggers */
    if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TRIGGER_AFTER, pTab,
          newIdx, -1, onError, endOfLoop, 0, 0) ){
      goto insert_cleanup;
    }
  }

................................................................................
    sqlite3VdbeAddOp(v, OP_Close, srcTab, 0);
  }else if( pSelect ){
    sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
    sqlite3VdbeAddOp(v, OP_Return, 0, 0);
    sqlite3VdbeResolveLabel(v, iCleanup);
  }

  if( !IsVirtual(pTab) && !isView ){
    /* Close all tables opened */
    sqlite3VdbeAddOp(v, OP_Close, base, 0);
    for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
      sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
    }
  }

Changes to src/update.c.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
294
295
296
297
298
299
300









301
302
303
304
305
306
307
308
...
343
344
345
346
347
348
349
350
351




352















353
354
355
356



357
358
359






360



361
362
363
364
365

366
367
368
369
370
371
372
373
374
375
376
377
...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
...
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548

549
550
551
552
553
554
555
**    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.146 2008/01/02 00:34:37 drh Exp $
*/
#include "sqliteInt.h"

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
................................................................................
  if( isView ){
    sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
  }

  /* Generate the code for triggers.
  */
  if( triggers_exist ){









    int iGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
    addr = sqlite3VdbeMakeLabel(v);
    iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
    if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
          newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
      goto update_cleanup;
    }
    iEndBeforeTrigger = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
................................................................................
  /* Initialize the count of updated rows
  */
  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.
    */






    sqlite3VdbeResolveLabel(v, addr);



    addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
    sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
    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_NotExists, iCur, addr);

    /* Generate the OLD table
    */
    sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
    if( !old_col_mask ){
      sqlite3VdbeAddOp(v, OP_Null, 0, 0);
................................................................................
    }
    sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
    if( !isView ){
      sqlite3TableAffinityStr(v, pTab);
    }
    if( pParse->nErr ) goto update_cleanup;
    sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0);
    if( !isView ){
      sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
    }

    sqlite3VdbeAddOp(v, OP_Goto, 0, iBeginBeforeTrigger);
    sqlite3VdbeJumpHere(v, iEndBeforeTrigger);

    if( !isView ){
      sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 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
    ** to be deleting some records.
    */
    sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); 
    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] ){
        KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
        sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
        sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
                       (char*)pKey, P3_KEYINFO_HANDOFF);
        assert( pParse->nTab>iCur+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 entries.
    ** So make the cursor point at the old record.
    */
    if( !triggers_exist ){
      addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
      sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
      sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);
    }
    sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
    sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);

    /* 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.)
    */
................................................................................
    sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
  }

  /* If there are triggers, close all the cursors after each iteration
  ** through the loop.  The fire the after triggers.
  */
  if( triggers_exist ){
    if( !isView ){
      for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
        if( openAll || aIdxUsed[i] )
          sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
      }
      sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
    }
    sqlite3VdbeAddOp(v, OP_Goto, 0, iBeginAfterTrigger);
    sqlite3VdbeJumpHere(v, iEndAfterTrigger);
  }

  /* Repeat the above with the next record to be updated, until
  ** all record selected by the WHERE clause have been updated.
  */
  sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
  sqlite3VdbeJumpHere(v, addr);

  /* Close all tables if there were no FOR EACH ROW triggers */
  if( !triggers_exist ){
    for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
      if( openAll || aIdxUsed[i] ){
        sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
      }
    }
    sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
  }else{

    sqlite3VdbeAddOp(v, OP_Close, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_Close, oldIdx, 0);
  }

  /*
  ** Return the number of rows that were changed. If this routine is 
  ** generating code because of a call to sqlite3NestedParse(), do not







|







 







>
>
>
>
>
>
>
>
>
|







 







|
|
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
<
<
>
>
>
|
<
<
>
>
>
>
>
>

>
>
>
|
|
|
|
<
>
|
<
|
<
<







 







<
<
<










<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







<
<
<
<
<







 







<
<
<
<
<
<
<










|
<
|
|
|
|
|
|
<
>







8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
...
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381



382
383
384
385


386
387
388
389
390
391
392
393
394
395
396
397
398
399

400
401

402


403
404
405
406
407
408
409
...
438
439
440
441
442
443
444



445
446
447
448
449
450
451
452
453
454



























455
456
457
458
459
460
461





462
463
464
465
466
467
468
...
513
514
515
516
517
518
519







520
521
522
523
524
525
526
527
528
529
530

531
532
533
534
535
536

537
538
539
540
541
542
543
544
**    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.147 2008/01/02 11:50:51 danielk1977 Exp $
*/
#include "sqliteInt.h"

#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
static void updateVirtualTable(
  Parse *pParse,       /* The parsing context */
................................................................................
  if( isView ){
    sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
  }

  /* Generate the code for triggers.
  */
  if( triggers_exist ){
    int iGoto;

    /* 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);

    iGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
    addr = sqlite3VdbeMakeLabel(v);
    iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v);
    if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TRIGGER_BEFORE, pTab,
          newIdx, oldIdx, onError, addr, &old_col_mask, &new_col_mask) ){
      goto update_cleanup;
    }
    iEndBeforeTrigger = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
................................................................................
  /* Initialize the count of updated rows
  */
  if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
    memCnt = pParse->nMem++;
    sqlite3VdbeAddOp(v, OP_MemInt, 0, memCnt);
  }

  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
    ** to be deleting some records.
    */
    sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite); 
    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] ){
        KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
        sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);



        sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
                       (char*)pKey, P3_KEYINFO_HANDOFF);
        assert( pParse->nTab>iCur+i+1 );
      }


    }

  }
  
  /* Jump back to this point if a trigger encounters an IGNORE constraint. */
  if( triggers_exist ){
    sqlite3VdbeResolveLabel(v, addr);
  }

  /* Top of the update loop */
  addr = sqlite3VdbeAddOp(v, OP_FifoRead, 0, 0);
  sqlite3VdbeAddOp(v, OP_StackDepth, -1, 0);
  sqlite3VdbeAddOp(v, OP_MemStore, mem1, 0);


  if( triggers_exist ){
    /* Make cursor iCur point to the record that is being updated.

    */


    sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);

    /* Generate the OLD table
    */
    sqlite3VdbeAddOp(v, OP_Rowid, iCur, 0);
    if( !old_col_mask ){
      sqlite3VdbeAddOp(v, OP_Null, 0, 0);
................................................................................
    }
    sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
    if( !isView ){
      sqlite3TableAffinityStr(v, pTab);
    }
    if( pParse->nErr ) goto update_cleanup;
    sqlite3VdbeAddOp(v, OP_Insert, newIdx, 0);




    sqlite3VdbeAddOp(v, OP_Goto, 0, iBeginBeforeTrigger);
    sqlite3VdbeJumpHere(v, iEndBeforeTrigger);

    if( !isView ){
      sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);
    }
  }

  if( !isView && !IsVirtual(pTab) ){




























    /* 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 entries.
    ** So make the cursor point at the old record.
    */





    sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
    sqlite3VdbeAddOp(v, OP_MemLoad, mem1, 0);

    /* 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.)
    */
................................................................................
    sqlite3VdbeAddOp(v, OP_MemIncr, 1, memCnt);
  }

  /* If there are triggers, close all the cursors after each iteration
  ** through the loop.  The fire the after triggers.
  */
  if( triggers_exist ){







    sqlite3VdbeAddOp(v, OP_Goto, 0, iBeginAfterTrigger);
    sqlite3VdbeJumpHere(v, iEndAfterTrigger);
  }

  /* Repeat the above with the next record to be updated, until
  ** all record selected by the WHERE clause have been updated.
  */
  sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
  sqlite3VdbeJumpHere(v, addr);

  /* Close all tables */

  for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
    if( openAll || aIdxUsed[i] ){
      sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
    }
  }
  sqlite3VdbeAddOp(v, OP_Close, iCur, 0);

  if( triggers_exist ){
    sqlite3VdbeAddOp(v, OP_Close, newIdx, 0);
    sqlite3VdbeAddOp(v, OP_Close, oldIdx, 0);
  }

  /*
  ** Return the number of rows that were changed. If this routine is 
  ** generating code because of a call to sqlite3NestedParse(), do not

Changes to src/vdbeaux.c.

697
698
699
700
701
702
703

704
705
706
707

708
709
710
711
712
713
714
      if( zP3==0 || pOp->opcode==OP_Noop ){
        zP3 = zTemp;
        zTemp[0] = 0;
      }
    }
  }
  assert( zP3!=0 );

  if( pOp->zComment && zP3==zTemp && (nP3 = strlen(zP3))<nTemp ){
    sqlite3_snprintf(nTemp-nP3, &zP3[nP3], "%s# %s",
                     nP3>0 ? " " : "", pOp->zComment);
  }

  return zP3;
}
#endif

/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**







>




>







697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
      if( zP3==0 || pOp->opcode==OP_Noop ){
        zP3 = zTemp;
        zTemp[0] = 0;
      }
    }
  }
  assert( zP3!=0 );
#ifdef SQLITE_DEBUG
  if( pOp->zComment && zP3==zTemp && (nP3 = strlen(zP3))<nTemp ){
    sqlite3_snprintf(nTemp-nP3, &zP3[nP3], "%s# %s",
                     nP3>0 ? " " : "", pOp->zComment);
  }
#endif
  return zP3;
}
#endif

/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**