diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 56c40b1..b097813 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -29,6 +29,7 @@ #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" +#include "catalog/pg_event_trigger.h" #include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" @@ -277,6 +278,10 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, case ACL_KIND_FOREIGN_SERVER: whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER; break; + case ACL_KIND_EVENT_TRIGGER: + elog(ERROR, "grantable rights not supported for event triggers"); + /* not reached, but keep compiler quiet */ + return ACL_NO_RIGHTS; case ACL_KIND_TYPE: whole_mask = ACL_ALL_RIGHTS_TYPE; break; @@ -3286,6 +3291,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = gettext_noop("permission denied for foreign-data wrapper %s"), /* ACL_KIND_FOREIGN_SERVER */ gettext_noop("permission denied for foreign server %s"), + /* ACL_KIND_EVENT_TRIGGER */ + gettext_noop("permission denied for event trigger %s"), /* ACL_KIND_EXTENSION */ gettext_noop("permission denied for extension %s"), }; @@ -3330,6 +3337,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = gettext_noop("must be owner of foreign-data wrapper %s"), /* ACL_KIND_FOREIGN_SERVER */ gettext_noop("must be owner of foreign server %s"), + /* ACL_KIND_EVENT_TRIGGER */ + gettext_noop("must be owner of event trigger %s"), /* ACL_KIND_EXTENSION */ gettext_noop("must be owner of extension %s"), }; @@ -3455,6 +3464,10 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, return pg_foreign_data_wrapper_aclmask(table_oid, roleid, mask, how); case ACL_KIND_FOREIGN_SERVER: return pg_foreign_server_aclmask(table_oid, roleid, mask, how); + case ACL_KIND_EVENT_TRIGGER: + elog(ERROR, "grantable rights not supported for event triggers"); + /* not reached, but keep compiler quiet */ + return ACL_NO_RIGHTS; case ACL_KIND_TYPE: return pg_type_aclmask(table_oid, roleid, mask, how); default: @@ -4876,6 +4889,33 @@ pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid) } /* + * Ownership check for an event trigger (specified by OID). + */ +bool +pg_event_trigger_ownercheck(Oid et_oid, Oid roleid) +{ + HeapTuple tuple; + Oid ownerId; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + tuple = SearchSysCache1(EVENTTRIGGEROID, ObjectIdGetDatum(et_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("event trigger with OID %u does not exist", + et_oid))); + + ownerId = ((Form_pg_event_trigger) GETSTRUCT(tuple))->evtowner; + + ReleaseSysCache(tuple); + + return has_privs_of_role(roleid, ownerId); +} + +/* * Ownership check for a database (specified by OID). */ bool diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index 40ae60d..5b8140b 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -997,6 +997,11 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, NameListToString(objname)); break; + case OBJECT_EVENT_TRIGGER: + if (!pg_event_trigger_ownercheck(address.objectId, roleid)) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, + NameListToString(objname)); + break; case OBJECT_LANGUAGE: if (!pg_language_ownercheck(address.objectId, roleid)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, @@ -1075,7 +1080,6 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, break; case OBJECT_TSPARSER: case OBJECT_TSTEMPLATE: - case OBJECT_EVENT_TRIGGER: /* We treat these object types as being owned by superusers */ if (!superuser_arg(roleid)) ereport(ERROR, diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 78ef831..19f9895 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -70,10 +70,6 @@ ExecRenameStmt(RenameStmt *stmt) RenameDatabase(stmt->subname, stmt->newname); break; - case OBJECT_EVENT_TRIGGER: - RenameEventTrigger(stmt->subname, stmt->newname); - break; - case OBJECT_FDW: RenameForeignDataWrapper(stmt->subname, stmt->newname); break; @@ -82,6 +78,10 @@ ExecRenameStmt(RenameStmt *stmt) RenameForeignServer(stmt->subname, stmt->newname); break; + case OBJECT_EVENT_TRIGGER: + RenameEventTrigger(stmt->subname, stmt->newname); + break; + case OBJECT_FUNCTION: RenameFunction(stmt->object, stmt->objarg, stmt->newname); break; diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c index 1b8529e..8f5d7e0 100644 --- a/src/backend/commands/dropcmds.c +++ b/src/backend/commands/dropcmds.c @@ -206,6 +206,10 @@ does_not_exist_skipping(ObjectType objtype, List *objname, List *objargs) args = NameListToString(list_truncate(objname, list_length(objname) - 1)); break; + case OBJECT_EVENT_TRIGGER: + msg = gettext_noop("event trigger \"%s\" does not exist, skipping"); + name = NameListToString(objname); + break; case OBJECT_RULE: msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping"); name = strVal(llast(objname)); diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index 561ce42..f9a3ea2 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -49,23 +49,6 @@ static void AlterEventTriggerOwner_internal(Relation rel, Oid newOwnerId); /* - * Check permission: event triggers are only available for superusers. Raise - * an exception when requirements are not fullfilled. - * - * It's not clear how to accept that database owners be able to create command - * triggers, a superuser could run a command that fires a trigger's procedure - * written by the database owner and now running with superuser privileges. - */ -static void -CheckEventTriggerPrivileges() -{ - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to use event triggers")))); -} - -/* * Insert Command Trigger Tuple * * Insert the new pg_event_trigger row, and return the OID assigned to the new @@ -158,7 +141,16 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString) Oid funcrettype; Oid evtowner = GetUserId(); - CheckEventTriggerPrivileges(); + /* + * It would be nice to allow database owners or even regular users to do + * this, but there are obvious privilege escalation risks which would have + * to somehow be plugged first. + */ + if (!superuser()) + ereport(ERROR, + (errmsg("permission denied to create event trigger \"%s\"", + stmt->trigname), + errhint("Must be superuser to create an event trigger."))); /* * Find and validate the trigger function. @@ -171,7 +163,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt, const char *queryString) if (funcrettype != EVTTRIGGEROID) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("function \"%s\" must return type \"command_trigger\"", + errmsg("function \"%s\" must return type \"event_trigger\"", NameListToString(stmt->funcname)))); /* @@ -224,15 +216,18 @@ AlterEventTrigger(AlterEventTrigStmt *stmt) Form_pg_event_trigger evtForm; char tgenabled = stmt->tgenabled; - CheckEventTriggerPrivileges(); - tgrel = heap_open(EventTriggerRelationId, RowExclusiveLock); - tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, CStringGetDatum(stmt->trigname)); + tup = SearchSysCacheCopy1(EVENTTRIGGERNAME, + CStringGetDatum(stmt->trigname)); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("event trigger \"%s\" does not exist", stmt->trigname))); + errmsg("event trigger \"%s\" does not exist", + stmt->trigname))); + if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, + stmt->trigname); /* tuple is a copy, so we can modify it below */ evtForm = (Form_pg_event_trigger) GETSTRUCT(tup); @@ -257,8 +252,6 @@ RenameEventTrigger(const char *trigname, const char *newname) Relation rel; Form_pg_event_trigger evtForm; - CheckEventTriggerPrivileges(); - rel = heap_open(EventTriggerRelationId, RowExclusiveLock); /* newname must be available */ @@ -273,6 +266,9 @@ RenameEventTrigger(const char *trigname, const char *newname) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("event trigger \"%s\" does not exist", trigname))); + if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, + trigname); evtForm = (Form_pg_event_trigger) GETSTRUCT(tup); @@ -344,22 +340,31 @@ AlterEventTriggerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) { Form_pg_event_trigger form; - CheckEventTriggerPrivileges(); - form = (Form_pg_event_trigger) GETSTRUCT(tup); - if (form->evtowner != newOwnerId) - { - form->evtowner = newOwnerId; + if (form->evtowner == newOwnerId) + return; - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); + if (!pg_event_trigger_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_EVENT_TRIGGER, + NameStr(form->evtname)); - /* Update owner dependency reference */ - changeDependencyOnOwner(EventTriggerRelationId, - HeapTupleGetOid(tup), - newOwnerId); - } + /* New owner must be a superuser */ + if (!superuser_arg(newOwnerId)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to change owner of event trigger \"%s\"", + NameStr(form->evtname)), + errhint("The owner of an event trigger must be a superuser."))); + + form->evtowner = newOwnerId; + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(EventTriggerRelationId, + HeapTupleGetOid(tup), + newOwnerId); } /* @@ -412,14 +417,14 @@ call_event_trigger_procedure(EventContext ev_ctx, TrigEvent tev, * Prepare the event trigger function context from the Command Context. * We prepare a dedicated Node here so as not to publish internal data. */ - trigdata.type = T_EventTriggerData; - trigdata.toplevel = ev_ctx->toplevel; - trigdata.tag = ev_ctx->tag; - trigdata.objectId = ev_ctx->objectId; + trigdata.type = T_EventTriggerData; + trigdata.toplevel = ev_ctx->toplevel; + trigdata.tag = ev_ctx->tag; + trigdata.objectId = ev_ctx->objectId; trigdata.schemaname = ev_ctx->schemaname; trigdata.objectname = ev_ctx->objectname; - trigdata.parsetree = ev_ctx->parsetree; - trigdata.when = pstrdup(event_to_string(tev)); + trigdata.parsetree = ev_ctx->parsetree; + trigdata.when = pstrdup(event_to_string(tev)); /* * Call the function, passing no arguments but setting a context. @@ -448,12 +453,6 @@ InitEventContext(EventContext evt, const Node *parsetree) evt->objectname = NULL; evt->schemaname = NULL; - /* - * Fill in the event command, which is an enum constant to match against - * what's stored into catalogs. As we are storing that on disk, we need the - * enum values to be stable, see src/include/catalog/pg_event_trigger.h for - * details. - */ switch (nodeTag(parsetree)) { case T_CreateSchemaStmt: diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ec5c8f8..068834b 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -214,7 +214,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, CreateUserStmt CreateUserMappingStmt CreateRoleStmt CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt - DropAssertStmt DropTrigStmt DropEventTrigStmt DropRuleStmt DropCastStmt + DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt @@ -755,7 +755,6 @@ stmt : | DropStmt | DropTableSpaceStmt | DropTrigStmt - | DropEventTrigStmt | DropRoleStmt | DropUserStmt | DropUserMappingStmt @@ -4378,27 +4377,6 @@ trigger_command: ; -DropEventTrigStmt: - DROP EVENT TRIGGER name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_EVENT_TRIGGER; - n->objects = list_make1(list_make1(makeString($4))); - n->behavior = $5; - n->missing_ok = false; - $$ = (Node *) n; - } - | DROP EVENT TRIGGER IF_P EXISTS name opt_drop_behavior - { - DropStmt *n = makeNode(DropStmt); - n->removeType = OBJECT_EVENT_TRIGGER; - n->objects = list_make1(list_make1(makeString($6))); - n->behavior = $7; - n->missing_ok = true; - $$ = (Node *) n; - } - ; - AlterEventTrigStmt: ALTER EVENT TRIGGER name enable_trigger { @@ -5002,6 +4980,7 @@ drop_type: TABLE { $$ = OBJECT_TABLE; } | VIEW { $$ = OBJECT_VIEW; } | INDEX { $$ = OBJECT_INDEX; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } + | EVENT TRIGGER { $$ = OBJECT_EVENT_TRIGGER; } | TYPE_P { $$ = OBJECT_TYPE; } | DOMAIN_P { $$ = OBJECT_DOMAIN; } | COLLATION { $$ = OBJECT_COLLATION; } diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index c8c1a81..b772f2b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -356,7 +356,7 @@ standard_ProcessUtility(Node *parsetree, completionTag[0] = '\0'; /* Event Trigger support for command_start */ - InitEventContext(&evt, (Node *)parsetree); + InitEventContext(&evt, parsetree); if (CommandFiresTriggersForEvent(&evt, E_CommandStart)) ExecEventTriggers(&evt, E_CommandStart); diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index d7770b8..8590f3c 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -293,6 +293,33 @@ trigger_out(PG_FUNCTION_ARGS) /* + * event_trigger_in - input routine for pseudo-type event_trigger. + */ +Datum +event_trigger_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type event_trigger"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * event_trigger_out - output routine for pseudo-type event_trigger. + */ +Datum +event_trigger_out(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot display a value of type event_trigger"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + + +/* * language_handler_in - input routine for pseudo-type LANGUAGE_HANDLER. */ Datum diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index bcd042e..09ca6dd 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -5313,7 +5313,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers) i_oid, i_evtname, i_evtevent, - i_evtowner, + i_evtowner, i_evttags, i_evtfname, i_evtenabled; @@ -5333,7 +5333,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers) "SELECT e.tableoid, e.oid, evtname, evtenabled, " "evtevent, (%s evtowner) AS evtowner, " "array_to_string(array(" - "select '''' || x || '''' " + "select quote_literal(x) " " from unnest(evttags) as t(x)), ', ') as evttags, " "e.evtfoid::regproc as evtfname " "FROM pg_event_trigger e " @@ -13756,7 +13756,7 @@ dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo) appendPQExpBuffer(query, "CREATE EVENT TRIGGER "); appendPQExpBufferStr(query, fmtId(evtinfo->dobj.name)); appendPQExpBuffer(query, " ON "); - appendPQExpBufferStr(query, evtinfo->evtevent); + appendPQExpBufferStr(query, fmtId(evtinfo->evtevent)); appendPQExpBufferStr(query, " "); if (strcmp("", evtinfo->evttags) != 0) @@ -13768,7 +13768,7 @@ dumpEventTrigger(Archive *fout, EventTriggerInfo *evtinfo) appendPQExpBuffer(query, "\n EXECUTE PROCEDURE "); appendPQExpBufferStr(query, evtinfo->evtfname); - appendPQExpBuffer(query, " ();\n"); + appendPQExpBuffer(query, "();\n"); if (evtinfo->evtenabled != 'O') { diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index 6d4c2ba..5318e7a 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -24,8 +24,9 @@ static const char *modulename = gettext_noop("sorter"); * Objects are sorted by priority levels, and within an equal priority level * by OID. (This is a relatively crude hack to provide semi-reasonable * behavior for old databases without full dependency info.) Note: collations, - * extensions, text search, foreign-data, and default ACL objects can't really - * happen here, so the rather bogus priorities for them don't matter. + * extensions, text search, foreign-data, event trigger, and default ACL + * objects can't really happen here, so the rather bogus priorities for them + * don't matter. * * NOTE: object-type priorities must match the section assignments made in * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY, @@ -114,7 +115,7 @@ static const int newObjectTypePriority[] = 24, /* DO_BLOB_DATA */ 22, /* DO_PRE_DATA_BOUNDARY */ 25, /* DO_POST_DATA_BOUNDARY */ - 30 /* DO_EVENT_TRIGGER */ + 32 /* DO_EVENT_TRIGGER */ }; static DumpId preDataBoundId; diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index bee7154..743f890 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3456,6 +3456,10 @@ DATA(insert OID = 2300 ( trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2 DESCR("I/O"); DATA(insert OID = 2301 ( trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2279" _null_ _null_ _null_ _null_ trigger_out _null_ _null_ _null_ )); DESCR("I/O"); +DATA(insert OID = 3594 ( event_trigger_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3838 "2275" _null_ _null_ _null_ _null_ event_trigger_in _null_ _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 3595 ( event_trigger_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "3838" _null_ _null_ _null_ _null_ event_trigger_out _null_ _null_ _null_ )); +DESCR("I/O"); DATA(insert OID = 2302 ( language_handler_in PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 2280 "2275" _null_ _null_ _null_ _null_ language_handler_in _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 2303 ( language_handler_out PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "2280" _null_ _null_ _null_ _null_ language_handler_out _null_ _null_ _null_ )); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 98c7dd5..86be998 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -650,7 +650,7 @@ DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void #define VOIDOID 2278 DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); #define TRIGGEROID 2279 -DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); #define EVTTRIGGEROID 3838 DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); #define LANGUAGE_HANDLEROID 2280 diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 2d1cccb..5700e54 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -195,6 +195,7 @@ typedef enum AclObjectKind ACL_KIND_TSCONFIGURATION, /* pg_ts_config */ ACL_KIND_FDW, /* pg_foreign_data_wrapper */ ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */ + ACL_KIND_EVENT_TRIGGER, /* pg_event_trigger */ ACL_KIND_EXTENSION, /* pg_extension */ MAX_ACL_KIND /* MUST BE LAST */ } AclObjectKind; @@ -322,6 +323,7 @@ extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid); extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid); extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid); extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid); +extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid); extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid); extern bool has_createrole_privilege(Oid roleid); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 1063403..8b9291c 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -532,6 +532,8 @@ extern Datum void_recv(PG_FUNCTION_ARGS); extern Datum void_send(PG_FUNCTION_ARGS); extern Datum trigger_in(PG_FUNCTION_ARGS); extern Datum trigger_out(PG_FUNCTION_ARGS); +extern Datum event_trigger_in(PG_FUNCTION_ARGS); +extern Datum event_trigger_out(PG_FUNCTION_ARGS); extern Datum language_handler_in(PG_FUNCTION_ARGS); extern Datum language_handler_out(PG_FUNCTION_ARGS); extern Datum fdw_handler_in(PG_FUNCTION_ARGS); diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 5b456aa..f772fd4 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -1571,7 +1571,7 @@ plperl_trigger_build_args(FunctionCallInfo fcinfo) } -/* Set up the arguments for a command trigger call. */ +/* Set up the arguments for an event trigger call. */ static SV * plperl_event_trigger_build_args(FunctionCallInfo fcinfo) { diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 77c5ed9..9d17686 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -536,7 +536,7 @@ do_compile(FunctionCallInfo fcinfo, if (rettypeid == VOIDOID || rettypeid == RECORDOID) /* okay */ ; - else if (rettypeid == TRIGGEROID) + else if (rettypeid == TRIGGEROID || rettypeid == EVTTRIGGEROID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("trigger functions can only be called as triggers"))); @@ -689,7 +689,7 @@ do_compile(FunctionCallInfo fcinfo, if (procStruct->pronargs != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), - errmsg("command trigger functions cannot have declared arguments"))); + errmsg("event trigger functions cannot have declared arguments"))); /* Add the variable tg_when */ var = plpgsql_build_variable("tg_when", 0, diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fd59d75..373facf 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -773,8 +773,8 @@ plpgsql_exec_trigger(PLpgSQL_function *func, return rettup; } -void plpgsql_exec_event_trigger(PLpgSQL_function *func, - EventTriggerData *trigdata) +void +plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata) { PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; diff --git a/src/test/regress/expected/event_triggers.out b/src/test/regress/expected/event_triggers.out index 415605b..6eac7e9 100644 --- a/src/test/regress/expected/event_triggers.out +++ b/src/test/regress/expected/event_triggers.out @@ -10,9 +10,6 @@ begin raise notice 'snitch: % % %.%', tg_when, tg_tag, tg_schemaname, tg_objectname; end; $$; --- --- TODO: REASSIGN OWNED and DROP OWNED --- create event trigger any_t on command_start execute procedure snitch(); create event trigger foo_t on command_start @@ -67,8 +64,12 @@ NOTICE: snitch: command_start CREATE SCHEMA . create schema cmd2; NOTICE: snitch: command_start CREATE SCHEMA . NOTICE: snitch: command_start CREATE SCHEMA . -create role regbob; -alter event trigger snitch owner to regbob; +create role regression_bob; +alter event trigger snitch owner to regression_bob; +ERROR: permission denied to change owner of event trigger "snitch" +HINT: The owner of an event trigger must be a superuser. +alter role regression_bob superuser; +alter event trigger snitch owner to regression_bob; create table cmd.foo(id bigserial primary key); NOTICE: snitch: command_start CREATE TABLE . NOTICE: snitch: command_start CREATE TABLE . @@ -158,7 +159,7 @@ NOTICE: snitch: command_start ALTER TABLE . create sequence test_seq_; NOTICE: snitch: command_start CREATE SEQUENCE . NOTICE: snitch: command_start CREATE SEQUENCE . -alter sequence test_seq_ owner to regbob; +alter sequence test_seq_ owner to regression_bob; NOTICE: snitch: command_start ALTER SEQUENCE . NOTICE: snitch: command_start ALTER SEQUENCE . alter sequence test_seq_ rename to test_seq; @@ -197,7 +198,7 @@ NOTICE: snitch: command_start ALTER SEQUENCE . create view view_test as select id, things from cmd.test; NOTICE: snitch: command_start CREATE VIEW . NOTICE: snitch: command_start CREATE VIEW . -alter view view_test owner to regbob; +alter view view_test owner to regression_bob; NOTICE: snitch: command_start ALTER VIEW . NOTICE: snitch: command_start ALTER VIEW . alter view view_test rename to view_test2; @@ -248,7 +249,7 @@ NOTICE: snitch: command_start ALTER FUNCTION . alter function notfun(int) set schema cmd; NOTICE: snitch: command_start ALTER FUNCTION . NOTICE: snitch: command_start ALTER FUNCTION . -alter function cmd.notfun(int) owner to regbob; +alter function cmd.notfun(int) owner to regression_bob; NOTICE: snitch: command_start ALTER FUNCTION . NOTICE: snitch: command_start ALTER FUNCTION . alter function cmd.notfun(int) cost 77; @@ -297,7 +298,7 @@ NOTICE: snitch: command_start ALTER TYPE . create type cmd.type_test AS (a integer, b integer, c text); NOTICE: snitch: command_start CREATE TYPE . NOTICE: snitch: command_start CREATE TYPE . -alter type cmd.type_test owner to regbob; +alter type cmd.type_test owner to regression_bob; NOTICE: snitch: command_start ALTER TYPE . NOTICE: snitch: command_start ALTER TYPE . alter type cmd.type_test rename to type_test2; @@ -349,7 +350,7 @@ NOTICE: snitch: command_start ALTER DOMAIN . alter domain cmd.us_postal_code drop constraint dummy_constraint; NOTICE: snitch: command_start ALTER DOMAIN . NOTICE: snitch: command_start ALTER DOMAIN . -alter domain cmd.us_postal_code owner to regbob; +alter domain cmd.us_postal_code owner to regression_bob; NOTICE: snitch: command_start ALTER DOMAIN . NOTICE: snitch: command_start ALTER DOMAIN . alter domain cmd.us_postal_code set schema cmd2; @@ -455,12 +456,14 @@ drop schema cmd2 cascade; NOTICE: snitch: command_start DROP SCHEMA . NOTICE: snitch: command_start DROP SCHEMA . -- fail because owning event trigger snitch -drop role regbob; -ERROR: role "regbob" cannot be dropped because some objects depend on it +drop role regression_bob; +ERROR: role "regression_bob" cannot be dropped because some objects depend on it DETAIL: owner of event trigger snitch drop event trigger any_t; -drop event trigger snitch; -drop role regbob; +drop event trigger if exists snitch; +drop event trigger if exists snitch; +NOTICE: event trigger "snitch" does not exist, skipping +drop role regression_bob; create table onerow(id integer); create or replace function insert_one_row() returns event_trigger diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 0dac40f..70eab92 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -134,11 +134,10 @@ WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p1.typelem != 0 AND p1.typlen < 0) AND NOT (p2.prorettype = p1.oid AND NOT p2.proretset) ORDER BY 1; - oid | typname | oid | proname -------+---------------+------+------------ - 1790 | refcursor | 46 | textin - 3838 | event_trigger | 2300 | trigger_in -(2 rows) + oid | typname | oid | proname +------+-----------+-----+--------- + 1790 | refcursor | 46 | textin +(1 row) -- Varlena array types will point to array_in -- Exception as of 8.1: int2vector and oidvector have their own I/O routines @@ -178,11 +177,10 @@ WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT (p2.oid = 'array_out'::regproc AND p1.typelem != 0 AND p1.typlen = -1))) ORDER BY 1; - oid | typname | oid | proname -------+---------------+------+------------- - 1790 | refcursor | 47 | textout - 3838 | event_trigger | 2301 | trigger_out -(2 rows) + oid | typname | oid | proname +------+-----------+-----+--------- + 1790 | refcursor | 47 | textout +(1 row) SELECT p1.oid, p1.typname, p2.oid, p2.proname FROM pg_type AS p1, pg_proc AS p2 diff --git a/src/test/regress/sql/event_triggers.sql b/src/test/regress/sql/event_triggers.sql index cf25ee7..84a00e4 100644 --- a/src/test/regress/sql/event_triggers.sql +++ b/src/test/regress/sql/event_triggers.sql @@ -11,10 +11,6 @@ begin end; $$; --- --- TODO: REASSIGN OWNED and DROP OWNED --- - create event trigger any_t on command_start execute procedure snitch(); @@ -68,9 +64,11 @@ alter event trigger foo_t rename to snitch; create schema cmd; create schema cmd2; -create role regbob; -alter event trigger snitch owner to regbob; +create role regression_bob; +alter event trigger snitch owner to regression_bob; +alter role regression_bob superuser; +alter event trigger snitch owner to regression_bob; create table cmd.foo(id bigserial primary key); create view cmd.v as select * from cmd.foo; @@ -103,7 +101,7 @@ alter table cmd.test set with oids; alter table cmd.test set without oids; create sequence test_seq_; -alter sequence test_seq_ owner to regbob; +alter sequence test_seq_ owner to regression_bob; alter sequence test_seq_ rename to test_seq; alter sequence test_seq set schema cmd; alter sequence cmd.test_seq start with 3; @@ -117,7 +115,7 @@ alter sequence cmd.test_seq cycle; alter sequence cmd.test_seq no cycle; create view view_test as select id, things from cmd.test; -alter view view_test owner to regbob; +alter view view_test owner to regression_bob; alter view view_test rename to view_test2; alter view view_test2 set schema cmd; alter view cmd.view_test2 alter column id set default 9; @@ -142,7 +140,7 @@ as $$ select t from cmd.foo where id = $1; $$; alter function fun(int) strict; alter function fun(int) rename to notfun; alter function notfun(int) set schema cmd; -alter function cmd.notfun(int) owner to regbob; +alter function cmd.notfun(int) owner to regression_bob; alter function cmd.notfun(int) cost 77; drop function cmd.notfun(int); @@ -170,7 +168,7 @@ create type cmd.compfoo AS (f1 int, f2 text); alter type cmd.compfoo add attribute f3 text; create type cmd.type_test AS (a integer, b integer, c text); -alter type cmd.type_test owner to regbob; +alter type cmd.type_test owner to regression_bob; alter type cmd.type_test rename to type_test2; alter type cmd.type_test2 set schema public; alter type public.type_test2 rename attribute a to z; @@ -191,7 +189,7 @@ alter domain cmd.us_postal_code drop default; alter domain cmd.us_postal_code drop not null; alter domain cmd.us_postal_code add constraint dummy_constraint check (value ~ '^\d{8}$'); alter domain cmd.us_postal_code drop constraint dummy_constraint; -alter domain cmd.us_postal_code owner to regbob; +alter domain cmd.us_postal_code owner to regression_bob; alter domain cmd.us_postal_code set schema cmd2; drop domain cmd2.us_postal_code; @@ -248,11 +246,12 @@ drop schema cmd1 cascade; drop schema cmd2 cascade; -- fail because owning event trigger snitch -drop role regbob; +drop role regression_bob; drop event trigger any_t; -drop event trigger snitch; -drop role regbob; +drop event trigger if exists snitch; +drop event trigger if exists snitch; +drop role regression_bob; create table onerow(id integer);