contrib/isn: Make weak mode a GUC setting, and fix related functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 16 Mar 2025 17:45:48 +0000 (13:45 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 16 Mar 2025 17:45:48 +0000 (13:45 -0400)
isn's weak mode used to be a simple static variable, settable only
via the isn_weak(boolean) function.  This wasn't optimal, as this
means it doesn't respect transactions nor respond to RESET ALL.

This patch makes isn.weak a GUC parameter instead, so that
it acts like any other user-settable parameter.

The isn_weak() functions are retained for backwards compatibility.
But we must fix their volatility markings: they were marked IMMUTABLE
which is surely incorrect, and PARALLEL RESTRICTED which isn't right
for GUC-related functions either.  Mark isn_weak(boolean) as
VOLATILE and PARALLEL UNSAFE, matching set_config().  Mark isn_weak()
as STABLE and PARALLEL SAFE, matching current_setting().

Reported-by: Viktor Holmberg <v@viktorh.net>
Diagnosed-by: Daniel Gustafsson <daniel@yesql.se>
Author: Viktor Holmberg <v@viktorh.net>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/790bc1f9-74dc-4b50-94d2-8147315b1556@Spark

contrib/isn/Makefile
contrib/isn/expected/isn.out
contrib/isn/isn--1.2--1.3.sql [new file with mode: 0644]
contrib/isn/isn.c
contrib/isn/isn.control
contrib/isn/isn.h
contrib/isn/meson.build
contrib/isn/sql/isn.sql
doc/src/sgml/isn.sgml

index 1037506c705646253d33cf7930b1196cdc370d51..bfe8977bb648d39c4c0e90b7e3c177475a5be680 100644 (file)
@@ -3,8 +3,8 @@
 MODULES = isn
 
 EXTENSION = isn
-DATA = isn--1.1.sql isn--1.1--1.2.sql \
-   isn--1.0--1.1.sql
+DATA = isn--1.0--1.1.sql isn--1.1.sql \
+   isn--1.1--1.2.sql isn--1.2--1.3.sql
 PGFILEDESC = "isn - data types for international product numbering standards"
 
 # the other .h files are data tables, we don't install those
index 2f05b7eb861ac68a5b25f2d4de4cd8884ac235fd..c533768d926c8443b21c9527032f42dc67858423 100644 (file)
@@ -279,6 +279,50 @@ FROM (VALUES ('9780123456786', 'UPC'),
  9771234567003 | ISSN  | t  |                |                                                        |        | 
 (3 rows)
 
+--
+-- test weak mode
+--
+SELECT '2222222222221'::ean13;  -- fail
+ERROR:  invalid check digit for EAN13 number: "2222222222221", should be 2
+LINE 1: SELECT '2222222222221'::ean13;
+               ^
+SET isn.weak TO TRUE;
+SELECT '2222222222221'::ean13;
+      ean13       
+------------------
+ 222-222222222-2!
+(1 row)
+
+SELECT is_valid('2222222222221'::ean13);
+ is_valid 
+----------
+ f
+(1 row)
+
+SELECT make_valid('2222222222221'::ean13);
+   make_valid    
+-----------------
+ 222-222222222-2
+(1 row)
+
+SELECT isn_weak();  -- backwards-compatibility wrappers for accessing the GUC
+ isn_weak 
+----------
+ t
+(1 row)
+
+SELECT isn_weak(false);
+ isn_weak 
+----------
+ f
+(1 row)
+
+SHOW isn.weak;
+ isn.weak 
+----------
+ off
+(1 row)
+
 --
 -- cleanup
 --
diff --git a/contrib/isn/isn--1.2--1.3.sql b/contrib/isn/isn--1.2--1.3.sql
new file mode 100644 (file)
index 0000000..a8e30ff
--- /dev/null
@@ -0,0 +1,7 @@
+/* contrib/isn/isn--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION isn UPDATE TO '1.3'" to load this file. \quit
+
+ALTER FUNCTION isn_weak(boolean) VOLATILE PARALLEL UNSAFE;
+ALTER FUNCTION isn_weak() STABLE PARALLEL SAFE;
index db765ee490d55045cdcc7fe1ad2b189974e44e10..5783c188737d96e42c43cd633b9c0df91ae8752a 100644 (file)
@@ -21,6 +21,7 @@
 #include "UPC.h"
 #include "fmgr.h"
 #include "isn.h"
+#include "utils/guc.h"
 
 PG_MODULE_MAGIC;
 
@@ -39,6 +40,7 @@ enum isn_type
 
 static const char *const isn_names[] = {"EAN13/UPC/ISxN", "EAN13/UPC/ISxN", "EAN13", "ISBN", "ISMN", "ISSN", "UPC"};
 
+/* GUC value */
 static bool g_weak = false;
 
 
@@ -929,6 +931,20 @@ _PG_init(void)
        if (!check_table(UPC_range, UPC_index))
            elog(ERROR, "UPC failed check");
    }
+
+   /* Define a GUC variable for weak mode. */
+   DefineCustomBoolVariable("isn.weak",
+                            "Accept input with invalid ISN check digits.",
+                            NULL,
+                            &g_weak,
+                            false,
+                            PGC_USERSET,
+                            0,
+                            NULL,
+                            NULL,
+                            NULL);
+
+   MarkGUCPrefixReserved("isn");
 }
 
 /* isn_out
@@ -1109,17 +1125,16 @@ make_valid(PG_FUNCTION_ARGS)
 
 /* this function temporarily sets weak input flag
  * (to lose the strictness of check digit acceptance)
- * It's a helper function, not intended to be used!!
  */
 PG_FUNCTION_INFO_V1(accept_weak_input);
 Datum
 accept_weak_input(PG_FUNCTION_ARGS)
 {
-#ifdef ISN_WEAK_MODE
-   g_weak = PG_GETARG_BOOL(0);
-#else
-   /* function has no effect */
-#endif                         /* ISN_WEAK_MODE */
+   bool        newvalue = PG_GETARG_BOOL(0);
+
+   (void) set_config_option("isn.weak", newvalue ? "on" : "off",
+                            PGC_USERSET, PGC_S_SESSION,
+                            GUC_ACTION_SET, true, 0, false);
    PG_RETURN_BOOL(g_weak);
 }
 
