Skip to content

Commit 66f4fcb

Browse files
alambwiedld
authored andcommitted
Use struct instead of named_struct when there are no aliases (apache#9897)
* Revert "use alias (apache#9894)" This reverts commit 9487ca0. * Use `struct` instead of `named_struct` when there are no aliases * Update docs * fmt
1 parent cd7a00b commit 66f4fcb

File tree

4 files changed

+63
-5
lines changed

4 files changed

+63
-5
lines changed

datafusion/sql/src/expr/mod.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
593593
}
594594
}
595595

596+
/// Parses a struct(..) expression
596597
fn parse_struct(
597598
&self,
598599
values: Vec<SQLExpr>,
@@ -603,6 +604,25 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
603604
if !fields.is_empty() {
604605
return not_impl_err!("Struct fields are not supported yet");
605606
}
607+
608+
if values
609+
.iter()
610+
.any(|value| matches!(value, SQLExpr::Named { .. }))
611+
{
612+
self.create_named_struct(values, input_schema, planner_context)
613+
} else {
614+
self.create_struct(values, input_schema, planner_context)
615+
}
616+
}
617+
618+
// Handles a call to struct(...) where the arguments are named. For example
619+
// `struct (v as foo, v2 as bar)` by creating a call to the `named_struct` function
620+
fn create_named_struct(
621+
&self,
622+
values: Vec<SQLExpr>,
623+
input_schema: &DFSchema,
624+
planner_context: &mut PlannerContext,
625+
) -> Result<Expr> {
606626
let args = values
607627
.into_iter()
608628
.enumerate()
@@ -647,6 +667,34 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
647667
)))
648668
}
649669

670+
// Handles a call to struct(...) where the arguments are not named. For example
671+
// `struct (v, v2)` by creating a call to the `struct` function
672+
// which will create a struct with fields named `c0`, `c1`, etc.
673+
fn create_struct(
674+
&self,
675+
values: Vec<SQLExpr>,
676+
input_schema: &DFSchema,
677+
planner_context: &mut PlannerContext,
678+
) -> Result<Expr> {
679+
let args = values
680+
.into_iter()
681+
.map(|value| {
682+
self.sql_expr_to_logical_expr(value, input_schema, planner_context)
683+
})
684+
.collect::<Result<Vec<_>>>()?;
685+
let struct_func = self
686+
.context_provider
687+
.get_function_meta("struct")
688+
.ok_or_else(|| {
689+
internal_datafusion_err!("Unable to find expected 'struct' function")
690+
})?;
691+
692+
Ok(Expr::ScalarFunction(ScalarFunction::new_udf(
693+
struct_func,
694+
args,
695+
)))
696+
}
697+
650698
fn parse_array_agg(
651699
&self,
652700
array_agg: ArrayAgg,

datafusion/sqllogictest/test_files/explain.slt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,8 +390,8 @@ query TT
390390
explain select struct(1, 2.3, 'abc');
391391
----
392392
logical_plan
393-
Projection: Struct({c0:1,c1:2.3,c2:abc}) AS named_struct(Utf8("c0"),Int64(1),Utf8("c1"),Float64(2.3),Utf8("c2"),Utf8("abc"))
393+
Projection: Struct({c0:1,c1:2.3,c2:abc}) AS struct(Int64(1),Float64(2.3),Utf8("abc"))
394394
--EmptyRelation
395395
physical_plan
396-
ProjectionExec: expr=[{c0:1,c1:2.3,c2:abc} as named_struct(Utf8("c0"),Int64(1),Utf8("c1"),Float64(2.3),Utf8("c2"),Utf8("abc"))]
396+
ProjectionExec: expr=[{c0:1,c1:2.3,c2:abc} as struct(Int64(1),Float64(2.3),Utf8("abc"))]
397397
--PlaceholderRowExec

datafusion/sqllogictest/test_files/struct.slt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ query TT
8585
explain select struct(a, b, c) from values;
8686
----
8787
logical_plan
88-
Projection: named_struct(Utf8("c0"), values.a, Utf8("c1"), values.b, Utf8("c2"), values.c)
88+
Projection: struct(values.a, values.b, values.c)
8989
--TableScan: values projection=[a, b, c]
9090
physical_plan
91-
ProjectionExec: expr=[named_struct(c0, a@0, c1, b@1, c2, c@2) as named_struct(Utf8("c0"),values.a,Utf8("c1"),values.b,Utf8("c2"),values.c)]
91+
ProjectionExec: expr=[struct(a@0, b@1, c@2) as struct(values.a,values.b,values.c)]
9292
--MemoryExec: partitions=1, partition_sizes=[1]
9393

9494
# error on 0 arguments
@@ -179,4 +179,4 @@ drop table values;
179179
query T
180180
select arrow_typeof(named_struct('first', 1, 'second', 2, 'third', 3));
181181
----
182-
Struct([Field { name: "first", data_type: Int64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "second", data_type: Int64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "third", data_type: Int64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }])
182+
Struct([Field { name: "first", data_type: Int64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "second", data_type: Int64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }, Field { name: "third", data_type: Int64, nullable: true, dict_id: 0, dict_is_ordered: false, metadata: {} }])

docs/source/user-guide/sql/scalar_functions.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3336,6 +3336,16 @@ select * from t;
33363336
| 3 | 4 |
33373337
+---+---+
33383338
3339+
-- use default names `c0`, `c1`
3340+
❯ select struct(a, b) from t;
3341+
+-----------------+
3342+
| struct(t.a,t.b) |
3343+
+-----------------+
3344+
| {c0: 1, c1: 2} |
3345+
| {c0: 3, c1: 4} |
3346+
+-----------------+
3347+
3348+
-- name the first field `field_a`
33393349
select struct(a as field_a, b) from t;
33403350
+--------------------------------------------------+
33413351
| named_struct(Utf8("field_a"),t.a,Utf8("c1"),t.b) |

0 commit comments

Comments
 (0)