If an AFTER DELETE trigger fires when a conflict row is deleted by REPLACE
conflict resolution, make sure the conflict really has been resolved and that
the trigger did not recreate the row before continuing.
Ticket [a8a4847a2d96f5de]

FossilOrigin-Name: eea1e7aa57e74c4329003f4550168e2aed9e33d2301a3ba84b10781a9cebbc1b
diff --git a/src/insert.c b/src/insert.c
index d9078b8..690845a 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1582,6 +1582,8 @@
           sqlite3MultiWrite(pParse);
           sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
                                    regNewData, 1, 0, OE_Replace, 1, -1);
+          sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
+          sqlite3RowidConstraint(pParse, OE_Abort, pTab);
         }else{
 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
           assert( HasRowid(pTab) );
@@ -1829,16 +1831,23 @@
       }
       default: {
         Trigger *pTrigger = 0;
+        int bRetryConstraintCheck = 0;
         assert( onError==OE_Replace );
         if( db->flags&SQLITE_RecTriggers ){
           pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
         }
         if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
           sqlite3MultiWrite(pParse);
+          bRetryConstraintCheck = 1;
         }
         sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
             regR, nPkField, 0, OE_Replace,
             (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), iThisCur);
+        if( bRetryConstraintCheck ){
+          sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
+                               regIdx, pIdx->nKeyCol); VdbeCoverage(v);
+          sqlite3UniqueConstraint(pParse, OE_Abort, pIdx);
+        }
         seenReplace = 1;
         break;
       }