Skip to content

Commit e7d9504

Browse files
Unparse struct to sql (#13493)
* unparse struct to sql * add roundtrip statement test for named_struct * quote keys if needed * add roundtrip statement test for get_field * improve error messages * fmt * fmt * match string literals only Co-authored-by: Jax Liu <liugs963@gmail.com> --------- Co-authored-by: Jax Liu <liugs963@gmail.com>
1 parent 9fb7aee commit e7d9504

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

datafusion/sql/src/unparser/expr.rs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,9 @@ impl Unparser<'_> {
462462
match func_name {
463463
"make_array" => self.make_array_to_sql(args),
464464
"array_element" => self.array_element_to_sql(args),
465-
// TODO: support for the construct and access functions of the `map` and `struct` types
465+
"named_struct" => self.named_struct_to_sql(args),
466+
"get_field" => self.get_field_to_sql(args),
467+
// TODO: support for the construct and access functions of the `map` type
466468
_ => self.scalar_function_to_sql_internal(func_name, args),
467469
}
468470
}
@@ -514,6 +516,57 @@ impl Unparser<'_> {
514516
})
515517
}
516518

519+
fn named_struct_to_sql(&self, args: &[Expr]) -> Result<ast::Expr> {
520+
if args.len() % 2 != 0 {
521+
return internal_err!("named_struct must have an even number of arguments");
522+
}
523+
524+
let args = args
525+
.chunks_exact(2)
526+
.map(|chunk| {
527+
let key = match &chunk[0] {
528+
Expr::Literal(ScalarValue::Utf8(Some(s))) => self.new_ident_quoted_if_needs(s.to_string()),
529+
_ => return internal_err!("named_struct expects even arguments to be strings, but received: {:?}", &chunk[0])
530+
};
531+
532+
Ok(ast::DictionaryField {
533+
key,
534+
value: Box::new(self.expr_to_sql(&chunk[1])?),
535+
})
536+
})
537+
.collect::<Result<Vec<_>>>()?;
538+
539+
Ok(ast::Expr::Dictionary(args))
540+
}
541+
542+
fn get_field_to_sql(&self, args: &[Expr]) -> Result<ast::Expr> {
543+
if args.len() != 2 {
544+
return internal_err!("get_field must have exactly 2 arguments");
545+
}
546+
547+
let mut id = match &args[0] {
548+
Expr::Column(col) => match self.col_to_sql(col)? {
549+
ast::Expr::Identifier(ident) => vec![ident],
550+
ast::Expr::CompoundIdentifier(idents) => idents,
551+
other => return internal_err!("expected col_to_sql to return an Identifier or CompoundIdentifier, but received: {:?}", other),
552+
},
553+
_ => return internal_err!("get_field expects first argument to be column, but received: {:?}", &args[0]),
554+
};
555+
556+
let field = match &args[1] {
557+
Expr::Literal(lit) => self.new_ident_quoted_if_needs(lit.to_string()),
558+
_ => {
559+
return internal_err!(
560+
"get_field expects second argument to be a string, but received: {:?}",
561+
&args[0]
562+
)
563+
}
564+
};
565+
id.push(field);
566+
567+
Ok(ast::Expr::CompoundIdentifier(id))
568+
}
569+
517570
pub fn sort_to_sql(&self, sort: &Sort) -> Result<ast::OrderByExpr> {
518571
let Sort {
519572
expr,
@@ -1524,6 +1577,7 @@ mod tests {
15241577
Signature, Volatility, WindowFrame, WindowFunctionDefinition,
15251578
};
15261579
use datafusion_expr::{interval_month_day_nano_lit, ExprFunctionExt};
1580+
use datafusion_functions::expr_fn::{get_field, named_struct};
15271581
use datafusion_functions_aggregate::count::count_udaf;
15281582
use datafusion_functions_aggregate::expr_fn::sum;
15291583
use datafusion_functions_nested::expr_fn::{array_element, make_array};
@@ -1937,6 +1991,11 @@ mod tests {
19371991
array_element(make_array(vec![lit(1), lit(2), lit(3)]), lit(1)),
19381992
"[1, 2, 3][1]",
19391993
),
1994+
(
1995+
named_struct(vec![lit("a"), lit("1"), lit("b"), lit(2)]),
1996+
"{a: '1', b: 2}",
1997+
),
1998+
(get_field(col("a.b"), "c"), "a.b.c"),
19401999
];
19412000

19422001
for (expr, expected) in tests {

datafusion/sql/tests/cases/plan_to_sql.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ fn roundtrip_statement() -> Result<()> {
188188
"SELECT ARRAY[1, 2, 3][1]",
189189
"SELECT [1, 2, 3]",
190190
"SELECT [1, 2, 3][1]",
191-
"SELECT left[1] FROM array"
191+
"SELECT left[1] FROM array",
192+
"SELECT {a:1, b:2}",
193+
"SELECT s.a FROM (SELECT {a:1, b:2} AS s)"
192194
];
193195

194196
// For each test sql string, we transform as follows:

0 commit comments

Comments
 (0)