On Fri, Sep 09, 2016 at 09:57:21AM -0400, Peter Eisentraut wrote:
> Review of the patch in the commit fest:
> 
> - Various naming/spelling inconsistencies: In the source, the module
>   is require_where, the documentation titles it require-where, the GUC
>   parameters are requires_where.*, but incorrectly documented.

Fixed.

> - Unusual indentation in the Makefile

Fixed.

> - Needs tests

Still needs some fixing.

> - Not sure about errcode(ERRCODE_CARDINALITY_VIOLATION), which is
>   documented in the code as "this means something returned the wrong
>   number of rows".  I think ERRCODE_SYNTAX_ERROR or something from
>   nearby there would be better.

Changed to ERRCODE_SYNTAX_ERROR.  CARDINALITY_VIOLATION was a bit too
cute.

> - errhint() string should end with a period.

Fixed.

> - The 7th argument of DefineCustomBoolVariable() is of type int, not
>   bool, so passing false is somewhat wrong, even if it works.

Fixed.

> - There ought to be a _PG_fini() function that undoes what _PG_init()
>   does.

Fixed.

> - The documentation should be expanded and clarified.  Given that this
>   is a "training wheels" module, we can be extra clear here.  I would
>   like to see some examples at least.

Working on this.

> - The documentation is a bit incorrect about the ways to load this
>   module.  shared_preload_libraries is not necessary.  session_ and
>   local_ (with prep) should also work.

I'm not 100% sure I understand what you want here.  I did manage to
get the thing loaded without a restart via LOAD, but that's it so far.
Will continue to poke at it.

> - The claim in the documentation that only superusers can do things
>   with this module is not generally correct.

I think that the claims are fixed.  This is SUSET, at least in this
patch, because anything short of that that changes query behavior
seems incautious.

Best,
David.
-- 
David Fetter <david(at)fetter(dot)org> http://fetter.org/
Phone: +1 415 235 3778  AIM: dfetter666  Yahoo!: dfetter
Skype: davidfetter      XMPP: david(dot)fetter(at)gmail(dot)com

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
diff --git a/contrib/Makefile b/contrib/Makefile
index 25263c0..4bd456f 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -40,6 +40,7 @@ SUBDIRS = \
                pgstattuple     \
                pg_visibility   \
                postgres_fdw    \
+               require_where   \
                seg             \
                spi             \
                tablefunc       \
