mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Fix event triggers for partitioned tables
Index DDL cascading on partitioned tables introduced a way for ALTER TABLE to be called reentrantly. This caused an an important deficiency in event trigger support to be exposed: on exiting the reentrant call, the alter table state object was clobbered, causing a crash when the outer alter table tries to finalize its processing. Fix the crash by creating a stack of event trigger state objects. There are still ways to cause things to misbehave (and probably other crashers) with more elaborate tricks, but at least it now doesn't crash in the obvious scenario. Backpatch to 9.5, where DDL deparsing of event triggers was introduced. Reported-by: Marco Slot Authors: Michaël Paquier, Álvaro Herrera Discussion: https://postgr.es/m/CANNhMLCpi+HQ7M36uPfGbJZEQLyTy7XvX=5EFkpR-b1bo0uJew@mail.gmail.com
This commit is contained in:
@ -48,6 +48,7 @@
|
|||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "catalog/storage.h"
|
#include "catalog/storage.h"
|
||||||
#include "commands/tablecmds.h"
|
#include "commands/tablecmds.h"
|
||||||
|
#include "commands/event_trigger.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
@ -191,7 +192,8 @@ relationHasPrimaryKey(Relation rel)
|
|||||||
void
|
void
|
||||||
index_check_primary_key(Relation heapRel,
|
index_check_primary_key(Relation heapRel,
|
||||||
IndexInfo *indexInfo,
|
IndexInfo *indexInfo,
|
||||||
bool is_alter_table)
|
bool is_alter_table,
|
||||||
|
IndexStmt *stmt)
|
||||||
{
|
{
|
||||||
List *cmds;
|
List *cmds;
|
||||||
int i;
|
int i;
|
||||||
@ -263,7 +265,11 @@ index_check_primary_key(Relation heapRel,
|
|||||||
* unduly.
|
* unduly.
|
||||||
*/
|
*/
|
||||||
if (cmds)
|
if (cmds)
|
||||||
|
{
|
||||||
|
EventTriggerAlterTableStart((Node *) stmt);
|
||||||
AlterTableInternal(RelationGetRelid(heapRel), cmds, false);
|
AlterTableInternal(RelationGetRelid(heapRel), cmds, false);
|
||||||
|
EventTriggerAlterTableEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1699,11 +1699,6 @@ EventTriggerCollectSimpleCommand(ObjectAddress address,
|
|||||||
* Note we don't collect the command immediately; instead we keep it in
|
* Note we don't collect the command immediately; instead we keep it in
|
||||||
* currentCommand, and only when we're done processing the subcommands we will
|
* currentCommand, and only when we're done processing the subcommands we will
|
||||||
* add it to the command list.
|
* add it to the command list.
|
||||||
*
|
|
||||||
* XXX -- this API isn't considering the possibility of an ALTER TABLE command
|
|
||||||
* being called reentrantly by an event trigger function. Do we need stackable
|
|
||||||
* commands at this level? Perhaps at least we should detect the condition and
|
|
||||||
* raise an error.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
EventTriggerAlterTableStart(Node *parsetree)
|
EventTriggerAlterTableStart(Node *parsetree)
|
||||||
@ -1728,6 +1723,7 @@ EventTriggerAlterTableStart(Node *parsetree)
|
|||||||
command->d.alterTable.subcmds = NIL;
|
command->d.alterTable.subcmds = NIL;
|
||||||
command->parsetree = copyObject(parsetree);
|
command->parsetree = copyObject(parsetree);
|
||||||
|
|
||||||
|
command->parent = currentEventTriggerState->currentCommand;
|
||||||
currentEventTriggerState->currentCommand = command;
|
currentEventTriggerState->currentCommand = command;
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcxt);
|
MemoryContextSwitchTo(oldcxt);
|
||||||
@ -1768,6 +1764,7 @@ EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Assert(IsA(subcmd, AlterTableCmd));
|
Assert(IsA(subcmd, AlterTableCmd));
|
||||||
|
Assert(OidIsValid(currentEventTriggerState->currentCommand));
|
||||||
Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
|
Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
|
||||||
|
|
||||||
oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
|
oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
|
||||||
@ -1793,11 +1790,15 @@ EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
|
|||||||
void
|
void
|
||||||
EventTriggerAlterTableEnd(void)
|
EventTriggerAlterTableEnd(void)
|
||||||
{
|
{
|
||||||
|
CollectedCommand *parent;
|
||||||
|
|
||||||
/* ignore if event trigger context not set, or collection disabled */
|
/* ignore if event trigger context not set, or collection disabled */
|
||||||
if (!currentEventTriggerState ||
|
if (!currentEventTriggerState ||
|
||||||
currentEventTriggerState->commandCollectionInhibited)
|
currentEventTriggerState->commandCollectionInhibited)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
parent = currentEventTriggerState->currentCommand->parent;
|
||||||
|
|
||||||
/* If no subcommands, don't collect */
|
/* If no subcommands, don't collect */
|
||||||
if (list_length(currentEventTriggerState->currentCommand->d.alterTable.subcmds) != 0)
|
if (list_length(currentEventTriggerState->currentCommand->d.alterTable.subcmds) != 0)
|
||||||
{
|
{
|
||||||
@ -1808,7 +1809,7 @@ EventTriggerAlterTableEnd(void)
|
|||||||
else
|
else
|
||||||
pfree(currentEventTriggerState->currentCommand);
|
pfree(currentEventTriggerState->currentCommand);
|
||||||
|
|
||||||
currentEventTriggerState->currentCommand = NULL;
|
currentEventTriggerState->currentCommand = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "commands/comment.h"
|
#include "commands/comment.h"
|
||||||
#include "commands/dbcommands.h"
|
#include "commands/dbcommands.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
|
#include "commands/event_trigger.h"
|
||||||
#include "commands/tablecmds.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/tablespace.h"
|
#include "commands/tablespace.h"
|
||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
@ -575,7 +576,7 @@ DefineIndex(Oid relationId,
|
|||||||
* Extra checks when creating a PRIMARY KEY index.
|
* Extra checks when creating a PRIMARY KEY index.
|
||||||
*/
|
*/
|
||||||
if (stmt->primary)
|
if (stmt->primary)
|
||||||
index_check_primary_key(rel, indexInfo, is_alter_table);
|
index_check_primary_key(rel, indexInfo, is_alter_table, stmt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We disallow indexes on system columns other than OID. They would not
|
* We disallow indexes on system columns other than OID. They would not
|
||||||
|
@ -6007,7 +6007,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
|
|||||||
|
|
||||||
/* Extra checks needed if making primary key */
|
/* Extra checks needed if making primary key */
|
||||||
if (stmt->primary)
|
if (stmt->primary)
|
||||||
index_check_primary_key(rel, indexInfo, true);
|
index_check_primary_key(rel, indexInfo, true, stmt);
|
||||||
|
|
||||||
/* Note we currently don't support EXCLUSION constraints here */
|
/* Note we currently don't support EXCLUSION constraints here */
|
||||||
if (stmt->primary)
|
if (stmt->primary)
|
||||||
|
@ -61,6 +61,8 @@ validateWithCheckOption(char *value)
|
|||||||
*
|
*
|
||||||
* Create a view relation and use the rules system to store the query
|
* Create a view relation and use the rules system to store the query
|
||||||
* for the view.
|
* for the view.
|
||||||
|
*
|
||||||
|
* EventTriggerAlterTableStart must have been called already.
|
||||||
*---------------------------------------------------------------------
|
*---------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
static ObjectAddress
|
static ObjectAddress
|
||||||
@ -186,6 +188,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
|
|||||||
atcmds = lappend(atcmds, atcmd);
|
atcmds = lappend(atcmds, atcmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* EventTriggerAlterTableStart called by ProcessUtilitySlow */
|
||||||
AlterTableInternal(viewOid, atcmds, true);
|
AlterTableInternal(viewOid, atcmds, true);
|
||||||
|
|
||||||
/* Make the new view columns visible */
|
/* Make the new view columns visible */
|
||||||
@ -217,6 +220,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
|
|||||||
atcmd->def = (Node *) options;
|
atcmd->def = (Node *) options;
|
||||||
atcmds = list_make1(atcmd);
|
atcmds = list_make1(atcmd);
|
||||||
|
|
||||||
|
/* EventTriggerAlterTableStart called by ProcessUtilitySlow */
|
||||||
AlterTableInternal(viewOid, atcmds, true);
|
AlterTableInternal(viewOid, atcmds, true);
|
||||||
|
|
||||||
ObjectAddressSet(address, RelationRelationId, viewOid);
|
ObjectAddressSet(address, RelationRelationId, viewOid);
|
||||||
|
@ -40,7 +40,8 @@ typedef enum
|
|||||||
|
|
||||||
extern void index_check_primary_key(Relation heapRel,
|
extern void index_check_primary_key(Relation heapRel,
|
||||||
IndexInfo *indexInfo,
|
IndexInfo *indexInfo,
|
||||||
bool is_alter_table);
|
bool is_alter_table,
|
||||||
|
IndexStmt *stmt);
|
||||||
|
|
||||||
extern Oid index_create(Relation heapRelation,
|
extern Oid index_create(Relation heapRelation,
|
||||||
const char *indexRelationName,
|
const char *indexRelationName,
|
||||||
|
@ -44,6 +44,7 @@ typedef struct CollectedATSubcmd
|
|||||||
typedef struct CollectedCommand
|
typedef struct CollectedCommand
|
||||||
{
|
{
|
||||||
CollectedCommandType type;
|
CollectedCommandType type;
|
||||||
|
|
||||||
bool in_extension;
|
bool in_extension;
|
||||||
Node *parsetree;
|
Node *parsetree;
|
||||||
|
|
||||||
@ -100,6 +101,8 @@ typedef struct CollectedCommand
|
|||||||
GrantObjectType objtype;
|
GrantObjectType objtype;
|
||||||
} defprivs;
|
} defprivs;
|
||||||
} d;
|
} d;
|
||||||
|
|
||||||
|
struct CollectedCommand *parent; /* when nested */
|
||||||
} CollectedCommand;
|
} CollectedCommand;
|
||||||
|
|
||||||
#endif /* DEPARSE_UTILITY_H */
|
#endif /* DEPARSE_UTILITY_H */
|
||||||
|
Reference in New Issue
Block a user