diff --git a/pkg/sql/catalog/descs/dist_sql_type_resolver.go b/pkg/sql/catalog/descs/dist_sql_type_resolver.go index eb56027ae021..fdcf141117d8 100644 --- a/pkg/sql/catalog/descs/dist_sql_type_resolver.go +++ b/pkg/sql/catalog/descs/dist_sql_type_resolver.go @@ -131,18 +131,8 @@ func (dt DistSQLTypeResolver) GetTypeDescriptor( // HydrateTypeSlice installs metadata into a slice of types.T's. func (dt DistSQLTypeResolver) HydrateTypeSlice(ctx context.Context, typs []*types.T) error { for _, t := range typs { - if t.UserDefined() { - id, err := typedesc.GetUserDefinedTypeDescID(t) - if err != nil { - return err - } - name, desc, err := dt.GetTypeDescriptor(ctx, id) - if err != nil { - return err - } - if err := desc.HydrateTypeInfoWithName(ctx, t, &name, dt); err != nil { - return err - } + if err := typedesc.EnsureTypeIsHydrated(ctx, t, dt); err != nil { + return err } } return nil diff --git a/pkg/sql/catalog/typedesc/table_implicit_record_type.go b/pkg/sql/catalog/typedesc/table_implicit_record_type.go index 2f6c1c28b6d0..838e5f389e02 100644 --- a/pkg/sql/catalog/typedesc/table_implicit_record_type.go +++ b/pkg/sql/catalog/typedesc/table_implicit_record_type.go @@ -41,6 +41,8 @@ type TableImplicitRecordType struct { privs *descpb.PrivilegeDescriptor } +var _ catalog.TypeDescriptor = (*TableImplicitRecordType)(nil) + // CreateImplicitRecordTypeFromTableDesc creates a TypeDescriptor that represents // the implicit record type for a table, which has 1 field for every visible // column in the table. @@ -231,14 +233,7 @@ func (v TableImplicitRecordType) HydrateTypeInfoWithName( Name: name.Object(), } typ.TypeMeta.Version = uint32(v.desc.GetVersion()) - for _, t := range typ.TupleContents() { - if t.UserDefined() { - if err := hydrateElementType(ctx, t, res); err != nil { - return err - } - } - } - return nil + return EnsureTypeIsHydrated(ctx, typ, res) } // MakeTypesT implements the TypeDescriptor interface. diff --git a/pkg/sql/catalog/typedesc/type_desc.go b/pkg/sql/catalog/typedesc/type_desc.go index 378cd1b3e73b..c314dda904de 100644 --- a/pkg/sql/catalog/typedesc/type_desc.go +++ b/pkg/sql/catalog/typedesc/type_desc.go @@ -735,39 +735,52 @@ func (desc *immutable) MakeTypesT( } } -// HydrateTypesInTableDescriptor uses typeLookup to install metadata in the -// types present in a table descriptor. typeLookup retrieves the fully -// qualified name and descriptor for a particular ID. -func HydrateTypesInTableDescriptor( - ctx context.Context, desc *descpb.TableDescriptor, res catalog.TypeDescriptorResolver, +// EnsureTypeIsHydrated makes sure that t is a fully-hydrated type. +func EnsureTypeIsHydrated( + ctx context.Context, t *types.T, res catalog.TypeDescriptorResolver, ) error { - hydrateCol := func(col *descpb.ColumnDescriptor) error { - if col.Type.UserDefined() { - // Look up its type descriptor. - td, err := GetUserDefinedTypeDescID(col.Type) - if err != nil { - return err - } - name, typDesc, err := res.GetTypeDescriptor(ctx, td) - if err != nil { - return err - } - // Note that this will no-op if the type is already hydrated. - if err := typDesc.HydrateTypeInfoWithName(ctx, col.Type, &name, res); err != nil { + // maybeHydrateType checks if t is a user-defined type that hasn't been + // hydrated yet, and installs the metadata if so. + maybeHydrateType := func(ctx context.Context, t *types.T, res catalog.TypeDescriptorResolver) error { + if !t.UserDefined() || t.IsHydrated() { + return nil + } + id, err := GetUserDefinedTypeDescID(t) + if err != nil { + return err + } + elemTypName, elemTypDesc, err := res.GetTypeDescriptor(ctx, id) + if err != nil { + return err + } + return elemTypDesc.HydrateTypeInfoWithName(ctx, t, &elemTypName, res) + } + if t.Family() == types.TupleFamily { + for _, typ := range t.TupleContents() { + if err := maybeHydrateType(ctx, typ, res); err != nil { return err } } return nil } + return maybeHydrateType(ctx, t, res) +} + +// HydrateTypesInTableDescriptor uses res to install metadata in the types +// present in a table descriptor. res retrieves the fully qualified name and +// descriptor for a particular ID. +func HydrateTypesInTableDescriptor( + ctx context.Context, desc *descpb.TableDescriptor, res catalog.TypeDescriptorResolver, +) error { for i := range desc.Columns { - if err := hydrateCol(&desc.Columns[i]); err != nil { + if err := EnsureTypeIsHydrated(ctx, desc.Columns[i].Type, res); err != nil { return err } } for i := range desc.Mutations { mut := &desc.Mutations[i] if col := mut.GetColumn(); col != nil { - if err := hydrateCol(col); err != nil { + if err := EnsureTypeIsHydrated(ctx, col.Type, res); err != nil { return err } } @@ -806,15 +819,9 @@ func (desc *immutable) HydrateTypeInfoWithName( case types.ArrayFamily: // Hydrate the element type. elemType := typ.ArrayContents() - return hydrateElementType(ctx, elemType, res) + return EnsureTypeIsHydrated(ctx, elemType, res) case types.TupleFamily: - for _, t := range typ.TupleContents() { - if t.UserDefined() { - if err := hydrateElementType(ctx, t, res); err != nil { - return err - } - } - } + return EnsureTypeIsHydrated(ctx, typ, res) default: return errors.AssertionFailedf("unhandled alias type family %s", typ.Family()) } @@ -825,21 +832,6 @@ func (desc *immutable) HydrateTypeInfoWithName( } } -func hydrateElementType(ctx context.Context, t *types.T, res catalog.TypeDescriptorResolver) error { - id, err := GetUserDefinedTypeDescID(t) - if err != nil { - return err - } - elemTypName, elemTypDesc, err := res.GetTypeDescriptor(ctx, id) - if err != nil { - return err - } - if err := elemTypDesc.HydrateTypeInfoWithName(ctx, t, &elemTypName, res); err != nil { - return err - } - return nil -} - // NumEnumMembers implements the TypeDescriptor interface. func (desc *immutable) NumEnumMembers() int { return len(desc.EnumMembers) diff --git a/pkg/sql/logictest/testdata/logic_test/enums b/pkg/sql/logictest/testdata/logic_test/enums index 37ee6f6f7936..ddb6c4354f61 100644 --- a/pkg/sql/logictest/testdata/logic_test/enums +++ b/pkg/sql/logictest/testdata/logic_test/enums @@ -1600,3 +1600,19 @@ SELECT * FROM enum_table ---- a c + +# Regression test for not hydrating an enum when it is used in a tuple. +statement ok +DROP TYPE IF EXISTS greeting; +CREATE TYPE greeting AS ENUM ('hello', 'howdy', 'hi'); +CREATE TABLE seed AS SELECT g::INT8 AS _int8 FROM generate_series(1, 5) AS g; +WITH + cte1 (col1) + AS ( + SELECT * FROM (VALUES (COALESCE((NULL, 'hello':::greeting), (1, 'howdy':::greeting))), ((2, 'hi':::greeting))) + ), + cte2 (col2) AS (SELECT _int8 FROM seed) +SELECT + col1, col2 +FROM + cte1, cte2;