-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
60827: sql: propagate result types to inputs during set ops with nulls and tuples r=yuzefovich a=yuzefovich **sql: move MergeResultTypes from physicalplan package and unexport** Release note: None **sql: propagate result types to inputs in set ops with nulls and tuples** The physical planning for set operations is special because it allows for the result types from its inputs to be of different type family in some cases. Namely, it is ok if one input has `types.Unknown` whereas the other input is of some "known" type. In order to handle such case the execbuilder plans casts; however, currently it is not possible to plan casts to `Tuple` type. As a result, the vectorized engine is not able to execute the query because it expects the data coming from both inputs to be of the same type. This commit modifies the logic of reconciling the result types from both inputs to additionally update unknown types on the inputs if the other input is of a Tuple type. Such behavior is acceptable because in the original plan we only expected to have NULL values, and the Tuple type is able to handle such case too. The problem can also be reproduced in a logic test with the row-by-row engine in the `fakedist` setting. This bug was introduced in a76ee31, and before that change we had the following behavior: overwrite `plan.ResultTypes` to the merged types and then plan a stage of distinct processors if we have UNION. This resulted in the input spec for the input stream to distinct having correctly set types. After the change, that was no longer the case. Before that change the vectorized engine couldn't handle such queries due to type mismatch during `SupportsVectorized` check, so we fell back to the row-by-row engine. However, this commit makes it so that the vectorized engine is able to execute such queries. Fixes: #59611. Release note (bug fix): CockroachDB previously could encounter an internal error when performing UNION operation when the first input resulted only in NULL values and consequent inputs produce tuples, and this is now fixed. Only 21.1 alpha versions are affected. 60853: sql: do not allow reference FK partial unique constraints r=mgartner a=mgartner This commit prevents users from creating a FK with a reference column that has a partial unique constraint. These constraints do not guarantee uniqueness in the entire table so they cannot be used. There is no release note because these constraints are gated behind the experimental_enable_unique_without_index_constraints session variable. Release note: None 60944: bulkio: Avoid using AssertionFailed error when planning replication. r=miretskiy a=miretskiy AssertionFailed error message is not appropriate. Return "unsupported error" instead. Release Notes: None 60945: sql: add tests with unique constraints and virtual columns r=RaduBerinde a=RaduBerinde Logic tests for unique indexes and constraints involving virtual columns. Informs #57608. Release note: None Co-authored-by: Yahor Yuzefovich <[email protected]> Co-authored-by: Marcus Gartner <[email protected]> Co-authored-by: Yevgeniy Miretskiy <[email protected]> Co-authored-by: Radu Berinde <[email protected]>
- Loading branch information
Showing
12 changed files
with
380 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package sql | ||
|
||
import ( | ||
"github.com/cockroachdb/cockroach/pkg/sql/types" | ||
"github.com/cockroachdb/errors" | ||
) | ||
|
||
// mergeResultTypesForSetOp reconciles the ResultTypes between two plans. It | ||
// enforces that each pair of ColumnTypes must either match or be null, in which | ||
// case the non-null type is used. This logic is necessary for cases like SELECT | ||
// NULL UNION SELECT 1. | ||
// | ||
// This method is intended to be used only for planning of set operations. | ||
func mergeResultTypesForSetOp(leftPlan, rightPlan *PhysicalPlan) ([]*types.T, error) { | ||
left, right := leftPlan.GetResultTypes(), rightPlan.GetResultTypes() | ||
if len(left) != len(right) { | ||
return nil, errors.Errorf("ResultTypes length mismatch: %d and %d", len(left), len(right)) | ||
} | ||
merged := make([]*types.T, len(left)) | ||
for i := range left { | ||
leftType, rightType := left[i], right[i] | ||
if rightType.Family() == types.UnknownFamily { | ||
merged[i] = leftType | ||
} else if leftType.Family() == types.UnknownFamily { | ||
merged[i] = rightType | ||
} else if leftType.Equivalent(rightType) { | ||
// The types are equivalent for the purpose of UNION. Precision, | ||
// Width, Oid, etc. do not affect the merging of values. | ||
merged[i] = leftType | ||
} else { | ||
return nil, errors.Errorf( | ||
"conflicting ColumnTypes: %s and %s", leftType.DebugString(), rightType.DebugString()) | ||
} | ||
} | ||
updateUnknownTypesForSetOp(leftPlan, merged) | ||
updateUnknownTypesForSetOp(rightPlan, merged) | ||
return merged, nil | ||
} | ||
|
||
// updateUnknownTypesForSetOp modifies plan's output types of the | ||
// types.UnknownFamily type family to be of the corresponding Tuple type coming | ||
// from the merged types. This is needed because at the moment the execbuilder | ||
// is not able to plan casts to tuples. | ||
// | ||
// This method is intended to be used only for planning of set operations. | ||
// TODO(yuzefovich): remove this once the execbuilder plans casts to tuples. | ||
func updateUnknownTypesForSetOp(plan *PhysicalPlan, merged []*types.T) { | ||
currentTypes := plan.GetResultTypes() | ||
for i := range merged { | ||
if merged[i].Family() == types.TupleFamily && currentTypes[i].Family() == types.UnknownFamily { | ||
for _, procIdx := range plan.ResultRouters { | ||
plan.Processors[procIdx].Spec.ResultTypes[i] = merged[i] | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package sql | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/roachpb" | ||
"github.com/cockroachdb/cockroach/pkg/sql/physicalplan" | ||
"github.com/cockroachdb/cockroach/pkg/sql/types" | ||
"github.com/cockroachdb/cockroach/pkg/util/leaktest" | ||
"github.com/cockroachdb/cockroach/pkg/util/log" | ||
"github.com/cockroachdb/cockroach/pkg/util/uuid" | ||
) | ||
|
||
func TestMergeResultTypesForSetOp(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
defer log.Scope(t).Close(t) | ||
|
||
empty := []*types.T{} | ||
null := []*types.T{types.Unknown} | ||
typeInt := []*types.T{types.Int} | ||
typeTuple := []*types.T{types.MakeTuple(typeInt)} | ||
|
||
testData := []struct { | ||
name string | ||
left []*types.T | ||
right []*types.T | ||
expected *[]*types.T | ||
err bool | ||
}{ | ||
{"both empty", empty, empty, &empty, false}, | ||
{"left empty", empty, typeInt, nil, true}, | ||
{"right empty", typeInt, empty, nil, true}, | ||
{"both null", null, null, &null, false}, | ||
{"left null", null, typeInt, &typeInt, false}, | ||
{"right null", typeInt, null, &typeInt, false}, | ||
{"both int", typeInt, typeInt, &typeInt, false}, | ||
{"left null, right tuple", null, typeTuple, &typeTuple, false}, | ||
{"right null, left tuple", typeTuple, null, &typeTuple, false}, | ||
} | ||
checkUnknownTypesUpdate := func(plan PhysicalPlan, orig, merged []*types.T) { | ||
for i, typ := range plan.GetResultTypes() { | ||
if orig[i].Family() == types.UnknownFamily { | ||
if typ.Family() == types.UnknownFamily && merged[i].Family() == types.TupleFamily { | ||
t.Fatal("should have updated types NULL to tuple type on the original plan") | ||
} | ||
if typ.Family() != types.UnknownFamily && merged[i].Family() != types.TupleFamily { | ||
t.Fatal("should have NOT updated types NULL to tuple type on the original plan") | ||
} | ||
} | ||
} | ||
} | ||
infra := physicalplan.MakePhysicalInfrastructure(uuid.FastMakeV4(), roachpb.NodeID(1)) | ||
var leftPlan, rightPlan PhysicalPlan | ||
leftPlan.PhysicalInfrastructure = &infra | ||
rightPlan.PhysicalInfrastructure = &infra | ||
leftPlan.ResultRouters = []physicalplan.ProcessorIdx{infra.AddProcessor(physicalplan.Processor{})} | ||
rightPlan.ResultRouters = []physicalplan.ProcessorIdx{infra.AddProcessor(physicalplan.Processor{})} | ||
for _, td := range testData { | ||
t.Run(td.name, func(t *testing.T) { | ||
leftPlan.Processors[0].Spec.ResultTypes = td.left | ||
rightPlan.Processors[1].Spec.ResultTypes = td.right | ||
result, err := mergeResultTypesForSetOp(&leftPlan, &rightPlan) | ||
if td.err { | ||
if err == nil { | ||
t.Fatalf("expected error, got %+v", result) | ||
} | ||
return | ||
} | ||
if err != nil { | ||
t.Fatalf("unexpected error: %s", err) | ||
} | ||
if !reflect.DeepEqual(*td.expected, result) { | ||
t.Fatalf("expected %+v, got %+v", *td.expected, result) | ||
} | ||
checkUnknownTypesUpdate(leftPlan, td.left, result) | ||
checkUnknownTypesUpdate(rightPlan, td.right, result) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.