aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2013-11-05 19:41:32 +0000
committerdrh <drh@noemail.net>2013-11-05 19:41:32 +0000
commit8d1b82e40bf69f65ab8371ff2dd718ffeff4f43f (patch)
tree17c970758b8930f9819d470ab19057152610532c
parent5a9a37b7b418cedf1a3e4b1ea42afe0c9b1722c2 (diff)
downloadsqlite-8d1b82e40bf69f65ab8371ff2dd718ffeff4f43f.tar.gz
sqlite-8d1b82e40bf69f65ab8371ff2dd718ffeff4f43f.zip
Fix conflict handling for the case when the rowid uses REPLACE but other
unique constraints use FAIL or IGNORE. FossilOrigin-Name: 573cc27427af297185f11aac8dce88ca31f471ca
-rw-r--r--manifest13
-rw-r--r--manifest.uuid2
-rw-r--r--src/insert.c33
-rw-r--r--test/conflict3.test356
4 files changed, 390 insertions, 14 deletions
diff --git a/manifest b/manifest
index 79bd212ec..805388e7c 100644
--- 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
diff --git a/manifest.uuid b/manifest.uuid
index 71b14f736..8e8a2061d 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-52a3d885192c5d31f956c5ee17e29e2d1f3d5c9f \ No newline at end of file
+573cc27427af297185f11aac8dce88ca31f471ca \ No newline at end of file
diff --git a/src/insert.c b/src/insert.c
index d65c49ed5..ff3da9482 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -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
index 000000000..b51a55ee7
--- /dev/null
+++ b/test/conflict3.test
@@ -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