Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions src/backend/gpopt/translate/CTranslatorQueryToDXL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4142,19 +4142,27 @@ CTranslatorQueryToDXL::TranslateJoinExprInFromToDXL(JoinExpr *join_expr)
{
continue;
}
GPOS_ASSERT(IsA(join_alias_node, Var) ||
Comment thread
yjhjstz marked this conversation as resolved.
IsA(join_alias_node, FuncExpr) ||
IsA(join_alias_node, CoalesceExpr));
// A merged USING column is a plain Var when both inputs have the same
// type, a CoalesceExpr for a FULL JOIN, and the outer input coerced to
// the common type otherwise (FuncExpr, RelabelType, ...). Any
// expression the scalar translator understands is acceptable here;
// unsupported nodes still raise the regular "unsupported feature"
// exception from TranslateExprToDXL.
CHAR *col_name_char_array = strVal(lfirst(lc_col_name));

// create the DXL node holding the target list entry and add it to proj list
CDXLNode *project_elem_dxlnode = TranslateExprToDXLProject(
(Expr *) join_alias_node, col_name_char_array);
project_list_dxlnode->AddChild(project_elem_dxlnode);

if (IsA(join_alias_node, CoalesceExpr))
if (!IsA(join_alias_node, Var))
{
// add coalesce expression to the computed columns
// TranslateExprToDXLProject reuses the input colid for a plain
// Var but allocates a new one for any other expression, so the
// column only exists if the join computes it. Quals are left
// alone by flatten_join_alias_var_optimizer and still reference
// the merged column through the mapping loaded below, hence the
// projection is required and not merely an optimization.
project_elem_dxlnode->AddRef();
project_list_computed_cols_dxlnode->AddChild(project_elem_dxlnode);
}
Expand Down
130 changes: 130 additions & 0 deletions src/test/regress/expected/join_gp.out
Original file line number Diff line number Diff line change
Expand Up @@ -3672,3 +3672,133 @@ select * from generate_series(1,2) t1, lateral (select t3.i from lat_oq_t2 t2 jo

drop table lat_oq_t2;
drop table lat_oq_t3;
--
-- Test that ORCA can plan a USING join whose merged column is the left input
-- coerced with a binary-compatible cast (RelabelType), e.g. varchar vs text or
-- int vs oid. The query-to-DXL translator used to assert on such join alias
-- vars and fall back to the Postgres planner.
--
create table using_coerce_t1(k varchar(10), f2 int) distributed by (f2);
create table using_coerce_t2(k text, f2 oid) distributed by (f2);
insert into using_coerce_t1 values ('a', 1), ('b', 2);
insert into using_coerce_t2 values ('a', 1), ('c', 3);
explain (costs off) select k from using_coerce_t1 left join using_coerce_t2 using(k) order by 1;
QUERY PLAN
--------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
Merge Key: ((using_coerce_t1.k)::character varying)
-> Sort
Sort Key: ((using_coerce_t1.k)::character varying)
-> Hash Right Join
Hash Cond: (using_coerce_t2.k = (using_coerce_t1.k)::text)
-> Redistribute Motion 3:3 (slice2; segments: 3)
Hash Key: using_coerce_t2.k
-> Seq Scan on using_coerce_t2
-> Hash
-> Redistribute Motion 3:3 (slice3; segments: 3)
Hash Key: using_coerce_t1.k
-> Seq Scan on using_coerce_t1
Optimizer: Postgres query optimizer
(14 rows)

select k from using_coerce_t1 left join using_coerce_t2 using(k) order by 1;
k
---
a
b
(2 rows)

select f2, count(*) from using_coerce_t1 left join using_coerce_t2 using(f2) group by f2 order by 1;
f2 | count
----+-------
1 | 1
2 | 1
(2 rows)

select f2, count(*) from using_coerce_t1 x(x0,x1) left join (using_coerce_t1 left join using_coerce_t2 using(f2)) on (x0 = 'a') group by f2 order by 1;
f2 | count
----+-------
1 | 1
2 | 1
| 1
(3 rows)

-- Quals are deliberately not flattened by flatten_join_alias_var_optimizer, so
-- a predicate on the merged column references the computed join output column
-- directly. Without a projection producing it ORCA fell back with
-- "Attribute number N not found in project list".
explain (costs off) select k from using_coerce_t1 left join using_coerce_t2 using(k) where k = 'a';
QUERY PLAN
---------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Nested Loop Left Join
-> Seq Scan on using_coerce_t1
Filter: ((k)::text = 'a'::text)
-> Materialize
-> Broadcast Motion 3:3 (slice2; segments: 3)
-> Seq Scan on using_coerce_t2
Filter: (k = 'a'::text)
Optimizer: Postgres query optimizer
(9 rows)

select k from using_coerce_t1 left join using_coerce_t2 using(k) where k = 'a';
k
---
a
(1 row)

select f2 from using_coerce_t1 join using_coerce_t2 using(f2) where f2 = 1;
f2
----
1
(1 row)

select k from using_coerce_t1 full join using_coerce_t2 using(k) where k = 'c';
k
---
c
(1 row)

drop table using_coerce_t1;
drop table using_coerce_t2;
--
-- Same, for a merged column that needs a real cast function (FuncExpr) rather
-- than a binary-compatible relabel.
--
create table using_coerce_t3(k int, v int) distributed by (v);
create table using_coerce_t4(k bigint, v int) distributed by (v);
insert into using_coerce_t3 values (1, 1), (2, 2);
insert into using_coerce_t4 values (1, 1), (3, 3);
explain (costs off) select k from using_coerce_t3 left join using_coerce_t4 using(k) where k = 1;
QUERY PLAN
------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Right Join
Hash Cond: (using_coerce_t4.k = using_coerce_t3.k)
-> Redistribute Motion 3:3 (slice2; segments: 3)
Hash Key: using_coerce_t4.k
-> Seq Scan on using_coerce_t4
-> Hash
-> Redistribute Motion 3:3 (slice3; segments: 3)
Hash Key: using_coerce_t3.k
-> Seq Scan on using_coerce_t3
Filter: ((k)::bigint = 1)
Optimizer: Postgres query optimizer
(12 rows)

select k from using_coerce_t3 left join using_coerce_t4 using(k) where k = 1;
k
---
1
(1 row)

select k from using_coerce_t3 full join using_coerce_t4 using(k) order by 1;
k
---
1
2
3
(3 rows)

drop table using_coerce_t3;
drop table using_coerce_t4;
128 changes: 128 additions & 0 deletions src/test/regress/expected/join_gp_optimizer.out
Original file line number Diff line number Diff line change
Expand Up @@ -3634,3 +3634,131 @@ select * from generate_series(1,2) t1, lateral (select t3.i from lat_oq_t2 t2 jo

drop table lat_oq_t2;
drop table lat_oq_t3;
--
-- Test that ORCA can plan a USING join whose merged column is the left input
-- coerced with a binary-compatible cast (RelabelType), e.g. varchar vs text or
-- int vs oid. The query-to-DXL translator used to assert on such join alias
-- vars and fall back to the Postgres planner.
--
create table using_coerce_t1(k varchar(10), f2 int) distributed by (f2);
create table using_coerce_t2(k text, f2 oid) distributed by (f2);
insert into using_coerce_t1 values ('a', 1), ('b', 2);
insert into using_coerce_t2 values ('a', 1), ('c', 3);
explain (costs off) select k from using_coerce_t1 left join using_coerce_t2 using(k) order by 1;
QUERY PLAN
--------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
Merge Key: ((using_coerce_t1.k)::character varying)
-> Sort
Sort Key: ((using_coerce_t1.k)::character varying)
-> Hash Left Join
Hash Cond: ((using_coerce_t1.k)::text = using_coerce_t2.k)
-> Seq Scan on using_coerce_t1
-> Hash
-> Broadcast Motion 3:3 (slice2; segments: 3)
-> Seq Scan on using_coerce_t2
Optimizer: GPORCA
(11 rows)

select k from using_coerce_t1 left join using_coerce_t2 using(k) order by 1;
k
---
a
b
(2 rows)

select f2, count(*) from using_coerce_t1 left join using_coerce_t2 using(f2) group by f2 order by 1;
f2 | count
----+-------
1 | 1
2 | 1
(2 rows)

select f2, count(*) from using_coerce_t1 x(x0,x1) left join (using_coerce_t1 left join using_coerce_t2 using(f2)) on (x0 = 'a') group by f2 order by 1;
f2 | count
----+-------
1 | 1
2 | 1
| 1
(3 rows)

-- Quals are deliberately not flattened by flatten_join_alias_var_optimizer, so
-- a predicate on the merged column references the computed join output column
-- directly. Without a projection producing it ORCA fell back with
-- "Attribute number N not found in project list".
explain (costs off) select k from using_coerce_t1 left join using_coerce_t2 using(k) where k = 'a';
QUERY PLAN
--------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3)
-> Hash Left Join
Hash Cond: ((using_coerce_t1.k)::text = using_coerce_t2.k)
-> Seq Scan on using_coerce_t1
Filter: ((k)::text = 'a'::text)
-> Hash
-> Broadcast Motion 3:3 (slice2; segments: 3)
-> Seq Scan on using_coerce_t2
Optimizer: GPORCA
(9 rows)

select k from using_coerce_t1 left join using_coerce_t2 using(k) where k = 'a';
k
---
a
(1 row)

select f2 from using_coerce_t1 join using_coerce_t2 using(f2) where f2 = 1;
f2
----
1
(1 row)

select k from using_coerce_t1 full join using_coerce_t2 using(k) where k = 'c';
k
---
c
(1 row)

drop table using_coerce_t1;
drop table using_coerce_t2;
--
-- Same, for a merged column that needs a real cast function (FuncExpr) rather
-- than a binary-compatible relabel.
--
create table using_coerce_t3(k int, v int) distributed by (v);
create table using_coerce_t4(k bigint, v int) distributed by (v);
insert into using_coerce_t3 values (1, 1), (2, 2);
insert into using_coerce_t4 values (1, 1), (3, 3);
explain (costs off) select k from using_coerce_t3 left join using_coerce_t4 using(k) where k = 1;
QUERY PLAN
----------------------------------------------------------------------------
Result
-> Gather Motion 3:1 (slice1; segments: 3)
-> Hash Left Join
Hash Cond: ((using_coerce_t3.k)::bigint = using_coerce_t4.k)
-> Redistribute Motion 3:3 (slice2; segments: 3)
Hash Key: (using_coerce_t3.k)::bigint
-> Seq Scan on using_coerce_t3
Filter: ((k)::bigint = 1)
-> Hash
-> Redistribute Motion 3:3 (slice3; segments: 3)
Hash Key: using_coerce_t4.k
-> Seq Scan on using_coerce_t4
Optimizer: GPORCA
(13 rows)

select k from using_coerce_t3 left join using_coerce_t4 using(k) where k = 1;
k
---
1
(1 row)

select k from using_coerce_t3 full join using_coerce_t4 using(k) order by 1;
k
---
1
2
3
(3 rows)

drop table using_coerce_t3;
drop table using_coerce_t4;
44 changes: 44 additions & 0 deletions src/test/regress/sql/join_gp.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1332,3 +1332,47 @@ select * from generate_series(1,2) t1, lateral (select t3.i from lat_oq_t2 t2 jo

drop table lat_oq_t2;
drop table lat_oq_t3;

--
-- Test that ORCA can plan a USING join whose merged column is the left input
-- coerced with a binary-compatible cast (RelabelType), e.g. varchar vs text or
-- int vs oid. The query-to-DXL translator used to assert on such join alias
-- vars and fall back to the Postgres planner.
--
create table using_coerce_t1(k varchar(10), f2 int) distributed by (f2);
create table using_coerce_t2(k text, f2 oid) distributed by (f2);
insert into using_coerce_t1 values ('a', 1), ('b', 2);
insert into using_coerce_t2 values ('a', 1), ('c', 3);

explain (costs off) select k from using_coerce_t1 left join using_coerce_t2 using(k) order by 1;
select k from using_coerce_t1 left join using_coerce_t2 using(k) order by 1;
select f2, count(*) from using_coerce_t1 left join using_coerce_t2 using(f2) group by f2 order by 1;
select f2, count(*) from using_coerce_t1 x(x0,x1) left join (using_coerce_t1 left join using_coerce_t2 using(f2)) on (x0 = 'a') group by f2 order by 1;

-- Quals are deliberately not flattened by flatten_join_alias_var_optimizer, so
-- a predicate on the merged column references the computed join output column
-- directly. Without a projection producing it ORCA fell back with
-- "Attribute number N not found in project list".
explain (costs off) select k from using_coerce_t1 left join using_coerce_t2 using(k) where k = 'a';
select k from using_coerce_t1 left join using_coerce_t2 using(k) where k = 'a';
select f2 from using_coerce_t1 join using_coerce_t2 using(f2) where f2 = 1;
select k from using_coerce_t1 full join using_coerce_t2 using(k) where k = 'c';

drop table using_coerce_t1;
drop table using_coerce_t2;

--
-- Same, for a merged column that needs a real cast function (FuncExpr) rather
-- than a binary-compatible relabel.
--
create table using_coerce_t3(k int, v int) distributed by (v);
create table using_coerce_t4(k bigint, v int) distributed by (v);
insert into using_coerce_t3 values (1, 1), (2, 2);
insert into using_coerce_t4 values (1, 1), (3, 3);

explain (costs off) select k from using_coerce_t3 left join using_coerce_t4 using(k) where k = 1;
select k from using_coerce_t3 left join using_coerce_t4 using(k) where k = 1;
select k from using_coerce_t3 full join using_coerce_t4 using(k) order by 1;

drop table using_coerce_t3;
drop table using_coerce_t4;
Loading