index 1cb5e2b23403619fbb0dcbcc1b47be79cc99284d..e7daea52b8451d5da813f4cbe03dcd49593be68a 100644 (file)
@@ -1,6 +1,6 @@
 # isn extension
 comment = 'data types for international product numbering standards'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/isn'
 relocatable = true
 trusted = true
index 038eb362c39815291a55374e30184db1fee5f044..399896ad417c02b60bf7ecee0c1ea1f64314d864 100644 (file)
@@ -18,7 +18,6 @@
 #include "fmgr.h"
 
 #undef ISN_DEBUG
-#define ISN_WEAK_MODE
 
 /*
  * uint64 is the internal storage format for ISNs.
index fbbeeff01bb66978ead2e812de5054969c8cdfaf..39cf781684eee9756145efed55f79609a9e6e4fe 100644 (file)
@@ -19,8 +19,9 @@ contrib_targets += isn
 install_data(
   'isn.control',
   'isn--1.0--1.1.sql',
-  'isn--1.1--1.2.sql',
   'isn--1.1.sql',
+  'isn--1.1--1.2.sql',
+  'isn--1.2--1.3.sql',
   kwargs: contrib_data_args,
 )
 
index 2c2ea077d1e145f01ad4c383769b5d938259406c..043e5580b0d367330e5ab6ff6550a92a342ebc6e 100644 (file)
@@ -120,6 +120,19 @@ FROM (VALUES ('9780123456786', 'UPC'),
       AS a(str,typ),
      LATERAL pg_input_error_info(a.str, a.typ) as errinfo;
 
+--
+-- test weak mode
+--
+SELECT '2222222222221'::ean13;  -- fail
+SET isn.weak TO TRUE;
+SELECT '2222222222221'::ean13;
+SELECT is_valid('2222222222221'::ean13);
+SELECT make_valid('2222222222221'::ean13);
+
+SELECT isn_weak();  -- backwards-compatibility wrappers for accessing the GUC
+SELECT isn_weak(false);
+SHOW isn.weak;
+
 --
 -- cleanup
 --
index 45a867d98c22815739d03a416ad475934b2bf871..1f08ada621846b07b4a5b82d1b32ca6966c54e2d 100644 (file)
   <para>
    The <filename>isn</filename> module provides the standard comparison operators,
    plus B-tree and hash indexing support for all these data types.  In
-   addition there are several specialized functions; shown in <xref linkend="isn-functions"/>.
+   addition, there are several specialized functions, shown in <xref linkend="isn-functions"/>.
    In this table,
    <type>isn</type> means any one of the module's data types.
   </para>
      <tbody>
       <row>
        <entry role="func_table_entry"><para role="func_signature">
-        <indexterm><primary>isn_weak</primary></indexterm>
-        <function>isn_weak</function> ( <type>boolean</type> )
-        <returnvalue>boolean</returnvalue>
+        <indexterm><primary>make_valid</primary></indexterm>
+        <function>make_valid</function> ( <type>isn</type> )
+        <returnvalue>isn</returnvalue>
        </para>
        <para>
-        Sets the weak input mode, and returns new setting.
+        Clears the invalid-check-digit flag of the value.
        </para></entry>
       </row>
 
       <row>
        <entry role="func_table_entry"><para role="func_signature">
-        <function>isn_weak</function> ()
+        <indexterm><primary>is_valid</primary></indexterm>
+        <function>is_valid</function> ( <type>isn</type> )
         <returnvalue>boolean</returnvalue>
        </para>
        <para>
-        Returns the current status of the weak mode.
+        Checks for the presence of the invalid-check-digit flag.
        </para></entry>
       </row>
 
       <row>
        <entry role="func_table_entry"><para role="func_signature">
-        <indexterm><primary>make_valid</primary></indexterm>
-        <function>make_valid</function> ( <type>isn</type> )
-        <returnvalue>isn</returnvalue>
+        <indexterm><primary>isn_weak</primary></indexterm>
+        <function>isn_weak</function> ( <type>boolean</type> )
+        <returnvalue>boolean</returnvalue>
        </para>
        <para>
-        Validates an invalid number (clears the invalid flag).
+        Sets the weak input mode, and returns the new setting.
+        This function is retained for backward compatibility.
+        The recommended way to set weak mode is via
+        the <varname>isn.weak</varname> configuration parameter.
        </para></entry>
       </row>
 
       <row>
        <entry role="func_table_entry"><para role="func_signature">
-        <indexterm><primary>is_valid</primary></indexterm>
-        <function>is_valid</function> ( <type>isn</type> )
+        <function>isn_weak</function> ()
         <returnvalue>boolean</returnvalue>
        </para>
        <para>
-        Checks for the presence of the invalid flag.
+        Returns the current status of the weak mode.
+        This function is retained for backward compatibility.
+        The recommended way to check weak mode is via
+        the <varname>isn.weak</varname> configuration parameter.
        </para></entry>
       </row>
      </tbody>
     </tgroup>
   </table>
+ </sect2>
 
-  <para>
-   <firstterm>Weak</firstterm> mode is used to be able to insert invalid data
-   into a table. Invalid means the check digit is wrong, not that there are
-   missing numbers.
-  </para>
+ <sect2 id="isn-configuration-parameters">
+  <title>Configuration Parameters</title>
+
+  <variablelist>
+   <varlistentry id="isn-configuration-parameters-weak">
+    <term>
+     <varname>isn.weak</varname> (<type>boolean</type>)
+     <indexterm>
+      <primary><varname>isn.weak</varname> configuration parameter</primary>
+     </indexterm>
+    </term>
+    <listitem>
+     <para>
+      <varname>isn.weak</varname> enables the weak input mode, which allows
+      ISN input values to be accepted even when their check digit is wrong.
+      The default is <literal>false</literal>, which rejects invalid check
+      digits.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
 
   <para>
    Why would you want to use the weak mode? Well, it could be that
   </para>
 
   <para>
-   You can also force the insertion of invalid numbers even when not in the
-   weak mode, by appending the <literal>!</literal> character at the end of the
-   number.
+   You can also force the insertion of marked-as-invalid numbers even when not
+   in the weak mode, by appending the <literal>!</literal> character at the
+   end of the number.
   </para>
 
   <para>
@@ -366,11 +389,11 @@ SELECT issn('3251231?');
 SELECT ismn('979047213542?');
 
 --Using the weak mode:
-SELECT isn_weak(true);
+SET isn.weak TO true;
 INSERT INTO test VALUES('978-0-11-000533-4');
 INSERT INTO test VALUES('9780141219307');
 INSERT INTO test VALUES('2-205-00876-X');
-SELECT isn_weak(false);
+SET isn.weak TO false;
 
 SELECT id FROM test WHERE NOT is_valid(id);
 UPDATE test SET id = make_valid(id) WHERE id = '2-205-00876-X!';