*** /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; + + +