aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/delete.c8
-rw-r--r--src/fkey.c74
-rw-r--r--src/insert.c4
-rw-r--r--src/sqliteInt.h12
-rw-r--r--src/update.c18
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