diff --git a/go/test/endtoend/vtgate/queries/union/union_test.go b/go/test/endtoend/vtgate/queries/union/union_test.go index d382d039f02..898f8b1d659 100644 --- a/go/test/endtoend/vtgate/queries/union/union_test.go +++ b/go/test/endtoend/vtgate/queries/union/union_test.go @@ -73,6 +73,17 @@ func TestUnionDistinct(t *testing.T) { t.Skip() mcmp.AssertMatches("select 1 from dual where 1 IN (select 1 as col union select 2)", "[[INT64(1)]]") }) + if utils.BinaryIsAtLeastAtVersion(19, "vtgate") { + mcmp.AssertMatches(`SELECT 1 from t1 UNION SELECT 2 from t1`, `[[INT64(1)] [INT64(2)]]`) + mcmp.AssertMatches(`SELECT 5 from t1 UNION SELECT 6 from t1`, `[[INT64(5)] [INT64(6)]]`) + mcmp.AssertMatchesNoOrder(`SELECT id1 from t1 UNION SELECT id2 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT 1 from t1 UNION SELECT id2 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT 5 from t1 UNION SELECT id2 from t1`, `[[INT64(5)] [INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT id1 from t1 UNION SELECT 2 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)]]`) + mcmp.AssertMatchesNoOrder(`SELECT id1 from t1 UNION SELECT 5 from t1`, `[[INT64(1)] [INT64(2)] [INT64(3)] [INT64(4)] [INT64(5)]]`) + mcmp.Exec(`select curdate() from t1 union select 3 from t1`) + mcmp.Exec(`select curdate() from t1 union select id1 from t1`) + } }) } diff --git a/go/vt/vtgate/planbuilder/operators/union_merging.go b/go/vt/vtgate/planbuilder/operators/union_merging.go index 953d779c6a1..1fb7d4fb454 100644 --- a/go/vt/vtgate/planbuilder/operators/union_merging.go +++ b/go/vt/vtgate/planbuilder/operators/union_merging.go @@ -17,8 +17,11 @@ limitations under the License. package operators import ( + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" ) @@ -180,25 +183,32 @@ func createMergedUnion( cols := make(sqlparser.SelectExprs, len(lhsExprs)) noDeps := len(lhsExprs) != len(rhsExprs) for idx, col := range lhsExprs { - ae, ok := col.(*sqlparser.AliasedExpr) + lae, ok := col.(*sqlparser.AliasedExpr) if !ok { cols[idx] = col noDeps = true continue } - col := sqlparser.NewColName(ae.ColumnName()) + col := sqlparser.NewColName(lae.ColumnName()) cols[idx] = aeWrap(col) if noDeps { continue } - deps := ctx.SemTable.RecursiveDeps(ae.Expr) - ae, ok = rhsExprs[idx].(*sqlparser.AliasedExpr) + deps := ctx.SemTable.RecursiveDeps(lae.Expr) + rae, ok := rhsExprs[idx].(*sqlparser.AliasedExpr) if !ok { noDeps = true continue } - deps = deps.Merge(ctx.SemTable.RecursiveDeps(ae.Expr)) + deps = deps.Merge(ctx.SemTable.RecursiveDeps(rae.Expr)) + rt, foundR := ctx.SemTable.TypeForExpr(rae.Expr) + lt, foundL := ctx.SemTable.TypeForExpr(lae.Expr) + if foundR && foundL { + types := []sqltypes.Type{rt.Type(), lt.Type()} + t := evalengine.AggregateTypes(types) + ctx.SemTable.ExprTypes[col] = evalengine.NewType(t, collations.Unknown) + } ctx.SemTable.Recursive[col] = deps } diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.json b/go/vt/vtgate/planbuilder/testdata/union_cases.json index 975b66bd6eb..3f25b60556b 100644 --- a/go/vt/vtgate/planbuilder/testdata/union_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/union_cases.json @@ -523,9 +523,8 @@ "Instructions": { "OperatorType": "Distinct", "Collations": [ - "(0:1)" + "0" ], - "ResultColumns": 1, "Inputs": [ { "OperatorType": "Route", @@ -534,8 +533,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select `1`, weight_string(`1`) from (select 1 from dual where 1 != 1 union select null from dual where 1 != 1 union select 1.0 from dual where 1 != 1 union select '1' from dual where 1 != 1 union select 2 from dual where 1 != 1 union select 2.0 from `user` where 1 != 1) as dt where 1 != 1", - "Query": "select `1`, weight_string(`1`) from (select 1 from dual union select null from dual union select 1.0 from dual union select '1' from dual union select 2 from dual union select 2.0 from `user`) as dt", + "FieldQuery": "select 1 from dual where 1 != 1 union select null from dual where 1 != 1 union select 1.0 from dual where 1 != 1 union select '1' from dual where 1 != 1 union select 2 from dual where 1 != 1 union select 2.0 from `user` where 1 != 1", + "Query": "select 1 from dual union select null from dual union select 1.0 from dual union select '1' from dual union select 2 from dual union select 2.0 from `user`", "Table": "`user`, dual" } ] @@ -1535,5 +1534,190 @@ "user.user" ] } + }, + { + "comment": "Select literals from table union Select literals from table", + "query": "SELECT 1 from user UNION SELECT 2 from user", + "plan": { + "QueryType": "SELECT", + "Original": "SELECT 1 from user UNION SELECT 2 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "0" + ], + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from `user` where 1 != 1 union select 2 from `user` where 1 != 1", + "Query": "select 1 from `user` union select 2 from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select column from table union Select literals from table", + "query": "select col1 from user union select 3 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select col1 from user union select 3 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select col1, weight_string(col1) from (select col1 from `user` where 1 != 1 union select 3 from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select col1, weight_string(col1) from (select col1 from `user` union select 3 from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select literals from table union Select column from table", + "query": "select 3 from user union select col1 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select 3 from user union select col1 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `3`, weight_string(`3`) from (select 3 from `user` where 1 != 1 union select col1 from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select `3`, weight_string(`3`) from (select 3 from `user` union select col1 from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select literals from table union Select now() from table", + "query": "select 3 from user union select now() from user", + "plan": { + "QueryType": "SELECT", + "Original": "select 3 from user union select now() from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `3`, weight_string(`3`) from (select 3 from `user` where 1 != 1 union select now() from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select `3`, weight_string(`3`) from (select 3 from `user` union select now() from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select now() from table union Select literals from table", + "query": "select now() from user union select 3 from user", + "plan": { + "QueryType": "SELECT", + "Original": "select now() from user union select 3 from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `now()`, weight_string(`now()`) from (select now() from `user` where 1 != 1 union select 3 from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select `now()`, weight_string(`now()`) from (select now() from `user` union select 3 from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "Select now() from table union Select column from table", + "query": "select now() from user union select id from user", + "plan": { + "QueryType": "SELECT", + "Original": "select now() from user union select id from user", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `now()`, weight_string(`now()`) from (select now() from `user` where 1 != 1 union select id from `user` where 1 != 1) as dt where 1 != 1", + "Query": "select `now()`, weight_string(`now()`) from (select now() from `user` union select id from `user`) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } } ]