Skip to content

Commit

Permalink
Merge pull request cockroachdb#75276 from cockroachdb/blathers/backpo…
Browse files Browse the repository at this point in the history
…rt-release-21.2-75219

release-21.2: opt: do not add invalid casts to match types in set operations
  • Loading branch information
mgartner authored Jan 24, 2022
2 parents 4065a73 + 91194f1 commit f479a04
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 9 deletions.
14 changes: 14 additions & 0 deletions pkg/sql/opt/optbuilder/testdata/union
Original file line number Diff line number Diff line change
Expand Up @@ -1079,3 +1079,17 @@ sort
├── columns: t68702.a:7 b:8 c:9
└── scan t68702
└── columns: t68702.a:7 b:8 c:9 rowid:10!null crdb_internal_mvcc_timestamp:11 tableoid:12


# Regression test for #70831. Do not add non-existent casts to match types on
# either side of a set operation.
# NOTE: In Postgres this example successfully returns a result. Our tuple typing
# and casting logic doesn't match Postgres's semantics exactly, so we return a
# user error. See
# https://github.com/cockroachdb/cockroach/issues/70831#issuecomment-929547297.
build
SELECT * FROM (VALUES (ARRAY[(true, NULL)])) AS v1
EXCEPT ALL
SELECT * FROM (VALUES (ARRAY[]::RECORD[])) AS v2
----
error (42804): EXCEPT types tuple{bool, unknown}[] and tuple[] cannot be matched
25 changes: 16 additions & 9 deletions pkg/sql/opt/optbuilder/union.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,15 +153,20 @@ func determineUnionType(left, right *types.T, clauseTag string) *types.T {
}

if left.Equivalent(right) {
// Do a best-effort attempt to determine which type is "larger".
if left.Width() > right.Width() {
return left
}
// In the default case, use the left type.
src, tgt := right, left
if left.Width() < right.Width() {
return right
// If the right type is "larger", use it.
src, tgt = left, right
}
// In other cases, use the left type.
return left
if _, ok := tree.LookupCastVolatility(src, tgt, nil /* sessionData */); !ok {
// Error if no cast exists from src to tgt.
panic(pgerror.Newf(
pgcode.DatatypeMismatch,
"%v types %s and %s cannot be matched", clauseTag, left, right,
))
}
return tgt
}
leftFam, rightFam := left.Family(), right.Family()

Expand Down Expand Up @@ -189,7 +194,7 @@ func determineUnionType(left, right *types.T, clauseTag string) *types.T {
return right
}

// TODO(radu): Postgres has more encompassing rules:
// TODO(#75103): Postgres has more encompassing rules:
// http://www.postgresql.org/docs/12/static/typeconv-union-case.html
panic(pgerror.Newf(
pgcode.DatatypeMismatch,
Expand All @@ -198,7 +203,9 @@ func determineUnionType(left, right *types.T, clauseTag string) *types.T {
}

// addCasts adds a projection to a scope, adding casts as necessary so that the
// resulting columns have the given types.
// resulting columns have the given types. This function assumes that there is a
// valid cast from the column types in dst.cols to the corresponding types in
// outTypes.
func (b *Builder) addCasts(dst *scope, outTypes []*types.T) *scope {
expr := dst.expr.(memo.RelExpr)
dstCols := dst.cols
Expand Down
6 changes: 6 additions & 0 deletions pkg/sql/opt/optbuilder/union_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ func TestUnionType(t *testing.T) {
right: types.String,
expected: nil,
},
{
// Error.
left: types.MakeArray(types.MakeTuple([]*types.T{types.Bool})),
right: types.MakeArray(types.MakeTuple([]*types.T{types.Bool, types.Int})),
expected: nil,
},
}

for _, tc := range testCases {
Expand Down

0 comments on commit f479a04

Please sign in to comment.