Skip to content

Commit

Permalink
Merge pull request #76618 from mgartner/backport21.1-76193
Browse files Browse the repository at this point in the history
release-21.1: optbuilder: do not create invalid casts when building CASE expressions
  • Loading branch information
mgartner authored Feb 24, 2022
2 parents a0946c0 + 22f6ef3 commit dc745e8
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
25 changes: 23 additions & 2 deletions pkg/sql/opt/optbuilder/scalar.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,39 @@ func (b *Builder) buildScalar(
input = memo.TrueSingleton
}

// validateCastToValType panics if tree.ReType with the given source
// type would create an invalid cast to valType.
validateCastToValType := func(src *types.T) {
if valType.Family() == types.AnyFamily || src.Identical(valType) {
// If valType's family is AnyFamily or src is identical to
// valType, then tree.Retype will not create a cast expression.
return
}
if _, ok := tree.LookupCastVolatility(src, valType); ok {
return
}
panic(pgerror.Newf(
pgcode.DatatypeMismatch,
"CASE types %s and %s cannot be matched", src, valType,
))
}

whens := make(memo.ScalarListExpr, 0, len(t.Whens)+1)
for i := range t.Whens {
condExpr := t.Whens[i].Cond.(tree.TypedExpr)
cond := b.buildScalar(condExpr, inScope, nil, nil, colRefs)
valExpr := tree.ReType(t.Whens[i].Val.(tree.TypedExpr), valType)
valExpr := t.Whens[i].Val.(tree.TypedExpr)
validateCastToValType(valExpr.ResolvedType())
valExpr = tree.ReType(valExpr, valType)
val := b.buildScalar(valExpr, inScope, nil, nil, colRefs)
whens = append(whens, b.factory.ConstructWhen(cond, val))
}
// Add the ELSE expression to the end of whens as a raw scalar expression.
var orElse opt.ScalarExpr
if t.Else != nil {
elseExpr := tree.ReType(t.Else.(tree.TypedExpr), valType)
elseExpr := t.Else.(tree.TypedExpr)
validateCastToValType(elseExpr.ResolvedType())
elseExpr = tree.ReType(elseExpr, valType)
orElse = b.buildScalar(elseExpr, inScope, nil, nil, colRefs)
} else {
orElse = b.factory.ConstructNull(valType)
Expand Down
15 changes: 15 additions & 0 deletions pkg/sql/opt/optbuilder/testdata/scalar
Original file line number Diff line number Diff line change
Expand Up @@ -1461,3 +1461,18 @@ is [type=bool]
│ │ └── null [type=unknown]
│ └── array: [type=string[]]
└── null [type=unknown]

# Regression test for #75365. Do not create invalid casts when building CASE
# expressions. We build a Select expressions here instead of a scalar so that
# logical properties are generated, which is required to reproduce the bug.
# TODO(#75103): We should be more permissive with casts of arrays of tuples.
# These tests should be successful, not user-facing errors.
build
SELECT CASE WHEN false THEN ARRAY[('', 0)] ELSE ARRAY[]::RECORD[] END
----
error (42804): CASE types tuple[] and tuple{string, int}[] cannot be matched

build
SELECT CASE WHEN false THEN ARRAY[('', 0)] WHEN true THEN ARRAY[]::RECORD[] ELSE ARRAY[('', 0)] END
----
error (42804): CASE types tuple[] and tuple{string, int}[] cannot be matched

0 comments on commit dc745e8

Please sign in to comment.