]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix conflict handling for the case when the rowid uses REPLACE but other
authordrh <drh@noemail.net>
Tue, 5 Nov 2013 19:41:32 +0000 (19:41 +0000)
committerdrh <drh@noemail.net>
Tue, 5 Nov 2013 19:41:32 +0000 (19:41 +0000)
unique constraints use FAIL or IGNORE.

FossilOrigin-Name: 573cc27427af297185f11aac8dce88ca31f471ca

manifest
manifest.uuid
src/insert.c
test/conflict3.test [new file with mode: 0644]

index 79bd212ec2cb6883c838f5c6d3e970e38ecacb93..805388e7c254d7a042f062f6cd9afb1e93135909 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Fix\sa\sbug\sin\ssecondary\sindex\sinitialization\swhen\sthe\ssecondary\sindex\sis\na\ssuperset\sof\sthe\sPRIMARY\sKEY\sfor\sa\sWITHOUT\sROWID\stable.
-D 2013-11-05T17:30:04.337
+C Fix\sconflict\shandling\sfor\sthe\scase\swhen\sthe\srowid\suses\sREPLACE\sbut\sother\nunique\sconstraints\suse\sFAIL\sor\sIGNORE.
+D 2013-11-05T19:41:32.450
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 0522b53cdc1fcfc18f3a98e0246add129136c654
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -182,7 +182,7 @@ F src/global.c 5caf4deab621abb45b4c607aad1bd21c20aac759
 F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
 F src/hash.h 8890a25af81fb85a9ad7790d32eedab4b994da22
 F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
-F src/insert.c 7559252147d3aad55951ecd5fadf8350729cb901
+F src/insert.c 66c8c99920cac74abba70cb7eecc5114e02ed3ea
 F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
 F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
 F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
@@ -386,6 +386,7 @@ F test/colmeta.test 087c42997754b8c648819832241daf724f813322
 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b
 F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8
 F test/conflict2.test 5d162ecbbde37f165de1992904dcbbed9fc7c012
+F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b
 F test/contrib01.test 2a1cbc0f2f48955d7d073f725765da6fbceda6b4
 F test/corrupt.test 4aabd06cff3fe759e3e658bcc17b71789710665e
 F test/corrupt2.test 9c0ab4becd50e9050bc1ebb8675456a4e5587bf0
@@ -1133,7 +1134,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P 4b41d989e894b9214a9b973228ef8446356f9fbb
-R a05ce9adf9842de08a1d6f032363e2df
+P 52a3d885192c5d31f956c5ee17e29e2d1f3d5c9f
+R 549795ebe9589f04f40d14f040b65091
 U drh
-Z 7d34ccd67e34b00bfe85d5ff3fefd3e0
+Z c7bda7b48cde94ee0d99c42dc49f8047
index 71b14f736fd20e7b7fcf9bf1f77f013b3ce855e1..8e8a2061d5dffcb8e8658d95a146557634fd6e9d 100644 (file)
@@ -1 +1 @@
-52a3d885192c5d31f956c5ee17e29e2d1f3d5c9f
\ No newline at end of file
+573cc27427af297185f11aac8dce88ca31f471ca
\ No newline at end of file
index d65c49ed58e0d2525c9cfa7b4c87d26977a3d701..ff3da94821b2933e2f5e7b7e9558be276eab8840 100644 (file)
@@ -1229,7 +1229,9 @@ void sqlite3GenerateConstraintChecks(
   int j1;              /* Addresss of jump instruction */
   int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
   int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
-  u8 isUpdate;
+  int ipkTop = 0;      /* Top of the rowid change constraint check */
+  int ipkBottom = 0;   /* Bottom of the rowid change constraint check */
+  u8 isUpdate;         /* True if this is an UPDATE operation */
 
   isUpdate = regOldData!=0;
   db = pParse->db;
@@ -1345,6 +1347,20 @@ void sqlite3GenerateConstraintChecks(
       sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
     }
 
+    /* If the response to a rowid conflict is REPLACE but the response
+    ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
+    ** to defer the running of the rowid conflict checking until after
+    ** the UNIQUE constraints have run.
+    */
+    if( onError==OE_Replace && overrideError!=OE_Replace ){
+      for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+        if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){
+          ipkTop = sqlite3VdbeAddOp0(v, OP_Goto);
+          break;
+        }
+      }
+    }
+
     /* Check to see if the new rowid already exists in the table.  Skip
     ** the following conflict logic if it does not. */
     sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
@@ -1400,12 +1416,16 @@ void sqlite3GenerateConstraintChecks(
         break;
       }
       case OE_Ignore: {
-        assert( seenReplace==0 );
+        /*assert( seenReplace==0 );*/
         sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
         break;
       }
     }
     sqlite3VdbeResolveLabel(v, addrRowidOk);
+    if( ipkTop ){
+      ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
+      sqlite3VdbeJumpHere(v, ipkTop);
+    }
   }
 
   /* Test all UNIQUE constraints by creating entries for each UNIQUE
@@ -1475,10 +1495,6 @@ void sqlite3GenerateConstraintChecks(
     }else if( onError==OE_Default ){
       onError = OE_Abort;
     }
-    if( seenReplace ){
-      if( onError==OE_Ignore ) onError = OE_Replace;
-      else if( onError==OE_Fail ) onError = OE_Abort;
-    }
     
     /* Check to see if the new index entry will be unique */
     regR = sqlite3GetTempRange(pParse, nPkField);
