From 68cb840e2e33fb32bc1a692541f1b14522521ec2 Mon Sep 17 00:00:00 2001 From: Alignyx Date: Thu, 10 Sep 2026 23:00:09 +0800 Subject: [PATCH] sql: use SQL-standard names for built-in regtype values Casting an array type OID to regtype exposes its catalog name: OID 1016 displays as _int8 instead of bigint[]. The OID is correct, but the built-in branch of performIntToOidCast attaches the name from PGName(). Use SQLStandardName() to format built-in regtype values. Its existing array handling formats the element type and appends [], so integer and floating-point arrays receive their canonical SQL names. Scalar aliases also use SQL names, while vector and pseudo-type names retain their special handling. User-defined types still go through resolveOID for search-path-aware qualification. Distinguish oid.T_any from anyelement in the shared formatter. Both belong to AnyFamily, but must retain distinct names when used by the regtype cast. Add SQL expression regressions covering array names, scalar aliases, pseudo-types, vectors, OID preservation, NULL and zero. Add an exact-name test for the shared pseudo-type formatter. Fixes #97456 Release note (bug fix): Fixed the names displayed when casting built-in type OIDs to regtype. Array types now use SQL names such as bigint[] instead of internal catalog names such as _int8. Scalar types use their corresponding SQL names, such as integer for int4. --- pkg/sql/sem/eval/cast.go | 2 +- pkg/sql/sem/eval/testdata/eval/regtype | 93 ++++++++++++++++++++++++++ pkg/sql/types/types.go | 3 + pkg/sql/types/types_test.go | 15 +++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 pkg/sql/sem/eval/testdata/eval/regtype diff --git a/pkg/sql/sem/eval/cast.go b/pkg/sql/sem/eval/cast.go index 587c6a093520..906efef5eedc 100644 --- a/pkg/sql/sem/eval/cast.go +++ b/pkg/sql/sem/eval/cast.go @@ -1125,7 +1125,7 @@ func performIntToOidCast( // independent of search_path. UDTs fall through to resolveOID for // search-path-aware schema qualification. if typ, ok := types.OidToType[o]; ok { - return tree.NewDOidWithTypeAndName(o, t, typ.PGName()), nil + return tree.NewDOidWithTypeAndName(o, t, typ.SQLStandardName()), nil } } // resolveOID applies Postgres's search-path-aware schema qualification diff --git a/pkg/sql/sem/eval/testdata/eval/regtype b/pkg/sql/sem/eval/testdata/eval/regtype new file mode 100644 index 000000000000..85c55dfe3e30 --- /dev/null +++ b/pkg/sql/sem/eval/testdata/eval/regtype @@ -0,0 +1,93 @@ +# Regression for #97456: regtype output uses SQL type names, including the +# element type's canonical name followed by [] for ordinary arrays. +eval +1016::OID::REGTYPE::STRING +---- +'bigint[]' + +eval +1016::REGTYPE::STRING +---- +'bigint[]' + +eval +1016::OID::REGTYPE::OID +---- +1016 + +eval +1005::OID::REGTYPE::STRING +---- +'smallint[]' + +eval +1007::OID::REGTYPE::STRING +---- +'integer[]' + +eval +1000::OID::REGTYPE::STRING +---- +'boolean[]' + +eval +1009::OID::REGTYPE::STRING +---- +'text[]' + +eval +1022::OID::REGTYPE::STRING +---- +'double precision[]' + +eval +1015::OID::REGTYPE::STRING +---- +'character varying[]' + +# The same canonical names apply to scalar built-in types. +eval +23::OID::REGTYPE::STRING +---- +'integer' + +eval +20::OID::REGTYPE::STRING +---- +'bigint' + +# Pseudo-types and vector types retain their distinct names. +eval +2276::OID::REGTYPE::STRING +---- +'any' + +eval +2283::OID::REGTYPE::STRING +---- +'anyelement' + +eval +2277::OID::REGTYPE::STRING +---- +'anyarray' + +eval +22::OID::REGTYPE::STRING +---- +'int2vector' + +eval +30::OID::REGTYPE::STRING +---- +'oidvector' + +eval +NULL::OID::REGTYPE +---- +NULL + +eval +0::OID::REGTYPE::OID +---- +0 diff --git a/pkg/sql/types/types.go b/pkg/sql/types/types.go index 33b5bd514536..7575b70f3566 100644 --- a/pkg/sql/types/types.go +++ b/pkg/sql/types/types.go @@ -1946,6 +1946,9 @@ func (t *T) SQLStandardNameWithTypmod(haveTypmod bool, typmod int, useFQName boo var buf strings.Builder switch t.Family() { case AnyFamily: + if t.Oid() == oid.T_any { + return "any" + } return "anyelement" case ArrayFamily: switch t.Oid() { diff --git a/pkg/sql/types/types_test.go b/pkg/sql/types/types_test.go index f4d4757d9e61..b702bb64e01f 100644 --- a/pkg/sql/types/types_test.go +++ b/pkg/sql/types/types_test.go @@ -1013,6 +1013,21 @@ func TestSQLStandardName(t *testing.T) { } } +func TestSQLStandardNamePseudoTypes(t *testing.T) { + for _, tc := range []struct { + typ *T + expected string + }{ + {Any, "any"}, + {AnyElement, "anyelement"}, + {AnyArray, "anyarray"}, + } { + t.Run(tc.expected, func(t *testing.T) { + require.Equal(t, tc.expected, tc.typ.SQLStandardName()) + }) + } +} + func TestWithoutTypeModifiers(t *testing.T) { const userDefinedOID = oidext.CockroachPredefinedOIDMax + 700 domain := MakeDomain(MakeString(2), userDefinedOID, userDefinedOID+1)