From 09c8f964cc5e16a38affa21ff5e3f57ae7c3e745 Mon Sep 17 00:00:00 2001 From: YangKeao Date: Sat, 11 May 2024 14:19:09 +0800 Subject: [PATCH] planner: fix the issue that UnionAll didn't handle the range bump case (#52542) close pingcap/tidb#52472 --- .../casetest/dag/testdata/plan_suite_out.json | 2 +- .../hint/testdata/integration_suite_out.json | 10 ++-- .../testdata/plan_suite_out.json | 60 +++++++++---------- pkg/planner/core/integration_test.go | 27 +++++++++ pkg/planner/core/logical_plan_builder.go | 2 +- pkg/types/field_type.go | 10 +++- tests/integrationtest/r/explain_easy.result | 2 +- 7 files changed, 69 insertions(+), 44 deletions(-) diff --git a/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json b/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json index 02c188b7b090a..e0fcf34eae5d4 100644 --- a/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json +++ b/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json @@ -120,7 +120,7 @@ }, { "SQL": "select * from ((SELECT 1 a,6 b) UNION (SELECT 2,5) UNION (SELECT 2, 4) ORDER BY 1) t order by 1, 2", - "Best": "UnionAll{Dual->Projection->Projection->Dual->Projection->Projection->Dual->Projection->Projection}->HashAgg->Projection->Sort" + "Best": "UnionAll{Dual->Projection->Dual->Projection->Dual->Projection}->HashAgg->Projection->Sort" }, { "SQL": "select * from (select *, NULL as xxx from t) t order by xxx", diff --git a/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json b/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json index c0a044e2e441e..db7113f881942 100644 --- a/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json +++ b/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json @@ -786,12 +786,10 @@ " └─ExchangeSender 3544.89 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: Column#14, collate: binary], [name: Column#15, collate: binary]", " └─HashAgg 3544.89 mpp[tiflash] group by:Column#14, Column#15, ", " └─Union 4431.11 mpp[tiflash] ", - " ├─Projection 3323.33 mpp[tiflash] cast(test.t.a, int(11) BINARY)->Column#14, test.t.b->Column#15", - " │ └─Selection 3323.33 mpp[tiflash] lt(test.t.a, 18), lt(test.t.a, 60)", - " │ └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo", - " └─Projection 1107.78 mpp[tiflash] cast(test.t.a, int(11) BINARY)->Column#14, test.t.b->Column#15", - " └─Selection 1107.78 mpp[tiflash] gt(test.t.b, 1), lt(test.t.a, 60)", - " └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo" + " ├─Selection 3323.33 mpp[tiflash] lt(test.t.a, 18), lt(test.t.a, 60)", + " │ └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo", + " └─Selection 1107.78 mpp[tiflash] gt(test.t.b, 1), lt(test.t.a, 60)", + " └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo" ], "Warn": null }, diff --git a/pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json b/pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json index 6ffc8c1fde96b..05c75cbb2b649 100644 --- a/pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json +++ b/pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json @@ -759,21 +759,19 @@ " └─ExchangeReceiver 3013.16 mpp[tiflash] ", " └─ExchangeSender 3013.16 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: Column#20, collate: binary], [name: Column#21, collate: binary]", " └─Union 3013.16 mpp[tiflash] ", - " ├─Projection 2126.93 mpp[tiflash] cast(Column#12, bigint(21) BINARY)->Column#20, test.t.b->Column#21", - " │ └─Selection 2126.93 mpp[tiflash] lt(Column#12, 18)", - " │ └─Projection 2658.67 mpp[tiflash] Column#12, test.t.b", - " │ └─HashAgg 2658.67 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#12, funcs:firstrow(test.t.b)->test.t.b", - " │ └─ExchangeReceiver 3323.33 mpp[tiflash] ", - " │ └─ExchangeSender 3323.33 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", - " │ └─Selection 3323.33 mpp[tiflash] lt(test.t.a, 60)", - " │ └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo", - " └─Projection 886.22 mpp[tiflash] cast(Column#20, bigint(21) BINARY)->Column#20, Column#21", - " └─Projection 886.22 mpp[tiflash] Column#19->Column#20, test.t.b->Column#21", - " └─HashAgg 886.22 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#19, funcs:firstrow(test.t.b)->test.t.b", - " └─ExchangeReceiver 1107.78 mpp[tiflash] ", - " └─ExchangeSender 1107.78 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", - " └─Selection 1107.78 mpp[tiflash] gt(test.t.b, 1), lt(test.t.a, 60)", - " └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo" + " ├─Selection 2126.93 mpp[tiflash] lt(Column#12, 18)", + " │ └─Projection 2658.67 mpp[tiflash] Column#12, test.t.b", + " │ └─HashAgg 2658.67 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#12, funcs:firstrow(test.t.b)->test.t.b", + " │ └─ExchangeReceiver 3323.33 mpp[tiflash] ", + " │ └─ExchangeSender 3323.33 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", + " │ └─Selection 3323.33 mpp[tiflash] lt(test.t.a, 60)", + " │ └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo", + " └─Projection 886.22 mpp[tiflash] Column#19->Column#20, test.t.b->Column#21", + " └─HashAgg 886.22 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#19, funcs:firstrow(test.t.b)->test.t.b", + " └─ExchangeReceiver 1107.78 mpp[tiflash] ", + " └─ExchangeSender 1107.78 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", + " └─Selection 1107.78 mpp[tiflash] gt(test.t.b, 1), lt(test.t.a, 60)", + " └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo" ], "Warn": null }, @@ -787,23 +785,21 @@ " └─ExchangeReceiver 3013.16 mpp[tiflash] ", " └─ExchangeSender 3013.16 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: Column#20, collate: binary], [name: Column#21, collate: binary]", " └─Union 3013.16 mpp[tiflash] ", - " ├─Projection 2126.93 mpp[tiflash] cast(Column#12, bigint(21) BINARY)->Column#20, test.t.b->Column#21", - " │ └─Selection 2126.93 mpp[tiflash] lt(Column#12, 18)", - " │ └─Projection 2658.67 mpp[tiflash] Column#12, test.t.b", - " │ └─HashAgg 2658.67 mpp[tiflash] group by:test.t.b, funcs:sum(Column#32)->Column#12, funcs:firstrow(test.t.b)->test.t.b", - " │ └─ExchangeReceiver 2658.67 mpp[tiflash] ", - " │ └─ExchangeSender 2658.67 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", - " │ └─HashAgg 2658.67 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#32", - " │ └─Selection 3323.33 mpp[tiflash] lt(test.t.a, 60)", - " │ └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo", - " └─Projection 886.22 mpp[tiflash] cast(Column#20, bigint(21) BINARY)->Column#20, Column#21", - " └─Projection 886.22 mpp[tiflash] Column#19->Column#20, test.t.b->Column#21", - " └─HashAgg 886.22 mpp[tiflash] group by:test.t.b, funcs:sum(Column#46)->Column#19, funcs:firstrow(test.t.b)->test.t.b", - " └─ExchangeReceiver 886.22 mpp[tiflash] ", - " └─ExchangeSender 886.22 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", - " └─HashAgg 886.22 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#46", - " └─Selection 1107.78 mpp[tiflash] gt(test.t.b, 1), lt(test.t.a, 60)", - " └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo" + " ├─Selection 2126.93 mpp[tiflash] lt(Column#12, 18)", + " │ └─Projection 2658.67 mpp[tiflash] Column#12, test.t.b", + " │ └─HashAgg 2658.67 mpp[tiflash] group by:test.t.b, funcs:sum(Column#32)->Column#12, funcs:firstrow(test.t.b)->test.t.b", + " │ └─ExchangeReceiver 2658.67 mpp[tiflash] ", + " │ └─ExchangeSender 2658.67 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", + " │ └─HashAgg 2658.67 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#32", + " │ └─Selection 3323.33 mpp[tiflash] lt(test.t.a, 60)", + " │ └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo", + " └─Projection 886.22 mpp[tiflash] Column#19->Column#20, test.t.b->Column#21", + " └─HashAgg 886.22 mpp[tiflash] group by:test.t.b, funcs:sum(Column#46)->Column#19, funcs:firstrow(test.t.b)->test.t.b", + " └─ExchangeReceiver 886.22 mpp[tiflash] ", + " └─ExchangeSender 886.22 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.t.b, collate: binary]", + " └─HashAgg 886.22 mpp[tiflash] group by:test.t.b, funcs:count(1)->Column#46", + " └─Selection 1107.78 mpp[tiflash] gt(test.t.b, 1), lt(test.t.a, 60)", + " └─TableFullScan 10000.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo" ], "Warn": null }, diff --git a/pkg/planner/core/integration_test.go b/pkg/planner/core/integration_test.go index 2e3069a41b377..453a9b4c0dabc 100644 --- a/pkg/planner/core/integration_test.go +++ b/pkg/planner/core/integration_test.go @@ -30,6 +30,7 @@ import ( "github.com/pingcap/tidb/pkg/infoschema" "github.com/pingcap/tidb/pkg/parser/auth" "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/pingcap/tidb/pkg/planner/core" "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/pingcap/tidb/pkg/table" @@ -2286,3 +2287,29 @@ func TestIssue48257(t *testing.T) { "└─TableFullScan 1.00 cop[tikv] table:t1 keep order:false", )) } + +func TestIssue52472(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("CREATE TABLE t1 ( c1 int);") + tk.MustExec("CREATE TABLE t2 ( c1 int unsigned);") + tk.MustExec("CREATE TABLE t3 ( c1 bigint unsigned);") + tk.MustExec("INSERT INTO t1 (c1) VALUES (8);") + tk.MustExec("INSERT INTO t2 (c1) VALUES (2454396638);") + + // union int and unsigned int will be promoted to long long + rs, err := tk.Exec("SELECT c1 FROM t1 UNION ALL SELECT c1 FROM t2") + require.NoError(t, err) + require.Len(t, rs.Fields(), 1) + require.Equal(t, mysql.TypeLonglong, rs.Fields()[0].Column.FieldType.GetType()) + require.NoError(t, rs.Close()) + + // union int (even literal) and unsigned bigint will be promoted to decimal + rs, err = tk.Exec("SELECT 0 UNION ALL SELECT c1 FROM t3") + require.NoError(t, err) + require.Len(t, rs.Fields(), 1) + require.Equal(t, mysql.TypeNewDecimal, rs.Fields()[0].Column.FieldType.GetType()) + require.NoError(t, rs.Close()) +} diff --git a/pkg/planner/core/logical_plan_builder.go b/pkg/planner/core/logical_plan_builder.go index adf5246adfe43..6f41cfe0ba5bc 100644 --- a/pkg/planner/core/logical_plan_builder.go +++ b/pkg/planner/core/logical_plan_builder.go @@ -1904,7 +1904,7 @@ func unionJoinFieldType(a, b *types.FieldType) *types.FieldType { } else if b.GetType() == mysql.TypeNull { return a } - resultTp := types.NewFieldType(types.MergeFieldType(a.GetType(), b.GetType())) + resultTp := types.AggFieldType([]*types.FieldType{a, b}) // This logic will be intelligible when it is associated with the buildProjection4Union logic. if resultTp.GetType() == mysql.TypeNewDecimal { // The decimal result type will be unsigned only when all the decimals to be united are unsigned. diff --git a/pkg/types/field_type.go b/pkg/types/field_type.go index 505fb33026700..60c00db8994c8 100644 --- a/pkg/types/field_type.go +++ b/pkg/types/field_type.go @@ -68,7 +68,7 @@ func AggFieldType(tps []*FieldType) *FieldType { currType = *t continue } - mtp := MergeFieldType(currType.GetType(), t.GetType()) + mtp := mergeFieldType(currType.GetType(), t.GetType()) isMixedSign = isMixedSign || (mysql.HasUnsignedFlag(currType.GetFlag()) != mysql.HasUnsignedFlag(t.GetFlag())) currType.SetType(mtp) currType.SetFlag(mergeTypeFlag(currType.GetFlag(), t.GetFlag())) @@ -367,12 +367,16 @@ func DefaultCharsetForType(tp byte) (defaultCharset string, defaultCollation str return charset.CharsetBin, charset.CollationBin } -// MergeFieldType merges two MySQL type to a new type. +// mergeFieldType merges two MySQL type to a new type. // This is used in hybrid field type expression. // For example "select case c when 1 then 2 when 2 then 'tidb' from t;" // The result field type of the case expression is the merged type of the two when clause. // See https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L1042 -func MergeFieldType(a byte, b byte) byte { +// +// This function doesn't handle the range bump: for example, when the unsigned long is merged with signed long, +// the result should be longlong. However, this function returns long for this case. Please use `AggFieldType` +// function if you need to handle the range bump. +func mergeFieldType(a byte, b byte) byte { ia := getFieldTypeIndex(a) ib := getFieldTypeIndex(b) return fieldTypeMergeRules[ia][ib] diff --git a/tests/integrationtest/r/explain_easy.result b/tests/integrationtest/r/explain_easy.result index 1a200d1932a66..27f5939d94d0c 100644 --- a/tests/integrationtest/r/explain_easy.result +++ b/tests/integrationtest/r/explain_easy.result @@ -516,7 +516,7 @@ StreamAgg 1.00 root funcs:count(1)->Column#22 │ └─Projection 1.00 root Column#33, Column#34, Column#35, Column#14, Column#15, Column#16 │ └─HashAgg 1.00 root group by:explain_easy.test01.region_id, explain_easy.test01.show_date, explain_easy.test01.stat_date, funcs:firstrow(explain_easy.test01.stat_date)->Column#33, funcs:firstrow(explain_easy.test01.show_date)->Column#34, funcs:firstrow(explain_easy.test01.region_id)->Column#35, funcs:firstrow(explain_easy.test01.stat_date)->Column#14, funcs:firstrow(explain_easy.test01.show_date)->Column#15, funcs:firstrow(explain_easy.test01.region_id)->Column#16, funcs:count(1)->Column#39 │ └─TableReader 0.01 root data:Selection - │ └─Selection 0.01 cop[tikv] eq(explain_easy.test01.period, 1), ge(explain_easy.test01.stat_date, 20191202), gt(cast(explain_easy.test01.registration_num, bigint(20) BINARY), 0), le(explain_easy.test01.stat_date, 20191202) + │ └─Selection 0.01 cop[tikv] eq(explain_easy.test01.period, 1), ge(explain_easy.test01.stat_date, 20191202), gt(cast(explain_easy.test01.registration_num, decimal(20,0) BINARY), 0), le(explain_easy.test01.stat_date, 20191202) │ └─TableFullScan 10000.00 cop[tikv] table:test01 keep order:false, stats:pseudo └─TableReader(Probe) 2.00 root data:TableRangeScan └─TableRangeScan 2.00 cop[tikv] table:b range: decided by [Column#16], keep order:false, stats:pseudo