[patches] [PATCH 2/2] sql: change constraints checks order
Bulat Niatshin
niatshin at tarantool.org
Wed Feb 21 20:34:53 MSK 2018
- Modify constraints checks execution order, if user specified ON
CONFLICT REPLACE
- Execute constraint check with ON CONFLICT REPLACE before BEFORE
triggers, if it fails, then remove conflicting entry and insert new,
after that - execute query as usual.
Closes #2963
---
src/box/sql/insert.c | 324 ++++++++++++++++++++++++++++++++----------------
src/box/sql/sqliteInt.h | 21 +++-
src/box/sql/update.c | 25 +++-
3 files changed, 255 insertions(+), 115 deletions(-)
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index bc1906fb6..f7ce06c0d 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -341,6 +341,9 @@ sqlite3Insert(Parse * pParse, /* Parser context */
int regData; /* register holding first column to insert */
int *aRegIdx = 0; /* One register allocated to each index */
+ u8 isReplace; /* Set to true if constraints may cause a replace */
+ u8 isUseSeek; /* True to use OPFLAG_SEEKRESULT */
+
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */
Trigger *pTrigger; /* List of triggers on pTab, if required */
@@ -661,67 +664,10 @@ sqlite3Insert(Parse * pParse, /* Parser context */
VdbeCoverage(v);
}
- /* Run the BEFORE and INSTEAD OF triggers, if there are any
- */
- endOfLoop = sqlite3VdbeMakeLabel(v);
- if (tmask & TRIGGER_BEFORE) {
- int regCols = sqlite3GetTempRange(pParse, pTab->nCol + 1);
-
- /* Create the new column data
- */
- for (i = j = 0; i < pTab->nCol; i++) {
- if (pColumn) {
- for (j = 0; j < pColumn->nId; j++) {
- if (pColumn->a[j].idx == i)
- break;
- }
- }
- if ((!useTempTable && !pList)
- || (pColumn && j >= pColumn->nId)
- || (pColumn == 0
- && IsOrdinaryHiddenColumn(&pTab->aCol[i]))) {
- if (i == pTab->iAutoIncPKey)
- sqlite3VdbeAddOp2(v, OP_Integer, -1,
- regCols + i + 1);
- else
- sqlite3ExprCode(pParse,
- pTab->aCol[i].pDflt,
- regCols + i + 1);
- } else if (useTempTable) {
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, j,
- regCols + i + 1);
- } else {
- assert(pSelect == 0); /* Otherwise useTempTable is true */
- sqlite3ExprCodeAndCache(pParse,
- pList->a[j].pExpr,
- regCols + i + 1);
- }
- if (pColumn == 0
- && !IsOrdinaryHiddenColumn(&pTab->aCol[i]))
- j++;
- }
-
- /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
- * do not attempt any conversions before assembling the record.
- * If this is a real table, attempt conversions as required by the
- * table column affinities.
- */
- if (!isView) {
- sqlite3TableAffinity(v, pTab, regCols + 1);
- }
-
- /* Fire BEFORE or INSTEAD OF triggers */
- sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0,
- TRIGGER_BEFORE, pTab,
- regCols - pTab->nCol - 1, onError,
- endOfLoop);
-
- sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol + 1);
- }
-
/* Compute the content of the next row to insert into a range of
* registers beginning at regIns.
*/
+ endOfLoop = sqlite3VdbeMakeLabel(v);
if (!isView) {
if (ipkColumn >= 0) {
if (useTempTable) {
@@ -836,39 +782,105 @@ sqlite3Insert(Parse * pParse, /* Parser context */
iRegStore);
}
}
+ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur,
+ iIdxCur, regIns, 0,
+ ipkColumn >= 0, onError,
+ endOfLoop, &isReplace, 0,
+ CONSTRAINT_REPLACE_MODE);
+ }
+
+ /* Run the BEFORE and INSTEAD OF triggers, if there are any. */
+ if (tmask & TRIGGER_BEFORE) {
+ int regCols = sqlite3GetTempRange(pParse, pTab->nCol + 1);
+
+ /* Create the new column data
+ */
+ for (i = j = 0; i < pTab->nCol; i++) {
+ if (pColumn) {
+ for (j = 0; j < pColumn->nId; j++) {
+ if (pColumn->a[j].idx == i)
+ break;
+ }
+ }
+ if ((!useTempTable && !pList)
+ || (pColumn && j >= pColumn->nId)
+ || (pColumn == 0
+ && IsOrdinaryHiddenColumn(&pTab->aCol[i]))) {
+ if (i == pTab->iAutoIncPKey)
+ sqlite3VdbeAddOp2(v, OP_Integer, -1,
+ regCols + i + 1);
+ else
+ sqlite3ExprCode(pParse,
+ pTab->aCol[i].pDflt,
+ regCols + i + 1);
+ } else if (useTempTable) {
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, j,
+ regCols + i + 1);
+ } else {
+ /* Otherwise useTempTable is true */
+ assert(pSelect == 0);
+ sqlite3ExprCodeAndCache(pParse,
+ pList->a[j].pExpr,
+ regCols + i + 1);
+ }
+ if (pColumn == 0
+ && !IsOrdinaryHiddenColumn(&pTab->aCol[i]))
+ j++;
+ }
+
+ /* If this is an INSERT on a view with an INSTEAD OF INSERT
+ * trigger, do not attempt any conversions before
+ * assembling the record.
+ * If this is a real table, attempt conversions
+ * as required by the table column affinities.
+ */
+ if (!isView) {
+ sqlite3TableAffinity(v, pTab, regCols + 1);
+ }
+
+ /* Fire BEFORE or INSTEAD OF triggers */
+ sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0,
+ TRIGGER_BEFORE, pTab,
+ regCols - pTab->nCol - 1, onError,
+ endOfLoop);
+
+ sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol + 1);
+ }
+
+ /* Update the count of rows that are inserted. */
+ if ((user_session->sql_flags & SQLITE_CountRows) != 0) {
+ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
+ }
+
+ if (!isView) {
+ /* Set the OPFLAG_USESEEKRESULT flag if either (a) there are
+ * no REPLACE constraints or (b) there are no triggers
+ * and this table is not a
+ * parent table in a foreign key constraint. It is safe
+ * to set the flag in the second case as if any
+ * REPLACE constraint is hit, an OP_Delete or OP_IdxDelete
+ * instruction will be executed on each cursor that is
+ * disturbed. And these instructions both clear the
+ * VdbeCursor.seekResult variable, disabling
+ * the OPFLAG_USESEEKRESULT functionality.
+ */
/* Generate code to check constraints and generate index keys
- and do the insertion.
+ * and do the insertion.
*/
- int isReplace; /* Set to true if constraints may cause a replace */
- int bUseSeek; /* True to use OPFLAG_SEEKRESULT */
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur,
iIdxCur, regIns, 0,
ipkColumn >= 0, onError,
- endOfLoop, &isReplace, 0);
+ endOfLoop, &isReplace, 0,
+ CONSTRAINT_DEFAULT_MODE);
sqlite3FkCheck(pParse, pTab, 0, regIns, 0);
- /* Set the OPFLAG_USESEEKRESULT flag if either (a) there are no REPLACE
- * constraints or (b) there are no triggers and this table is not a
- * parent table in a foreign key constraint. It is safe to set the
- * flag in the second case as if any REPLACE constraint is hit, an
- * OP_Delete or OP_IdxDelete instruction will be executed on each
- * cursor that is disturbed. And these instructions both clear the
- * VdbeCursor.seekResult variable, disabling the OPFLAG_USESEEKRESULT
- * functionality.
- */
- bUseSeek = isReplace == 0 || (pTrigger == 0 &&
+ isUseSeek = isReplace == 0 || (pTrigger == 0 &&
((user_session->sql_flags &
SQLITE_ForeignKeys) == 0 ||
sqlite3FkReferences(pTab) == 0));
sqlite3CompleteInsertion(pParse, pTab, iIdxCur, aRegIdx,
- bUseSeek, onError);
- }
-
- /* Update the count of rows that are inserted
- */
- if ((user_session->sql_flags & SQLITE_CountRows) != 0) {
- sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
+ isUseSeek, onError);
}
if (pTrigger) {
@@ -1015,7 +1027,8 @@ checkConstraintUnchanged(Expr * pExpr, int *aiChng)
* of the pTab->pIndex list.
*
* This routine also generates code to check constraints. NOT NULL,
- * CHECK, and UNIQUE constraints are all checked. If a constraint fails,
+ * CHECK, and UNIQUE constraints are all checked if they need bytecode
+ * for successful execution. If a constraint fails,
* then the appropriate action is performed. There are five possible
* actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE.
*
@@ -1024,11 +1037,16 @@ checkConstraintUnchanged(Expr * pExpr, int *aiChng)
* any ROLLBACK The current transaction is rolled back and
* sqlite3_step() returns immediately with a
* return code of SQLITE_CONSTRAINT.
+ * Constraint verification will be done
+ * by VDBE bytecode.
*
* any ABORT Back out changes from the current command
* only (do not do a complete rollback) then
* cause sqlite3_step() to return immediately
* with SQLITE_CONSTRAINT.
+ * Constraint will be verified by Tarantool,
+ * no need for additional bytecode in that
+ * case.
*
* any FAIL Sqlite3_step() returns immediately with a
* return code of SQLITE_CONSTRAINT. The
@@ -1039,13 +1057,19 @@ checkConstraintUnchanged(Expr * pExpr, int *aiChng)
* row is skipped, without throwing an error.
* Processing continues with the next row.
* (There is an immediate jump to ignoreDest.)
+ * Constraint verification will be done
+ * by VDBE bytecode.
*
* NOT NULL REPLACE The NULL value is replace by the default
* value for that column. If the default value
* is NULL, the action is the same as ABORT.
+ * Constraint verification will be done
+ * by VDBE bytecode.
*
* UNIQUE REPLACE The other row that conflicts with the row
* being inserted is removed.
+ * Constraint verification will be done
+ * by VDBE bytecode.
*
* CHECK REPLACE Illegal. The results in an exception.
*
@@ -1053,6 +1077,23 @@ checkConstraintUnchanged(Expr * pExpr, int *aiChng)
* Or if overrideError==OE_Default, then the pParse->onError parameter
* is used. Or if pParse->onError==OE_Default then the onError value
* for the constraint is used.
+ *
+ * There are possible modes in which this function is working:
+ * 1) CONSTRAINT_REPLACE_MODE - It will generate
+ * bytecode for UNIQUE and NOT NULL constraints with ON CONFLICT REPLACE
+ * error action, for other constraints this call will be no-op.
+ * If conflict happens during that stage, the appropriate conflicting
+ * entry will be deleted by bytecode.
+ *
+ * sqlite3GenerateConstraintChecks with CONSTRAINT_MAIN_MODE assumes
+ * that it was called before generating bytecode for firing BEFORE triggers.
+ *
+ * 2) CONSTRAINT_DEFAULT_MODE - function with that mode argument will
+ * generate bytecode for all constraints, including the ones with
+ * ON CONFLICT REPLACE error action. For constraints with REPLACE action,
+ * it assumes that possible conflicting entry was deleted before,
+ * but unpresence still will be ensured with ABORT action. For other
+ * error actions type everything is usual.
*/
void
sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
@@ -1065,8 +1106,9 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
u8 pkChng, /* Non-zero if the PRIMARY KEY changed */
u8 overrideError, /* Override onError to this if not OE_Default */
int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
- int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */
- int *aiChng) /* column i is unchanged if aiChng[i]<0 */
+ u8 *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */
+ int *aiChng, /* column i is unchanged if aiChng[i]<0 */
+ int mode) /* Type of working mode - REPLACE or DEFAULT */
{
Vdbe *v; /* VDBE under constrution */
Index *pIdx; /* Pointer to one of the indices */
@@ -1104,23 +1146,43 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
continue;
}
if (aiChng && aiChng[i] < 0) {
- /* Don't bother checking for NOT NULL on columns that do not change */
+ /* Don't bother checking for NOT NULL on columns
+ * that do not change. */
continue;
}
onError = pTab->aCol[i].notNull;
- if (onError == OE_None || (pTab->tabFlags & TF_Autoincrement && pTab->iAutoIncPKey == i))
+ if (onError == OE_None || (pTab->tabFlags & TF_Autoincrement &&
+ pTab->iAutoIncPKey == i)) {
continue; /* This column is allowed to be NULL */
+ }
if (overrideError != OE_Default) {
onError = overrideError;
- } else if (onError == OE_Default) {
+ } else if (onError == OE_Default &&
+ mode == CONSTRAINT_DEFAULT_MODE) {
+ onError = OE_Abort;
+ }
+ if (mode == CONSTRAINT_DEFAULT_MODE && onError == OE_Replace) {
onError = OE_Abort;
}
+ if (mode == CONSTRAINT_REPLACE_MODE && onError != OE_Replace) {
+ continue;
+ }
if (onError == OE_Replace && pTab->aCol[i].pDflt == 0) {
onError = OE_Abort;
}
- assert(onError == OE_Rollback || onError == OE_Abort
- || onError == OE_Fail || onError == OE_Ignore
- || onError == OE_Replace);
+
+ /* Assert that one of conditions below is true:
+ * 1) if mode is CONSTRAINT_DEFAULT_MODE, then check
+ * that error action is ROLLBACK, ABORT, FAIL or IGNORE.
+ * 2) if error action is CONSTRAINT_REPLACE_MODE,
+ * then check that error action is REPLACE or ABORT.
+ */
+ assert((mode == CONSTRAINT_DEFAULT_MODE && /* 1) */
+ (onError == OE_Rollback || onError == OE_Abort ||
+ onError == OE_Fail ||
+ onError == OE_Ignore)) ||
+ (mode == CONSTRAINT_REPLACE_MODE && /* 2) */
+ (onError == OE_Abort || onError == OE_Replace)));
switch (onError) {
case OE_Abort:
sqlite3MayAbort(pParse);
@@ -1159,11 +1221,10 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
}
}
- /* Test all CHECK constraints
- */
+ /* Test all CHECK constraint (will be done only in DEFAULT mode). */
#ifndef SQLITE_OMIT_CHECK
- if (pTab->pCheck && (user_session->sql_flags &
- SQLITE_IgnoreChecks) == 0) {
+ if (mode == CONSTRAINT_DEFAULT_MODE && pTab->pCheck &&
+ (user_session->sql_flags & SQLITE_IgnoreChecks) == 0) {
ExprList *pCheck = pTab->pCheck;
pParse->ckBase = regNewData + 1;
onError =
@@ -1194,7 +1255,7 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
sqlite3VdbeResolveLabel(v, allOk);
}
}
-#endif /* !defined(SQLITE_OMIT_CHECK) */
+#endif /* !defined(SQLITE_OMIT_CHECK) */
/* Test all UNIQUE constraints by creating entries for each UNIQUE
* index and making sure that duplicate entries do not already exist.
@@ -1216,12 +1277,43 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
if (aRegIdx[ix] == 0)
continue; /* Skip indices that do not change */
+
+ iThisCur = iIdxCur + ix;
+ addrUniqueOk = sqlite3VdbeMakeLabel(v);
+
+ /* Find out what action to take in case
+ * there is a uniqueness conflict.
+ */
+ onError = pIdx->onError;
+ if (!IsUniqueIndex(pIdx)) {
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ continue; /* pIdx is not a UNIQUE index */
+ }
+ if (overrideError != OE_Default) {
+ onError = overrideError;
+ } else if (onError == OE_Default) {
+ onError = OE_Abort;
+ }
+ /* If we are checking ON CONFLICT REPLACE before UPDATE/INSERT,
+ * then skip iteration if onError is not OE_Replace
+ */
+ if (mode == CONSTRAINT_REPLACE_MODE && onError != OE_Replace) {
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ continue;
+ }
+ /* When we check all UNIQUE constraints during UPDATE/INSERT,
+ * onError action for constraints with ON CONFLICT REPLACE
+ * should be ABORT, because REPLACE actions have been already
+ * done in the beginning of insertion/update.
+ */
+ if (mode == CONSTRAINT_DEFAULT_MODE && onError == OE_Replace) {
+ onError = OE_Abort;
+ }
+
if (bAffinityDone == 0) {
sqlite3TableAffinity(v, pTab, regNewData+1);
bAffinityDone = 1;
}
- iThisCur = iIdxCur + ix;
- addrUniqueOk = sqlite3VdbeMakeLabel(v);
/* Skip partial indices for which the WHERE clause is not true */
if (pIdx->pPartIdxWhere) {
@@ -1233,7 +1325,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
}
/* Create a record for this index entry as it should appear after
- * the insert or update. Store that record in the aRegIdx[ix] register
+ * the insert or update. Store that record in the
+ * aRegIdx[ix] register.
*/
regIdx = aRegIdx[ix] + 1;
for (i = 0; i < pIdx->nColumn; i++) {
@@ -1325,17 +1418,23 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
onError = OE_Abort;
}
- /* Collision detection may be omitted if all of the following are true:
- * (1) The conflict resolution algorithm is REPLACE
- * (2) There are no secondary indexes on the table
- * (3) No delete triggers need to be fired if there is a conflict
- * (4) No FK constraint counters need to be updated if a conflict occurs.
+ /* Collision detection may be omitted if all of the
+ * following are true:
+ * (1) The conflict resolution algorithm is REPLACE.
+ * (2) There are no secondary indexes on the table.
+ * (3) No delete triggers need to be fired if there
+ * is a conflict.
+ * (4) No FK constraint counters need to be updated
+ * if a conflict occurs.
*/
if ((ix == 0 && pIdx->pNext == 0) /* Condition 2 */
- && onError == OE_Replace /* Condition 1 */
- && (0 == (user_session->sql_flags & SQLITE_RecTriggers) /* Condition 3 */
+ /* Condition 1 */
+ && onError == OE_Replace
+ /* Condition 3 */
+ && (0 == (user_session->sql_flags & SQLITE_RecTriggers)
||0 == sqlite3TriggersExist(pTab, TK_DELETE, 0, 0))
- && (0 == (user_session->sql_flags & SQLITE_ForeignKeys) || /* Condition 4 */
+ /* Condition 4 */
+ && (0 == (user_session->sql_flags & SQLITE_ForeignKeys) ||
(0 == pTab->pFKey && 0 == sqlite3FkReferences(pTab)))
) {
sqlite3VdbeResolveLabel(v, addrUniqueOk);
@@ -1408,10 +1507,19 @@ sqlite3GenerateConstraintChecks(Parse * pParse, /* The parser context */
}
}
- /* Generate code that executes if the new index entry is not unique */
- assert(onError == OE_Rollback || onError == OE_Abort
- || onError == OE_Fail || onError == OE_Ignore
- || onError == OE_Replace);
+ /* Generate code that executes if the new index entry is
+ * not unique.
+ * Assert that one of conditions below is true:
+ * 1) if mode is CONSTRAINT_DEFAULT_MODE, then check
+ * that error action is ROLLBACK, ABORT, FAIL or IGNORE.
+ * 2) if error action is CONSTRAINT_REPLACE_MODE,
+ * then check that error action is REPLACE or ABORT.
+ */
+ assert((mode == CONSTRAINT_DEFAULT_MODE && (onError == OE_Rollback
+ || onError == OE_Abort || onError == OE_Fail
+ || onError == OE_Ignore || onError == OE_Replace)) ||
+ (mode == CONSTRAINT_REPLACE_MODE && (onError == OE_Abort ||
+ onError == OE_Replace)));
switch (onError) {
case OE_Fail:
case OE_Rollback:{
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 7caf7cb19..aa4c2632c 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1559,6 +1559,25 @@ struct FKey {
#define OE_Default 10 /* Do whatever the default action is */
+/* There are two modes in sqlite3GenerateConstraintChecks function.
+ * (a) CONSTRAINT_REPLACE_MODE:
+ * It will generate bytecode for
+ * constraint with ON CONFLICT REPLACE clause, for other constraints with
+ * other onError option sqlite3GenerateConstraintChecks will be no-op.
+ * sqlite3GenerateConstraintChecks with REPLACE MODE should be called
+ * before running BEFORE triggers.
+ * (b) CONSTRAINT_DEFAULT_MODE:
+ * sqlite3GenerateConstraintChecks with that mode argument will
+ * generate required bytecode for all constraints, including the one
+ * that have ON CONFLICT REPLACE clause. But in that case if error happens,
+ * REPLACE action won't be executed, instead of this error action will be
+ * ABORT, because REPLACE for that constraint should be done earlier,
+ * when sqlite3GenerateConstraintChecks was called in
+ * CONSTRAINT_REPLACE_MODE
+ */
+#define CONSTRAINT_REPLACE_MODE 1
+#define CONSTRAINT_DEFAULT_MODE 2
+
/*
* An instance of the following structure is passed as the first
* argument to sqlite3VdbeKeyCompare and is used to control the
@@ -3283,7 +3302,7 @@ int sqlite3GenerateIndexKey(Parse *, Index *, int, int, int, int *, Index *,
int);
void sqlite3ResolvePartIdxLabel(Parse *, int);
void sqlite3GenerateConstraintChecks(Parse *, Table *, int *, int, int, int,
- int, u8, u8, int, int *, int *);
+ int, u8, u8, int, u8 *, int *, int);
void sqlite3CompleteInsertion(Parse *, Table *, int, int *, int, u8);
int sqlite3OpenTableAndIndices(Parse *, Table *, int, u8, int, u8 *, int *,
int *, u8, u8);
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 8d0fe73ff..406125b30 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -544,6 +544,18 @@ sqlite3Update(Parse * pParse, /* The parser context */
}
}
+ if (!isView) {
+ u8 isReplace = 0; /* True if REPLACE conflict resolution might happen */
+
+ /* Do constraint checks. */
+ assert(regOldPk > 0);
+ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur,
+ iIdxCur, regNewPk,
+ regOldPk, chngPk, onError,
+ labelContinue, &isReplace,
+ aXRef, CONSTRAINT_REPLACE_MODE);
+ }
+
/* Fire any BEFORE UPDATE triggers. This happens before constraints are
* verified. One could argue that this is wrong.
*/
@@ -584,16 +596,17 @@ sqlite3Update(Parse * pParse, /* The parser context */
}
if (!isView) {
- int addr1 = 0; /* Address of jump instruction */
- int bReplace = 0; /* True if REPLACE conflict resolution might happen */
+ int addr1 = 0; /* Address of jump instruction. */
+ u8 isReplace = 0; /* True if REPLACE conflict resolution
+ might happen. */
/* Do constraint checks. */
assert(regOldPk > 0);
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur,
iIdxCur, regNewPk,
regOldPk, chngPk, onError,
- labelContinue, &bReplace,
- aXRef);
+ labelContinue, &isReplace,
+ aXRef, CONSTRAINT_DEFAULT_MODE);
/* Do FK constraint checks. */
if (hasFK) {
@@ -601,7 +614,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
}
/* Delete the index entries associated with the current record. */
- if (bReplace || chngPk) {
+ if (isReplace || chngPk) {
addr1 =
sqlite3VdbeAddOp4Int(v, OP_NotFound,
iDataCur, 0, regKey,
@@ -634,7 +647,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
}
#endif
- if (bReplace || chngPk) {
+ if (isReplace || chngPk) {
sqlite3VdbeJumpHere(v, addr1);
}
--
2.14.1
More information about the Tarantool-patches
mailing list