This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new e7d9504fd9 Unparse struct to sql (#13493)
e7d9504fd9 is described below

commit e7d9504fd9d12cc8242b0b5d4b92c3fad4fb0e97
Author: delamarch3 <[email protected]>
AuthorDate: Thu Nov 21 14:02:33 2024 +0000

    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 <[email protected]>
    
    ---------
    
    Co-authored-by: Jax Liu <[email protected]>
---
 datafusion/sql/src/unparser/expr.rs       | 61 ++++++++++++++++++++++++++++++-
 datafusion/sql/tests/cases/plan_to_sql.rs |  4 +-
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/datafusion/sql/src/unparser/expr.rs 
b/datafusion/sql/src/unparser/expr.rs
index f1f28258f9..ae2607de00 100644
--- a/datafusion/sql/src/unparser/expr.rs
+++ b/datafusion/sql/src/unparser/expr.rs
@@ -462,7 +462,9 @@ impl Unparser<'_> {
         match func_name {
             "make_array" => self.make_array_to_sql(args),
             "array_element" => self.array_element_to_sql(args),
-            // TODO: support for the construct and access functions of the 
`map` and `struct` types
+            "named_struct" => self.named_struct_to_sql(args),
+            "get_field" => self.get_field_to_sql(args),
+            // TODO: support for the construct and access functions of the 
`map` type
             _ => self.scalar_function_to_sql_internal(func_name, args),
         }
     }
@@ -514,6 +516,57 @@ impl Unparser<'_> {
         })
     }
 
+    fn named_struct_to_sql(&self, args: &[Expr]) -> Result<ast::Expr> {
+        if args.len() % 2 != 0 {
+            return internal_err!("named_struct must have an even number of 
arguments");
+        }
+
+        let args = args
+            .chunks_exact(2)
+            .map(|chunk| {
+                let key = match &chunk[0] {
+                    Expr::Literal(ScalarValue::Utf8(Some(s))) => 
self.new_ident_quoted_if_needs(s.to_string()),
+                    _ => return internal_err!("named_struct expects even 
arguments to be strings, but received: {:?}", &chunk[0])
+                };
+
+                Ok(ast::DictionaryField {
+                    key,
+                    value: Box::new(self.expr_to_sql(&chunk[1])?),
+                })
+            })
+            .collect::<Result<Vec<_>>>()?;
+
+        Ok(ast::Expr::Dictionary(args))
+    }
+
+    fn get_field_to_sql(&self, args: &[Expr]) -> Result<ast::Expr> {
+        if args.len() != 2 {
+            return internal_err!("get_field must have exactly 2 arguments");
+        }
+
+        let mut id = match &args[0] {
+            Expr::Column(col) => match self.col_to_sql(col)? {
+                ast::Expr::Identifier(ident) => vec![ident],
+                ast::Expr::CompoundIdentifier(idents) => idents,
+                other => return internal_err!("expected col_to_sql to return 
an Identifier or CompoundIdentifier, but received: {:?}", other),
+            },
+            _ => return internal_err!("get_field expects first argument to be 
column, but received: {:?}", &args[0]),
+        };
+
+        let field = match &args[1] {
+            Expr::Literal(lit) => 
self.new_ident_quoted_if_needs(lit.to_string()),
+            _ => {
+                return internal_err!(
+                "get_field expects second argument to be a string, but 
received: {:?}",
+                &args[0]
+            )
+            }
+        };
+        id.push(field);
+
+        Ok(ast::Expr::CompoundIdentifier(id))
+    }
+
     pub fn sort_to_sql(&self, sort: &Sort) -> Result<ast::OrderByExpr> {
         let Sort {
             expr,
@@ -1524,6 +1577,7 @@ mod tests {
         Signature, Volatility, WindowFrame, WindowFunctionDefinition,
     };
     use datafusion_expr::{interval_month_day_nano_lit, ExprFunctionExt};
+    use datafusion_functions::expr_fn::{get_field, named_struct};
     use datafusion_functions_aggregate::count::count_udaf;
     use datafusion_functions_aggregate::expr_fn::sum;
     use datafusion_functions_nested::expr_fn::{array_element, make_array};
@@ -1937,6 +1991,11 @@ mod tests {
                 array_element(make_array(vec![lit(1), lit(2), lit(3)]), 
lit(1)),
                 "[1, 2, 3][1]",
             ),
+            (
+                named_struct(vec![lit("a"), lit("1"), lit("b"), lit(2)]),
+                "{a: '1', b: 2}",
+            ),
+            (get_field(col("a.b"), "c"), "a.b.c"),
         ];
 
         for (expr, expected) in tests {
diff --git a/datafusion/sql/tests/cases/plan_to_sql.rs 
b/datafusion/sql/tests/cases/plan_to_sql.rs
index f9d97cdc74..58d99549de 100644
--- a/datafusion/sql/tests/cases/plan_to_sql.rs
+++ b/datafusion/sql/tests/cases/plan_to_sql.rs
@@ -188,7 +188,9 @@ fn roundtrip_statement() -> Result<()> {
             "SELECT ARRAY[1, 2, 3][1]",
             "SELECT [1, 2, 3]",
             "SELECT [1, 2, 3][1]",
-            "SELECT left[1] FROM array"
+            "SELECT left[1] FROM array",
+            "SELECT {a:1, b:2}",
+            "SELECT s.a FROM (SELECT {a:1, b:2} AS s)"
     ];
 
     // For each test sql string, we transform as follows:


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to