SQLite4
Check-in [ff493aaa41]
Not logged in

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

Overview
Comment:Fix some problems with REPLACE and other conflict handling.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | primary-keys
Files: files | file ages | folders
SHA1: ff493aaa41b6fc181a59faffd63c22cb7b9516f0
User & Date: dan 2012-04-11 18:48:51
Context
2012-04-12
11:49
Fix a problem in kvmemRemoveNode. check-in: f3c424ddf3 user: dan tags: primary-keys
2012-04-11
18:48
Fix some problems with REPLACE and other conflict handling. check-in: ff493aaa41 user: dan tags: primary-keys
15:01
Fix various bugs. check-in: a70fb0629b user: dan tags: primary-keys
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/build.c.

  1957   1957   ** Also write code to modify the sqlite_master table and internal schema
  1958   1958   ** if a root-page of another table is moved by the btree-layer whilst
  1959   1959   ** erasing iTable (this can happen with an auto-vacuum database).
  1960   1960   */ 
  1961   1961   static void destroyRootPage(Parse *pParse, int iTable, int iDb){
  1962   1962     Vdbe *v = sqlite4GetVdbe(pParse);
  1963   1963     sqlite4VdbeAddOp2(v, OP_Clear, iTable, iDb);
         1964  +#if 0
  1964   1965     sqlite4MayAbort(pParse);
         1966  +#endif
  1965   1967   }
  1966   1968   
  1967   1969   /*
  1968   1970   ** Write VDBE code to erase table pTab and all associated indices on disk.
  1969   1971   ** Code to update the sqlite_master tables and internal schema definitions
  1970   1972   ** in case a root-page belonging to another table is moved by the btree layer
  1971   1973   ** is also added (this can happen with an auto-vacuum database).

Changes to src/insert.c.

  1211   1211     int iPk = 0;
  1212   1212     for(p=pTab->pIndex; p && p->eIndexType!=SQLITE_INDEX_PRIMARYKEY; p=p->pNext){
  1213   1213       iPk++;
  1214   1214     }
  1215   1215     if( piPk ) *piPk = iPk;
  1216   1216     return p;
  1217   1217   }
         1218  +
         1219  +/*
         1220  +** Index pIdx is a UNIQUE index. This function returns a pointer to a buffer
         1221  +** containing an error message to tell the user that the UNIQUE constraint
         1222  +** has failed.
         1223  +**
         1224  +** The returned buffer should be freed by the caller using sqlite4DbFree().
         1225  +*/
         1226  +static char *notUniqueMessage(
         1227  +  Parse *pParse,                  /* Parse context */
         1228  +  Index *pIdx                     /* Index to generate error message for */
         1229  +){
         1230  +  const int nCol = pIdx->nColumn; /* Number of columns indexed by pIdx */
         1231  +  StrAccum errMsg;                /* Buffer to build error message within */
         1232  +  int iCol;                       /* Used to iterate through indexed columns */
         1233  +
         1234  +  sqlite4StrAccumInit(&errMsg, 0, 0, 200);
         1235  +  errMsg.db = pParse->db;
         1236  +  sqlite4StrAccumAppend(&errMsg, (nCol>1 ? "columns " : "column "), -1);
         1237  +  for(iCol=0; iCol<pIdx->nColumn; iCol++){
         1238  +    const char *zCol = indexColumnName(pIdx, iCol);
         1239  +    sqlite4StrAccumAppend(&errMsg, (iCol==0 ? "" : ", "), -1);
         1240  +    sqlite4StrAccumAppend(&errMsg, zCol, -1);
         1241  +  }
         1242  +  sqlite4StrAccumAppend(&errMsg, (nCol>1 ? " are" : " is"), -1);
         1243  +  sqlite4StrAccumAppend(&errMsg, " not unique", -1);
         1244  +  return sqlite4StrAccumFinish(&errMsg);
         1245  +}
  1218   1246   
  1219   1247   /*
  1220   1248   ** This function generates code used as part of both INSERT and UPDATE
  1221   1249   ** statements. The generated code performs two tasks:
  1222   1250   **
  1223   1251   **   1. Checks all NOT NULL, CHECK and UNIQUE database constraints, 
  1224   1252   **      including the implicit NOT NULL and UNIQUE constraints imposed
................................................................................
  1248   1276   ** aRegIdx:
  1249   1277   **   Array sized so that there is one entry for each index (including the
  1250   1278   **   PK index) attached to the database table. Entries are in the same order
  1251   1279   **   as the linked list of Index structures attached to the table. 
  1252   1280   **
  1253   1281   **   If an array entry is non-zero, it contains the register that the 
  1254   1282   **   corresponding index key should be written to. If an entry is zero, then
  1255         -**   the corresponding index key is not required by the caller and that any 
  1256         -**   UNIQUE enforced by the index does not need to be checked.
         1283  +**   the corresponding index key is not required by the caller. In this case
         1284  +**   any UNIQUE constraint enforced by the index does not need to be checked.
  1257   1285   **
  1258   1286   ** 
  1259   1287   **
  1260   1288   ** Generate code to do constraint checks prior to an INSERT or an UPDATE.
  1261   1289   **
  1262   1290   ** The input is a range of consecutive registers as follows:
  1263   1291   **
................................................................................
  1341   1369     int rowidChng,      /* True if the rowid might collide with existing entry */
  1342   1370     int isUpdate,       /* True for UPDATE, False for INSERT */
  1343   1371   
  1344   1372     int overrideError,  /* Override onError to this if not OE_Default */
  1345   1373     int ignoreDest,     /* Jump to this label on an OE_Ignore resolution */
  1346   1374     int *pbMayReplace   /* OUT: Set to true if constraint may cause a replace */
  1347   1375   ){
         1376  +  u8 aPkRoot[10];                 /* Root page number for pPk as a varint */ 
         1377  +  int nPkRoot;                    /* Size of aPkRoot in bytes */
  1348   1378     Index *pPk;                     /* Primary key index for table pTab */
  1349   1379     int i;              /* loop counter */
  1350   1380     Vdbe *v;            /* VDBE under constrution */
  1351   1381     int nCol;           /* Number of columns */
  1352   1382     int onError;        /* Conflict resolution strategy */
  1353   1383     int iCur;           /* Table cursor number */
  1354   1384     Index *pIdx;         /* Pointer to one of the indices */
................................................................................
  1355   1385     int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
  1356   1386   
  1357   1387     v = sqlite4GetVdbe(pParse);
  1358   1388     assert( v!=0 );
  1359   1389     assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  1360   1390     nCol = pTab->nCol;
  1361   1391     pPk = sqlite4FindPrimaryKey(pTab, 0);
         1392  +  nPkRoot = sqlite4PutVarint64(aPkRoot, pPk->tnum);
  1362   1393   
  1363   1394     assert( pPk->eIndexType==SQLITE_INDEX_PRIMARYKEY );
  1364   1395   
  1365   1396     /* Test all NOT NULL constraints. */
  1366   1397     generateNotNullChecks(pParse, pTab, regContent, overrideError, ignoreDest);
  1367   1398   
  1368   1399     /* Test all CHECK constraints */
................................................................................
  1371   1402     /* Test all UNIQUE constraints by creating entries for each UNIQUE
  1372   1403     ** index and making sure that duplicate entries do not already exist.
  1373   1404     ** Add the new records to the indices as we go.
  1374   1405     */
  1375   1406     for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
  1376   1407       int nTmpReg;                  /* Number of temp registers required */
  1377   1408       int regTmp;                   /* First temp register allocated */
  1378         -    int regShort;                 /* Reg. for number of bytes in short key */
         1409  +    int regPk;                    /* PK of conflicting row (for REPLACE) */
  1379   1410   
  1380   1411       if( aRegIdx[iCur]==0 ) continue;  /* Skip unused indices */
  1381   1412   
  1382   1413       /* Create an index key. Primary key indexes consists of just the primary
  1383   1414       ** key values. Other indexes consists of the indexed columns followed by
  1384   1415       ** the primary key values.  */
  1385   1416       nTmpReg = 1 + pIdx->nColumn + (pIdx==pPk ? 0 : pPk->nColumn);
  1386   1417       regTmp = sqlite4GetTempRange(pParse, nTmpReg);
  1387         -    regShort = regTmp + nTmpReg - 1;
         1418  +    regPk = regTmp + nTmpReg - 1;
         1419  +
  1388   1420       for(i=0; i<pIdx->nColumn; i++){
  1389   1421         int idx = pIdx->aiColumn[i];
  1390   1422         sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i);
  1391   1423       }
  1392   1424       if( pIdx!=pPk ){
  1393   1425         for(i=0; i<pPk->nColumn; i++){
  1394   1426           int idx = pPk->aiColumn[i];
  1395   1427           sqlite4VdbeAddOp2(v, OP_SCopy, regContent+idx, regTmp+i+pIdx->nColumn);
  1396   1428         }
  1397   1429       }
  1398   1430       sqlite4VdbeAddOp3(v, OP_MakeIdxKey, baseCur+iCur, regTmp, aRegIdx[iCur]);
  1399         -    sqlite4VdbeChangeP4(v, -1, (const char *)regShort, P4_INT32);
  1400   1431       VdbeComment((v, "key for %s", pIdx->zName));
  1401   1432   
  1402   1433       /* If Index.onError==OE_None, then pIdx is not a UNIQUE or PRIMARY KEY 
  1403   1434       ** index. In this case there is no need to test the index for uniqueness
  1404   1435       ** - all that is required is to populate the aRegIdx[iCur] register. Jump 
  1405   1436       ** to the next iteration of the loop if this is the case.  */
  1406   1437       onError = pIdx->onError;
  1407   1438       if( onError!=OE_None ){
  1408   1439         int iTest;                  /* Address of OP_IsUnique instruction */
  1409         -
  1410         -      iTest = sqlite4VdbeAddOp3(v, OP_IsUnique, baseCur+iCur, 0, aRegIdx[iCur]);
  1411         -      sqlite4VdbeChangeP4(v, -1, (const char *)regShort, P4_INT32);
         1440  +      int regOut = 0;
  1412   1441   
  1413   1442         /* Figure out what to do if a UNIQUE constraint is encountered. 
  1414   1443         **
  1415   1444         ** TODO: If a previous constraint is a REPLACE, why change IGNORE to
  1416   1445         ** REPLACE and FAIL to ABORT here?  */
  1417   1446         if( overrideError!=OE_Default ){
  1418   1447           onError = overrideError;
................................................................................
  1419   1448         }else if( onError==OE_Default ){
  1420   1449           onError = OE_Abort;
  1421   1450         }
  1422   1451         if( seenReplace ){
  1423   1452           if( onError==OE_Ignore ) onError = OE_Replace;
  1424   1453           else if( onError==OE_Fail ) onError = OE_Abort;
  1425   1454         }
         1455  +
         1456  +      if( onError==OE_Replace ){
         1457  +        sqlite4VdbeAddOp3(v, OP_Blob, nPkRoot, regPk, 0);
         1458  +        sqlite4VdbeChangeP4(v, -1, aPkRoot, nPkRoot);
         1459  +        regOut = regPk;
         1460  +      }
         1461  +      iTest = sqlite4VdbeAddOp3(v, OP_IsUnique, baseCur+iCur, 0, aRegIdx[iCur]);
         1462  +      sqlite4VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(regOut), P4_INT32);
  1426   1463         
  1427   1464         switch( onError ){
  1428   1465           case OE_Rollback:
  1429   1466           case OE_Abort:
  1430   1467           case OE_Fail: {
  1431         -          int j;
  1432         -          StrAccum errMsg;
  1433         -          const char *zSep;
  1434         -          char *zErr;
  1435         -
  1436         -          sqlite4StrAccumInit(&errMsg, 0, 0, 200);
  1437         -          errMsg.db = pParse->db;
  1438         -          zSep = pIdx->nColumn>1 ? "columns " : "column ";
  1439         -          for(j=0; j<pIdx->nColumn; j++){
  1440         -            const char *zCol = indexColumnName(pIdx, j);
  1441         -            sqlite4StrAccumAppend(&errMsg, zSep, -1);
  1442         -            zSep = ", ";
  1443         -            sqlite4StrAccumAppend(&errMsg, zCol, -1);
  1444         -          }
  1445         -          sqlite4StrAccumAppend(&errMsg,
  1446         -              pIdx->nColumn>1 ? " are not unique" : " is not unique", -1);
  1447         -          zErr = sqlite4StrAccumFinish(&errMsg);
         1468  +          char *zErr = notUniqueMessage(pParse, pIdx);
  1448   1469             sqlite4HaltConstraint(pParse, onError, zErr, 0);
  1449         -          sqlite4DbFree(errMsg.db, zErr);
         1470  +          sqlite4DbFree(pParse->db, zErr);
  1450   1471             break;
  1451   1472           }
         1473  +
  1452   1474           case OE_Ignore: {
  1453   1475             assert( seenReplace==0 );
  1454   1476             sqlite4VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
  1455   1477             break;
  1456   1478           }
  1457   1479           default: {
  1458         -          assert( 0 );
  1459         -#if 0
  1460         -          Trigger *pTrigger = 0;
         1480  +          Trigger *pTrigger;
  1461   1481             assert( onError==OE_Replace );
  1462   1482             sqlite4MultiWrite(pParse);
  1463         -          if( pParse->db->flags&SQLITE_RecTriggers ){
  1464         -            pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
  1465         -          }
         1483  +          pTrigger = sqlite4TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
  1466   1484             sqlite4GenerateRowDelete(
  1467         -              pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace
         1485  +              pParse, pTab, baseCur, regOut, 0, pTrigger, OE_Replace
  1468   1486             );
  1469   1487             seenReplace = 1;
  1470         -#endif
  1471   1488             break;
  1472   1489           }
  1473   1490         }
  1474   1491   
  1475   1492         /* If the OP_IsUnique passes (no constraint violation) jump here */
  1476   1493         sqlite4VdbeJumpHere(v, iTest);
  1477   1494       }

