jian he <jian.universal...@gmail.com> writes: > trying to do it this way. > not sure the following error message is expected.
> SELECT pg_basetype(-1); > ERROR: cache lookup failed for type 4294967295 Yeah, that's not really OK. You could say it's fine for bogus input, but we've found over the years that it's better for catalog inspection functions like this to be forgiving of bad input. Otherwise, your query can blow up in unexpected ways due to race conditions (ie somebody just dropped the type you are interested in). A fairly common solution to that is to return NULL for bad input, but in this case we could just have it return the OID unchanged. Either way though, we can't use getBaseType as-is. We could imagine extending that function to support a "noerror"-like flag, but I believe it's already a hot-spot and I'd rather not complicate it further. So what I suggest doing is just duplicating the code; there's not very much of it. I did a little polishing of the docs and test cases too, ending with the v7 attached. I think this is about ready to go unless there are objections to the definition. Not sure what I think about your 0002 proposal to extend \dD with this. Aside from the server-version-compatibility problem, I think it's about 90% redundant because \dD already shows the immediate base type. The new column would only be different in the case of nested domains, which I think are not common. \dD's output is already pretty wide, so on the whole I'm inclined to leave it alone. regards, tom lane
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 93b0bc2bc6..b3687b3645 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -25129,6 +25129,29 @@ 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 not the OID of a domain type, + returns the argument as-is. 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> diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index d4a92d0b3f..d2b4ba8a72 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -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,48 @@ 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. + * + * 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)) + break; /* return the bogus OID as-is */ + 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. diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 07023ee61d..134e3b22fd 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -3889,6 +3889,9 @@ { 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', diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out index dc58793e3f..71d9f1952c 100644 --- a/src/test/regress/expected/domain.out +++ b/src/test/regress/expected/domain.out @@ -1292,3 +1292,28 @@ SELECT * FROM information_schema.check_constraints regression | public | pos_int_not_null | VALUE IS NOT NULL (4 rows) +-- +-- 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 1 not error + pg_basetype +------------- + 1 +(1 row) + +drop domain mytext cascade; +NOTICE: drop cascades to type mytext_child_1 diff --git a/src/test/regress/sql/domain.sql b/src/test/regress/sql/domain.sql index ae1b7fbf97..8a5121b056 100644 --- a/src/test/regress/sql/domain.sql +++ b/src/test/regress/sql/domain.sql @@ -862,3 +862,15 @@ SELECT * FROM information_schema.check_constraints FROM information_schema.domain_constraints WHERE domain_name IN ('con', 'dom', 'pos_int', 'things')) ORDER BY constraint_name; + +-- +-- 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 1 not error + +drop domain mytext cascade;