Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rWgej-006jQ8-Qa for pgsql-hackers@arkaria.postgresql.org; Sun, 04 Feb 2024 17:51:50 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1rWgei-007CFa-Pz for pgsql-hackers@arkaria.postgresql.org; Sun, 04 Feb 2024 17:51:48 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rWgei-007CFS-5t for pgsql-hackers@lists.postgresql.org; Sun, 04 Feb 2024 17:51:48 +0000 Received: from pb-smtp2.pobox.com ([64.147.108.71]) by magus.postgresql.org with esmtps (TLS1.2) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rWgee-005Rsd-Q0 for pgsql-hackers@lists.postgresql.org; Sun, 04 Feb 2024 17:51:47 +0000 Received: from pb-smtp2.pobox.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id E3D711D735F for ; Sun, 4 Feb 2024 12:51:40 -0500 (EST) (envelope-from david@justatheory.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed; d=pobox.com; h=from :content-type:mime-version:subject:message-id:date:to; s=sasl; bh=aGcsj0ODtTTY3EMg8LT3ybFnXA4mUyBzktfapLNNYaU=; b=OFN4U/yyB09r 1nVXeaAjCAdqAS+dDcdZfm+W3MZAYw4GRmP39+zI+FrQhkaN/O8SpLU0nT0lLdUG n3yS9en+JFQDTrLFm9MmFepLKUpVeOGK3bWeZNG9TcbsJh1n0TgcEotNXnaoHcAQ c8jY4gLFUK63zinrRfgCzQsmytk1YZ8= Received: from pb-smtp2.nyi.icgroup.com (unknown [127.0.0.1]) by pb-smtp2.pobox.com (Postfix) with ESMTP id DA72D1D735B for ; Sun, 4 Feb 2024 12:51:40 -0500 (EST) (envelope-from david@justatheory.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed; d=justatheory.com; h=from:content-type:mime-version:subject:message-id:date:to; s=2016-12.pbsmtp; bh=aGcsj0ODtTTY3EMg8LT3ybFnXA4mUyBzktfapLNNYaU=; b=aCgMk5hmd74BsEYX5VbPTZ+A5AugZXrKYX9BwSPFJZ36KlWzWDMnnwCEOQdcl/fsOYzAidMivlNntZgEj6yfuclzLTVC+dFYT+6o+xChKELS+jW1dHRYh0A3QpNiUErmA7VfbWkan3HJN2Zo0W7GxXhqOf7L1fkxqDnpK8OnMiY= Received: from smtpclient.apple (unknown [158.222.197.125]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by pb-smtp2.pobox.com (Postfix) with ESMTPSA id 422491D735A for ; Sun, 4 Feb 2024 12:51:40 -0500 (EST) (envelope-from david@justatheory.com) From: "David E. Wheeler" Content-Type: multipart/mixed; boundary="Apple-Mail=_6CA1CDA3-BDAA-490E-AADE-BAFF9C0B2ACC" Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3774.300.61.1.2\)) Subject: Patch: Add parse_type Function Message-Id: Date: Sun, 4 Feb 2024 12:51:29 -0500 To: pgsql-hackers@lists.postgresql.org X-Mailer: Apple Mail (2.3774.300.61.1.2) X-Pobox-Relay-ID: 0CD13B20-C386-11EE-A3BC-25B3960A682E-76319746!pb-smtp2.pobox.com List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --Apple-Mail=_6CA1CDA3-BDAA-490E-AADE-BAFF9C0B2ACC Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hackers, Attached is a patch to add a new function, `parse_type()`. It parses a = type string and returns a record with the typid and typmod for the type, = or raises an error if the type is invalid. It=E2=80=99s effectively a = thin layer over the parser=E2=80=99s parseTypeString() function. The purpose of this function is to allow uses to resolve any type name, = including aliases, to its formal name as rendered by, for example, = pg_dump. An example: david=3D# WITH s(s) AS ( SELECT * FROM unnest(ARRAY[ 'timestamp(4)', 'interval(0)', 'interval second(0)', 'timestamptz', 'timestamptz(6)', 'varchar', 'varchar(128)'=20 ]) =20 ), p(type, typid, typmod) AS ( SELECT s, ((parse_type(s)).*) FROM s =20 ) =20 SELECT type, format_type(typid, typmod) FROM p; type | format_type =20 --------------------+-------------------------------- timestamp(4) | timestamp(4) without time zone interval(0) | interval(0) interval second(0) | interval second(0) timestamptz | timestamp with time zone timestamptz(6) | timestamp(6) with time zone varchar | character varying varchar(128) | character varying(128) (7 rows) The use case leading to this patch was pgTAP column data type = assertions. pgTAP traditionally required full type names for testing = data types, but recently added support for aliases. This broke for some = types, because it was difficult to extract the typmod correctly, and = even when we did, it failed for types such as `interval second(0)`, = where `second (0)` is the typmod, and there was no sensible way to = access the bit mask to generate the proper typmod to pass to = `format_type()`. Erik Wienhold wrote this function to solve the problem, and I = volunteered to submit a patch. Potential issues and questions for this patch: * Is `parse_type()` the right name to use? I can see arguments for = `parse_type_string()`, `pg_parse_type()`, and `pg_parse_type_string()`. = Whatever the consensus is works for me. * The function returns a record, which is a little fussy. I could see = adding a `format_type_string()` function that just contains `SELECT = format_type(p.typmod, p.typid) FROM parse_type($1) p` and returns the = formatted type. But I think it might also be useful to have access to = the typmod and typid directly via this method, since they=E2=80=99re = already exposed as `format_type()`=E2=80=99s arguments. * Is format_type.c the right home for the function? I put it there = because I closely associate it with format_type(), but happy to move it = to a more appropriate location. * I tried to link to `format_type` from the docs entry for `parse_type`, = and added an ID for the former, but I=E2=80=99m not sure it resolves. Best, David --Apple-Mail=_6CA1CDA3-BDAA-490E-AADE-BAFF9C0B2ACC Content-Disposition: attachment; filename=v1-0001-Add-parse_type-SQL-function.patch Content-Type: application/octet-stream; x-unix-mode=0644; name="v1-0001-Add-parse_type-SQL-function.patch" Content-Transfer-Encoding: quoted-printable =46rom=20b426a1a258b65237632039abffb1ab101ce709c9=20Mon=20Sep=2017=20= 00:00:00=202001=0AFrom:=20"David=20E.=20Wheeler"=20= =0ADate:=20Sun,=204=20Feb=202024=2012:49:18=20= -0500=0ASubject:=20[PATCH=20v1]=20Add=20parse_type()=20SQL=20function=0A=0A= The=20`parse_type()`=20function=20uses=20the=20underlying=20= `parseTypeString()`=20C=0Afunction=20to=20parse=20a=20string=20= representing=20a=20data=20type=20into=20a=20type=20ID=20and=0Atypmod=20= suitabld=20for=20passing=20to=20`format_type()`.=20This=20allows=20one=20= to=0Aderive=20the=20formal=20SQL=20name=20for=20a=20type=20from=20a=20= string=20that=20may=20be=20an=0Aalias:=0A=0A=20=20=20=20SELECT=20= format_type(p.typid,=20p.typmod)=0A=20=20=20=20=20=20FROM=20= parse_type('timestamp(4)')=20p;=0A=0A=20=20=20=20=20=20=20=20=20=20=20=20= format_type=0A=20=20=20=20--------------------------------=0A=20=20=20=20= timestamp(4)=20without=20time=20zone=0A=0AAlso=20accounts=20for=20data=20= typs=20that=20require=20the=20SQL=20grammar=20to=20be=20parsed:=0A=0A=20=20= =20=20SELECT=20format_type(p.typid,=20p.typmod)=0A=20=20=20=20=20=20FROM=20= parse_type('interval=20second(0)')=20p;=0A=0A=20=20=20=20=20=20=20=20= format_type=0A=20=20=20=20--------------------=0A=20=20=20=20interval=20= second(0)=0A=0AUseful=20for=20unit=20tests=20for=20against=20column=20= data=20types,=20for=20example.=0AOriginally=20written=20by=20Erik=20= Wienhold=20for=20use=20in=20pgTAP.=0A---=0A=20doc/src/sgml/func.sgml=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=2031=20= ++++++++++-=0A=20src/backend/utils/adt/format_type.c=20=20=20=20=20=20=20= |=2066=20+++++++++++++++++++++++=0A=20src/include/catalog/pg_proc.dat=20=20= =20=20=20=20=20=20=20=20=20|=20=204=20++=0A=20= src/include/utils/builtins.h=20=20=20=20=20=20=20=20=20=20=20=20=20=20|=20= =202=20+=0A=20src/test/regress/expected/create_type.out=20|=2047=20= ++++++++++++++++=0A=20src/test/regress/sql/create_type.sql=20=20=20=20=20= =20|=2027=20++++++++++=0A=206=20files=20changed,=20176=20insertions(+),=20= 1=20deletion(-)=0A=0Adiff=20--git=20a/doc/src/sgml/func.sgml=20= b/doc/src/sgml/func.sgml=0Aindex=206788ba8ef4..12e2d69568=20100644=0A---=20= a/doc/src/sgml/func.sgml=0A+++=20b/doc/src/sgml/func.sgml=0A@@=20= -24754,7=20+24754,7=20@@=20SELECT=20= pg_type_is_visible('myschema.widget'::regtype);=0A=20=0A=20=20=20=20=20=20= =0A=20=20=20=20=20=20=20=0A-=20=20=20=20=20=20=20=0A+=20=20=20=20= =20=20=20=0A=20=20=20=20=20=20=20=20=20=0A=20=20= =20=20=20=20=20=20=20=20format_type=0A=20=20=20=20=20=20= =20=20=20=0A@@=20-24768,6=20+24768,35=20@@=20SELECT=20= pg_type_is_visible('myschema.widget'::regtype);=0A=20=20=20=20=20=20=20=20= =0A=20=20=20=20=20=20=20=0A=20=0A+=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=0A+=20=20=20=20= =20=20=20=20=0A+=20=20=20=20=20=20=20=20=20= parse_type=0A+=20=20=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=20=20parse_type=20(=20= type=20text=20)=0A+=20=20=20=20=20=20= =20=20record=0A+=20=20=20=20=20=20=20=20(=20= typid=20oid,=0A+=20=20=20=20=20=20=20= =20=20=20typmod=20int4=20)=0A+=20=20=20= =20=20=20=20=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =20Parses=20a=20string=20representing=20an=20SQL=20type=20declaration=20= as=20used=20in=20a=0A+=20=20=20=20=20=20=20=20CREATE=20= TABLE=20statement,=20optionally=20schema-qualified.=0A+=20=20=20= =20=20=20=20=20Returns=20a=20record=20with=20two=20fields,=20= typid=20and=0A+=20=20=20=20=20=20=20=20= typid,=20representing=20the=20OID=20and=20= modifier=20for=20the=0A+=20=20=20=20=20=20=20=20type.=20These=20= correspond=20to=20the=20parameters=20to=20pass=20to=20the=0A+=20=20=20=20= =20=20=20=20format_type=20= function.=0A+=20=20=20=20=20=20=20=0A+=20=20=20=20=20=20=20= =0A+=20=20=20=20=20=20=20=20For=20example:=0A+=0A= +SELECT=20format_type(p.typid,=20p.typmod)=20FROM=20= parse_type('timestamp(4)')=20p;=0A+=20=20=20=20=20=20=20=20format_type=0A= +=20--------------------------------=0A+=20=20timestamp(4)=20without=20= time=20zone=0A+=0A+=20=20=20=20=20=20=20=0A= +=20=20=20=20=20=20=0A+=0A=20=20=20=20=20=20=20=0A=20=20=20=20= =20=20=20=20=0A=20=20=20=20= =20=20=20=20=20=0Adiff=20--git=20= a/src/backend/utils/adt/format_type.c=20= b/src/backend/utils/adt/format_type.c=0Aindex=2028ba0fbd19..2477c8b8e0=20= 100644=0A---=20a/src/backend/utils/adt/format_type.c=0A+++=20= b/src/backend/utils/adt/format_type.c=0A@@=20-26,6=20+26,9=20@@=0A=20= #include=20"utils/lsyscache.h"=0A=20#include=20"utils/numeric.h"=0A=20= #include=20"utils/syscache.h"=0A+#include=20"fmgr.h"=0A+#include=20= "funcapi.h"=0A+#include=20"parser/parse_type.h"=0A=20=0A=20static=20char=20= *printTypmod(const=20char=20*typname,=20int32=20typmod,=20Oid=20= typmodout);=0A=20=0A@@=20-482,3=20+485,66=20@@=20= oidvectortypes(PG_FUNCTION_ARGS)=0A=20=0A=20=09= PG_RETURN_TEXT_P(cstring_to_text(result));=0A=20}=0A+=0A+/*=0A+=20*=20= Given=20a=20string=20that=20is=20supposed=20to=20be=20a=20SQL-compatible=20= type=20declaration,=0A+=20*=20such=20as=20"int4"=20or=20"integer"=20or=20= "character=20varying(32)",=20parse=0A+=20*=20the=20string=20and=20= convert=20it=20to=20a=20type=20OID=20and=20type=20modifier.=0A+=20*=0A+=20= *=20Raises=20an=20error=20on=20an=20invalid=20type.=0A+=20*/=0A+=0A+/*=0A= +=20*=20parse_type()=20is=20the=20inverse=20of=20= pg_catalog.format_type():=20it=20takes=20a=20string=0A+=20*=20= representing=20an=20SQL-compatible=20type=20declaration,=20such=20as=20= "int4"=20or=20"integer"=0A+=20*=20or=20"character=20varying(32)",=20= parses=20it,=20and=20returns=20the=20OID=20and=20type=20modifier.=0A+=20= *=20Returns=20NULL=20for=20an=20invalid=20type.=0A+=20*=0A+=20*=20= Internally=20it=20relies=20on=20the=20Postgres=20core=20= parseTypeString()=20function=20defined=0A+=20*=20in=20= src/backend/parser/parse_type.c.=0A+=20*/=0A= +PG_FUNCTION_INFO_V1(parse_type);=0A+=0A+Datum=0A= +parse_type(PG_FUNCTION_ARGS)=0A+{=0A+#define=20PARSE_TYPE_STRING_COLS=20= 2=20/*=20Returns=20two=20columns.=20*/=0A+=09const=20char=20*type;=20=20=20= =20=20=20=20=20=20=20=20=20/*=20the=20type=20string=20we=20want=20to=20= resolve=20*/=0A+=09Oid=20=20=20=20=20=20=20=20=20typid;=20=20=20=20=20=20= =20=20=20=20=20/*=20the=20resolved=20type=20oid=20*/=0A+=09int32=20=20=20= =20=20=20=20typmod;=20=20=20=20=20=20=20=20=20=20/*=20the=20resolved=20= type=20modifier=20*/=0A+=09TupleDesc=20=20=20tupdesc;=0A+=09HeapTuple=20=20= =20rettuple;=0A+=09Datum=20=20=20=20=20=20=20= values[PARSE_TYPE_STRING_COLS]=20=3D=20{0};=0A+=09bool=20=20=20=20=20=20=20= =20nulls[PARSE_TYPE_STRING_COLS]=20=3D=20{0};=0A+=0A+=09type=20=3D=20= text_to_cstring(PG_GETARG_TEXT_PP(0));=0A+=0A+=09/*=0A+=09=20*=20Build=20= a=20tuple=20descriptor=20for=20our=20result=20type;=20return=20an=20= error=20if=20not=0A+=09=20*=20called=20in=20a=20context=20that=20expects=20= a=20record.=0A+=09=20*/=0A+=09if=20(get_call_result_type(fcinfo,=20NULL,=20= &tupdesc)=20!=3D=20TYPEFUNC_COMPOSITE)=20{=0A+=09=09ereport(=0A+=09=09=09= ERROR,=0A+=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),=0A+=09=09=09= errmsg("function=20returning=20record=20called=20in=20context=20that=20= cannot=20accept=20type=20record"))=0A+=09=09);=0A+=09}=0A+=0A+=09= BlessTupleDesc(tupdesc);=0A+=0A+=09/*=0A+=09=20*=20Parse=20type-name=20= argument=20to=20obtain=20type=20OID=20and=20encoded=20typmod.=20We=20= don't=0A+=09=20*=20need=20to=20check=20for=20parseTypeString=20failure,=20= but=20just=20let=20the=20error=20be=0A+=09=20*=20raised.=20The=200=20arg=20= works=20both=20as=20the=20`Node=20*escontext`=20arg=20in=20Postgres=2016=0A= +=09=20*=20and=20the=20`bool=20missing_ok`=20arg=20in=209.4-15.=0A+=09=20= */=0A+=09(void)=20parseTypeString(type,=20&typid,=20&typmod,=200);=0A+=0A= +=09/*=20Create=20and=20return=20tuple.=20*/=0A+=09values[0]=20=3D=20= typid;=0A+=09values[1]=20=3D=20typmod;=0A+=09rettuple=20=3D=20= heap_form_tuple(tupdesc,=20values,=20nulls);=0A+=09return=20= HeapTupleGetDatum(rettuple);=0A+#undef=20PARSE_TYPE_STRING_COLS=0A+}=0A= diff=20--git=20a/src/include/catalog/pg_proc.dat=20= b/src/include/catalog/pg_proc.dat=0Aindex=2029af4ce65d..31d7024010=20= 100644=0A---=20a/src/include/catalog/pg_proc.dat=0A+++=20= b/src/include/catalog/pg_proc.dat=0A@@=20-2184,6=20+2184,10=20@@=0A=20{=20= oid=20=3D>=20'1081',=20descr=20=3D>=20'format=20a=20type=20oid=20and=20= atttypmod=20to=20canonical=20SQL',=0A=20=20=20proname=20=3D>=20= 'format_type',=20proisstrict=20=3D>=20'f',=20provolatile=20=3D>=20's',=0A= =20=20=20prorettype=20=3D>=20'text',=20proargtypes=20=3D>=20'oid=20= int4',=20prosrc=20=3D>=20'format_type'=20},=0A+{=20oid=20=3D>=20'8401',=20= descr=20=3D>=20'parse=20a=20type=20string=20into=20its=20a=20type=20oid=20= and=20atttypmod',=0A+=20=20proname=20=3D>=20'parse_type',=20proisstrict=20= =3D>=20'f',=20provolatile=20=3D>=20's',=0A+=20=20prorettype=20=3D>=20= 'record',=20proargtypes=20=3D>=20'text',=20prosrc=20=3D>=20'parse_type',=0A= +=20=20proallargtypes=20=3D>=20'{text,oid,int4}',=20proargmodes=20=3D>=20= '{i,o,o}',=20proargnames=20=3D>=20'{typname,typid,typmod}'=20},=0A=20{=20= oid=20=3D>=20'1084',=20descr=20=3D>=20'I/O',=0A=20=20=20proname=20=3D>=20= 'date_in',=20provolatile=20=3D>=20's',=20prorettype=20=3D>=20'date',=0A=20= =20=20proargtypes=20=3D>=20'cstring',=20prosrc=20=3D>=20'date_in'=20},=0A= diff=20--git=20a/src/include/utils/builtins.h=20= b/src/include/utils/builtins.h=0Aindex=20359c570f23..264ad090be=20100644=0A= ---=20a/src/include/utils/builtins.h=0A+++=20= b/src/include/utils/builtins.h=0A@@=20-133,6=20+133,8=20@@=20extern=20= char=20*format_type_with_typemod(Oid=20type_oid,=20int32=20typemod);=0A=20= =0A=20extern=20int32=20type_maximum_size(Oid=20type_oid,=20int32=20= typemod);=0A=20=0A+extern=20Datum=20parse_type(PG_FUNCTION_ARGS);=0A+=0A=20= /*=20quote.c=20*/=0A=20extern=20char=20*quote_literal_cstr(const=20char=20= *rawstr);=0A=20=0Adiff=20--git=20= a/src/test/regress/expected/create_type.out=20= b/src/test/regress/expected/create_type.out=0Aindex=20= 7383fcdbb1..5a3ebfc658=20100644=0A---=20= a/src/test/regress/expected/create_type.out=0A+++=20= b/src/test/regress/expected/create_type.out=0A@@=20-249,6=20+249,53=20@@=20= select=20format_type('bpchar'::regtype,=20-1);=0A=20=20bpchar=0A=20(1=20= row)=0A=20=0A+--=20Test=20parse_type=0A+SELECT=20*=20FROM=20= parse_type('text')=20p(typid,=20typmod);=0A+=20typid=20|=20typmod=20=0A= +-------+--------=0A+=20=20=20=2025=20|=20=20=20=20=20-1=0A+(1=20row)=0A= +=0A+--=20Test=20parse_type=20errors=0A+SELECT=20parse_type('nonesuch');=20= --=20error=20expected=0A+ERROR:=20=20type=20"nonesuch"=20does=20not=20= exist=0A+SELECT=20parse_type('interval=20nonesuch');=20--=20grammar=20= error=20expected=0A+ERROR:=20=20syntax=20error=20at=20or=20near=20= "nonesuch"=0A+LINE=201:=20SELECT=20parse_type('interval=20nonesuch');=0A= +=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20^=0A+CONTEXT:=20=20= invalid=20type=20name=20"interval=20nonesuch"=0A+SELECT=20= parse_type('year(4)');=20--=20grammar=20error=20expected=0A+ERROR:=20=20= type=20"year"=20does=20not=20exist=0A+--=20Test=20parse_type=20with=20= various=20aliases=20and=20grammar-based=20types=0A+WITH=20s(s)=20AS=20(=0A= +=20=20=20=20SELECT=20*=20FROM=20unnest(ARRAY[=0A+=20=20=20=20=20=20=20=20= 'timestamp(4)',=0A+=20=20=20=20=20=20=20=20'interval(0)',=0A+=20=20=20=20= =20=20=20=20'interval=20second(0)',=0A+=20=20=20=20=20=20=20=20= 'timestamptz',=0A+=20=20=20=20=20=20=20=20'timestamptz(6)',=0A+=20=20=20=20= =20=20=20=20'varchar',=0A+=20=20=20=20=20=20=20=20'varchar(128)',=0A+=20=20= =20=20=20=20=20=20'mytab'=0A+=20=20=20=20])=0A+),=0A+p(typid,=20typmod)=20= AS=20(=0A+=20=20=20=20SELECT=20((parse_type(s)).*)=0A+=20=20=20=20=20=20= FROM=20s=0A+)=0A+SELECT=20format_type(typid,=20typmod)=20FROM=20p;=0A+=20= =20=20=20=20=20=20=20=20=20format_type=20=20=20=20=20=20=20=20=20=20=20=0A= +--------------------------------=0A+=20timestamp(4)=20without=20time=20= zone=0A+=20interval(0)=0A+=20interval=20second(0)=0A+=20timestamp=20with=20= time=20zone=0A+=20timestamp(6)=20with=20time=20zone=0A+=20character=20= varying=0A+=20character=20varying(128)=0A+=20mytab=0A+(8=20rows)=0A+=0A=20= --=20Test=20non-error-throwing=20APIs=20using=20widget,=20which=20still=20= throws=20errors=0A=20SELECT=20pg_input_is_valid('(1,2,3)',=20'widget');=0A= =20=20pg_input_is_valid=20=0Adiff=20--git=20= a/src/test/regress/sql/create_type.sql=20= b/src/test/regress/sql/create_type.sql=0Aindex=20c25018029c..c14c7590a9=20= 100644=0A---=20a/src/test/regress/sql/create_type.sql=0A+++=20= b/src/test/regress/sql/create_type.sql=0A@@=20-192,6=20+192,33=20@@=20= select=20format_type('bpchar'::regtype,=20null);=0A=20--=20this=20= behavior=20difference=20is=20intentional=0A=20select=20= format_type('bpchar'::regtype,=20-1);=0A=20=0A+--=20Test=20parse_type=0A= +SELECT=20*=20FROM=20parse_type('text')=20p(typid,=20typmod);=0A+=0A+--=20= Test=20parse_type=20errors=0A+SELECT=20parse_type('nonesuch');=20--=20= error=20expected=0A+SELECT=20parse_type('interval=20nonesuch');=20--=20= grammar=20error=20expected=0A+SELECT=20parse_type('year(4)');=20--=20= grammar=20error=20expected=0A+=0A+--=20Test=20parse_type=20with=20= various=20aliases=20and=20grammar-based=20types=0A+WITH=20s(s)=20AS=20(=0A= +=20=20=20=20SELECT=20*=20FROM=20unnest(ARRAY[=0A+=20=20=20=20=20=20=20=20= 'timestamp(4)',=0A+=20=20=20=20=20=20=20=20'interval(0)',=0A+=20=20=20=20= =20=20=20=20'interval=20second(0)',=0A+=20=20=20=20=20=20=20=20= 'timestamptz',=0A+=20=20=20=20=20=20=20=20'timestamptz(6)',=0A+=20=20=20=20= =20=20=20=20'varchar',=0A+=20=20=20=20=20=20=20=20'varchar(128)',=0A+=20=20= =20=20=20=20=20=20'mytab'=0A+=20=20=20=20])=0A+),=0A+p(typid,=20typmod)=20= AS=20(=0A+=20=20=20=20SELECT=20((parse_type(s)).*)=0A+=20=20=20=20=20=20= FROM=20s=0A+)=0A+SELECT=20format_type(typid,=20typmod)=20FROM=20p;=0A+=0A= =20--=20Test=20non-error-throwing=20APIs=20using=20widget,=20which=20= still=20throws=20errors=0A=20SELECT=20pg_input_is_valid('(1,2,3)',=20= 'widget');=0A=20SELECT=20pg_input_is_valid('(1,2)',=20'widget');=20=20--=20= hard=20error=20expected=0A--=20=0A2.43.0=0A=0A= --Apple-Mail=_6CA1CDA3-BDAA-490E-AADE-BAFF9C0B2ACC--