Changes to src/vdbe.c.

  2982   2982     break;
  2983   2983   }
  2984   2984   
  2985   2985   /* Opcode: IsUnique P1 P2 P3 P4 *
  2986   2986   **
  2987   2987   ** Cursor P1 is open on an index that enforces a UNIQUE constraint. 
  2988   2988   ** Register P3 contains an encoded key suitable to be inserted into the 
  2989         -** index.
  2990         -**
  2991         -** Jump to instruction P2 if the encoded key can be inserted into the 
  2992         -** index without violating a unique constraint. Otherwise, fall through
         2989  +** index. If the key can be inserted into the index without violating
         2990  +** a UNIQUE constraint, jump to instruction P2. Otherwise, fall through
  2993   2991   ** to the next instruction.
         2992  +**
         2993  +** If P4 is a non-zero integer and the jump is not taken, then it is
         2994  +** a register that currently contains a blob. At the start of the blob
         2995  +** is a varint that contains the index number for the PRIMARY KEY index
         2996  +** of the table. The contents of P4 are overwritten with an index key
         2997  +** composed of the varint from the start of the initial blob content
         2998  +** and the PRIMARY KEY values from the index entry causing the UNIQUE
         2999  +** constraint to fail.
  2994   3000   */
  2995   3001   case OP_IsUnique: {        /* jump, in3 */
  2996   3002     VdbeCursor *pC;
  2997   3003     Mem *pShort;
  2998   3004     Mem *pProbe;
         3005  +  Mem *pOut;
         3006  +  int iOut;
  2999   3007     int nShort;
  3000   3008     int dir;
         3009  +  u64 dummy;
  3001   3010   
  3002   3011     KVByteArray *aKey;              /* Key read from cursor */
  3003   3012     KVSize nKey;                    /* Size of aKey in bytes */
  3004   3013   
  3005   3014     assert( pOp->p4type==P4_INT32 );
  3006   3015   
  3007   3016     pProbe = &aMem[pOp->p3];
  3008         -  pShort = &aMem[pOp->p4.i];
  3009         -  nShort = pShort->u.i;
  3010   3017     pC = p->apCsr[pOp->p1];
         3018  +  pOut = (pOp->p4.i==0 ? 0 : &aMem[pOp->p4.i]);
         3019  +  assert( pOut==0 || (pOut->flags & MEM_Blob) );
         3020  +
         3021  +  nShort = sqlite4VdbeShortKey(pProbe->z, pProbe->n, 
         3022  +      pC->pKeyInfo->nField - pC->pKeyInfo->nPK
         3023  +  );
  3011   3024     assert( nShort<=pProbe->n );
  3012   3025     assert( (nShort==pProbe->n)==(pC->pKeyInfo->nPK==0) );
  3013   3026   
  3014   3027     dir = (nShort < pProbe->n);
  3015   3028     rc = sqlite4KVCursorSeek(pC->pKVCur, pProbe->z, nShort, dir);
  3016   3029   
  3017   3030     if( rc==SQLITE_NOTFOUND ){
................................................................................
  3022   3035       rc = sqlite4KVCursorKey(pC->pKVCur, &aKey, &nKey);
  3023   3036       if( rc==SQLITE_OK ){
  3024   3037         if( nKey<nShort 
  3025   3038          || memcmp(pProbe->z, aKey, nShort)
  3026   3039          || (nKey==pProbe->n && 0==memcmp(pProbe->z, aKey, nKey))
  3027   3040         ){
  3028   3041           pc = pOp->p2-1;
         3042  +      }else if( pOut ){
         3043  +        iOut = sqlite4GetVarint64(pOut->z, pOut->n, &dummy);
         3044  +        rc = sqlite4VdbeMemGrow(pOut, iOut+(pProbe->n - nShort), 1);
         3045  +        if( rc==SQLITE_OK ){
         3046  +          memcpy(&pOut->z[iOut], &aKey[nShort], (pProbe->n - nShort));
         3047  +          pOut->n = iOut + (pProbe->n - nShort);
         3048  +        }
  3029   3049         }
  3030   3050       }
  3031   3051     }
  3032   3052   
  3033   3053   #if 0
  3034   3054     u16 ii;
  3035   3055     VdbeCursor *pCx;

Changes to src/vdbeInt.h.

   386    386     KeyInfo *pKeyInfo,           /* Collating sequence information */
   387    387     u8 **pzOut,                  /* Write the resulting key here */
   388    388     int *pnOut,                  /* Number of bytes in the key */
   389    389     int *pnShort                 /* Number of bytes omitting primary key */
   390    390   );
   391    391   int sqlite4VdbeEncodeIntKey(u8 *aBuf,sqlite4_int64 v);
   392    392   int sqlite4VdbeDecodeIntKey(const KVByteArray*, KVSize, sqlite4_int64*);
          393  +int sqlite4VdbeShortKey(u8 *, int, int);
   393    394   int sqlite4MemCompare(const Mem*, const Mem*, const CollSeq*);
   394    395   int sqlite4VdbeExec(Vdbe*);
   395    396   int sqlite4VdbeList(Vdbe*);
   396    397   int sqlite4VdbeHalt(Vdbe*);
   397    398   int sqlite4VdbeChangeEncoding(Mem *, int);
   398    399   int sqlite4VdbeMemTooBig(Mem*);
   399    400   int sqlite4VdbeMemCopy(Mem*, const Mem*);

