diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 1 | ||||
-rw-r--r-- | src/fkey.c | 58 | ||||
-rw-r--r-- | src/sqliteInt.h | 7 | ||||
-rw-r--r-- | src/trigger.c | 4 |
4 files changed, 68 insertions, 2 deletions
diff --git a/src/build.c b/src/build.c index 544014df2..215b3c0a9 100644 --- a/src/build.c +++ b/src/build.c @@ -2061,6 +2061,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ sqlite3VdbeAddOp0(v, OP_VBegin); } #endif + sqlite3FkDropTable(pParse, pName, pTab); /* Drop all triggers associated with the table being dropped. Code ** is generated to remove entries from sqlite_master and/or diff --git a/src/fkey.c b/src/fkey.c index 804d1ed97..706e054e2 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -588,6 +588,64 @@ static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ } /* +** This function is called to generate code that runs when table pTab is +** being dropped from the database. The SrcList passed as the second argument +** to this function contains a single entry guaranteed to resolve to +** table pTab. +** +** Normally, no code is required. However, if either +** +** (a) The table is the parent table of a FK constraint, or +** (b) The table is the child table of a deferred FK constraint and it is +** determined at runtime that there are outstanding deferred FK +** constraint violations in the database, +** +** then the equivalent of "DELETE FROM <tbl>" is executed before dropping +** the table from the database. Triggers are disabled while running this +** DELETE, but foreign key actions are not. +*/ +void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ + sqlite3 *db = pParse->db; + if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) && !pTab->pSelect ){ + int iSkip = 0; + Vdbe *v = sqlite3GetVdbe(pParse); + + assert( v ); /* VDBE has already been allocated */ + if( sqlite3FkReferences(pTab)==0 ){ + /* Search for a deferred foreign key constraint for which this table + ** is the child table. If one cannot be found, return without + ** generating any VDBE code. If one can be found, then jump over + ** the entire DELETE if there are no outstanding deferred constraints + ** when this statement is run. */ + FKey *p; + for(p=pTab->pFKey; p; p=p->pNextFrom){ + if( p->isDeferred ) break; + } + if( !p ) return; + iSkip = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); + } + + pParse->disableTriggers = 1; + sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0); + pParse->disableTriggers = 0; + + /* If the DELETE has generated immediate foreign key constraint + ** violations, halt the VDBE and return an error at this point, before + ** any modifications to the schema are made. This is because statement + ** transactions are not able to rollback schema changes. */ + sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); + sqlite3HaltConstraint( + pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + ); + + if( iSkip ){ + sqlite3VdbeResolveLabel(v, iSkip); + } + } +} + +/* ** 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. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 60414f762..2ae1ec808 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2122,6 +2122,7 @@ struct Parse { u32 oldmask; /* Mask of old.* columns referenced */ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ + u8 disableTriggers; /* True to disable triggers */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ @@ -2949,15 +2950,17 @@ VTable *sqlite3GetVTable(sqlite3*, Table*); */ #if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) void sqlite3FkCheck(Parse*, Table*, ExprList*, int, int); + void sqlite3FkDropTable(Parse*, SrcList *, Table*); void sqlite3FkActions(Parse*, Table*, ExprList*, int); int sqlite3FkRequired(Parse*, Table*, ExprList*); u32 sqlite3FkOldmask(Parse*, Table*, ExprList*); FKey *sqlite3FkReferences(Table *); #else - #define sqlite3FkCheck(a,b,c,d,e) #define sqlite3FkActions(a,b,c,d) - #define sqlite3FkRequired(a,b,c) 0 + #define sqlite3FkCheck(a,b,c,d,e) + #define sqlite3FkDropTable(a,b,c) #define sqlite3FkOldmask(a,b,c) 0 + #define sqlite3FkRequired(a,b,c) 0 #endif #ifndef SQLITE_OMIT_FOREIGN_KEY void sqlite3FkDelete(Table*); diff --git a/src/trigger.c b/src/trigger.c index dfe8e9a17..319f9bc70 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -50,6 +50,10 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; Trigger *pList = 0; /* List of triggers to return */ + if( pParse->disableTriggers ){ + return 0; + } + if( pTmpSchema!=pTab->pSchema ){ HashElem *p; for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ |