-C Stop\srequiring\sthe\sglobal\sVFS\smutex\sto\saccess\sthe\sunixInodeInfo.pUnused\sfield.\nThe\sunixInodeInfo\smutex\sis\ssufficient.
-D 2018-08-13T22:50:34.627
+C Fix\sUPSERT\sso\sthat\sit\schecks\sthe\starget-constraint\sfirst\sand\sfires\sthe\nDO\sUPDATE\sif\sthat\sconstraint\sis\sviolated\sregardless\sof\swhether\sor\snot\nother\sconstraints\sare\sin\sviolation.\s\sThis\saligns\sSQLite\sbehavior\swith\nwhat\sPostgreSQL\sdoes.\sFix\sfor\sticket\s[908f001483982c43cdb476dfb590a1a].
+D 2018-08-14T15:12:52.710
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6
F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
-F src/insert.c 894594952bcda1dc6e1549871e4022517563545ffc7a3f4e9e5f3faa788893fd
+F src/insert.c c723716f0de7aa0a679300f7d3541c89645f4a9882161cecdb3093fc07f8cc4b
F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e
F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b
F src/main.c df233667bbb6f05a8492ea93e0995abbeb816eab53e51e638a0dece1de0e83a3
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855
F src/vdbe.c b11baa48b293dc48fbd51c6a9029f88bdf4cd117c01225b2a2b5e90e5928a8a3
-F src/vdbe.h d93abdc8bc9295e0a256e582c19f548c545dc498319d108bbc9dd29de31c48a2
+F src/vdbe.h 5081dcc497777efe5e9ebe7330d283a044a005e4bdda2e2e984f03bf89a0d907
F src/vdbeInt.h 8ea493d994c6697cf7bccc60583a80a0222560490410f60f1113e90d36643ce0
F src/vdbeapi.c 2ba821c5929a2769e4b217dd85843479c718b8989d414723ec8af0616a83d611
-F src/vdbeaux.c b610cef3d8d381c9287d02c2e61590acc0a1b4de1cc0188d560f5ef345527a24
+F src/vdbeaux.c f03d4a1961ec282abaec5dbf7e5576ddb1eb01e6157335a232d8d9e57fd5eca1
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
F src/vdbemem.c 720df42ad8e5c7cb883573de40a185afef4a214903098a16f2bb14b62b2399b7
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
F test/update.test 1148de8d913e9817717990603aadeca07aab9ddbb10a30f167cbfd8d3a3ccb60
F test/update2.test 5e67667e1c54017d964e626db765cf8bedcf87483c184f4c575bdb8c1dd2313e
-F test/upsert1.test ecd8f69e20183f298464992762fee24750508b3cbefc5badba8a259a3fc887ec
+F test/upsert1.test 994bde41800bb77dbe32fcd2e1f6c4b49cc9f2c6cd345731c774dff02b51c110
F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 8b1e0010b9e0b548a90087f4d25843d2b40f7e9551722ac587fa925d37b510c2
-R c0b44ea86365eec38ab4ca860c436c2d
+P e3ea43dabf099dc2954c23d348638e7b2a8b9122d2994154bc649a2c09260c46
+R 57b28032d1f7240227e60efc94d5cf5d
U drh
-Z 61c21fbc1d363c0a3fe7befb1f84f230
+Z f4b7118e2c3ed77a16d581f95e6f4881
-e3ea43dabf099dc2954c23d348638e7b2a8b9122d2994154bc649a2c09260c46
\ No newline at end of file
+529fb55e3d00472f13446117527b0896827b11e870b581af7fe7cbb7392ef3cd
\ No newline at end of file
return !w.eCode;
}
-/*
-** An instance of the ConstraintAddr object remembers the byte-code addresses
-** for sections of the constraint checks that deal with uniqueness constraints
-** on the rowid and on the upsert constraint.
-**
-** This information is passed into checkReorderConstraintChecks() to insert
-** some OP_Goto operations so that the rowid and upsert constraints occur
-** in the correct order relative to other constraints.
-*/
-typedef struct ConstraintAddr ConstraintAddr;
-struct ConstraintAddr {
- int ipkTop; /* Subroutine for rowid constraint check */
- int upsertTop; /* Label for upsert constraint check subroutine */
- int upsertTop2; /* Copy of upsertTop not cleared by the call */
- int upsertBtm; /* upsert constraint returns to this label */
- int ipkBtm; /* Return opcode rowid constraint check */
-};
-
-/*
-** Generate any OP_Goto operations needed to cause constraints to be
-** run that haven't already been run.
-*/
-static void reorderConstraintChecks(Vdbe *v, ConstraintAddr *p){
- if( p->upsertTop ){
- testcase( sqlite3VdbeLabelHasBeenResolved(v, p->upsertTop) );
- sqlite3VdbeGoto(v, p->upsertTop);
- VdbeComment((v, "call upsert subroutine"));
- sqlite3VdbeResolveLabel(v, p->upsertBtm);
- p->upsertTop = 0;
- }
- if( p->ipkTop ){
- sqlite3VdbeGoto(v, p->ipkTop);
- VdbeComment((v, "call rowid unique-check subroutine"));
- sqlite3VdbeJumpHere(v, p->ipkBtm);
- p->ipkTop = 0;
- }
-}
-
/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
int addr1; /* Address 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 */
- ConstraintAddr sAddr;/* Address information for constraint reordering */
Index *pUpIdx = 0; /* Index to which to apply the upsert */
u8 isUpdate; /* True if this is an UPDATE operation */
u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
int upsertBypass = 0; /* Address of Goto to bypass upsert subroutine */
+ int upsertJump = 0; /* Address of Goto that jumps into upsert subroutine */
+ int ipkTop = 0; /* Top of the IPK uniqueness check */
+ int ipkBottom = 0; /* OP_Goto at the end of the IPK uniqueness check */
isUpdate = regOldData!=0;
db = pParse->db;
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
- memset(&sAddr, 0, sizeof(sAddr));
/* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
** normal rowid tables. nPkField is the number of key fields in the
/* UNIQUE and PRIMARY KEY constraints should be handled in the following
** order:
**
- ** (1) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
- ** (2) OE_Update
+ ** (1) OE_Update
+ ** (2) OE_Abort, OE_Fail, OE_Rollback, OE_Ignore
** (3) OE_Replace
**
** OE_Fail and OE_Ignore must happen before any changes are made.
** could happen in any order, but they are grouped up front for
** convenience.
**
+ ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43
+ ** The order of constraints used to have OE_Update as (2) and OE_Abort
+ ** and so forth as (1). But apparently PostgreSQL checks the OE_Update
+ ** constraint before any others, so it had to be moved.
+ **
** Constraint checking code is generated in this order:
** (A) The rowid constraint
** (B) Unique index constraints that do not have OE_Replace as their
overrideError = OE_Ignore;
pUpsert = 0;
}else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){
- /* If the constraint-target is on some column other than
- ** then ROWID, then we might need to move the UPSERT around
- ** so that it occurs in the correct order. */
- sAddr.upsertTop = sAddr.upsertTop2 = sqlite3VdbeMakeLabel(v);
- sAddr.upsertBtm = sqlite3VdbeMakeLabel(v);
+ /* If the constraint-target uniqueness check must be run first.
+ ** Jump to that uniqueness check now */
+ upsertJump = sqlite3VdbeAddOp0(v, OP_Goto);
+ VdbeComment((v, "UPSERT constraint goes first"));
}
}
** to defer the running of the rowid conflict checking until after
** the UNIQUE constraints have run.
*/
- assert( OE_Update>OE_Replace );
- assert( OE_Ignore<OE_Replace );
- assert( OE_Fail<OE_Replace );
- assert( OE_Abort<OE_Replace );
- assert( OE_Rollback<OE_Replace );
- if( onError>=OE_Replace
- && (pUpsert || onError!=overrideError)
- && pTab->pIndex
+ if( onError==OE_Replace /* IPK rule is REPLACE */
+ && onError!=overrideError /* Rules for other contraints are different */
+ && pTab->pIndex /* There exist other constraints */
){
- sAddr.ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
+ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
+ VdbeComment((v, "defer IPK REPLACE until last"));
}
if( isUpdate ){
}
}
sqlite3VdbeResolveLabel(v, addrRowidOk);
- if( sAddr.ipkTop ){
- sAddr.ipkBtm = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, sAddr.ipkTop-1);
+ if( ipkTop ){
+ ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, ipkTop-1);
}
}
int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
- if( bAffinityDone==0 ){
- sqlite3TableAffinity(v, pTab, regNewData+1);
- bAffinityDone = 1;
- }
if( pUpIdx==pIdx ){
- addrUniqueOk = sAddr.upsertBtm;
+ addrUniqueOk = upsertJump+1;
upsertBypass = sqlite3VdbeGoto(v, 0);
VdbeComment((v, "Skip upsert subroutine"));
- sqlite3VdbeResolveLabel(v, sAddr.upsertTop2);
+ sqlite3VdbeJumpHere(v, upsertJump);
}else{
addrUniqueOk = sqlite3VdbeMakeLabel(v);
}
+ if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
+ sqlite3TableAffinity(v, pTab, regNewData+1);
+ bAffinityDone = 1;
+ }
VdbeNoopComment((v, "uniqueness check for %s", pIdx->zName));
iThisCur = iIdxCur+ix;
}
}
- /* Invoke subroutines to handle IPK replace and upsert prior to running
- ** the first REPLACE constraint check. */
- if( onError==OE_Replace ){
- testcase( sAddr.ipkTop );
- testcase( sAddr.upsertTop
- && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
- reorderConstraintChecks(v, &sAddr);
- }
-
/* Collision detection may be omitted if all of the following are true:
** (1) The conflict resolution algorithm is REPLACE
** (2) The table is a WITHOUT ROWID table
}
}
if( pUpIdx==pIdx ){
+ sqlite3VdbeGoto(v, upsertJump+1);
sqlite3VdbeJumpHere(v, upsertBypass);
}else{
sqlite3VdbeResolveLabel(v, addrUniqueOk);
}
if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
+ }
+ /* If the IPK constraint is a REPLACE, run it last */
+ if( ipkTop ){
+ sqlite3VdbeGoto(v, ipkTop+1);
+ VdbeComment((v, "Do IPK REPLACE"));
+ sqlite3VdbeJumpHere(v, ipkBottom);
}
- testcase( sAddr.ipkTop!=0 );
- testcase( sAddr.upsertTop
- && sqlite3VdbeLabelHasBeenResolved(v,sAddr.upsertTop) );
- reorderConstraintChecks(v, &sAddr);
-
+
*pbMayReplace = seenReplace;
VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
}
void sqlite3VdbeMakeReady(Vdbe*,Parse*);
int sqlite3VdbeFinalize(Vdbe*);
void sqlite3VdbeResolveLabel(Vdbe*, int);
-#ifdef SQLITE_COVERAGE_TEST
- int sqlite3VdbeLabelHasBeenResolved(Vdbe*,int);
-#endif
int sqlite3VdbeCurrentAddr(Vdbe*);
#ifdef SQLITE_DEBUG
int sqlite3VdbeAssertMayAbort(Vdbe *, int);
}
}
-#ifdef SQLITE_COVERAGE_TEST
-/*
-** Return TRUE if and only if the label x has already been resolved.
-** Return FALSE (zero) if label x is still unresolved.
-**
-** This routine is only used inside of testcase() macros, and so it
-** only exists when measuring test coverage.
-*/
-int sqlite3VdbeLabelHasBeenResolved(Vdbe *v, int x){
- return v->pParse->aLabel && v->pParse->aLabel[ADDR(x)]>=0;
-}
-#endif /* SQLITE_COVERAGE_TEST */
-
/*
** Mark the VDBE as one that can only be run one time.
*/
PRAGMA integrity_check;
} {ok}
+# 2018-08-14
+# Ticket https://www.sqlite.org/src/info/908f001483982c43
+# If there are multiple uniqueness contraints, the UPSERT should fire
+# if the one constraint it targets fails, regardless of whether or not
+# the other constraints pass or fail. In other words, the UPSERT constraint
+# should be tested first.
+#
+do_execsql_test upsert1-700 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INTEGER PRIMARY KEY, b INT, c INT, d INT, e INT);
+ CREATE UNIQUE INDEX t1b ON t1(b);
+ CREATE UNIQUE INDEX t1e ON t1(e);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(e) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-710 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(a) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-720 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(b) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-730 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INT, b INT, c INT, d INT, e INT);
+ CREATE UNIQUE INDEX t1a ON t1(a);
+ CREATE UNIQUE INDEX t1b ON t1(b);
+ CREATE UNIQUE INDEX t1e ON t1(e);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(e) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-740 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(a) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-750 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(b) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-760 {
+ DROP TABLE t1;
+ CREATE TABLE t1(a INT PRIMARY KEY, b INT, c INT, d INT, e INT) WITHOUT ROWID;
+ CREATE UNIQUE INDEX t1a ON t1(a);
+ CREATE UNIQUE INDEX t1b ON t1(b);
+ CREATE UNIQUE INDEX t1e ON t1(e);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(e) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-770 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(a) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+do_execsql_test upsert1-780 {
+ DELETE FROM t1;
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,3,4,5);
+ INSERT INTO t1(a,b,c,d,e) VALUES(1,2,33,44,5)
+ ON CONFLICT(b) DO UPDATE SET c=excluded.c;
+ SELECT * FROM t1;
+} {1 2 33 4 5}
+
+
finish_test