Changes to src/vdbecodec.c.

   526    526         p->aOut[p->nOut++] = 0x22;  /* Large positive values */
   527    527         e = encodeLargeFloatKey(r, p);
   528    528         if( e<=10 ) p->aOut[i] = 0x17+e;
   529    529       }
   530    530     }else
   531    531     if( flags & MEM_Str ){
   532    532       if( enlargeEncoderAllocation(p, pMem->n*4 + 2) ) return SQLITE_NOMEM;
   533         -    p->aOut[p->nOut++] = 0x0e;   /* Text */
          533  +    p->aOut[p->nOut++] = 0x24;   /* Text */
   534    534       if( pColl==0 || pColl->xMkKey==0 ){
   535    535         memcpy(p->aOut+p->nOut, pMem->z, pMem->n);
   536    536         p->nOut += pMem->n;
   537    537       }else{
   538    538         n = pColl->xMkKey(pColl->pUser, pMem->z, pMem->n, p->aOut+p->nOut,
   539    539                           p->nAlloc - p->nOut);
   540    540         if( n > p->nAlloc - p->nOut ){
................................................................................
   550    550       unsigned char s, t;
   551    551       assert( flags & MEM_Blob );
   552    552       n = pMem->n;
   553    553       a = (u8*)pMem->z;
   554    554       s = 1;
   555    555       t = 0;
   556    556       if( enlargeEncoderAllocation(p, (n*8+6)/7 + 2) ) return SQLITE_NOMEM;
   557         -    p->aOut[p->nOut++] = 0x0f;   /* Blob */
          557  +    p->aOut[p->nOut++] = 0x25;   /* Blob */
   558    558        for(i=0; i<n; i++){
   559    559         unsigned char x = a[i];
   560    560         p->aOut[p->nOut++] = 0x80 | t | (x>>s);
   561    561         if( s<7 ){
   562    562           t = x<<(7-s);
   563    563           s++;
   564    564         }else{
................................................................................
   571    571       p->aOut[p->nOut++] = 0x00;
   572    572     }
   573    573     if( sortOrder==SQLITE_SO_DESC ){
   574    574       for(i=iStart; i<p->nOut; i++) p->aOut[i] ^= 0xff;
   575    575     }
   576    576     return SQLITE_OK;
   577    577   }
          578  +
          579  +/*
          580  +** Variables aKey/nKey contain an encoded index key. This function returns
          581  +** the length (in bytes) of the key with all but the first nField fields
          582  +** removed.
          583  +*/
          584  +int sqlite4VdbeShortKey(
          585  +  u8 *aKey,                       /* Buffer containing encoded key */
          586  +  int nKey,                       /* Size of buffer aKey[] in bytes */
          587  +  int nField                      /* Number of fields */
          588  +){
          589  +  u8 *p = aKey;
          590  +  u8 *pEnd = &aKey[nKey];
          591  +  u64 dummy;
          592  +  int i;
          593  +
          594  +  p = aKey;
          595  +  p += sqlite4GetVarint64(p, pEnd-p, &dummy);
          596  +
          597  +  for(i=0; i<nField; i++){
          598  +    switch( *(p++) ){
          599  +      case 0x05:                  /* NULL */
          600  +      case 0x06:                  /* NaN */
          601  +      case 0x07:                  /* -ve infinity */
          602  +      case 0x15:                  /* zero */
          603  +      case 0x23:                  /* +ve infinity */
          604  +        break;
          605  +
          606  +      case 0x24:                  /* Text */
          607  +      case 0x25:                  /* Blob */
          608  +        while( *(p++) );
          609  +        break;
          610  +
          611  +      case 0x22:                  /* Large positive number */
          612  +      case 0x16:                  /* Small positive number */
          613  +      case 0x14:                  /* Small negative number */
          614  +      case 0x08:                  /* Large negative number */
          615  +        p += sqlite4GetVarint64(p, pEnd-p, &dummy);
          616  +        p += sqlite4GetVarint64(p, pEnd-p, &dummy);
          617  +        break;
          618  +
          619  +      default:                    /* Medium sized number */
          620  +        p += sqlite4GetVarint64(p, pEnd-p, &dummy);
          621  +        break;
          622  +    }
          623  +  }
          624  +
          625  +  return (p - aKey);
          626  +}
   578    627   
   579    628   /*
   580    629   ** Generate a record key from one or more data values
   581    630   **
   582    631   ** Space to hold the key is obtained from sqlite4DbMalloc() and should
   583    632   ** be freed by the caller using sqlite4DbFree() to avoid a memory leak.
   584    633   */

Changes to test/conflict.test.

    49     49     4 REPLACE                 0 4   1  0
    50     50     5 {INSERT OR FAIL}        1 {}  1  0
    51     51     6 {INSERT OR ABORT}       1 {}  1  0
    52     52     7 {INSERT OR ROLLBACK}    1 {}  {} 0
    53     53   } {
    54     54     do_test conflict-1.$i {
    55     55       set ::sqlite_opentemp_count 0
    56         -    set r0 [catch {execsql [subst {
           56  +    execsql {
    57     57         DELETE FROM t1;
    58     58         DELETE FROM t2;
    59     59         INSERT INTO t1 VALUES(1,2,3);
    60     60         BEGIN;
    61     61         INSERT INTO t2 VALUES(1); 
           62  +    }
           63  +    set r0 [catch {execsql [subst {
    62     64         $cmd INTO t1 VALUES(1,2,4);
    63     65       }]} r1]
    64     66       catch {execsql {COMMIT}}
    65     67       if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
    66     68       set r2 [execsql {SELECT x FROM t2}]
    67     69       set r3 $::sqlite_opentemp_count
    68     70       list $r0 $r1 $r2 $r3
................................................................................
    95     97     3 {INSERT OR REPLACE}     0 4   1
    96     98     4 REPLACE                 0 4   1
    97     99     5 {INSERT OR FAIL}        1 {}  1
    98    100     6 {INSERT OR ABORT}       1 {}  1
    99    101     7 {INSERT OR ROLLBACK}    1 {}  {}
   100    102   } {
   101    103     do_test conflict-2.$i {
   102         -    set r0 [catch {execsql [subst {
          104  +    execsql {
   103    105         DELETE FROM t1;
   104    106         DELETE FROM t2;
   105    107         INSERT INTO t1 VALUES(1,2,3);
   106    108         BEGIN;
   107    109         INSERT INTO t2 VALUES(1); 
          110  +    }
          111  +    set r0 [catch {execsql [subst {
   108    112         $cmd INTO t1 VALUES(1,2,4);
   109    113       }]} r1]
   110    114       catch {execsql {COMMIT}}
   111    115       if {$r0} {set r1 {}} {set r1 [execsql {SELECT c FROM t1}]}
   112    116       set r2 [execsql {SELECT x FROM t2}]
   113    117       list $r0 $r1 $r2
   114    118     } [list $t0 $t1 $t2]