diff --git a/contrib/require_where/Makefile b/contrib/require_where/Makefile
new file mode 100644
index 0000000..0cf3663
--- /dev/null
+++ b/contrib/require_where/Makefile
@@ -0,0 +1,17 @@
+# contrib/require_where/Makefile
+
+MODULE_big = require_where
+OBJS = require_where.o
+
+PGFILEDESC = 'require_where - require DELETE and/or UPDATE to have a WHERE 
clause'
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS = $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/require_where
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_builddir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/require_where/require_where.c 
b/contrib/require_where/require_where.c
new file mode 100644
index 0000000..181b3bb
--- /dev/null
+++ b/contrib/require_where/require_where.c
@@ -0,0 +1,92 @@
+/*
+ * --------------------------------------------------------------------------
+ *
+ * require_where.c
+ *
+ * Copyright (C) 2016, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *                     contrib/require_where/require_where.c
+ *
+ * --------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "fmgr.h"
+
+#include "parser/analyze.h"
+
+#include "utils/elog.h"
+#include "utils/guc.h"
+
+PG_MODULE_MAGIC;
+
+void           _PG_init(void);
+void           _PG_fini(void);
+
+static         post_parse_analyze_hook_type original_post_parse_analyze_hook = 
NULL;
+static bool    require_where_delete = false;
+static bool    require_where_update = false;
+
+static void
+require_where_check(ParseState *pstate, Query *query)
+{
+
+       if (require_where_delete && query->commandType == CMD_DELETE)
+       {
+               Assert(query->jointree != NULL);
+               if (query->jointree->quals == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("DELETE requires a WHERE 
clause"),
+                                        errhint("To delete all rows, use 
\"WHERE true\" or similar.")));
+       }
+
+       if (require_where_update && query->commandType == CMD_UPDATE)
+       {
+               Assert(query->jointree != NULL);
+               if (query->jointree->quals == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_SYNTAX_ERROR),
+                                        errmsg("UPDATE requires a WHERE 
clause"),
+                                        errhint("To update all rows, use 
\"WHERE true\" or similar.")));
+       }
+
+       if (original_post_parse_analyze_hook != NULL)
+               (*original_post_parse_analyze_hook) (pstate, query);
+}
+
+void
+_PG_init(void)
+{
+       DefineCustomBoolVariable("require_where.delete",
+                                                        "Require every DELETE 
statement to have a WHERE clause.",
+                                                        NULL,
+                                                        &require_where_delete,
+                                                        false,
+                                                        PGC_SUSET,
+                                                        0,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL);
+
+       DefineCustomBoolVariable("require_where.update",
+                                                        "Require every UPDATE 
statement to have a WHERE clause.",
+                                                        NULL,
+                                                        &require_where_update,
+                                                        false,
+                                                        PGC_SUSET,
+                                                        0,
+                                                        NULL,
+                                                        NULL,
+                                                        NULL);
+
+       original_post_parse_analyze_hook = post_parse_analyze_hook;
+       post_parse_analyze_hook = require_where_check;
+}
+
+void
+_PG_fini(void)
+{
+       post_parse_analyze_hook = original_post_parse_analyze_hook;
+}
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index c8708ec..7ca62a4 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -135,6 +135,7 @@ CREATE EXTENSION <replaceable>module_name</> FROM 
unpackaged;
  &pgtrgm;
  &pgvisibility;
  &postgres-fdw;
+ &require_where;
  &seg;
  &sepgsql;
  &contrib-spi;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 4383711..54b6bdc 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -140,6 +140,7 @@
 <!ENTITY pgtrgm          SYSTEM "pgtrgm.sgml">
 <!ENTITY pgvisibility    SYSTEM "pgvisibility.sgml">
 <!ENTITY postgres-fdw    SYSTEM "postgres-fdw.sgml">
+<!ENTITY require_where   SYSTEM "require_where.sgml">
 <!ENTITY seg             SYSTEM "seg.sgml">
 <!ENTITY contrib-spi     SYSTEM "contrib-spi.sgml">
 <!ENTITY sepgsql         SYSTEM "sepgsql.sgml">
diff --git a/doc/src/sgml/require_where.sgml b/doc/src/sgml/require_where.sgml
new file mode 100644
index 0000000..0b2e290
--- /dev/null
+++ b/doc/src/sgml/require_where.sgml
@@ -0,0 +1,89 @@
+<!-- doc/src/sgml/require_where.sgml -->
+
+<sect1 id="require_where" xreflabel="require_where">
+ <title>require_where</title>
+
+ <indexterm zone="require_where">
+  <primary>require_where</primary>
+ </indexterm>
+
+ <para>
+  This module allows a superuser to require that WHERE clauses on
+  either DELETE or UPDATE not be empty using a custom GUC to control
+  each.
+ </para>
+
+ <para>
+  To use this module, you must include <literal>require_where</literal>
+  in the <xref linkend="guc-shared-preload-libraries"> parameter.
+ </para>
+
+ <para>
+  Here is an example showing how to set up a database cluster with
+  <literal>require_where</literal>.
+<screen>
+$ psql -U postgres
+# SHOW shared_preload_libraries; /* Make sure not to clobber something by 
accident */
+
+If you found something, 
+# ALTER SYSTEM SET 
shared_preload_libraries='the,stuff,you,found,require_where';
+
+Otherwise,
+# ALTER SYSTEM SET shared_preload_libraries='require_where';
+
+Then restart <productname>PostgreSQL</productname>
+</screen>
+ </para>
+
+
+ <sect2>
+  <title>GUC Parameters</title>
+
+  <variablelist>
+   <varlistentry id="guc-require_where-delete" 
xreflabel="require_where.delete">
+    <term>
+     <varname>require_where.delete</varname> (<type>boolean</type>)
+     <indexterm>
+      <primary><varname>require_where.delete</varname> configuration 
parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      This parameter defaults to <literal>false</literal>.  When set
+      to <literal>true</literal>, every <literal>DELETE</literal> requires
+      some kind of <literal>WHERE</literal> clause in order to
+      execute.  When <literal>DELETE</literal> is meant to span the
+      entire relation, one can issue a <literal>WHERE true</literal>
+      or equivalent.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry id="guc-require_where-update" 
xreflabel="require_where.update">
+    <term>
+     <varname>require_where.update</varname> (<type>boolean</type>)
+     <indexterm>
+      <primary><varname>require_where.update</varname> configuration 
parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      This parameter defaults to <literal>false</literal>.  When set
+      to <literal>true</literal>, every <literal>UPDATE</literal> requires
+      some kind of <literal>WHERE</literal> clause in order to
+      execute.  When <literal>UPDATE</literal> is meant to span the
+      entire relation, one can issue a <literal>WHERE true</literal>
+      or equivalent.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </sect2>
+
+ <sect2>
+  <title>Authors</title>
+
+  <para>
+   David Fetter <email>da...@fetter.org</email>, Oakland, California, USA
+  </para>
+ </sect2>
+</sect1>
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to