Add to_regtypemod function to extract typemod from a string type name.
authorTom Lane <[email protected]>
Wed, 20 Mar 2024 21:11:23 +0000 (17:11 -0400)
committerTom Lane <[email protected]>
Wed, 20 Mar 2024 21:11:28 +0000 (17:11 -0400)
In combination with to_regtype, this allows converting a string to
the "canonicalized" form emitted by format_type.  That usage requires
parsing the string twice, which is slightly annoying but not really
too expensive.  We considered alternatives such as returning a record
type, but that way was notationally uglier than this, and possibly
less flexible.

Like to_regtype(), we'd rather that this return NULL for any bad
input, but the underlying type-parsing logic isn't yet capable of
not throwing syntax errors.  Adjust the documentation for both
functions to point that out.

In passing, fix up a couple of nearby entries in the System Catalog
Information Functions table that had not gotten the word about our
since-v13 convention for displaying function usage examples.

David Wheeler and Erik Wienhold, reviewed by Pavel Stehule, Jim Jones,
and others.

Discussion: https://postgr.es/m/DF2324CA-2673-4ABE-B382-26B5770B6AA3@justatheory.com

doc/src/sgml/func.sgml
src/backend/utils/adt/regproc.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/test/regress/expected/regproc.out
src/test/regress/sql/regproc.sql

index 5b225ccf4f5f14f738036deab06b71ca91d872c7..030ea8affdfc6b2c1be3bd47f78f0458f7272c56 100644 (file)
@@ -24872,7 +24872,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
 
      <tbody>
       <row>
-       <entry role="func_table_entry"><para role="func_signature">
+       <entry id="format-type" xreflabel="format_type" role="func_table_entry"><para role="func_signature">
         <indexterm>
          <primary>format_type</primary>
         </indexterm>
@@ -25387,18 +25387,8 @@ SELECT currval(pg_get_serial_sequence('sometable', 'id'));
         OID for comparison purposes but displays as a type name.
        </para>
        <para>
-        For example:
-<programlisting>
-SELECT pg_typeof(33);
- pg_typeof
------------
- integer
-
-SELECT typlen FROM pg_type WHERE oid = pg_typeof(33);
- typlen
---------
-      4
-</programlisting>
+        <literal>pg_typeof(33)</literal>
+        <returnvalue>integer</returnvalue>
        </para></entry>
       </row>
 
@@ -25418,18 +25408,12 @@ SELECT typlen FROM pg_type WHERE oid = pg_typeof(33);
         collatable data type, then an error is raised.
        </para>
        <para>
-        For example:
-<programlisting>
-SELECT collation for (description) FROM pg_description LIMIT 1;
- pg_collation_for
-------------------
- "default"
-
-SELECT collation for ('foo' COLLATE "de_DE");
- pg_collation_for
-------------------
- "de_DE"
-</programlisting>
+        <literal>collation for ('foo'::text)</literal>
+        <returnvalue>"default"</returnvalue>
+       </para>
+       <para>
+        <literal>collation for ('foo' COLLATE "de_DE")</literal>
+        <returnvalue>"de_DE"</returnvalue>
        </para></entry>
       </row>
 
@@ -25570,7 +25554,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
       </row>
 
       <row>
-       <entry role="func_table_entry"><para role="func_signature">
+       <entry id="to-regtype" xreflabel="to_regtype" role="func_table_entry"><para role="func_signature">
         <indexterm>
          <primary>to_regtype</primary>
         </indexterm>
@@ -25578,11 +25562,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
         <returnvalue>regtype</returnvalue>
        </para>
        <para>
-        Translates a textual type name to its OID.  A similar result is
-        obtained by casting the string to type <type>regtype</type> (see
-        <xref linkend="datatype-oid"/>); however, this function will return
-        <literal>NULL</literal> rather than throwing an error if the name is
-        not found.
+        Parses a string of text, extracts a potential type name from it,
+        and translates that name into a type OID.  A syntax error in the
+        string will result in an error; but if the string is a
+        syntactically valid type name that happens not to be found in the
+        catalogs, the result is <literal>NULL</literal>.  A similar result
+        is obtained by casting the string to type <type>regtype</type>
+        (see <xref linkend="datatype-oid"/>), except that that will throw
+        error for name not found.
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>to_regtypemod</primary>
+        </indexterm>
+        <function>to_regtypemod</function> ( <type>text</type> )
+        <returnvalue>integer</returnvalue>
+       </para>
+       <para>
+        Parses a string of text, extracts a potential type name from it,
+        and translates its type modifier, if any.  A syntax error in the
+        string will result in an error; but if the string is a
+        syntactically valid type name that happens not to be found in the
+        catalogs, the result is <literal>NULL</literal>.  The result is
+        <literal>-1</literal> if no type modifier is present.
+       </para>
+       <para>
+        <function>to_regtypemod</function> can be combined with
+        <xref linkend="to-regtype"/> to produce appropriate inputs for
+        <xref linkend="format-type"/>, allowing a string representing a
+        type name to be canonicalized.
+       </para>
+       <para>
+        <literal>format_type(to_regtype('varchar(32)'), to_regtypemod('varchar(32)'))</literal>
+        <returnvalue>character varying(32)</returnvalue>
        </para></entry>
       </row>
      </tbody>
