aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2013-04-27 23:11:28 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2013-04-27 23:11:51 -0400
commit5525e6c40bbda351a19b48317eba0f79aa32e447 (patch)
treedabf59d3903d4fac0db7295af5ed35e2b0f5c6dc /src
parentbbb4db4e04d4691d7bc42fa19995aee3e80fe2dc (diff)
downloadpostgresql-5525e6c40bbda351a19b48317eba0f79aa32e447.tar.gz
postgresql-5525e6c40bbda351a19b48317eba0f79aa32e447.zip
Fix unsafe event-trigger coding in ProcessUtility().
We mustn't run any of the event-trigger support code when handling utility statements like START TRANSACTION or ABORT, because that code may need to refresh event-trigger cache data, which requires being inside a valid transaction. (This mistake explains the consistent build failures exhibited by the CLOBBER_CACHE_ALWAYS buildfarm members, as well as some irreproducible failures on other members.) The least messy fix seems to be to break standard_ProcessUtility into two functions, one that handles all the statements not supported by event triggers, and one that contains the event-trigger support code and handles the statements that are supported by event triggers. This change also fixes several inconsistencies, such as four cases where support had been installed for "ddl_event_start" but not "ddl_event_end" triggers, plus the fact that InvokeDDLCommandEventTriggersIfSupported() paid no mind to isCompleteQuery. Dimitri Fontaine and Tom Lane
Diffstat (limited to 'src')
-rw-r--r--src/backend/tcop/utility.c1195
1 files changed, 569 insertions, 626 deletions
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 954040cfb85..2710db12321 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -68,6 +68,15 @@
/* Hook for plugins to get control in ProcessUtility() */
ProcessUtility_hook_type ProcessUtility_hook = NULL;
+/* local function declarations */
+static void ProcessUtilitySlow(Node *parsetree,
+ const char *queryString,
+ ParamListInfo params,
+ DestReceiver *dest,
+ char *completionTag,
+ ProcessUtilityContext context);
+static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
+
/*
* Verify user has ownership of specified relation, else ereport.
@@ -342,70 +351,17 @@ ProcessUtility(Node *parsetree,
dest, completionTag, context);
}
-#define InvokeDDLCommandEventTriggers(parsetree, fncall) \
- do { \
- if (isCompleteQuery) \
- { \
- EventTriggerDDLCommandStart(parsetree); \
- } \
- fncall; \
- if (isCompleteQuery) \
- { \
- EventTriggerSQLDrop(parsetree); \
- EventTriggerDDLCommandEnd(parsetree); \
- } \
- } while (0)
-
-#define InvokeDDLCommandEventTriggersIfSupported(parsetree, fncall, objtype) \
- do { \
- bool _supported = EventTriggerSupportsObjectType(objtype); \
- \
- if (_supported) \
- { \
- EventTriggerDDLCommandStart(parsetree); \
- } \
- fncall; \
- if (_supported) \
- { \
- EventTriggerSQLDrop(parsetree); \
- EventTriggerDDLCommandEnd(parsetree); \
- } \
- } while (0)
-
/*
- * UTILITY_BEGIN_QUERY and UTILITY_END_QUERY are a pair of macros to enclose
- * execution of a single DDL command, to ensure the event trigger environment
- * is appropriately set up before starting, and tore down after completion or
- * error.
+ * standard_ProcessUtility itself deals only with utility commands for
+ * which we do not provide event trigger support. Commands that do have
+ * such support are passed down to ProcessUtilitySlow, which contains the
+ * necessary infrastructure for such triggers.
+ *
+ * This division is not just for performance: it's critical that the
+ * event trigger code not be invoked when doing START TRANSACTION for
+ * example, because we might need to refresh the event trigger cache,
+ * which requires being in a valid transaction.
*/
-#define UTILITY_BEGIN_QUERY(isComplete) \
- do { \
- bool _needCleanup; \
- \
- _needCleanup = (isComplete) && EventTriggerBeginCompleteQuery(); \
- \
- PG_TRY(); \
- { \
- /* avoid empty statement when followed by a semicolon */ \
- (void) 0
-
-#define UTILITY_END_QUERY() \
- } \
- PG_CATCH(); \
- { \
- if (_needCleanup) \
- { \
- EventTriggerEndCompleteQuery(); \
- } \
- PG_RE_THROW(); \
- } \
- PG_END_TRY(); \
- if (_needCleanup) \
- { \
- EventTriggerEndCompleteQuery(); \
- } \
- } while (0)
-
void
standard_ProcessUtility(Node *parsetree,
const char *queryString,
@@ -415,15 +371,12 @@ standard_ProcessUtility(Node *parsetree,
ProcessUtilityContext context)
{
bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
- bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
check_xact_readonly(parsetree);
if (completionTag)
completionTag[0] = '\0';
- UTILITY_BEGIN_QUERY(isCompleteQuery);
-
switch (nodeTag(parsetree))
{
/*
@@ -571,93 +524,8 @@ standard_ProcessUtility(Node *parsetree,
completionTag);
break;
- /*
- * relation and attribute manipulation
- */
- case T_CreateSchemaStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateSchemaCommand((CreateSchemaStmt *) parsetree,
- queryString));
- break;
-
- case T_CreateStmt:
- case T_CreateForeignTableStmt:
- {
- List *stmts;
- ListCell *l;
- Oid relOid;
-
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
-
- /* Run parse analysis ... */
- stmts = transformCreateStmt((CreateStmt *) parsetree,
- queryString);
-
- /* ... and do it */
- foreach(l, stmts)
- {
- Node *stmt = (Node *) lfirst(l);
-
- if (IsA(stmt, CreateStmt))
- {
- Datum toast_options;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
-
- /* Create the table itself */
- relOid = DefineRelation((CreateStmt *) stmt,
- RELKIND_RELATION,
- InvalidOid);
-
- /*
- * Let AlterTableCreateToastTable decide if this one
- * needs a secondary relation too.
- */
- CommandCounterIncrement();
-
- /* parse and validate reloptions for the toast table */
- toast_options = transformRelOptions((Datum) 0,
- ((CreateStmt *) stmt)->options,
- "toast",
- validnsps,
- true, false);
- (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options,
- true);
-
- AlterTableCreateToastTable(relOid, toast_options);
- }
- else if (IsA(stmt, CreateForeignTableStmt))
- {
- /* Create the table itself */
- relOid = DefineRelation((CreateStmt *) stmt,
- RELKIND_FOREIGN_TABLE,
- InvalidOid);
- CreateForeignTable((CreateForeignTableStmt *) stmt,
- relOid);
- }
- else
- {
- /* Recurse for anything else */
- ProcessUtility(stmt,
- queryString,
- params,
- None_Receiver,
- NULL,
- PROCESS_UTILITY_GENERATED);
- }
-
- /* Need CCI between commands */
- if (lnext(l) != NULL)
- CommandCounterIncrement();
- }
-
- if (isCompleteQuery)
- {
- EventTriggerSQLDrop(parsetree);
- EventTriggerDDLCommandEnd(parsetree);
- }
- }
+ case T_DoStmt:
+ ExecuteDoStmt((DoStmt *) parsetree);
break;
case T_CreateTableSpaceStmt:
@@ -677,104 +545,6 @@ standard_ProcessUtility(Node *parsetree,
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
break;
- case T_CreateExtensionStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateExtension((CreateExtensionStmt *) parsetree));
- break;
-
- case T_AlterExtensionStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree));
- break;
-
- case T_AlterExtensionContentsStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree));
- break;
-
- case T_CreateFdwStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateForeignDataWrapper((CreateFdwStmt *) parsetree));
- break;
-
- case T_AlterFdwStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterForeignDataWrapper((AlterFdwStmt *) parsetree));
- break;
-
- case T_CreateForeignServerStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateForeignServer((CreateForeignServerStmt *) parsetree));
- break;
-
- case T_AlterForeignServerStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterForeignServer((AlterForeignServerStmt *) parsetree));
- break;
-
- case T_CreateUserMappingStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateUserMapping((CreateUserMappingStmt *) parsetree));
- break;
-
- case T_AlterUserMappingStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterUserMapping((AlterUserMappingStmt *) parsetree));
- break;
-
- case T_DropUserMappingStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- RemoveUserMapping((DropUserMappingStmt *) parsetree));
- break;
-
- case T_DropStmt:
- {
- DropStmt *stmt = (DropStmt *) parsetree;
-
- if (isCompleteQuery
- && EventTriggerSupportsObjectType(stmt->removeType))
- EventTriggerDDLCommandStart(parsetree);
-
- switch (stmt->removeType)
- {
- case OBJECT_INDEX:
- if (stmt->concurrent)
- PreventTransactionChain(isTopLevel,
- "DROP INDEX CONCURRENTLY");
- /* fall through */
-
- case OBJECT_TABLE:
- case OBJECT_SEQUENCE:
- case OBJECT_VIEW:
- case OBJECT_MATVIEW:
- case OBJECT_FOREIGN_TABLE:
- RemoveRelations((DropStmt *) parsetree);
- break;
- default:
- RemoveObjects((DropStmt *) parsetree);
- break;
- }
-
- if (isCompleteQuery
- && EventTriggerSupportsObjectType(stmt->removeType))
- {
- EventTriggerSQLDrop(parsetree);
- EventTriggerDDLCommandEnd(parsetree);
- }
-
- break;
- }
-
case T_TruncateStmt:
ExecuteTruncate((TruncateStmt *) parsetree);
break;
@@ -814,312 +584,16 @@ standard_ProcessUtility(Node *parsetree,
DeallocateQuery((DeallocateStmt *) parsetree);
break;
- /*
- * schema
- */
- case T_RenameStmt:
- {
- RenameStmt *stmt = (RenameStmt *) parsetree;
-
- InvokeDDLCommandEventTriggersIfSupported(parsetree,
- ExecRenameStmt(stmt),
- stmt->renameType);
- break;
- }
-
- case T_AlterObjectSchemaStmt:
- {
- AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
- InvokeDDLCommandEventTriggersIfSupported(parsetree,
- ExecAlterObjectSchemaStmt(stmt),
- stmt->objectType);
- break;
- }
-
- case T_AlterOwnerStmt:
- {
- AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
- InvokeDDLCommandEventTriggersIfSupported(parsetree,
- ExecAlterOwnerStmt(stmt),
- stmt->objectType);
- break;
- }
-
- case T_AlterTableStmt:
- {
- AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
- Oid relid;
- List *stmts;
- ListCell *l;
- LOCKMODE lockmode;
-
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
-
- /*
- * Figure out lock mode, and acquire lock. This also does
- * basic permissions checks, so that we won't wait for a lock
- * on (for example) a relation on which we have no
- * permissions.
- */
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
-
- if (OidIsValid(relid))
- {
- /* Run parse analysis ... */
- stmts = transformAlterTableStmt(atstmt, queryString);
-
- /* ... and do it */
- foreach(l, stmts)
- {
- Node *stmt = (Node *) lfirst(l);
-
- if (IsA(stmt, AlterTableStmt))
- {
- /* Do the table alteration proper */
- AlterTable(relid, lockmode, (AlterTableStmt *) stmt);
- }
- else
- {
- /* Recurse for anything else */
- ProcessUtility(stmt,
- queryString,
- params,
- None_Receiver,
- NULL,
- PROCESS_UTILITY_GENERATED);
- }
-
- /* Need CCI between commands */
- if (lnext(l) != NULL)
- CommandCounterIncrement();
- }
- }
- else
- ereport(NOTICE,
- (errmsg("relation \"%s\" does not exist, skipping",
- atstmt->relation->relname)));
-
- if (isCompleteQuery)
- {
- EventTriggerSQLDrop(parsetree);
- EventTriggerDDLCommandEnd(parsetree);
- }
- }
- break;
-
- case T_AlterDomainStmt:
- {
- AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
-
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
-
- /*
- * Some or all of these functions are recursive to cover
- * inherited things, so permission checks are done there.
- */
- switch (stmt->subtype)
- {
- case 'T': /* ALTER DOMAIN DEFAULT */
-
- /*
- * Recursively alter column default for table and, if
- * requested, for descendants
- */
- AlterDomainDefault(stmt->typeName,
- stmt->def);
- break;
- case 'N': /* ALTER DOMAIN DROP NOT NULL */
- AlterDomainNotNull(stmt->typeName,
- false);
- break;
- case 'O': /* ALTER DOMAIN SET NOT NULL */
- AlterDomainNotNull(stmt->typeName,
- true);
- break;
- case 'C': /* ADD CONSTRAINT */
- AlterDomainAddConstraint(stmt->typeName,
- stmt->def);
- break;
- case 'X': /* DROP CONSTRAINT */
- AlterDomainDropConstraint(stmt->typeName,
- stmt->name,
- stmt->behavior,
- stmt->missing_ok);
- break;
- case 'V': /* VALIDATE CONSTRAINT */
- AlterDomainValidateConstraint(stmt->typeName,
- stmt->name);
- break;
- default: /* oops */
- elog(ERROR, "unrecognized alter domain type: %d",
- (int) stmt->subtype);
- break;
- }
- }
- break;
-
case T_GrantStmt:
+ /* no event triggers for global objects */
ExecuteGrantStmt((GrantStmt *) parsetree);
break;
case T_GrantRoleStmt:
+ /* no event triggers for global objects */
GrantRole((GrantRoleStmt *) parsetree);
break;
- case T_AlterDefaultPrivilegesStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree));
- break;
-
- /*
- * **************** object creation / destruction *****************
- */
- case T_DefineStmt:
- {
- DefineStmt *stmt = (DefineStmt *) parsetree;
-
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
-
- switch (stmt->kind)
- {
- case OBJECT_AGGREGATE:
- DefineAggregate(stmt->defnames, stmt->args,
- stmt->oldstyle, stmt->definition);
- break;
- case OBJECT_OPERATOR:
- Assert(stmt->args == NIL);
- DefineOperator(stmt->defnames, stmt->definition);
- break;
- case OBJECT_TYPE:
- Assert(stmt->args == NIL);
- DefineType(stmt->defnames, stmt->definition);
- break;
- case OBJECT_TSPARSER:
- Assert(stmt->args == NIL);
- DefineTSParser(stmt->defnames, stmt->definition);
- break;
- case OBJECT_TSDICTIONARY:
- Assert(stmt->args == NIL);
- DefineTSDictionary(stmt->defnames, stmt->definition);
- break;
- case OBJECT_TSTEMPLATE:
- Assert(stmt->args == NIL);
- DefineTSTemplate(stmt->defnames, stmt->definition);
- break;
- case OBJECT_TSCONFIGURATION:
- Assert(stmt->args == NIL);
- DefineTSConfiguration(stmt->defnames, stmt->definition);
- break;
- case OBJECT_COLLATION:
- Assert(stmt->args == NIL);
- DefineCollation(stmt->defnames, stmt->definition);
- break;
- default:
- elog(ERROR, "unrecognized define stmt type: %d",
- (int) stmt->kind);
- break;
- }
- }
- break;
-
- case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
- {
- CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
-
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineCompositeType(stmt->typevar, stmt->coldeflist));
- }
- break;
-
- case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineEnum((CreateEnumStmt *) parsetree));
- break;
-
- case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineRange((CreateRangeStmt *) parsetree));
- break;
-
- case T_AlterEnumStmt: /* ALTER TYPE (enum) */
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterEnum((AlterEnumStmt *) parsetree, isTopLevel));
- break;
-
- case T_ViewStmt: /* CREATE VIEW */
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineView((ViewStmt *) parsetree, queryString));
- break;
-
- case T_CreateFunctionStmt: /* CREATE FUNCTION */
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateFunction((CreateFunctionStmt *) parsetree, queryString));
- break;
-
- case T_AlterFunctionStmt: /* ALTER FUNCTION */
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterFunction((AlterFunctionStmt *) parsetree));
- break;
-
- case T_IndexStmt: /* CREATE INDEX */
- {
- IndexStmt *stmt = (IndexStmt *) parsetree;
-
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- if (stmt->concurrent)
- PreventTransactionChain(isTopLevel,
- "CREATE INDEX CONCURRENTLY");
-
- CheckRelationOwnership(stmt->relation, true);
-
- /* Run parse analysis ... */
- stmt = transformIndexStmt(stmt, queryString);
-
- /* ... and do it */
- DefineIndex(stmt,
- InvalidOid, /* no predefined OID */
- false, /* is_alter_table */
- true, /* check_rights */
- false, /* skip_build */
- false); /* quiet */
- }
- break;
-
- case T_RuleStmt: /* CREATE RULE */
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineRule((RuleStmt *) parsetree, queryString));
- break;
-
- case T_CreateSeqStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineSequence((CreateSeqStmt *) parsetree));
- break;
-
- case T_AlterSeqStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterSequence((AlterSeqStmt *) parsetree));
- break;
-
- case T_DoStmt:
- ExecuteDoStmt((DoStmt *) parsetree);
- break;
-
case T_CreatedbStmt:
/* no event triggers for global objects */
PreventTransactionChain(isTopLevel, "CREATE DATABASE");
@@ -1210,20 +684,6 @@ standard_ProcessUtility(Node *parsetree,
ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
break;
- case T_CreateTableAsStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- ExecCreateTableAs((CreateTableAsStmt *) parsetree,
- queryString, params, completionTag));
- break;
-
- case T_RefreshMatViewStmt:
- if (isCompleteQuery)
- EventTriggerDDLCommandStart(parsetree);
- ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
- queryString, params, completionTag);
- break;
-
case T_VariableSetStmt:
ExecSetVariableStmt((VariableSetStmt *) parsetree);
break;
@@ -1242,13 +702,6 @@ standard_ProcessUtility(Node *parsetree,
DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
break;
- case T_CreateTrigStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
- InvalidOid, InvalidOid, false));
- break;
-
case T_CreateEventTrigStmt:
/* no event triggers on event triggers */
CreateEventTrigger((CreateEventTrigStmt *) parsetree);
@@ -1259,21 +712,6 @@ standard_ProcessUtility(Node *parsetree,
AlterEventTrigger((AlterEventTrigStmt *) parsetree);
break;
- case T_CreatePLangStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateProceduralLanguage((CreatePLangStmt *) parsetree));
- break;
-
- /*
- * ******************************** DOMAIN statements ****
- */
- case T_CreateDomainStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineDomain((CreateDomainStmt *) parsetree));
- break;
-
/*
* ******************************** ROLE statements ****
*/
@@ -1297,12 +735,6 @@ standard_ProcessUtility(Node *parsetree,
DropRole((DropRoleStmt *) parsetree);
break;
- case T_DropOwnedStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- DropOwnedObjects((DropOwnedStmt *) parsetree));
- break;
-
case T_ReassignOwnedStmt:
/* no event triggers for global objects */
ReassignOwnedObjects((ReassignOwnedStmt *) parsetree);
@@ -1372,62 +804,573 @@ standard_ProcessUtility(Node *parsetree,
(int) stmt->kind);
break;
}
- break;
}
break;
- case T_CreateConversionStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateConversionCommand((CreateConversionStmt *) parsetree));
- break;
+ /*
+ * The following statements are supported by Event Triggers only
+ * in some cases, so we "fast path" them in the other cases.
+ */
- case T_CreateCastStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- CreateCast((CreateCastStmt *) parsetree));
- break;
+ case T_DropStmt:
+ {
+ DropStmt *stmt = (DropStmt *) parsetree;
- case T_CreateOpClassStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineOpClass((CreateOpClassStmt *) parsetree));
+ if (EventTriggerSupportsObjectType(stmt->removeType))
+ ProcessUtilitySlow(parsetree, queryString, params,
+ dest, completionTag, context);
+ else
+ ExecDropStmt(stmt, isTopLevel);
+ }
break;
- case T_CreateOpFamilyStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- DefineOpFamily((CreateOpFamilyStmt *) parsetree));
- break;
+ case T_RenameStmt:
+ {
+ RenameStmt *stmt = (RenameStmt *) parsetree;
- case T_AlterOpFamilyStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterOpFamily((AlterOpFamilyStmt *) parsetree));
+ if (EventTriggerSupportsObjectType(stmt->renameType))
+ ProcessUtilitySlow(parsetree, queryString, params,
+ dest, completionTag, context);
+ else
+ ExecRenameStmt(stmt);
+ }
break;
- case T_AlterTSDictionaryStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterTSDictionary((AlterTSDictionaryStmt *) parsetree));
+ case T_AlterObjectSchemaStmt:
+ {
+ AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
+
+ if (EventTriggerSupportsObjectType(stmt->objectType))
+ ProcessUtilitySlow(parsetree, queryString, params,
+ dest, completionTag, context);
+ else
+ ExecAlterObjectSchemaStmt(stmt);
+ }
break;
- case T_AlterTSConfigurationStmt:
- InvokeDDLCommandEventTriggers(
- parsetree,
- AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree));
+ case T_AlterOwnerStmt:
+ {
+ AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
+
+ if (EventTriggerSupportsObjectType(stmt->objectType))
+ ProcessUtilitySlow(parsetree, queryString, params,
+ dest, completionTag, context);
+ else
+ ExecAlterOwnerStmt(stmt);
+ }
break;
default:
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(parsetree));
+ /* All other statement types have event trigger support */
+ ProcessUtilitySlow(parsetree, queryString, params,
+ dest, completionTag, context);
break;
}
+}
+
+/*
+ * The "Slow" variant of ProcessUtility should only receive statements
+ * supported by the event triggers facility. Therefore, we always
+ * perform the trigger support calls if the context allows it.
+ */
+static void
+ProcessUtilitySlow(Node *parsetree,
+ const char *queryString,
+ ParamListInfo params,
+ DestReceiver *dest,
+ char *completionTag,
+ ProcessUtilityContext context)
+{
+ bool isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
+ bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
+ bool needCleanup;
+
+ /* All event trigger calls are done only when isCompleteQuery is true */
+ needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
+
+ /* PG_TRY block is to ensure we call EventTriggerEndCompleteQuery */
+ PG_TRY();
+ {
+ if (isCompleteQuery)
+ EventTriggerDDLCommandStart(parsetree);
+
+ switch (nodeTag(parsetree))
+ {
+ /*
+ * relation and attribute manipulation
+ */
+ case T_CreateSchemaStmt:
+ CreateSchemaCommand((CreateSchemaStmt *) parsetree,
+ queryString);
+ break;
+
+ case T_CreateStmt:
+ case T_CreateForeignTableStmt:
+ {
+ List *stmts;
+ ListCell *l;
+ Oid relOid;
+
+ /* Run parse analysis ... */
+ stmts = transformCreateStmt((CreateStmt *) parsetree,
+ queryString);
+
+ /* ... and do it */
+ foreach(l, stmts)
+ {
+ Node *stmt = (Node *) lfirst(l);
+
+ if (IsA(stmt, CreateStmt))
+ {
+ Datum toast_options;
+ static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+
+ /* Create the table itself */
+ relOid = DefineRelation((CreateStmt *) stmt,
+ RELKIND_RELATION,
+ InvalidOid);
+
+ /*
+ * Let AlterTableCreateToastTable decide if this
+ * one needs a secondary relation too.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * parse and validate reloptions for the toast
+ * table
+ */
+ toast_options = transformRelOptions((Datum) 0,
+ ((CreateStmt *) stmt)->options,
+ "toast",
+ validnsps,
+ true,
+ false);
+ (void) heap_reloptions(RELKIND_TOASTVALUE,
+ toast_options,
+ true);
+
+ AlterTableCreateToastTable(relOid, toast_options);
+ }
+ else if (IsA(stmt, CreateForeignTableStmt))
+ {
+ /* Create the table itself */
+ relOid = DefineRelation((CreateStmt *) stmt,
+ RELKIND_FOREIGN_TABLE,
+ InvalidOid);
+ CreateForeignTable((CreateForeignTableStmt *) stmt,
+ relOid);
+ }
+ else
+ {
+ /* Recurse for anything else */
+ ProcessUtility(stmt,
+ queryString,
+ params,
+ None_Receiver,
+ NULL,
+ PROCESS_UTILITY_GENERATED);
+ }
+
+ /* Need CCI between commands */
+ if (lnext(l) != NULL)
+ CommandCounterIncrement();
+ }
+ }
+ break;
+
+ case T_AlterTableStmt:
+ {
+ AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
+ Oid relid;
+ List *stmts;
+ ListCell *l;
+ LOCKMODE lockmode;
+
+ /*
+ * Figure out lock mode, and acquire lock. This also does
+ * basic permissions checks, so that we won't wait for a
+ * lock on (for example) a relation on which we have no
+ * permissions.
+ */
+ lockmode = AlterTableGetLockLevel(atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+
+ if (OidIsValid(relid))
+ {
+ /* Run parse analysis ... */
+ stmts = transformAlterTableStmt(atstmt, queryString);
+
+ /* ... and do it */
+ foreach(l, stmts)
+ {
+ Node *stmt = (Node *) lfirst(l);
+
+ if (IsA(stmt, AlterTableStmt))
+ {
+ /* Do the table alteration proper */
+ AlterTable(relid, lockmode,
+ (AlterTableStmt *) stmt);
+ }
+ else
+ {
+ /* Recurse for anything else */
+ ProcessUtility(stmt,
+ queryString,
+ params,
+ None_Receiver,
+ NULL,
+ PROCESS_UTILITY_GENERATED);
+ }
+
+ /* Need CCI between commands */
+ if (lnext(l) != NULL)
+ CommandCounterIncrement();
+ }
+ }
+ else
+ ereport(NOTICE,
+ (errmsg("relation \"%s\" does not exist, skipping",
+ atstmt->relation->relname)));
+ }
+ break;
+
+ case T_AlterDomainStmt:
+ {
+ AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
+
+ /*
+ * Some or all of these functions are recursive to cover
+ * inherited things, so permission checks are done there.
+ */
+ switch (stmt->subtype)
+ {
+ case 'T': /* ALTER DOMAIN DEFAULT */
+
+ /*
+ * Recursively alter column default for table and,
+ * if requested, for descendants
+ */
+ AlterDomainDefault(stmt->typeName,
+ stmt->def);
+ break;
+ case 'N': /* ALTER DOMAIN DROP NOT NULL */
+ AlterDomainNotNull(stmt->typeName,
+ false);
+ break;
+ case 'O': /* ALTER DOMAIN SET NOT NULL */
+ AlterDomainNotNull(stmt->typeName,
+ true);
+ break;
+ case 'C': /* ADD CONSTRAINT */
+ AlterDomainAddConstraint(stmt->typeName,
+ stmt->def);
+ break;
+ case 'X': /* DROP CONSTRAINT */
+ AlterDomainDropConstraint(stmt->typeName,
+ stmt->name,
+ stmt->behavior,
+ stmt->missing_ok);
+ break;
+ case 'V': /* VALIDATE CONSTRAINT */
+ AlterDomainValidateConstraint(stmt->typeName,
+ stmt->name);
+ break;
+ default: /* oops */
+ elog(ERROR, "unrecognized alter domain type: %d",
+ (int) stmt->subtype);
+ break;
+ }
+ }
+ break;
+
+ /*
+ * ************* object creation / destruction **************
+ */
+ case T_DefineStmt:
+ {
+ DefineStmt *stmt = (DefineStmt *) parsetree;
+
+ switch (stmt->kind)
+ {
+ case OBJECT_AGGREGATE:
+ DefineAggregate(stmt->defnames, stmt->args,
+ stmt->oldstyle, stmt->definition);
+ break;
+ case OBJECT_OPERATOR:
+ Assert(stmt->args == NIL);
+ DefineOperator(stmt->defnames, stmt->definition);
+ break;
+ case OBJECT_TYPE:
+ Assert(stmt->args == NIL);
+ DefineType(stmt->defnames, stmt->definition);
+ break;
+ case OBJECT_TSPARSER:
+ Assert(stmt->args == NIL);
+ DefineTSParser(stmt->defnames, stmt->definition);
+ break;
+ case OBJECT_TSDICTIONARY:
+ Assert(stmt->args == NIL);
+ DefineTSDictionary(stmt->defnames,
+ stmt->definition);
+ break;
+ case OBJECT_TSTEMPLATE:
+ Assert(stmt->args == NIL);
+ DefineTSTemplate(stmt->defnames,
+ stmt->definition);
+ break;
+ case OBJECT_TSCONFIGURATION:
+ Assert(stmt->args == NIL);
+ DefineTSConfiguration(stmt->defnames,
+ stmt->definition);
+ break;
+ case OBJECT_COLLATION:
+ Assert(stmt->args == NIL);
+ DefineCollation(stmt->defnames, stmt->definition);
+ break;
+ default:
+ elog(ERROR, "unrecognized define stmt type: %d",
+ (int) stmt->kind);
+ break;
+ }
+ }
+ break;
+
+ case T_IndexStmt: /* CREATE INDEX */
+ {
+ IndexStmt *stmt = (IndexStmt *) parsetree;
+
+ if (stmt->concurrent)
+ PreventTransactionChain(isTopLevel,
+ "CREATE INDEX CONCURRENTLY");
+
+ CheckRelationOwnership(stmt->relation, true);
+
+ /* Run parse analysis ... */
+ stmt = transformIndexStmt(stmt, queryString);
+
+ /* ... and do it */
+ DefineIndex(stmt,
+ InvalidOid, /* no predefined OID */
+ false, /* is_alter_table */
+ true, /* check_rights */
+ false, /* skip_build */
+ false); /* quiet */
+ }
+ break;
+
+ case T_CreateExtensionStmt:
+ CreateExtension((CreateExtensionStmt *) parsetree);
+ break;
+
+ case T_AlterExtensionStmt:
+ ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree);
+ break;
+
+ case T_AlterExtensionContentsStmt:
+ ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree);
+ break;
+
+ case T_CreateFdwStmt:
+ CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
+ break;
+
+ case T_AlterFdwStmt:
+ AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
+ break;
+
+ case T_CreateForeignServerStmt:
+ CreateForeignServer((CreateForeignServerStmt *) parsetree);
+ break;
+
+ case T_AlterForeignServerStmt:
+ AlterForeignServer((AlterForeignServerStmt *) parsetree);
+ break;
+
+ case T_CreateUserMappingStmt:
+ CreateUserMapping((CreateUserMappingStmt *) parsetree);
+ break;
+
+ case T_AlterUserMappingStmt:
+ AlterUserMapping((AlterUserMappingStmt *) parsetree);
+ break;
+
+ case T_DropUserMappingStmt:
+ RemoveUserMapping((DropUserMappingStmt *) parsetree);
+ break;
+
+ case T_CompositeTypeStmt: /* CREATE TYPE (composite) */
+ {
+ CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
+
+ DefineCompositeType(stmt->typevar, stmt->coldeflist);
+ }
+ break;
+
+ case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */
+ DefineEnum((CreateEnumStmt *) parsetree);
+ break;
+
+ case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
+ DefineRange((CreateRangeStmt *) parsetree);
+ break;
+
+ case T_AlterEnumStmt: /* ALTER TYPE (enum) */
+ AlterEnum((AlterEnumStmt *) parsetree, isTopLevel);
+ break;
+
+ case T_ViewStmt: /* CREATE VIEW */
+ DefineView((ViewStmt *) parsetree, queryString);
+ break;
+
+ case T_CreateFunctionStmt: /* CREATE FUNCTION */
+ CreateFunction((CreateFunctionStmt *) parsetree, queryString);
+ break;
+
+ case T_AlterFunctionStmt: /* ALTER FUNCTION */
+ AlterFunction((AlterFunctionStmt *) parsetree);
+ break;
+
+ case T_RuleStmt: /* CREATE RULE */
+ DefineRule((RuleStmt *) parsetree, queryString);
+ break;
+
+ case T_CreateSeqStmt:
+ DefineSequence((CreateSeqStmt *) parsetree);
+ break;
+
+ case T_AlterSeqStmt:
+ AlterSequence((AlterSeqStmt *) parsetree);
+ break;
+
+ case T_CreateTableAsStmt:
+ ExecCreateTableAs((CreateTableAsStmt *) parsetree,
+ queryString, params, completionTag);
+ break;
+
+ case T_RefreshMatViewStmt:
+ ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
+ queryString, params, completionTag);
+ break;
+
+ case T_CreateTrigStmt:
+ (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
+ InvalidOid, InvalidOid, false);
+ break;
+
+ case T_CreatePLangStmt:
+ CreateProceduralLanguage((CreatePLangStmt *) parsetree);
+ break;
+
+ case T_CreateDomainStmt:
+ DefineDomain((CreateDomainStmt *) parsetree);
+ break;
+
+ case T_CreateConversionStmt:
+ CreateConversionCommand((CreateConversionStmt *) parsetree);
+ break;
+
+ case T_CreateCastStmt:
+ CreateCast((CreateCastStmt *) parsetree);
+ break;
+
+ case T_CreateOpClassStmt:
+ DefineOpClass((CreateOpClassStmt *) parsetree);
+ break;
+
+ case T_CreateOpFamilyStmt:
+ DefineOpFamily((CreateOpFamilyStmt *) parsetree);
+ break;
+
+ case T_AlterOpFamilyStmt:
+ AlterOpFamily((AlterOpFamilyStmt *) parsetree);
+ break;
+
+ case T_AlterTSDictionaryStmt:
+ AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
+ break;
+
+ case T_AlterTSConfigurationStmt:
+ AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
+ break;
+
+ case T_DropStmt:
+ ExecDropStmt((DropStmt *) parsetree, isTopLevel);
+ break;
+
+ case T_RenameStmt:
+ ExecRenameStmt((RenameStmt *) parsetree);
+ break;
+
+ case T_AlterObjectSchemaStmt:
+ ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree);
+ break;
+
+ case T_AlterOwnerStmt:
+ ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
+ break;
+
+ case T_DropOwnedStmt:
+ DropOwnedObjects((DropOwnedStmt *) parsetree);
+ break;
+
+ case T_AlterDefaultPrivilegesStmt:
+ ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt *) parsetree);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(parsetree));
+ break;
+ }
+
+ if (isCompleteQuery)
+ {
+ EventTriggerSQLDrop(parsetree);
+ EventTriggerDDLCommandEnd(parsetree);
+ }
+ }
+ PG_CATCH();
+ {
+ if (needCleanup)
+ EventTriggerEndCompleteQuery();
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
- UTILITY_END_QUERY();
+ if (needCleanup)
+ EventTriggerEndCompleteQuery();
}
/*
+ * Dispatch function for DropStmt
+ */
+static void
+ExecDropStmt(DropStmt *stmt, bool isTopLevel)
+{
+ switch (stmt->removeType)
+ {
+ case OBJECT_INDEX:
+ if (stmt->concurrent)
+ PreventTransactionChain(isTopLevel,
+ "DROP INDEX CONCURRENTLY");
+ /* fall through */
+
+ case OBJECT_TABLE:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ case OBJECT_MATVIEW:
+ case OBJECT_FOREIGN_TABLE:
+ RemoveRelations(stmt);
+ break;
+ default:
+ RemoveObjects(stmt);
+ break;
+ }
+}
+
+
+/*
* UtilityReturnsTuples
* Return "true" if this utility statement will send output to the
* destination.
@@ -2449,7 +2392,7 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT FOR UPDATE";
break;
default:
- tag = "???";
+ tag = "???";
break;
}
}