mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-22 20:22:44 +03:00
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
This commit is contained in:
@@ -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
|
||||
|
||||
58
src/fkey.c
58
src/fkey.c
@@ -587,6 +587,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
|
||||
|
||||
@@ -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*);
|
||||
|
||||
@@ -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)){
|
||||
|
||||
Reference in New Issue
Block a user