summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlvaro Herrera2018-10-06 22:17:46 +0000
committerAlvaro Herrera2018-10-06 22:17:46 +0000
commita2a5159ed678f0e4a0efa7e8aabbd4c03eb36843 (patch)
tree905a786eb1a0d3bde6a1808a6727a2a297bfe10f
parent3c9dd963cec6d4c314bccf74256acc893108a4be (diff)
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
-rw-r--r--src/backend/catalog/index.c8
-rw-r--r--src/backend/commands/event_trigger.c13
-rw-r--r--src/backend/commands/indexcmds.c3
-rw-r--r--src/backend/commands/tablecmds.c2
-rw-r--r--src/backend/commands/view.c4
-rw-r--r--src/include/catalog/index.h3
-rw-r--r--src/include/tcop/deparse_utility.h3
7 files changed, 26 insertions, 10 deletions
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 18f5ca246e1..b8c1daf09a4 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -45,6 +45,7 @@
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "commands/tablecmds.h"
+#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "miscadmin.h"
@@ -186,7 +187,8 @@ relationHasPrimaryKey(Relation rel)
void
index_check_primary_key(Relation heapRel,
IndexInfo *indexInfo,
- bool is_alter_table)
+ bool is_alter_table,
+ IndexStmt *stmt)
{
List *cmds;
int i;
@@ -258,7 +260,11 @@ index_check_primary_key(Relation heapRel,
* unduly.
*/
if (cmds)
+ {
+ EventTriggerAlterTableStart((Node *) stmt);
AlterTableInternal(RelationGetRelid(heapRel), cmds, false);
+ EventTriggerAlterTableEnd();
+ }
}
/*
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 6c3aad58d74..4a61044858a 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1700,11 +1700,6 @@ EventTriggerCollectSimpleCommand(ObjectAddress address,
* 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
* 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
EventTriggerAlterTableStart(Node *parsetree)
@@ -1729,6 +1724,7 @@ EventTriggerAlterTableStart(Node *parsetree)
command->d.alterTable.subcmds = NIL;
command->parsetree = copyObject(parsetree);
+ command->parent = currentEventTriggerState->currentCommand;
currentEventTriggerState->currentCommand = command;
MemoryContextSwitchTo(oldcxt);
@@ -1769,6 +1765,7 @@ EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
return;
Assert(IsA(subcmd, AlterTableCmd));
+ Assert(OidIsValid(currentEventTriggerState->currentCommand));
Assert(OidIsValid(currentEventTriggerState->currentCommand->d.alterTable.objectId));
oldcxt = MemoryContextSwitchTo(currentEventTriggerState->cxt);
@@ -1794,11 +1791,15 @@ EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
void
EventTriggerAlterTableEnd(void)
{
+ CollectedCommand *parent;
+
/* ignore if event trigger context not set, or collection disabled */
if (!currentEventTriggerState ||
currentEventTriggerState->commandCollectionInhibited)
return;
+ parent = currentEventTriggerState->currentCommand->parent;
+
/* If no subcommands, don't collect */
if (list_length(currentEventTriggerState->currentCommand->d.alterTable.subcmds) != 0)
{
@@ -1809,7 +1810,7 @@ EventTriggerAlterTableEnd(void)
else
pfree(currentEventTriggerState->currentCommand);
- currentEventTriggerState->currentCommand = NULL;
+ currentEventTriggerState->currentCommand = parent;
}
/*
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 6355dcfbf15..f4fd3c128e4 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -28,6 +28,7 @@
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
+#include "commands/event_trigger.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "mb/pg_wchar.h"
@@ -565,7 +566,7 @@ DefineIndex(Oid relationId,
* Extra checks when creating a PRIMARY KEY index.
*/
if (stmt->primary)
- index_check_primary_key(rel, indexInfo, is_alter_table);
+ index_check_primary_key(rel, indexInfo, is_alter_table, stmt);
/*
* Report index creation if appropriate (delay this till after most of the
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a36458bf024..bc64794b66a 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -5977,7 +5977,7 @@ ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
/* Extra checks needed if making primary key */
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 */
if (stmt->primary)
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index 0caa39b7466..ae73e2d934b 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -61,6 +61,8 @@ validateWithCheckOption(char *value)
*
* Create a view relation and use the rules system to store the query
* for the view.
+ *
+ * EventTriggerAlterTableStart must have been called already.
*---------------------------------------------------------------------
*/
static ObjectAddress
@@ -186,6 +188,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
atcmds = lappend(atcmds, atcmd);
}
+ /* EventTriggerAlterTableStart called by ProcessUtilitySlow */
AlterTableInternal(viewOid, atcmds, true);
/* Make the new view columns visible */
@@ -217,6 +220,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
atcmd->def = (Node *) options;
atcmds = list_make1(atcmd);
+ /* EventTriggerAlterTableStart called by ProcessUtilitySlow */
AlterTableInternal(viewOid, atcmds, true);
ObjectAddressSet(address, RelationRelationId, viewOid);
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index a29174b6905..f982489168e 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -40,7 +40,8 @@ typedef enum
extern void index_check_primary_key(Relation heapRel,
IndexInfo *indexInfo,
- bool is_alter_table);
+ bool is_alter_table,
+ IndexStmt *stmt);
extern Oid index_create(Relation heapRelation,
const char *indexRelationName,
diff --git a/src/include/tcop/deparse_utility.h b/src/include/tcop/deparse_utility.h
index d276eeb2287..8dd7bd61983 100644
--- a/src/include/tcop/deparse_utility.h
+++ b/src/include/tcop/deparse_utility.h
@@ -44,6 +44,7 @@ typedef struct CollectedATSubcmd
typedef struct CollectedCommand
{
CollectedCommandType type;
+
bool in_extension;
Node *parsetree;
@@ -100,6 +101,8 @@ typedef struct CollectedCommand
GrantObjectType objtype;
} defprivs;
} d;
+
+ struct CollectedCommand *parent; /* when nested */
} CollectedCommand;
#endif /* DEPARSE_UTILITY_H */