diff options
author | dan <dan@noemail.net> | 2009-09-28 14:49:01 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2009-09-28 14:49:01 +0000 |
commit | d66c83095edcfb0f26e7ddf24f28455048d02b12 (patch) | |
tree | afb321fdcb5fb10572e6b895a621ca79b8166b19 /src/fkey.c | |
parent | 9277efa3d576ab243077404bb5966f9a67a73004 (diff) | |
download | sqlite-d66c83095edcfb0f26e7ddf24f28455048d02b12.tar.gz sqlite-d66c83095edcfb0f26e7ddf24f28455048d02b12.zip |
Fix the DROP TABLE command so that it cannot be used to bypass foreign key constraints (if foreign key support is enabled).
FossilOrigin-Name: 8353808c9e70412fdee31bfda7810e948f1c7485
Diffstat (limited to 'src/fkey.c')
-rw-r--r-- | src/fkey.c | 58 |
1 files changed, 58 insertions, 0 deletions
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. |