diff --git a/contrib/Makefile b/contrib/Makefile
index 8e18785..ac9a2c5 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -9,6 +9,7 @@ SUBDIRS = \
auto_explain \
btree_gin \
btree_gist \
+ check_dml \
chkpass \
citext \
cube \
diff --git a/contrib/check_dml/Makefile b/contrib/check_dml/Makefile
new file mode 100644
index 0000000..4d9df0e
--- /dev/null
+++ b/contrib/check_dml/Makefile
@@ -0,0 +1,15 @@
+# $PostgreSQL$
+
+MODULE_big = check_dml
+OBJS = check_dml.o
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/check_dml
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/check_dml/check_dml.c b/contrib/check_dml/check_dml.c
new file mode 100644
index 0000000..0e9769d
--- /dev/null
+++ b/contrib/check_dml/check_dml.c
@@ -0,0 +1,96 @@
+/*-------------------------------------------------------------------------
+ *
+ * check_dml.c
+ *
+ *
+ * Copyright (c) 2010, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "executor/executor.h"
+#include "utils/guc.h"
+#include "utils/lsyscache.h"
+
+PG_MODULE_MAGIC;
+
+/* GUC variables */
+static char *check_dml_table_prefix = NULL;
+
+/* Saved hook values in case of unload */
+static ExecutorCheckPerms_hook_type prev_ExecutorCheckPerms = NULL;
+
+void _PG_init(void);
+void _PG_fini(void);
+
+static void check_dml_ExecutorCheckPerms(List *rangeTable);
+
+
+/*
+ * Module load callback
+ */
+void
+_PG_init(void)
+{
+ /* Define custom GUC variables. */
+ DefineCustomStringVariable("check_dml.table_prefix",
+ "Disallows DML access to tables whose names do not begin with this string.",
+ "An empty string effectively disables this feature.",
+ &check_dml_table_prefix,
+ "",
+ PGC_SUSET,
+ 0,
+ NULL,
+ NULL);
+
+ /* Install hooks. */
+ prev_ExecutorCheckPerms = ExecutorCheckPerms_hook;
+ ExecutorCheckPerms_hook = check_dml_ExecutorCheckPerms;
+}
+
+/*
+ * Module unload callback
+ */
+void
+_PG_fini(void)
+{
+ /* Uninstall hooks. */
+ ExecutorCheckPerms_hook = prev_ExecutorCheckPerms;
+}
+
+/*
+ * ExecutorStart hook: start up logging if needed
+ */
+static void
+check_dml_ExecutorCheckPerms(List *rangeTable)
+{
+ if (check_dml_table_prefix && check_dml_table_prefix[0] != '\0')
+ {
+ ListCell *lc;
+
+ foreach (lc, rangeTable)
+ {
+ RangeTblEntry *rte = lfirst(lc);
+ char *relname;
+
+ /* Only need to check relations. */
+ if (rte->rtekind != RTE_RELATION)
+ continue;
+
+ /* Make sure it starts with the required prefix. */
+ relname = get_rel_name(rte->relid);
+ if (strncmp(check_dml_table_prefix,
+ relname,
+ strlen(check_dml_table_prefix)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("table name \"%s\" does not begin with \"%s\"",
+ relname, check_dml_table_prefix)));
+ pfree(relname);
+ }
+ }
+}
diff --git a/doc/src/sgml/check-dml.sgml b/doc/src/sgml/check-dml.sgml
new file mode 100644
index 0000000..81701fe
--- /dev/null
+++ b/doc/src/sgml/check-dml.sgml
@@ -0,0 +1,67 @@
+
+
+
+ check_dml
+
+
+ check_dml
+
+
+
+ The check_dml allows DML> statements to
+ be denied access to all tables whose names do not begin with a specified
+ prefix. This is mostly intended as an example of how ExecutorCheckPerms_hook
+ can be used to create a custom permissions-checking policy.
+
+
+
+ The module provides no SQL-accessible functions. It should be loaded
+ into all sessions by including check_dml> in
+ in
+ postgresql.conf>.
+
+
+
+ Configuration parameters
+
+
+
+
+ check_dml.table_prefix (string)
+
+
+ check_dml.table_prefix> configuration parameter
+
+
+
+ When check_dml.table_prefix is set to a non-empty
+ value, any attempt to access a table whose name does not begin with the
+ specified prefix will be rejected.
+
+
+
+
+
+
+ In order to set these parameters in your postgresql.conf> file,
+ you will need to add auto_explain> to
+ . Typical usage might be:
+
+
+
+# postgresql.conf
+shared_preload_libraries = 'check_dml'
+
+custom_variable_classes = 'check_dml'
+
+
+
+ check_dml> could be used to implement per-user access policies
+ by using
+ ALTER USER ... SET (check_dml.table_prefix = ...).
+ Whether this is actually any better than existing permissions-checking
+ mechanisms is arguable.
+
+
+
+
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index 1d26aa9..77816a3 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -84,6 +84,7 @@ psql -d dbname -f SHAREDIR>/contrib/module>.sql
&auto-explain;
&btree-gin;
&btree-gist;
+ &check-dml;
&chkpass;
&citext;
&cube;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 9dce7ba..e40735f 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -96,6 +96,7 @@
+
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d299310..3d2082f 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -63,6 +63,9 @@ ExecutorStart_hook_type ExecutorStart_hook = NULL;
ExecutorRun_hook_type ExecutorRun_hook = NULL;
ExecutorEnd_hook_type ExecutorEnd_hook = NULL;
+/* Hook for plugin to get control in ExecCheckRTPerms() */
+ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL;
+
/* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc, int eflags);
static void ExecEndPlan(PlanState *planstate, EState *estate);
@@ -416,6 +419,9 @@ ExecCheckRTPerms(List *rangeTable)
{
ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
}
+
+ if (ExecutorCheckPerms_hook)
+ (*ExecutorCheckPerms_hook)(rangeTable);
}
/*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 820314c..caff1b4 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -74,6 +74,10 @@ extern PGDLLIMPORT ExecutorRun_hook_type ExecutorRun_hook;
typedef void (*ExecutorEnd_hook_type) (QueryDesc *queryDesc);
extern PGDLLIMPORT ExecutorEnd_hook_type ExecutorEnd_hook;
+/* Hook for plugins to get control in ExecCheckRTPerms() */
+typedef void (*ExecutorCheckPerms_hook_type) (List *);
+extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook;
+
/*
* prototypes from functions in execAmi.c