Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
72406: catalog/descs: hydrate types for tuples r=yuzefovich a=yuzefovich

Before we can use the user-defined types, we have to hydrate them. We do
so for enums as well as arrays of enums, but we forgot to do so for
tuples containing enums in some cases. This is now fixed.

Release note (bug fix): Previously, CockroachDB could encounter an
internal error or crash when some queries involving tuples with ENUMs
were executed in a distributed manner. This is now fixed.

Co-authored-by: Yahor Yuzefovich <[email protected]>
  • Loading branch information
craig[bot] and yuzefovich committed Nov 5, 2021
2 parents ed35bbe + e7c8ccb commit 092fae5
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 63 deletions.
14 changes: 2 additions & 12 deletions pkg/sql/catalog/descs/dist_sql_type_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 3 additions & 8 deletions pkg/sql/catalog/typedesc/table_implicit_record_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
78 changes: 35 additions & 43 deletions pkg/sql/catalog/typedesc/type_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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())
}
Expand All @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/enums
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit 092fae5

Please sign in to comment.