From 1218ca9956ee60afc6975f14c1a4c953bd6bbaa7 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 20 Mar 2024 17:11:23 -0400 Subject: [PATCH] Add to_regtypemod function to extract typemod from a string type name. 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 | 77 ++++++++++++++++----------- src/backend/utils/adt/regproc.c | 20 +++++++ src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.dat | 3 ++ src/test/regress/expected/regproc.out | 37 +++++++++++++ src/test/regress/sql/regproc.sql | 8 +++ 6 files changed, 115 insertions(+), 32 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 5b225ccf4f..030ea8affd 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -24872,7 +24872,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); - + format_type @@ -25387,18 +25387,8 @@ SELECT currval(pg_get_serial_sequence('sometable', 'id')); OID for comparison purposes but displays as a type name. - For example: - -SELECT pg_typeof(33); - pg_typeof ------------ - integer - -SELECT typlen FROM pg_type WHERE oid = pg_typeof(33); - typlen --------- - 4 - + pg_typeof(33) + integer @@ -25418,18 +25408,12 @@ SELECT typlen FROM pg_type WHERE oid = pg_typeof(33); collatable data type, then an error is raised. - For example: - -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" - + collation for ('foo'::text) + "default" + + + collation for ('foo' COLLATE "de_DE") + "de_DE" @@ -25570,7 +25554,7 @@ SELECT collation for ('foo' COLLATE "de_DE"); - + to_regtype @@ -25578,11 +25562,42 @@ SELECT collation for ('foo' COLLATE "de_DE"); regtype - Translates a textual type name to its OID. A similar result is - obtained by casting the string to type regtype (see - ); however, this function will return - NULL 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 NULL. A similar result + is obtained by casting the string to type regtype + (see ), except that that will throw + error for name not found. + + + + + + + to_regtypemod + + to_regtypemod ( text ) + integer + + + 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 NULL. The result is + -1 if no type modifier is present. + + + to_regtypemod can be combined with + to produce appropriate inputs for + , allowing a string representing a + type name to be canonicalized. + + + format_type(to_regtype('varchar(32)'), to_regtypemod('varchar(32)')) + character varying(32) diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 1e3bf3f5fd..18d5b7d166 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -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" */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index b05db0fa0a..3c4e49b73a 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202403201 +#define CATALOG_VERSION_NO 202403202 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 177d81a891..042f66f714 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -7155,6 +7155,9 @@ { 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' }, diff --git a/src/test/regress/expected/regproc.out b/src/test/regress/expected/regproc.out index a9420850b8..97b917502c 100644 --- a/src/test/regress/expected/regproc.out +++ b/src/test/regress/expected/regproc.out @@ -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 diff --git a/src/test/regress/sql/regproc.sql b/src/test/regress/sql/regproc.sql index de2aa881a8..232289ac39 100644 --- a/src/test/regress/sql/regproc.sql +++ b/src/test/regress/sql/regproc.sql @@ -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');