diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/insert.c | 115 | ||||
-rw-r--r-- | src/vdbe.h | 3 | ||||
-rw-r--r-- | src/vdbeaux.c | 13 |
3 files changed, 36 insertions, 95 deletions
diff --git a/src/insert.c b/src/insert.c index 16971a044..12a6bb805 100644 --- a/src/insert.c +++ b/src/insert.c @@ -1179,44 +1179,6 @@ static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){ } /* -** 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. ** @@ -1325,11 +1287,13 @@ void sqlite3GenerateConstraintChecks( 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; @@ -1337,7 +1301,6 @@ void sqlite3GenerateConstraintChecks( 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 @@ -1441,8 +1404,8 @@ void sqlite3GenerateConstraintChecks( /* 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. @@ -1451,6 +1414,11 @@ void sqlite3GenerateConstraintChecks( ** 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 @@ -1470,11 +1438,10 @@ void sqlite3GenerateConstraintChecks( 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")); } } @@ -1506,16 +1473,12 @@ void sqlite3GenerateConstraintChecks( ** 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 ){ @@ -1610,9 +1573,9 @@ void sqlite3GenerateConstraintChecks( } } 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); } } @@ -1630,18 +1593,18 @@ void sqlite3GenerateConstraintChecks( 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; @@ -1713,15 +1676,6 @@ void sqlite3GenerateConstraintChecks( } } - /* 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 @@ -1843,18 +1797,21 @@ void sqlite3GenerateConstraintChecks( } } 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)); } diff --git a/src/vdbe.h b/src/vdbe.h index 183242a7b..d42acc0cc 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -238,9 +238,6 @@ void sqlite3VdbeClearObject(sqlite3*,Vdbe*); 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); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index c8b61ba22..68784d5fa 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -437,19 +437,6 @@ void sqlite3VdbeResolveLabel(Vdbe *v, int x){ } } -#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. */ |