@@ -1544,7 +1560,6 @@ void sqlite3GenerateConstraintChecks(
         break;
       }
       case OE_Ignore: {
-        assert( seenReplace==0 );
         sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
         break;
       }
@@ -1564,6 +1579,10 @@ void sqlite3GenerateConstraintChecks(
     sqlite3VdbeResolveLabel(v, addrUniqueOk);
     sqlite3ReleaseTempRange(pParse, regR, nPkField);
   }
+  if( ipkTop ){
+    sqlite3VdbeAddOp2(v, OP_Goto, 0, ipkTop+1);
+    sqlite3VdbeJumpHere(v, ipkBottom);
+  }
   
   if( pbMayReplace ){
     *pbMayReplace = seenReplace;
diff --git a/test/conflict3.test b/test/conflict3.test
new file mode 100644 (file)
index 0000000..b51a55e
--- /dev/null
@@ -0,0 +1,356 @@
+# 2013-11-05
+#
+# The author disclaims copyright to this source code.  In place of
+# 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+# This file implements tests for the conflict resolution extension
+# to SQLite.
+#
+# This file focuses on making sure that combinations of REPLACE,
+# IGNORE, and FAIL conflict resolution play well together.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+
+ifcapable !conflict {
+  finish_test
+  return
+}
+
+do_execsql_test conflict-1.1 {
+  CREATE TABLE t1(
+    a INTEGER PRIMARY KEY ON CONFLICT REPLACE, 
+    b UNIQUE ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-1.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-1.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-1.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Replete the tests above, but this time on a table non-INTEGER primary key.
+#
+do_execsql_test conflict-2.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a INT PRIMARY KEY ON CONFLICT REPLACE, 
+    b UNIQUE ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-2.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-2.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-2.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Replete again on a WITHOUT ROWID table.
+#
+do_execsql_test conflict-3.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a INT PRIMARY KEY ON CONFLICT REPLACE, 
+    b UNIQUE ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL
+  ) WITHOUT ROWID;
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-3.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-3.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-3.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Arrange the table rows in a different order and repeat.
+#
+do_execsql_test conflict-4.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    b UNIQUE ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL,
+    a INT PRIMARY KEY ON CONFLICT REPLACE
+  ) WITHOUT ROWID;
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-4.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-4.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-4.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Arrange the table rows in a different order and repeat.
+#
+do_execsql_test conflict-5.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    b UNIQUE ON CONFLICT IGNORE,
+    a INT PRIMARY KEY ON CONFLICT REPLACE,
+    c UNIQUE ON CONFLICT FAIL
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-5.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-5.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-5.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Arrange the table rows in a different order and repeat.
+#
+do_execsql_test conflict-6.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    c UNIQUE ON CONFLICT FAIL,
+    a INT PRIMARY KEY ON CONFLICT REPLACE,
+    b UNIQUE ON CONFLICT IGNORE
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-6.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-6.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-6.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-7.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a UNIQUE ON CONFLICT REPLACE, 
+    b INTEGER PRIMARY KEY ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-7.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-7.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-7.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-8.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a UNIQUE ON CONFLICT REPLACE, 
+    b INT PRIMARY KEY ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-8.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-8.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-8.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-9.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a UNIQUE ON CONFLICT REPLACE, 
+    b INT PRIMARY KEY ON CONFLICT IGNORE,
+    c UNIQUE ON CONFLICT FAIL
+  ) WITHOUT ROWID;
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-9.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-9.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-9.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-10.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a UNIQUE ON CONFLICT REPLACE, 
+    b UNIQUE ON CONFLICT IGNORE,
+    c INTEGER PRIMARY KEY ON CONFLICT FAIL
+  );
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-10.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-10.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-10.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+# Change which column is the PRIMARY KEY
+#
+do_execsql_test conflict-11.1 {
+  DROP TABLE t1;
+  CREATE TABLE t1(
+    a UNIQUE ON CONFLICT REPLACE, 
+    b UNIQUE ON CONFLICT IGNORE,
+    c PRIMARY KEY ON CONFLICT FAIL
+  ) WITHOUT ROWID;
+  INSERT INTO t1(a,b,c) VALUES(1,2,3), (2,3,4);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert a row that conflicts on column B.  The insert should be ignored.
+#
+do_execsql_test conflict-11.2 {
+  INSERT INTO t1(a,b,c) VALUES(3,2,5);
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4}
+
+# Insert two rows where the second conflicts on C.  The first row show go
+# and and then there should be a constraint error.
+#
+do_test conflict-11.3 {
+  catchsql {INSERT INTO t1(a,b,c) VALUES(4,5,6), (5,6,4);}
+} {1 {UNIQUE constraint failed: t1.c}}
+do_execsql_test conflict-11.4 {
+  SELECT a,b,c FROM t1 ORDER BY a;
+} {1 2 3 2 3 4 4 5 6}
+
+
+finish_test