| Index: third_party/sqlite/sqlite-src-3100200/src/fkey.c
|
| diff --git a/third_party/sqlite/sqlite-src-3080704/src/fkey.c b/third_party/sqlite/sqlite-src-3100200/src/fkey.c
|
| similarity index 92%
|
| copy from third_party/sqlite/sqlite-src-3080704/src/fkey.c
|
| copy to third_party/sqlite/sqlite-src-3100200/src/fkey.c
|
| index e816bd95daf30bcfa985eb89785c1e0c95d37a7a..2abd06c693b7614977c4bc8c221b0a889ed80b69 100644
|
| --- a/third_party/sqlite/sqlite-src-3080704/src/fkey.c
|
| +++ b/third_party/sqlite/sqlite-src-3100200/src/fkey.c
|
| @@ -249,16 +249,16 @@ int sqlite3FkLocateIndex(
|
| int i, j;
|
| for(i=0; i<nCol; i++){
|
| i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
|
| - char *zDfltColl; /* Def. collation for column */
|
| + const char *zDfltColl; /* Def. collation for column */
|
| char *zIdxCol; /* Name of indexed column */
|
|
|
| + if( iCol<0 ) break; /* No foreign keys against expression indexes */
|
| +
|
| /* If the index uses a collation sequence that is different from
|
| ** the default collation sequence for the column, this index is
|
| ** unusable. Bail out early in this case. */
|
| zDfltColl = pParent->aCol[iCol].zColl;
|
| - if( !zDfltColl ){
|
| - zDfltColl = "BINARY";
|
| - }
|
| + if( !zDfltColl ) zDfltColl = sqlite3StrBINARY;
|
| if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break;
|
|
|
| zIdxCol = pParent->aCol[iCol].zName;
|
| @@ -374,7 +374,7 @@ static void fkLookupParent(
|
|
|
| sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
|
| sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v);
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
|
| + sqlite3VdbeGoto(v, iOk);
|
| sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
| sqlite3VdbeJumpHere(v, iMustBeInt);
|
| sqlite3ReleaseTempReg(pParse, regTemp);
|
| @@ -404,6 +404,7 @@ static void fkLookupParent(
|
| for(i=0; i<nCol; i++){
|
| int iChild = aiCol[i]+1+regData;
|
| int iParent = pIdx->aiColumn[i]+1+regData;
|
| + assert( pIdx->aiColumn[i]>=0 );
|
| assert( aiCol[i]!=pTab->iPKey );
|
| if( pIdx->aiColumn[i]==pTab->iPKey ){
|
| /* The parent key is a composite key that includes the IPK column */
|
| @@ -412,11 +413,11 @@ static void fkLookupParent(
|
| sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v);
|
| sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
|
| }
|
| - sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
|
| + sqlite3VdbeGoto(v, iOk);
|
| }
|
|
|
| sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec,
|
| - sqlite3IndexAffinityStr(v,pIdx), nCol);
|
| + sqlite3IndexAffinityStr(pParse->db,pIdx), nCol);
|
| sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v);
|
|
|
| sqlite3ReleaseTempReg(pParse, regRec);
|
| @@ -437,7 +438,7 @@ static void fkLookupParent(
|
| OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
|
| }else{
|
| if( nIncr>0 && pFKey->isDeferred==0 ){
|
| - sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
| + sqlite3MayAbort(pParse);
|
| }
|
| sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
|
| }
|
| @@ -509,6 +510,10 @@ static Expr *exprTableColumn(
|
| ** code for an SQL UPDATE operation, this function may be called twice -
|
| ** once to "delete" the old row and once to "insert" the new row.
|
| **
|
| +** Parameter nIncr is passed -1 when inserting a row (as this may decrease
|
| +** the number of FK violations in the db) or +1 when deleting one (as this
|
| +** may increase the number of FK constraint problems).
|
| +**
|
| ** The code generated by this function scans through the rows in the child
|
| ** table that correspond to the parent table row being deleted or inserted.
|
| ** For each child row found, one of the following actions is taken:
|
| @@ -608,6 +613,7 @@ static void fkScanChildren(
|
| assert( pIdx!=0 );
|
| for(i=0; i<pPk->nKeyCol; i++){
|
| i16 iCol = pIdx->aiColumn[i];
|
| + assert( iCol>=0 );
|
| pLeft = exprTableRegister(pParse, pTab, regData, iCol);
|
| pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
|
| pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
|
| @@ -625,13 +631,9 @@ static void fkScanChildren(
|
| sqlite3ResolveExprNames(&sNameContext, pWhere);
|
|
|
| /* Create VDBE to loop through the entries in pSrc that match the WHERE
|
| - ** clause. If the constraint is not deferred, throw an exception for
|
| - ** each row found. Otherwise, for deferred constraints, increment the
|
| - ** deferred constraint counter by nIncr for each row selected. */
|
| + ** clause. For each row found, increment either the deferred or immediate
|
| + ** foreign key constraint counter. */
|
| pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
|
| - if( nIncr>0 && pFKey->isDeferred==0 ){
|
| - sqlite3ParseToplevel(pParse)->mayAbort = 1;
|
| - }
|
| sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
|
| if( pWInfo ){
|
| sqlite3WhereEnd(pWInfo);
|
| @@ -811,6 +813,24 @@ static int fkParentIsModified(
|
| }
|
|
|
| /*
|
| +** Return true if the parser passed as the first argument is being
|
| +** used to code a trigger that is really a "SET NULL" action belonging
|
| +** to trigger pFKey.
|
| +*/
|
| +static int isSetNullAction(Parse *pParse, FKey *pFKey){
|
| + Parse *pTop = sqlite3ParseToplevel(pParse);
|
| + if( pTop->pTriggerPrg ){
|
| + Trigger *p = pTop->pTriggerPrg->pTrigger;
|
| + if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
|
| + || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
|
| + ){
|
| + return 1;
|
| + }
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +/*
|
| ** This function is called when inserting, deleting or updating a row of
|
| ** table pTab to generate VDBE code to perform foreign key constraint
|
| ** processing for the operation.
|
| @@ -862,7 +882,7 @@ void sqlite3FkCheck(
|
| int *aiCol;
|
| int iCol;
|
| int i;
|
| - int isIgnore = 0;
|
| + int bIgnore = 0;
|
|
|
| if( aChange
|
| && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
|
| @@ -913,6 +933,7 @@ void sqlite3FkCheck(
|
| if( aiCol[i]==pTab->iPKey ){
|
| aiCol[i] = -1;
|
| }
|
| + assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
|
| #ifndef SQLITE_OMIT_AUTHORIZATION
|
| /* Request permission to read the parent key columns. If the
|
| ** authorization callback returns SQLITE_IGNORE, behave as if any
|
| @@ -921,7 +942,7 @@ void sqlite3FkCheck(
|
| int rcauth;
|
| char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName;
|
| rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb);
|
| - isIgnore = (rcauth==SQLITE_IGNORE);
|
| + bIgnore = (rcauth==SQLITE_IGNORE);
|
| }
|
| #endif
|
| }
|
| @@ -936,12 +957,18 @@ void sqlite3FkCheck(
|
| /* A row is being removed from the child table. Search for the parent.
|
| ** If the parent does not exist, removing the child row resolves an
|
| ** outstanding foreign key constraint violation. */
|
| - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore);
|
| + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore);
|
| }
|
| - if( regNew!=0 ){
|
| + if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){
|
| /* A row is being added to the child table. If a parent row cannot
|
| - ** be found, adding the child row has violated the FK constraint. */
|
| - fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore);
|
| + ** be found, adding the child row has violated the FK constraint.
|
| + **
|
| + ** If this operation is being performed as part of a trigger program
|
| + ** that is actually a "SET NULL" action belonging to this very
|
| + ** foreign key, then omit this scan altogether. As all child key
|
| + ** values are guaranteed to be NULL, it is not possible for adding
|
| + ** this row to cause an FK violation. */
|
| + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore);
|
| }
|
|
|
| sqlite3DbFree(db, aiFree);
|
| @@ -962,8 +989,8 @@ void sqlite3FkCheck(
|
| && !pParse->pToplevel && !pParse->isMultiWrite
|
| ){
|
| assert( regOld==0 && regNew!=0 );
|
| - /* Inserting a single row into a parent table cannot cause an immediate
|
| - ** foreign key violation. So do nothing in this case. */
|
| + /* Inserting a single row into a parent table cannot cause (or fix)
|
| + ** an immediate foreign key violation. So do nothing in this case. */
|
| continue;
|
| }
|
|
|
| @@ -987,13 +1014,28 @@ void sqlite3FkCheck(
|
| fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1);
|
| }
|
| if( regOld!=0 ){
|
| - /* If there is a RESTRICT action configured for the current operation
|
| - ** on the parent table of this FK, then throw an exception
|
| - ** immediately if the FK constraint is violated, even if this is a
|
| - ** deferred trigger. That's what RESTRICT means. To defer checking
|
| - ** the constraint, the FK should specify NO ACTION (represented
|
| - ** using OE_None). NO ACTION is the default. */
|
| + int eAction = pFKey->aAction[aChange!=0];
|
| fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
|
| + /* If this is a deferred FK constraint, or a CASCADE or SET NULL
|
| + ** action applies, then any foreign key violations caused by
|
| + ** removing the parent key will be rectified by the action trigger.
|
| + ** So do not set the "may-abort" flag in this case.
|
| + **
|
| + ** Note 1: If the FK is declared "ON UPDATE CASCADE", then the
|
| + ** may-abort flag will eventually be set on this statement anyway
|
| + ** (when this function is called as part of processing the UPDATE
|
| + ** within the action trigger).
|
| + **
|
| + ** Note 2: At first glance it may seem like SQLite could simply omit
|
| + ** all OP_FkCounter related scans when either CASCADE or SET NULL
|
| + ** applies. The trouble starts if the CASCADE or SET NULL action
|
| + ** trigger causes other triggers or action rules attached to the
|
| + ** child table to fire. In these cases the fk constraint counters
|
| + ** might be set incorrectly if any OP_FkCounter related scans are
|
| + ** omitted. */
|
| + if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){
|
| + sqlite3MayAbort(pParse);
|
| + }
|
| }
|
| pItem->zName = 0;
|
| sqlite3SrcListDelete(db, pSrc);
|
| @@ -1023,7 +1065,10 @@ u32 sqlite3FkOldmask(
|
| Index *pIdx = 0;
|
| sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
|
| if( pIdx ){
|
| - for(i=0; i<pIdx->nKeyCol; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
| + for(i=0; i<pIdx->nKeyCol; i++){
|
| + assert( pIdx->aiColumn[i]>=0 );
|
| + mask |= COLUMN_MASK(pIdx->aiColumn[i]);
|
| + }
|
| }
|
| }
|
| }
|
| @@ -1145,7 +1190,9 @@ static Trigger *fkActionTrigger(
|
|
|
| iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
|
| assert( iFromCol>=0 );
|
| - tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid";
|
| + assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) );
|
| + assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
|
| + tToCol.z = pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName;
|
| tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName;
|
|
|
| tToCol.n = sqlite3Strlen30(tToCol.z);
|
| @@ -1157,10 +1204,10 @@ static Trigger *fkActionTrigger(
|
| ** parent table are used for the comparison. */
|
| pEq = sqlite3PExpr(pParse, TK_EQ,
|
| sqlite3PExpr(pParse, TK_DOT,
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld),
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol)
|
| + sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
|
| + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)
|
| , 0),
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol)
|
| + sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0)
|
| , 0);
|
| pWhere = sqlite3ExprAnd(db, pWhere, pEq);
|
|
|
| @@ -1172,12 +1219,12 @@ static Trigger *fkActionTrigger(
|
| if( pChanges ){
|
| pEq = sqlite3PExpr(pParse, TK_IS,
|
| sqlite3PExpr(pParse, TK_DOT,
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld),
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol),
|
| + sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
|
| + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0),
|
| 0),
|
| sqlite3PExpr(pParse, TK_DOT,
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew),
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol),
|
| + sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
|
| + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0),
|
| 0),
|
| 0);
|
| pWhen = sqlite3ExprAnd(db, pWhen, pEq);
|
| @@ -1187,8 +1234,8 @@ static Trigger *fkActionTrigger(
|
| Expr *pNew;
|
| if( action==OE_Cascade ){
|
| pNew = sqlite3PExpr(pParse, TK_DOT,
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew),
|
| - sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol)
|
| + sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
|
| + sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)
|
| , 0);
|
| }else if( action==OE_SetDflt ){
|
| Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt;
|
| @@ -1235,13 +1282,12 @@ static Trigger *fkActionTrigger(
|
| pTrigger = (Trigger *)sqlite3DbMallocZero(db,
|
| sizeof(Trigger) + /* struct Trigger */
|
| sizeof(TriggerStep) + /* Single step in trigger program */
|
| - nFrom + 1 /* Space for pStep->target.z */
|
| + nFrom + 1 /* Space for pStep->zTarget */
|
| );
|
| if( pTrigger ){
|
| pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
|
| - pStep->target.z = (char *)&pStep[1];
|
| - pStep->target.n = nFrom;
|
| - memcpy((char *)pStep->target.z, zFrom, nFrom);
|
| + pStep->zTarget = (char *)&pStep[1];
|
| + memcpy((char *)pStep->zTarget, zFrom, nFrom);
|
|
|
| pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
|
| pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
|
|
|