index 1e3bf3f5fd6f6b63a365d6129ff7d72c670fc494..18d5b7d166c48035920529bef370f293e3c16eb4 100644 (file)
@@ -1220,6 +1220,26 @@ to_regtype(PG_FUNCTION_ARGS)
    PG_RETURN_DATUM(result);
 }
 
+/*
+ * to_regtypemod   - converts "typename" to type modifier
+ *
+ * If the name is not found, we return NULL.
+ */
+Datum
+to_regtypemod(PG_FUNCTION_ARGS)
+{
+   char       *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
+   Oid         typid;
+   int32       typmod;
+   ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+   /* We rely on parseTypeString to parse the input. */
+   if (!parseTypeString(typ_name, &typid, &typmod, (Node *) &escontext))
+       PG_RETURN_NULL();
+
+   PG_RETURN_INT32(typmod);
+}
+
 /*
  * regtypeout      - converts type OID to "typ_name"
  */
index b05db0fa0ae39b80b1013096dc51e071f154e62e..3c4e49b73a03a410d85ba1058144f824c4ff7d59 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202403201
+#define CATALOG_VERSION_NO 202403202
 
 #endif
index 177d81a891c3341ddd8722b2cad6094ba2a5ab1c..042f66f714558cd6aa8920225f7e01644709bbe2 100644 (file)
 { oid => '3493', descr => 'convert type name to regtype',
   proname => 'to_regtype', provolatile => 's', prorettype => 'regtype',
   proargtypes => 'text', prosrc => 'to_regtype' },
+{ oid => '8401', descr => 'convert type name to type modifier',
+  proname => 'to_regtypemod', provolatile => 's', prorettype => 'int4',
+  proargtypes => 'text', prosrc => 'to_regtypemod' },
 { oid => '1079', descr => 'convert text to regclass',
   proname => 'regclass', provolatile => 's', prorettype => 'regclass',
   proargtypes => 'text', prosrc => 'text_regclass' },
index a9420850b87ec5872047dc4073ff603b5bd663a6..97b917502cabbaeb20b5921ce952bf5fb28b57fb 100644 (file)
@@ -447,6 +447,43 @@ SELECT to_regnamespace('foo.bar');
  
 (1 row)
 
+-- Test to_regtypemod
+SELECT to_regtypemod('text');
+ to_regtypemod 
+---------------
+            -1
+(1 row)
+
+SELECT to_regtypemod('timestamp(4)');
+ to_regtypemod 
+---------------
+             4
+(1 row)
+
+SELECT to_regtypemod('no_such_type(4)');
+ to_regtypemod 
+---------------
+              
+(1 row)
+
+SELECT format_type(to_regtype('varchar(32)'), to_regtypemod('varchar(32)'));
+      format_type      
+-----------------------
+ character varying(32)
+(1 row)
+
+SELECT format_type(to_regtype('bit'), to_regtypemod('bit'));
+ format_type 
+-------------
+ bit(1)
+(1 row)
+
+SELECT format_type(to_regtype('"bit"'), to_regtypemod('"bit"'));
+ format_type 
+-------------
+ "bit"
+(1 row)
+
 -- Test soft-error API
 SELECT * FROM pg_input_error_info('ng_catalog.pg_class', 'regclass');
                     message                    | detail | hint | sql_error_code 
index de2aa881a8dc0aa84839562ab3ad0555c7cc6219..232289ac398234f0eef170b1d130df41fb463939 100644 (file)
@@ -123,6 +123,14 @@ SELECT to_regnamespace('Nonexistent');
 SELECT to_regnamespace('"Nonexistent"');
 SELECT to_regnamespace('foo.bar');
 
+-- Test to_regtypemod
+SELECT to_regtypemod('text');
+SELECT to_regtypemod('timestamp(4)');
+SELECT to_regtypemod('no_such_type(4)');
+SELECT format_type(to_regtype('varchar(32)'), to_regtypemod('varchar(32)'));
+SELECT format_type(to_regtype('bit'), to_regtypemod('bit'));
+SELECT format_type(to_regtype('"bit"'), to_regtypemod('"bit"'));
+
 -- Test soft-error API
 
 SELECT * FROM pg_input_error_info('ng_catalog.pg_class', 'regclass');