Skip to content

Commit 9887464

Browse files
committed
fix: handle case of non-literal expressions with same name
1 parent f4464ba commit 9887464

File tree

2 files changed

+57
-37
lines changed

2 files changed

+57
-37
lines changed

datafusion/sql/src/set_expr.rs

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -199,23 +199,27 @@ fn alias_set_expr(set_expr: &mut SetExpr, schema: &DFSchemaRef) {
199199
}
200200
}
201201

202-
// Adds aliases to literal value expressions where missing, based on the input schema, as these are
203-
// the ones that can cause duplicate name issues (e.g. `SELECT 0, 0` has two columns named `Int64(0)`).
204-
// Other expressions typically have unique names.
202+
// Aliases unnamed expressions in the provided select items using the provided schema.
203+
// This helps with set expression queries where the right side has duplicate expressions,
204+
// but the left side has unique column names, which control the output schema anyway.
205205
fn alias_select_items(items: &mut [SelectItem], schema: &DFSchemaRef) {
206206
let mut col_idx = 0;
207207
for item in items.iter_mut() {
208208
match item {
209-
SelectItem::UnnamedExpr(expr) if is_literal_value(expr) => {
210-
if let Some(field) = schema.fields().get(col_idx) {
209+
SelectItem::UnnamedExpr(expr) => {
210+
if !matches!(
211+
expr,
212+
SQLExpr::Identifier(_) | SQLExpr::CompoundIdentifier(_)
213+
) && let Some(field) = schema.fields().get(col_idx)
214+
{
211215
*item = SelectItem::ExprWithAlias {
212216
expr: expr.clone(),
213217
alias: Ident::new(field.name()),
214218
};
215219
}
216220
col_idx += 1;
217221
}
218-
SelectItem::UnnamedExpr(_) | SelectItem::ExprWithAlias { .. } => {
222+
SelectItem::ExprWithAlias { .. } => {
219223
col_idx += 1;
220224
}
221225
SelectItem::Wildcard(_) | SelectItem::QualifiedWildcard(_, _) => {
@@ -224,14 +228,3 @@ fn alias_select_items(items: &mut [SelectItem], schema: &DFSchemaRef) {
224228
}
225229
}
226230
}
227-
228-
/// Returns true if the expression is a literal value that could cause duplicate names.
229-
fn is_literal_value(expr: &SQLExpr) -> bool {
230-
matches!(
231-
expr,
232-
SQLExpr::Value(_)
233-
| SQLExpr::UnaryOp { .. }
234-
| SQLExpr::TypedString { .. }
235-
| SQLExpr::Interval(_)
236-
)
237-
}

datafusion/sql/tests/sql_integration.rs

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2656,55 +2656,82 @@ fn union_all_by_name_same_column_names() {
26562656
}
26572657

26582658
#[test]
2659-
fn union_all_with_duplicate_literals() {
2660-
let sql = "SELECT 0 a, 0 b UNION ALL SELECT 1, 1";
2659+
fn union_all_with_duplicate_expressions() {
2660+
let sql = "\
2661+
SELECT 0 a, 0 b \
2662+
UNION ALL SELECT 1, 1 \
2663+
UNION ALL SELECT count(*), count(*) FROM orders";
26612664
let plan = logical_plan(sql).unwrap();
26622665
assert_snapshot!(
26632666
plan,
26642667
@r"
26652668
Union
2666-
Projection: Int64(0) AS a, Int64(0) AS b
2667-
EmptyRelation: rows=1
2668-
Projection: Int64(1) AS a, Int64(1) AS b
2669-
EmptyRelation: rows=1
2669+
Union
2670+
Projection: Int64(0) AS a, Int64(0) AS b
2671+
EmptyRelation: rows=1
2672+
Projection: Int64(1) AS a, Int64(1) AS b
2673+
EmptyRelation: rows=1
2674+
Projection: count(*) AS a, count(*) AS b
2675+
Aggregate: groupBy=[[]], aggr=[[count(*)]]
2676+
TableScan: orders
26702677
"
26712678
);
26722679
}
26732680

26742681
#[test]
2675-
fn intersect_with_duplicate_literals() {
2676-
let sql = "SELECT 1 as a, 0 as b, 0 as c INTERSECT SELECT 1, 0, 0";
2682+
fn intersect_with_duplicate_expressions() {
2683+
let sql = "\
2684+
SELECT 0 a, 0 b \
2685+
INTERSECT SELECT 1, 1 \
2686+
INTERSECT SELECT count(*), count(*) FROM orders";
26772687
let plan = logical_plan(sql).unwrap();
26782688
assert_snapshot!(
26792689
plan,
26802690
@r"
2681-
LeftSemi Join: left.a = right.a, left.b = right.b, left.c = right.c
2691+
LeftSemi Join: left.a = right.a, left.b = right.b
26822692
Distinct:
26832693
SubqueryAlias: left
2684-
Projection: Int64(1) AS a, Int64(0) AS b, Int64(0) AS c
2685-
EmptyRelation: rows=1
2694+
LeftSemi Join: left.a = right.a, left.b = right.b
2695+
Distinct:
2696+
SubqueryAlias: left
2697+
Projection: Int64(0) AS a, Int64(0) AS b
2698+
EmptyRelation: rows=1
2699+
SubqueryAlias: right
2700+
Projection: Int64(1) AS a, Int64(1) AS b
2701+
EmptyRelation: rows=1
26862702
SubqueryAlias: right
2687-
Projection: Int64(1) AS a, Int64(0) AS b, Int64(0) AS c
2688-
EmptyRelation: rows=1
2703+
Projection: count(*) AS a, count(*) AS b
2704+
Aggregate: groupBy=[[]], aggr=[[count(*)]]
2705+
TableScan: orders
26892706
"
26902707
);
26912708
}
26922709

26932710
#[test]
2694-
fn except_with_duplicate_literals() {
2695-
let sql = "SELECT 1 as a, 0 as b, 0 as c EXCEPT SELECT 2, 0, 0";
2711+
fn except_with_duplicate_expressions() {
2712+
let sql = "\
2713+
SELECT 0 a, 0 b \
2714+
EXCEPT SELECT 1, 1 \
2715+
EXCEPT SELECT count(*), count(*) FROM orders";
26962716
let plan = logical_plan(sql).unwrap();
26972717
assert_snapshot!(
26982718
plan,
26992719
@r"
2700-
LeftAnti Join: left.a = right.a, left.b = right.b, left.c = right.c
2720+
LeftAnti Join: left.a = right.a, left.b = right.b
27012721
Distinct:
27022722
SubqueryAlias: left
2703-
Projection: Int64(1) AS a, Int64(0) AS b, Int64(0) AS c
2704-
EmptyRelation: rows=1
2723+
LeftAnti Join: left.a = right.a, left.b = right.b
2724+
Distinct:
2725+
SubqueryAlias: left
2726+
Projection: Int64(0) AS a, Int64(0) AS b
2727+
EmptyRelation: rows=1
2728+
SubqueryAlias: right
2729+
Projection: Int64(1) AS a, Int64(1) AS b
2730+
EmptyRelation: rows=1
27052731
SubqueryAlias: right
2706-
Projection: Int64(2) AS a, Int64(0) AS b, Int64(0) AS c
2707-
EmptyRelation: rows=1
2732+
Projection: count(*) AS a, count(*) AS b
2733+
Aggregate: groupBy=[[]], aggr=[[count(*)]]
2734+
TableScan: orders
27082735
"
27092736
);
27102737
}

0 commit comments

Comments
 (0)