SQLite

Check-in [4145b3e050]
Login

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

Overview
Comment:If a DELETE trigger fired by an UPDATE OR REPLACE statement deletes the row being updated, do not attempt to proceed with the original UPDATE operation. Fix for [d6a0fbc1].
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 4145b3e05051ba6729105157a876793ad14a05d895fc10fe704f437eab16ea93
User & Date: dan 2019-08-31 17:14:35.569
References
2019-08-31
17:15 Closed ticket [d6a0fbc14e]: REINDEX causes "UNIQUE constraint failed" error when using TRIGGER plus 6 other changes (artifact: 9c168f3506 user: dan)
Context
2019-08-31
20:13
Improvements to the algorithm that determines which SELECT in a sequence of nested SELECT statements that an aggregate function belongs to. This resolves an issue identified by dbsqlfuzz. (check-in: d768007473 user: drh tags: trunk)
17:14
If a DELETE trigger fired by an UPDATE OR REPLACE statement deletes the row being updated, do not attempt to proceed with the original UPDATE operation. Fix for [d6a0fbc1]. (check-in: 4145b3e050 user: dan tags: trunk)
01:33
Remove some affinity tests that became unreachable due to the prior change. (check-in: e2db1123fa user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/update.c.
711
712
713
714
715
716
717
718
719
720
721
722
723
724











725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
      if( aXRef[i]<0 && i!=pTab->iPKey ){
        sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
      }
    }
  }

  if( !isView ){
    int addr1 = 0;        /* Address of jump instruction */

    /* Do constraint checks. */
    assert( regOldRowid>0 );
    sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
        regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
        aXRef, 0);












    /* Do FK constraint checks. */
    if( hasFK ){
      sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
    }

    /* Delete the index entries associated with the current record.  */
    if( bReplace || chngKey ){
      if( pPk ){
        addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
      }else{
        addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
      }
      VdbeCoverageNeverTaken(v);
    }
    sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);

    /* If changing the rowid value, or if there are foreign key constraints
    ** to process, delete the old record. Otherwise, add a noop OP_Delete
    ** to invoke the pre-update hook.
    **
    ** That (regNew==regnewRowid+1) is true is also important for the 







<
<





>
>
>
>
>
>
>
>
>
>
>







<
<
<
<
<
<
<
<







711
712
713
714
715
716
717


718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740








741
742
743
744
745
746
747
      if( aXRef[i]<0 && i!=pTab->iPKey ){
        sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
      }
    }
  }

  if( !isView ){


    /* Do constraint checks. */
    assert( regOldRowid>0 );
    sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
        regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
        aXRef, 0);

    /* If REPLACE conflict handling may have been used, or if the PK of the
    ** row is changing, then the GenerateConstraintChecks() above may have
    ** moved cursor iDataCur. Reseek it. */
    if( bReplace || chngKey ){
      if( pPk ){
        sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
      }else{
        sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
      }
    }

    /* Do FK constraint checks. */
    if( hasFK ){
      sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
    }

    /* Delete the index entries associated with the current record.  */








    sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);

    /* If changing the rowid value, or if there are foreign key constraints
    ** to process, delete the old record. Otherwise, add a noop OP_Delete
    ** to invoke the pre-update hook.
    **
    ** That (regNew==regnewRowid+1) is true is also important for the 
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
      sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
    }
#else
    if( hasFK>1 || chngKey ){
      sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
    }
#endif
    if( bReplace || chngKey ){
      sqlite3VdbeJumpHere(v, addr1);
    }

    if( hasFK ){
      sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
    }
  
    /* Insert the new index entries and the new record. */
    sqlite3CompleteInsertion(







<
<
<







763
764
765
766
767
768
769



770
771
772
773
774
775
776
      sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
    }
#else
    if( hasFK>1 || chngKey ){
      sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
    }
#endif




    if( hasFK ){
      sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
    }
  
    /* Insert the new index entries and the new record. */
    sqlite3CompleteInsertion(
Changes to test/conflict3.test.
362
363
364
365
366
367
368









369


























































370

do_execsql_test 12.2 {
  REPLACE INTO t2 VALUES(NULL, '112'), (111, '111B');
}
do_execsql_test 12.3 {
  SELECT * FROM t2;
} {111 111B 112 112}





































































finish_test








>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
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
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
do_execsql_test 12.2 {
  REPLACE INTO t2 VALUES(NULL, '112'), (111, '111B');
}
do_execsql_test 12.3 {
  SELECT * FROM t2;
} {111 111B 112 112}

#-------------------------------------------------------------------------
ifcapable trigger {
  reset_db
  do_execsql_test 13.1.0 {
    PRAGMA recursive_triggers = true;
    CREATE TABLE t0 (c0 UNIQUE, c1 UNIQUE);
    CREATE TRIGGER tr0 AFTER DELETE ON t0 BEGIN 
      DELETE FROM t0; 
    END;

    INSERT INTO t0 VALUES(1, NULL);
    INSERT INTO t0 VALUES(0, NULL);
  }

  do_execsql_test 13.1.1 {
    UPDATE OR REPLACE t0 SET c1 = 1;
  }

  integrity_check 13.1.2

  do_execsql_test 13.1.3 {
    SELECT * FROM t0
  } {}

  do_execsql_test 13.2.0 {
    CREATE TABLE t2 (a PRIMARY KEY, b UNIQUE, c UNIQUE) WITHOUT ROWID;
    CREATE TRIGGER tr3 AFTER DELETE ON t2 BEGIN 
      DELETE FROM t2; 
    END;

    INSERT INTO t2 VALUES(1, 1, 1);
    INSERT INTO t2 VALUES(2, 2, 2);
  }

  do_execsql_test 13.2.1 {
    UPDATE OR REPLACE t2 SET c = 0;
  }

  integrity_check 13.2.2

  do_execsql_test 13.2.3 {
    SELECT * FROM t2
  } {}

  do_execsql_test 13.3.0 {
    CREATE TABLE t1(a, b);
    CREATE TABLE log(x);
    CREATE INDEX i1 ON t1(a);
    INSERT INTO t1 VALUES(1, 2);

    CREATE TRIGGER tb BEFORE UPDATE ON t1 BEGIN
      DELETE FROM t1;
    END;
    CREATE TRIGGER ta AFTER UPDATE ON t1 BEGIN
      INSERT INTO log VALUES('fired!');
    END;

    UPDATE t1 SET b=3;
  }

  do_execsql_test 13.3.1 {
    SELECT * FROM t1;
  } {}
  do_execsql_test 13.3.2 {
    SELECT * FROM log;
  } {}
}

finish_test