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 @@
+<!-- $PostgreSQL$ -->
+
+<sect1 id="check-dml">
+ <title>check_dml</title>
+
+ <indexterm zone="check-dml">
+  <primary>check_dml</primary>
+ </indexterm>
+
+ <para>
+  The <filename>check_dml</filename> allows <acronym>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.
+ </para>
+
+ <para>
+  The module provides no SQL-accessible functions.  It should be loaded
+  into all sessions by including <literal>check_dml</> in
+  <xref linkend="guc-shared-preload-libraries"> in
+  <filename>postgresql.conf</>.
+ </para>
+
+ <sect2>
+  <title>Configuration parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term>
+     <varname>check_dml.table_prefix</varname> (<type>string</type>)
+    </term>
+    <indexterm>
+     <primary><varname>check_dml.table_prefix</> configuration parameter</primary>
+    </indexterm>
+    <listitem>
+     <para>
+      When <varname>check_dml.table_prefix</varname> 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.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>
+   In order to set these parameters in your <filename>postgresql.conf</> file,
+   you will need to add <literal>auto_explain</> to
+   <xref linkend="guc-custom-variable-classes">.  Typical usage might be:
+  </para>
+
+  <programlisting>
+# postgresql.conf
+shared_preload_libraries = 'check_dml'
+
+custom_variable_classes = 'check_dml'
+  </programlisting>
+
+  <para>
+   <filename>check_dml</> could be used to implement per-user access policies
+   by using
+   <literal>ALTER USER ... SET (check_dml.table_prefix = ...)</literal>.
+   Whether this is actually any better than existing permissions-checking
+   mechanisms is arguable.
+  </para>
+ </sect2>
+
+</sect1>
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 <replaceable>SHAREDIR</>/contrib/<replaceable>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 @@
 <!entity auto-explain    SYSTEM "auto-explain.sgml">
 <!entity btree-gin       SYSTEM "btree-gin.sgml">
 <!entity btree-gist      SYSTEM "btree-gist.sgml">
+<!entity check-dml       SYSTEM "check-dml.sgml">
 <!entity chkpass         SYSTEM "chkpass.sgml">
 <!entity citext          SYSTEM "citext.sgml">
 <!entity cube            SYSTEM "cube.sgml">
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
