aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2018-10-06 19:17:46 -0300
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2018-10-06 19:17:46 -0300
commit1a852f7c1e27eef563cf0577c2604baec1be5512 (patch)
treeda947a40a7bf7704f570cde0ec51bbc6d41eb998 /src/backend/commands
parent6bf278df891d7601eda2824a8b0df15dc52b7def (diff)
downloadpostgresql-1a852f7c1e27eef563cf0577c2604baec1be5512.tar.gz
postgresql-1a852f7c1e27eef563cf0577c2604baec1be5512.zip
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
Diffstat (limited to 'src/backend/commands')
-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
4 files changed, 14 insertions, 8 deletions
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index eecc85d14e5..2c1dc47541c 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -1696,11 +1696,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)
@@ -1725,6 +1720,7 @@ EventTriggerAlterTableStart(Node *parsetree)
command->d.alterTable.subcmds = NIL;
command->parsetree = copyObject(parsetree);
+ command->parent = currentEventTriggerState->currentCommand;
currentEventTriggerState->currentCommand = command;
MemoryContextSwitchTo(oldcxt);
@@ -1765,6 +1761,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);
@@ -1790,11 +1787,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)
{
@@ -1805,7 +1806,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 ab3d9a0a489..3975f62c001 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -34,6 +34,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"
@@ -666,7 +667,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);
/*
* If this table is partitioned and we're creating a unique index or a
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 61fefe1d7ca..165db2044a8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -7045,7 +7045,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 ffb71c0ea7c..2f0ba12d222 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -61,6 +61,8 @@ validateWithCheckOption(const 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);