Add pg_basetype() function to extract a domain's base type.
authorTom Lane <[email protected]>
Sat, 30 Mar 2024 17:57:19 +0000 (13:57 -0400)
committerTom Lane <[email protected]>
Sat, 30 Mar 2024 17:57:19 +0000 (13:57 -0400)
This SQL-callable function behaves much like our internal utility
function getBaseType(), except it returns NULL rather than failing for
an invalid type OID.  (That behavior is modeled on our experience with
other catalog-inquiry functions such as the ACL checking functions.)
The key advantage over doing a join to pg_type is that it will loop
as needed to find the bottom base type of a nest of domains.

Steve Chavez, reviewed by jian he and others

Discussion: https://postgr.es/m/CAGRrpzZSX8j=MQcbCSEisFA=ic=K3bknVfnFjAv1diVJxFHJvg@mail.gmail.com

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

index 93b0bc2bc6e40a4d0fc60a3fe3662891da3a5714..b694b2883c32997050465b9a2dad0d91a0c41798 100644 (file)
@@ -25129,6 +25129,30 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>pg_basetype</primary>
+        </indexterm>
+        <function>pg_basetype</function> ( <type>regtype</type> )
+        <returnvalue>regtype</returnvalue>
+       </para>
+       <para>
+        Returns the OID of the base type of a domain identified by its
+        type OID.  If the argument is the OID of a non-domain type,
+        returns the argument as-is.  Returns NULL if the argument is
+        not a valid type OID.  If there's a chain of domain dependencies,
+        it will recurse until finding the base type.
+       </para>
+       <para>
+        Assuming <literal>CREATE DOMAIN mytext AS text</literal>:
+       </para>
+       <para>
+        <literal>pg_basetype('mytext'::regtype)</literal>
+        <returnvalue>text</returnvalue>
+       </para></entry>
+      </row>
+
       <row>
        <entry id="pg-char-to-encoding" role="func_table_entry"><para role="func_signature">
         <indexterm>
index d4a92d0b3fa2fbc04867f7db9e4b3d7766c0e91f..0e6c45807a1ec840068c79a3310e7ebc1a8613f3 100644 (file)
@@ -44,6 +44,7 @@
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/ruleutils.h"
+#include "utils/syscache.h"
 #include "utils/timestamp.h"
 
 
@@ -566,6 +567,50 @@ pg_typeof(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * Return the base type of the argument.
+ *     If the given type is a domain, return its base type;
+ *     otherwise return the type's own OID.
+ *     Return NULL if the type OID doesn't exist or points to a
+ *     non-existent base type.
+ *
+ * This is a SQL-callable version of getBaseType().  Unlike that function,
+ * we don't want to fail for a bogus type OID; this is helpful to keep race
+ * conditions from turning into query failures when scanning the catalogs.
+ * Hence we need our own implementation.
+ */
+Datum
+pg_basetype(PG_FUNCTION_ARGS)
+{
+   Oid         typid = PG_GETARG_OID(0);
+
+   /*
+    * We loop to find the bottom base type in a stack of domains.
+    */
+   for (;;)
+   {
+       HeapTuple   tup;
+       Form_pg_type typTup;
+
+       tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+       if (!HeapTupleIsValid(tup))
+           PG_RETURN_NULL();   /* return NULL for bogus OID */
+       typTup = (Form_pg_type) GETSTRUCT(tup);
+       if (typTup->typtype != TYPTYPE_DOMAIN)
+       {
+           /* Not a domain, so done */
+           ReleaseSysCache(tup);
+           break;
+       }
+
+       typid = typTup->typbasetype;
+       ReleaseSysCache(tup);
+   }
+
+   PG_RETURN_OID(typid);
+}
+
+
 /*
  * Implementation of the COLLATE FOR expression; returns the collation
  * of the argument.
index 0303973822e787fe0029e3dbbe79109730fce3fa..86ace33dbeb63cbba65e1783ab4a259f9f5e87b0 100644 (file)
@@ -57,6 +57,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202403301
+#define CATALOG_VERSION_NO 202403302
 
 #endif
index 07023ee61d9ce0e19e9d7279a8003affb11a5462..134e3b22fd8feb74b0114cf971f0dc8063c98655 100644 (file)
 { oid => '1619', descr => 'type of the argument',
   proname => 'pg_typeof', proisstrict => 'f', provolatile => 's',
   prorettype => 'regtype', proargtypes => 'any', prosrc => 'pg_typeof' },
+{ oid => '8312', descr => 'base type of a domain type',
+  proname => 'pg_basetype', provolatile => 's', prorettype => 'regtype',
+  proargtypes => 'regtype', prosrc => 'pg_basetype' },
 { oid => '3162',
   descr => 'collation of the argument; implementation of the COLLATION FOR expression',
   proname => 'pg_collation_for', proisstrict => 'f', provolatile => 's',
index dc58793e3f515e925ebc3d3c77b055ccc9145734..fa8459e10ff8b7509a633bec15c09bf00673cc67 100644 (file)
@@ -1244,6 +1244,31 @@ alter domain testdomain1 rename constraint unsigned to unsigned_foo;
 alter domain testdomain1 drop constraint unsigned_foo;
 drop domain testdomain1;
 --
+-- Get the base type of a domain
+--
+create domain mytext as text;
+create domain mytext_child_1 as mytext;
+select pg_basetype('mytext'::regtype);
+ pg_basetype 
+-------------
+ text
+(1 row)
+
+select pg_basetype('mytext_child_1'::regtype);
+ pg_basetype 
+-------------
+ text
+(1 row)
+
+select pg_basetype(1);  -- expect NULL not error
+ pg_basetype 
+-------------
+(1 row)
+
+drop domain mytext cascade;
+NOTICE:  drop cascades to type mytext_child_1
+--
 -- Information schema
 --
 SELECT * FROM information_schema.column_domain_usage
index ae1b7fbf97aaa4689ed658017a1e66094a59de5f..763c68f1db64119237f8399c0ef530eff66e1bff 100644 (file)
@@ -839,6 +839,18 @@ alter domain testdomain1 rename constraint unsigned to unsigned_foo;
 alter domain testdomain1 drop constraint unsigned_foo;
 drop domain testdomain1;
 
+--
+-- Get the base type of a domain
+--
+create domain mytext as text;
+create domain mytext_child_1 as mytext;
+
+select pg_basetype('mytext'::regtype);
+select pg_basetype('mytext_child_1'::regtype);
+select pg_basetype(1);  -- expect NULL not error
+
+drop domain mytext cascade;
+
 
 --
 -- Information schema