*** /dev/null
--- b/contrib/noddl/Makefile
***************
*** 0 ****
--- 1,17 ----
+ # contrib/lo/Makefile
+
+ MODULES = noddl
+
+ EXTENSION = noddl
+ DATA = noddl--1.0.sql
+
+ ifdef USE_PGXS
+ PG_CONFIG = pg_config
+ PGXS := $(shell $(PG_CONFIG) --pgxs)
+ include $(PGXS)
+ else
+ subdir = contrib/noddl
+ top_builddir = ../..
+ include $(top_builddir)/src/Makefile.global
+ include $(top_srcdir)/contrib/contrib-global.mk
+ endif
*** /dev/null
--- b/contrib/noddl/noddl--1.0.sql
***************
*** 0 ****
--- 1,12 ----
+ /* contrib/lo/lo--1.0.sql */
+
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION lo" to load this file. \quit
+
+ CREATE FUNCTION noddl()
+ RETURNS pg_catalog.event_trigger
+ AS 'noddl'
+ LANGUAGE C;
+
+ CREATE EVENT TRIGGER noddl on ddl_command_start
+ execute procedure noddl();
*** /dev/null
--- b/contrib/noddl/noddl.c
***************
*** 0 ****
--- 1,35 ----
+ /*
+ * PostgreSQL definitions for noddl event trigger extension.
+ *
+ * contrib/noddl/noddl.c
+ */
+
+ #include "postgres.h"
+ #include "commands/event_trigger.h"
+
+
+ PG_MODULE_MAGIC;
+
+ /* forward declarations */
+ Datum noddl(PG_FUNCTION_ARGS);
+
+
+ /*
+ * This is the trigger that protects us from orphaned large objects
+ */
+ PG_FUNCTION_INFO_V1(noddl);
+
+ Datum
+ noddl(PG_FUNCTION_ARGS)
+ {
+ EventTriggerData *trigdata = (EventTriggerData *) fcinfo->context;
+
+ if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */
+ elog(ERROR, "not fired by event trigger manager");
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("command %s denied", trigdata->tag)));
+
+ PG_RETURN_NULL();
+ }
*** /dev/null
--- b/contrib/noddl/noddl.control
***************
*** 0 ****
--- 1,4 ----
+ # noddl extension
+ comment = 'Event Trigger to deny DDL operations'
+ default_version = '1.0'
+ relocatable = true
*** a/doc/src/sgml/event-trigger.sgml
--- b/doc/src/sgml/event-trigger.sgml
***************
*** 607,610 ****
--- 607,861 ----
+
+ Writing Event Trigger Functions in C
+
+
+ event trigger
+ in C
+
+
+
+ This section describes the low-level details of the interface to an
+ event trigger function. This information is only needed when writing
+ event trigger functions in C. If you are using a higher-level language
+ then these details are handled for you. In most cases you should
+ consider using a procedural language before writing your event triggers
+ in C. The documentation of each procedural language explains how to
+ write an event trigger in that language.
+
+
+
+ Event trigger functions must use the version 1> function
+ manager interface.
+
+
+
+ When a function is called by the event trigger manager, it is not passed
+ any normal arguments, but it is passed a context> pointer
+ pointing to a EventTriggerData> structure. C functions can
+ check whether they were called from the event trigger manager or not by
+ executing the macro:
+
+ CALLED_AS_EVENT_TRIGGER(fcinfo)
+
+ which expands to:
+
+ ((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData))
+
+ If this returns true, then it is safe to cast
+ fcinfo->context> to type EventTriggerData
+ * and make use of the pointed-to
+ EventTriggerData> structure. The function must
+ not alter the EventTriggerData>
+ structure or any of the data it points to.
+
+
+
+ struct EventTriggerData is defined in
+ commands/event_trigger.h:
+
+
+ typedef struct EventTriggerData
+ {
+ NodeTag type;
+ const char *event; /* event name */
+ Node *parsetree; /* parse tree */
+ const char *tag; /* command tag */
+ } EventTriggerData;
+
+
+ where the members are defined as follows:
+
+
+
+ type>
+
+
+ Always T_EventTriggerData.
+
+
+
+
+
+ tg_event>
+
+
+ Describes the event for which the function is called. On of:
+
+
+
+ "ddl_command_start"
+
+
+ The event trigger is run first thing in the command
+ implementation, nothing did happen yet (no object is locked, the
+ system didn't check if the object(s) still exist, etc).
+
+
+
+
+
+ "ddl_command_end"
+
+
+ The event trigger is run as the last step in the command
+ implementation, while the locks are still kept when locks have
+ been taken.
+
+
+
+
+
+ "sql_drop"
+
+
+ The event trigger is run at the last step in the command
+ implementation, unless there's
+ a "ddl_command_end" event trigger registered
+ against the same command too. This event is only fired when
+ a DROP command did occur.
+
+
+
+
+
+
+
+
+
+
+ parsetree>
+
+
+ A pointer to a structure describing the relation that the trigger fired for.
+ Look at utils/rel.h> for details about
+ this structure. The most interesting things are
+ tg_relation->rd_att> (descriptor of the relation
+ tuples) and tg_relation->rd_rel->relname>
+ (relation name; the type is not char*> but
+ NameData>; use
+ SPI_getrelname(tg_relation)> to get a char*> if you
+ need a copy of the name).
+
+
+
+
+
+ tag>
+
+
+ The command tag against which the Event Trigger is run.
+
+
+
+
+
+
+
+
+ An event trigger function must return a NULL> pointer
+ (not> an SQL null value, that is, do not
+ set isNull true).
+
+
+
+
+ A Complete Event Trigger Example
+
+
+ Here is a very simple example of an event trigger function written in C.
+ (Examples of triggers written in procedural languages can be found in
+ the documentation of the procedural languages.)
+
+
+
+ The function noddl> raises an exception each time it's
+ called, preventing the command to run, whatever the
+ command is.
+
+
+
+ This is the source code of the trigger function:
+ context;
+
+ if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */
+ elog(ERROR, "not fired by event trigger manager");
+
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("command %s denied", trigdata->tag)));
+
+ PG_RETURN_NULL();
+ }
+ ]]>
+
+
+
+
+ After you have compiled the source code (see ), declare the function and the triggers:
+
+ CREATE FUNCTION noddl()
+ RETURNS pg_catalog.event_trigger
+ AS 'noddl'
+ LANGUAGE C;
+
+ CREATE EVENT TRIGGER noddl on ddl_command_start
+ execute procedure noddl();
+
+
+
+
+ Now you can test the operation of the trigger:
+
+ =# \dy
+ List of event triggers
+ Name | Event | Owner | Enabled | Procedure | Tags
+ -------+-------------------+-------+---------+-----------+------
+ noddl | ddl_command_start | dim | enabled | noddl |
+ (1 row)
+
+ =# CREATE TABLE foo(id serial);
+ ERROR: command CREATE TABLE denied
+
+
+
+
+ In order to be able to run some DDL commands when you need to do so,
+ either DROP the event trigger or just disable it in
+ the DDL transaction, like so:
+
+ BEGIN;
+ ALTER EVENT TRIGGER noddl DISABLE;
+ CREATE TABLE foo(id serial);
+ COMMIT;
+
+
+