}
#-------------------------------------------------------------------------
-reset_db
db2 close
+reset_db
forcedelete test.db2
sqlite3 db2 test.db2
return "OMIT"
}
-proc do_conflict_test {tn script clist} {
+proc do_conflict_test {tn bNoUpdateLoop script clist} {
uplevel [list do_test $tn.1 [subst -nocommands {
sqlite3session S db "main"
S delete
}] {}]
- uplevel [list do_test $tn.2 {
- set ::conflict_list [list]
- sqlite3changeset_apply_v2 db2 $::changeset xConflict
- set ::conflict_list
- } [list {*}$clist]]
+ if {$bNoUpdateLoop} {
+ uplevel [list do_test $tn.2.no {
+ set ::conflict_list [list]
+ sqlite3changeset_apply_v2 -noupdateloop db2 $::changeset xConflict
+ set ::conflict_list
+ } [list {*}$clist]]
+ } else {
+ uplevel [list do_test $tn.2 {
+ set ::conflict_list [list]
+ sqlite3changeset_apply_v2 db2 $::changeset xConflict
+ set ::conflict_list
+ } [list {*}$clist]]
+ }
}
do_test 4.0 {
"
}
-do_conflict_test 4.1.1 {
+do_conflict_test 4.1.1 0 {
swap t1 a c 4 5
swap t1 a c 2 3
swap t1 a c 8 1
1 8 2 3 3 2 4 5 5 4 6 6 7 7 8 1 9 9 10 10
}
-do_conflict_test 4.2.1 {
+do_conflict_test 4.2.1 0 {
swap t1 a d 10 9
swap t1 a d 8 7
swap t1 a d 7 6
INSERT INTO t1(a, b, c, d) VALUES(11, 11, 11, 11);
}
-do_conflict_test 4.3.1 {
+do_conflict_test 4.3.1 0 {
swap t1 a c 3 6
db eval { UPDATE t1 SET d=11 WHERE a=10; }
swap t1 a d 4 8
{UPDATE t1 CONSTRAINT {i 10 {} {} {} {} i 9} {{} {} {} {} {} {} i 11}}
}
-do_conflict_test 4.3.2 {
+do_conflict_test 4.3.2 0 {
swap t1 a c 1 2
swap t1 a c 3 4
db eval { UPDATE t1 SET c=11 WHERE a=10; }
{UPDATE t1 CONSTRAINT {i 10 {} {} i 10 {} {}} {{} {} {} {} i 11 {} {}}}
}
-do_conflict_test 4.3.3 {
+do_conflict_test 4.3.3 0 {
swap t1 a c 1 2
swap t1 a c 2 3
swap t1 a c 3 4
swap t1 a c 4 1
} {
}
-
db2 close
+
+#-------------------------------------------------------------------------
+reset_db
+forcedelete test.db2
+sqlite3 db2 test.db2
+
+do_test 5.0 {
+ do_common_sql {
+ CREATE TABLE t1(a INT PRIMARY KEY, b UNIQUE);
+ INSERT INTO t1 VALUES('one', 'one');
+ INSERT INTO t1 VALUES('two', 'two');
+ INSERT INTO t1 VALUES('three', 'three');
+ INSERT INTO t1 VALUES('four', 'four');
+ INSERT INTO t1 VALUES('five', 'five');
+ }
+} {}
+
+do_conflict_test 5.1 1 {
+} {
+}
+
+do_conflict_test 5.2 1 {
+ swap t1 a b one two
+} {
+ {UPDATE t1 CONSTRAINT {t two t two} {{} {} t one}}
+ {UPDATE t1 CONSTRAINT {t one t one} {{} {} t two}}
+}
+
+do_conflict_test 5.2 0 {
+ swap t1 a b four five
+} {
+}
+
+
finish_test
u8 bRebaseStarted; /* If table header is already in rebase */
u8 bRebase; /* True to collect rebase information */
u8 bIgnoreNoop; /* True to ignore no-op conflicts */
+ u8 bNoUpdateLoop; /* No update-loop processing */
int bRowid;
char *zErr; /* Error message, if any */
};
}
/* Step (2) */
- while( rc==SQLITE_OK && pApply->constraints.nBuf ){
+ while( rc==SQLITE_OK && pApply->constraints.nBuf && !pApply->bNoUpdateLoop ){
SessionBuffer cons = {0, 0, 0};
sqlite3_changeset_iter *pUp = 0;
sqlite3_stmt *pInsert = 0;
sApply.bRebase = (ppRebase && pnRebase);
sApply.bInvertConstraints = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
sApply.bIgnoreNoop = !!(flags & SQLITE_CHANGESETAPPLY_IGNORENOOP);
+ sApply.bNoUpdateLoop = !!(flags & SQLITE_CHANGESETAPPLY_NOUPDATELOOP);
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
}
** database behave as if they were declared with "ON UPDATE NO ACTION ON
** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
** or SET DEFAULT.
+**
+** <dt>SQLITE_CHANGESETAPPLY_NOUPDATELOOP <dd>
+** Sometimes, a changeset contains two or more update statements such that
+** although after applying all updates the database will contain no
+** constraint violations, no single update can be applied before the others.
+** The simplest example of this is a pair of UPDATEs that have "swapped"
+** two column values with a UNIQUE constraint.
+** <p>
+** Usually, sqlite3changeset_apply() and similar functions work hard to try
+** to find a way to apply such a changeset. However, if this flag is set,
+** then all such updates are considered CONSTRAINT conflicts.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
+#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP 0x0010
/*
** CAPI3REF: Constants Passed To The Conflict Handler
}
else if( n>2 && n<=11 && 0==sqlite3_strnicmp("-ignorenoop", z1, n) ){
flags |= SQLITE_CHANGESETAPPLY_IGNORENOOP;
+ }
+ else if( n>3 && n<=13 && 0==sqlite3_strnicmp("-noupdateloop", z1, n) ){
+ flags |= SQLITE_CHANGESETAPPLY_NOUPDATELOOP;
}else{
break;
}
-C Merge\slatest\strunk\schanges\swith\sthis\sbranch.
-D 2026-05-21T15:13:17.189
+C Add\sthe\sSQLITE_CHANGESETAPPLY_NOUPDATELOOP\soption\sto\sthe\ssqlite3_changeset_apply_v2/3()\smethod.\sTo\sdisable\sthe\sextra\sprocessing\sto\scommit\schangesets\sthat\sswap\stwo\sor\smore\svalues\ssubject\sto\sa\sUNIQUE\sconstraint\sbetween\srows.
+D 2026-05-22T14:22:14.502
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F ext/session/sessionchange.test 6618cb1c1338a4b6df173b6ac42d09623fb71269962abf23ebb7617fe9f45a50
F ext/session/sessionchange2.test 8f59185216882adc8b34bb5ba63887459acf3df58493bcffa12e4d05ab6a6b85
F ext/session/sessionconflict.test 19e4a53795c4c930bfec49e809311e09b2a9e202d9446e56d7a8b139046a0c07 x
-F ext/session/sessionconflict2.test 795768e963c96078b7939f397824e26aede6e5563a0e42f7b11c12795f313f3a x
+F ext/session/sessionconflict2.test d7f4caf59360dbca8a4698b9a3a322adf6f547f810ad41d131cacb730e02dd5e x
F ext/session/sessiondiff.test e89f7aedcdd89e5ebac3a455224eb553a171e9586fc3e1e6a7b3388d2648ba8d
F ext/session/sessionfault.test c2b43d01213b389a3f518e90775fca2120812ba51e50444c4066962263e45c11
F ext/session/sessionfault2.test b0d6a7c1d7398a7e800d84657404909c7d385965ea8576dc79ed344c46fbf41c
F ext/session/sessionsize.test 8fcf4685993c3dbaa46a24183940ab9f5aa9ed0d23e5fb63bfffbdb56134b795
F ext/session/sessionstat1.test 5e718d5888c0c49bbb33a7a4f816366db85f59f6a4f97544a806421b85dc2dec
F ext/session/sessionwor.test 6fd9a2256442cebde5b2284936ae9e0d54bde692d0f5fd009ecef8511f4cf3fc
-F ext/session/sqlite3session.c 6d3d284fa35e01a68db8d6f1aae0fd83a92c69d4442d6b88aed09b556fc2b707
-F ext/session/sqlite3session.h 063e7bf7be2fff874456f452a224b5b3013b25682d108933b0351c93a1279b9c
-F ext/session/test_session.c 3773e750b5c751956fdbef41a998cc1ba02d59c3dede74e75866e3446a900e69
+F ext/session/sqlite3session.c 9d1cce13a48d821a31b36d99123ab25da87c3ae8b3bb96a926dfcc233a35ba9c
+F ext/session/sqlite3session.h ca7c4422c1514a95056cc8d333217df6b1829d39058126b1de85d10cd62d7a9c
+F ext/session/test_session.c 05c1f90c04de5474158bf8f7712a6f7a1d47477ce0402bbe0e55fc4a9ef1f49b
F ext/wasm/GNUmakefile 65feef4ec48e62249f90278c4c08a3fe3c69e2461ff560b61c03cd73606e0949
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 2e87804e12c98f1d194b7a06162a88441d33bb443efcfe00dc6565a780d2f259
F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 07f81e256fde5796c0d8bcedff8fad4368639386f40881c0844e9c86a69b6fad 869a51ae84dfaaf824c872e4b3024f35eea7fa67bb584759a2d42ebf8404ef6e
-R f21e35a31a0e9f095263f7664daa2ccc
+P c6226d508d6af28fcd42fecd482014c3e284923871d3d8f5177f728cbf3457c5
+R 1eabf6c03fde51e4f558b57e68078de8
U dan
-Z 7d6fcd668efb6e232e66f35845c58a26
+Z 7d1f8058359dfe62c0d29a8d06d235a4
# Remove this line to create a well-formed Fossil manifest.
-c6226d508d6af28fcd42fecd482014c3e284923871d3d8f5177f728cbf3457c5
+eba02092bcf1713a646ecf8febd53a83bc803cf1b17e2e4fc590c14d886a8d27