diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/delete.c | 8 | ||||
-rw-r--r-- | src/fkey.c | 74 | ||||
-rw-r--r-- | src/insert.c | 4 | ||||
-rw-r--r-- | src/sqliteInt.h | 12 | ||||
-rw-r--r-- | src/update.c | 18 |
5 files changed, 71 insertions, 45 deletions
diff --git a/src/delete.c b/src/delete.c index d93ee36a0..9904d98d0 100644 --- a/src/delete.c +++ b/src/delete.c @@ -342,7 +342,7 @@ void sqlite3DeleteFrom( ** this optimization caused the row change count (the value returned by ** API function sqlite3_count_changes) to be set incorrectly. */ if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) - && 0==sqlite3FkRequired(pParse, pTab, 0) + && 0==sqlite3FkRequired(pParse, pTab, 0, 0) ){ assert( !isView ); sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, @@ -492,14 +492,14 @@ void sqlite3GenerateRowDelete( /* If there are any triggers to fire, allocate a range of registers to ** use for the old.* references in the triggers. */ - if( sqlite3FkRequired(pParse, pTab, 0) || pTrigger ){ + if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ u32 mask; /* Mask of OLD.* columns in use */ int iCol; /* Iterator used while populating OLD.* */ /* TODO: Could use temporary registers here. Also could attempt to ** avoid copying the contents of the rowid register. */ mask = sqlite3TriggerOldmask(pParse, pTrigger, 0, pTab, onconf); - mask |= sqlite3FkOldmask(pParse, pTab, 0); + mask |= sqlite3FkOldmask(pParse, pTab); iOld = pParse->nMem+1; pParse->nMem += (1 + pTab->nCol); @@ -528,7 +528,7 @@ void sqlite3GenerateRowDelete( /* Do FK processing. This call checks that any FK constraints that ** refer to this table (i.e. constraints attached to other tables) ** are not violated by deleting this row. */ - sqlite3FkCheck(pParse, pTab, 0, iOld, 0); + sqlite3FkCheck(pParse, pTab, iOld, 0); } /* Delete the index and table entries. Skip this step if pTab is really diff --git a/src/fkey.c b/src/fkey.c index 7ec256d7b..4d6a32ad6 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -672,14 +672,11 @@ void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ ** For an UPDATE operation, this function is called twice. Once before ** the original record is deleted from the table using the calling convention ** described for DELETE. Then again after the original record is deleted -** but before the new record is inserted using the INSERT convention. In -** both cases parameter pChanges is passed the list of columns being -** updated by the statement. +** but before the new record is inserted using the INSERT convention. */ void sqlite3FkCheck( Parse *pParse, /* Parse context */ Table *pTab, /* Row is being deleted from this table */ - ExprList *pChanges, /* Changed columns if this is an UPDATE */ int regOld, /* Previous row data is stored here */ int regNew /* New row data is stored here */ ){ @@ -725,11 +722,6 @@ void sqlite3FkCheck( } assert( pFKey->nCol==1 || (aiFree && pIdx) ); - /* If the key does not overlap with the pChanges list, skip this FK. */ - if( pChanges ){ - /* TODO */ - } - if( aiFree ){ aiCol = aiFree; }else{ @@ -782,14 +774,6 @@ void sqlite3FkCheck( } assert( aiCol || pFKey->nCol==1 ); - /* Check if this update statement has modified any of the child key - ** columns for this foreign key constraint. If it has not, there is - ** no need to search the child table for rows in violation. This is - ** just an optimization. Things would work fine without this check. */ - if( pChanges ){ - /* TODO */ - } - /* Create a SrcList structure containing a single table (the table ** the foreign key that refers to this table is attached to). This ** is required for the sqlite3WhereXXX() interface. */ @@ -822,14 +806,11 @@ void sqlite3FkCheck( /* ** This function is called before generating code to update or delete a -** row contained in table pTab. If the operation is an update, then -** pChanges is a pointer to the list of columns to modify. If this is a -** delete, then pChanges is NULL. +** row contained in table pTab. */ u32 sqlite3FkOldmask( Parse *pParse, /* Parse context */ - Table *pTab, /* Table being modified */ - ExprList *pChanges /* Non-NULL for UPDATE operations */ + Table *pTab /* Table being modified */ ){ u32 mask = 0; if( pParse->db->flags&SQLITE_ForeignKeys ){ @@ -851,9 +832,13 @@ u32 sqlite3FkOldmask( /* ** This function is called before generating code to update or delete a -** row contained in table pTab. If the operation is an update, then -** pChanges is a pointer to the list of columns to modify. If this is a -** delete, then pChanges is NULL. +** row contained in table pTab. If the operation is a DELETE, then +** parameter aChange is passed a NULL value. For an UPDATE, aChange points +** to an array of size N, where N is the number of columns in table pTab. +** If the i'th column is not modified by the UPDATE, then the corresponding +** entry in the aChange[] array is set to -1. If the column is modified, +** the value is 0 or greater. Parameter chngRowid is set to true if the +** UPDATE statement modifies the rowid fields of the table. ** ** If any foreign key processing will be required, this function returns ** true. If there is no foreign key related processing, this function @@ -862,10 +847,45 @@ u32 sqlite3FkOldmask( int sqlite3FkRequired( Parse *pParse, /* Parse context */ Table *pTab, /* Table being modified */ - ExprList *pChanges /* Non-NULL for UPDATE operations */ + int *aChange, /* Non-NULL for UPDATE operations */ + int chngRowid /* True for UPDATE that affects rowid */ ){ if( pParse->db->flags&SQLITE_ForeignKeys ){ - if( sqlite3FkReferences(pTab) || pTab->pFKey ) return 1; + if( !aChange ){ + /* A DELETE operation. Foreign key processing is required if the + ** table in question is either the child or parent table for any + ** foreign key constraint. */ + return (sqlite3FkReferences(pTab) || pTab->pFKey); + }else{ + /* This is an UPDATE. Foreign key processing is only required if the + ** operation modifies one or more child or parent key columns. */ + int i; + FKey *p; + + /* Check if any child key columns are being modified. */ + for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(i=0; i<p->nCol; i++){ + int iChildKey = p->aCol[i].iFrom; + if( aChange[iChildKey]>=0 ) return 1; + if( iChildKey==pTab->iPKey && chngRowid ) return 1; + } + } + + /* Check if any parent key columns are being modified. */ + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + for(i=0; i<p->nCol; i++){ + char *zKey = p->aCol[i].zCol; + int iKey; + for(iKey=0; iKey<pTab->nCol; iKey++){ + Column *pCol = &pTab->aCol[iKey]; + if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){ + if( aChange[iKey]>=0 ) return 1; + if( iKey==pTab->iPKey && chngRowid ) return 1; + } + } + } + } + } } return 0; } diff --git a/src/insert.c b/src/insert.c index d71407420..94b741af9 100644 --- a/src/insert.c +++ b/src/insert.c @@ -979,7 +979,7 @@ void sqlite3Insert( sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx, keyColumn>=0, 0, onError, endOfLoop, &isReplace ); - sqlite3FkCheck(pParse, pTab, 0, 0, regIns); + sqlite3FkCheck(pParse, pTab, 0, regIns); sqlite3CompleteInsertion( pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0 ); @@ -1271,7 +1271,7 @@ void sqlite3GenerateConstraintChecks( pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); } sqlite3MultiWrite(pParse); - if( pTrigger || sqlite3FkRequired(pParse, pTab, 0) ){ + if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ sqlite3GenerateRowDelete( pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace ); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 2ae1ec808..f97a81ca6 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2949,18 +2949,18 @@ VTable *sqlite3GetVTable(sqlite3*, Table*); ** provided (enforcement of FK constraints requires the triggers sub-system). */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) - void sqlite3FkCheck(Parse*, Table*, ExprList*, int, int); + void sqlite3FkCheck(Parse*, Table*, int, int); void sqlite3FkDropTable(Parse*, SrcList *, Table*); void sqlite3FkActions(Parse*, Table*, ExprList*, int); - int sqlite3FkRequired(Parse*, Table*, ExprList*); - u32 sqlite3FkOldmask(Parse*, Table*, ExprList*); + int sqlite3FkRequired(Parse*, Table*, int*, int); + u32 sqlite3FkOldmask(Parse*, Table*); FKey *sqlite3FkReferences(Table *); #else #define sqlite3FkActions(a,b,c,d) - #define sqlite3FkCheck(a,b,c,d,e) + #define sqlite3FkCheck(a,b,c,d) #define sqlite3FkDropTable(a,b,c) - #define sqlite3FkOldmask(a,b,c) 0 - #define sqlite3FkRequired(a,b,c) 0 + #define sqlite3FkOldmask(a,b) 0 + #define sqlite3FkRequired(a,b,c,d) 0 #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite3FkDelete(Table*); diff --git a/src/update.c b/src/update.c index f443dbc49..3703c1b59 100644 --- a/src/update.c +++ b/src/update.c @@ -159,8 +159,6 @@ void sqlite3Update( # define isView 0 #endif - hasFK = sqlite3FkRequired(pParse, pTab, pChanges); - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto update_cleanup; } @@ -230,6 +228,8 @@ void sqlite3Update( #endif } + hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid); + /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used @@ -389,7 +389,7 @@ void sqlite3Update( /* If there are triggers on this table, populate an array of registers ** with the required old.* column data. */ if( hasFK || pTrigger ){ - u32 oldmask = sqlite3FkOldmask(pParse, pTab, pChanges); + u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); oldmask |= sqlite3TriggerOldmask(pParse, pTrigger, pChanges, pTab, onError); for(i=0; i<pTab->nCol; i++){ if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<<i)) ){ @@ -445,7 +445,9 @@ void sqlite3Update( aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); /* Do FK constraint checks. */ - sqlite3FkCheck(pParse, pTab, pChanges, regOldRowid, 0); + if( hasFK ){ + sqlite3FkCheck(pParse, pTab, regOldRowid, 0); + } /* Delete the index entries associated with the current record. */ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); @@ -457,7 +459,9 @@ void sqlite3Update( } sqlite3VdbeJumpHere(v, j1); - sqlite3FkCheck(pParse, pTab, pChanges, 0, regNewRowid); + if( hasFK ){ + sqlite3FkCheck(pParse, pTab, 0, regNewRowid); + } /* Insert the new index entries and the new record. */ sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0); @@ -465,7 +469,9 @@ void sqlite3Update( /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to ** handle rows (possibly in other tables) that refer via a foreign key ** to the row just updated. */ - sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); + if( hasFK ){ + sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); + } } /* Increment the row counter |