datafusion_sql/unparser/
mod.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! [`Unparser`] for converting `Expr` to SQL text
19
20pub mod ast;
21mod expr;
22mod plan;
23mod rewrite;
24mod utils;
25
26use self::dialect::{DefaultDialect, Dialect};
27use crate::unparser::extension_unparser::UserDefinedLogicalNodeUnparser;
28pub use expr::expr_to_sql;
29pub use plan::plan_to_sql;
30use std::sync::Arc;
31pub mod dialect;
32pub mod extension_unparser;
33
34/// Convert a DataFusion [`Expr`] to [`sqlparser::ast::Expr`]
35///
36/// See [`expr_to_sql`] for background. `Unparser` allows greater control of
37/// the conversion, but with a more complicated API.
38///
39/// To get more human-readable output, see [`Self::with_pretty`]
40///
41/// # Example
42/// ```
43/// use datafusion_expr::{col, lit};
44/// use datafusion_sql::unparser::Unparser;
45/// let expr = col("a").gt(lit(4)); // form an expression `a > 4`
46/// let unparser = Unparser::default();
47/// let sql = unparser.expr_to_sql(&expr).unwrap();// convert to AST
48/// // use the Display impl to convert to SQL text
49/// assert_eq!(sql.to_string(), "(a > 4)");
50/// // now convert to pretty sql
51/// let unparser = unparser.with_pretty(true);
52/// let sql = unparser.expr_to_sql(&expr).unwrap();
53/// assert_eq!(sql.to_string(), "a > 4"); // note lack of parenthesis
54/// ```
55///
56/// [`Expr`]: datafusion_expr::Expr
57pub struct Unparser<'a> {
58    dialect: &'a dyn Dialect,
59    pretty: bool,
60    extension_unparsers: Vec<Arc<dyn UserDefinedLogicalNodeUnparser>>,
61}
62
63impl<'a> Unparser<'a> {
64    pub fn new(dialect: &'a dyn Dialect) -> Self {
65        Self {
66            dialect,
67            pretty: false,
68            extension_unparsers: vec![],
69        }
70    }
71
72    /// Create pretty SQL output, better suited for human consumption
73    ///
74    /// See example on the struct level documentation
75    ///
76    /// # Pretty Output
77    ///
78    /// By default, `Unparser` generates SQL text that will parse back to the
79    /// same parsed [`Expr`], which is useful for creating machine readable
80    /// expressions to send to other systems. However, the resulting expressions are
81    /// not always nice to read for humans.
82    ///
83    /// For example
84    ///
85    /// ```sql
86    /// ((a + 4) > 5)
87    /// ```
88    ///
89    /// This method removes parenthesis using to the precedence rules of
90    /// DataFusion. If the output is reparsed, the resulting [`Expr`] produces
91    /// same value as the original in DataFusion, but with a potentially
92    /// different order of operations.
93    ///
94    /// Note that this setting may create invalid SQL for other SQL query
95    /// engines with different precedence rules
96    ///
97    /// # Example
98    /// ```
99    /// use datafusion_expr::{col, lit};
100    /// use datafusion_sql::unparser::Unparser;
101    /// let expr = col("a").gt(lit(4)).and(col("b").lt(lit(5))); // form an expression `a > 4 AND b < 5`
102    /// let unparser = Unparser::default().with_pretty(true);
103    /// let sql = unparser.expr_to_sql(&expr).unwrap();
104    /// assert_eq!(sql.to_string(), "a > 4 AND b < 5"); // note lack of parenthesis
105    /// ```
106    ///
107    /// [`Expr`]: datafusion_expr::Expr
108    pub fn with_pretty(mut self, pretty: bool) -> Self {
109        self.pretty = pretty;
110        self
111    }
112
113    /// Add a custom unparser for user defined logical nodes
114    ///
115    /// DataFusion allows user to define custom logical nodes. This method allows to add custom child unparsers for these nodes.
116    /// Implementation of [`UserDefinedLogicalNodeUnparser`] can be added to the root unparser to handle custom logical nodes.
117    ///
118    /// The child unparsers are called iteratively.
119    /// There are two methods in [`Unparser`] will be called:
120    /// - `extension_to_statement`: This method is called when the custom logical node is a custom statement.
121    ///   If multiple child unparsers return a non-None value, the last unparsing result will be returned.
122    /// - `extension_to_sql`: This method is called when the custom logical node is part of a statement.
123    ///   If multiple child unparsers are registered for the same custom logical node, all of them will be called in order.
124    pub fn with_extension_unparsers(
125        mut self,
126        extension_unparsers: Vec<Arc<dyn UserDefinedLogicalNodeUnparser>>,
127    ) -> Self {
128        self.extension_unparsers = extension_unparsers;
129        self
130    }
131}
132
133impl Default for Unparser<'_> {
134    fn default() -> Self {
135        Self {
136            dialect: &DefaultDialect {},
137            pretty: false,
138            extension_unparsers: vec![],
139        }
140    }
141}