@@ -64,6 +64,15 @@ use sqlparser::ast::{
64
64
///
65
65
/// [`ExprFunctionExt`]: crate::expr_fn::ExprFunctionExt
66
66
///
67
+ /// # Printing Expressions
68
+ ///
69
+ /// You can print `Expr`s using the the `Debug` trait, `Display` trait, or
70
+ /// [`Self::human_display`]. See the [examples](#examples-displaying-exprs) below.
71
+ ///
72
+ /// If you need SQL to pass to other systems, consider using [`Unparser`].
73
+ ///
74
+ /// [`Unparser`]: https://docs.rs/datafusion/latest/datafusion/sql/unparser/struct.Unparser.html
75
+ ///
67
76
/// # Schema Access
68
77
///
69
78
/// See [`ExprSchemable::get_type`] to access the [`DataType`] and nullability
@@ -76,9 +85,9 @@ use sqlparser::ast::{
76
85
/// `Expr` and [`TreeNode::transform`] can be used to rewrite an expression. See
77
86
/// the examples below and [`TreeNode`] for more information.
78
87
///
79
- /// # Examples
88
+ /// # Examples: Creating and Using `Expr`s
80
89
///
81
- /// ## Column references and literals
90
+ /// ## Column References and Literals
82
91
///
83
92
/// [`Expr::Column`] refer to the values of columns and are often created with
84
93
/// the [`col`] function. For example to create an expression `c1` referring to
@@ -104,6 +113,7 @@ use sqlparser::ast::{
104
113
/// // All literals are strongly typed in DataFusion. To make an `i64` 42:
105
114
/// let expr = lit(42i64);
106
115
/// assert_eq!(expr, Expr::Literal(ScalarValue::Int64(Some(42))));
116
+ /// assert_eq!(expr, Expr::Literal(ScalarValue::Int64(Some(42))));
107
117
/// // To make a (typed) NULL:
108
118
/// let expr = Expr::Literal(ScalarValue::Int64(None));
109
119
/// // to make an (untyped) NULL (the optimizer will coerce this to the correct type):
@@ -171,7 +181,51 @@ use sqlparser::ast::{
171
181
/// ]);
172
182
/// ```
173
183
///
174
- /// # Visiting and Rewriting `Expr`s
184
+ /// # Examples: Displaying `Exprs`
185
+ ///
186
+ /// There are three ways to print an `Expr` depending on the usecase.
187
+ ///
188
+ /// ## Use `Debug` trait
189
+ ///
190
+ /// Following Rust conventions, the `Debug` implementation prints out the
191
+ /// internal structure of the expression, which is useful for debugging.
192
+ ///
193
+ /// ```
194
+ /// # use datafusion_expr::{lit, col};
195
+ /// let expr = col("c1") + lit(42);
196
+ /// assert_eq!(format!("{expr:?}"), "BinaryExpr(BinaryExpr { left: Column(Column { relation: None, name: \"c1\" }), op: Plus, right: Literal(Int32(42)) })");
197
+ /// ```
198
+ ///
199
+ /// ## Use the `Display` trait (detailed expression)
200
+ ///
201
+ /// The `Display` implementation prints out the expression in a SQL-like form,
202
+ /// but has additional details such as the data type of literals. This is useful
203
+ /// for understanding the expression in more detail and is used for the low level
204
+ /// [`ExplainFormat::Indent`] explain plan format.
205
+ ///
206
+ /// [`ExplainFormat::Indent`]: crate::logical_plan::ExplainFormat::Indent
207
+ ///
208
+ /// ```
209
+ /// # use datafusion_expr::{lit, col};
210
+ /// let expr = col("c1") + lit(42);
211
+ /// assert_eq!(format!("{expr}"), "c1 + Int32(42)");
212
+ /// ```
213
+ ///
214
+ /// ## Use [`Self::human_display`] (human readable)
215
+ ///
216
+ /// [`Self::human_display`] prints out the expression in a SQL-like form, optimized
217
+ /// for human consumption by end users. It is used for the
218
+ /// [`ExplainFormat::Tree`] explain plan format.
219
+ ///
220
+ /// [`ExplainFormat::Tree`]: crate::logical_plan::ExplainFormat::Tree
221
+ ///
222
+ ///```
223
+ /// # use datafusion_expr::{lit, col};
224
+ /// let expr = col("c1") + lit(42);
225
+ /// assert_eq!(format!("{}", expr.human_display()), "c1 + 42");
226
+ /// ```
227
+ ///
228
+ /// # Examples: Visiting and Rewriting `Expr`s
175
229
///
176
230
/// Here is an example that finds all literals in an `Expr` tree:
177
231
/// ```
@@ -1147,6 +1201,31 @@ impl Expr {
1147
1201
SchemaDisplay ( self )
1148
1202
}
1149
1203
1204
+ /// Human readable display formatting for this expression.
1205
+ ///
1206
+ /// This function is primarily used in printing the explain tree output,
1207
+ /// (e.g. `EXPLAIN FORMAT TREE <query>`), providing a readable format to
1208
+ /// show how expressions are used in physical and logical plans. See the
1209
+ /// [`Expr`] for other ways to format expressions
1210
+ ///
1211
+ /// Note this format is intended for human consumption rather than SQL for
1212
+ /// other systems. If you need SQL to pass to other systems, consider using
1213
+ /// [`Unparser`].
1214
+ ///
1215
+ /// [`Unparser`]: https://docs.rs/datafusion/latest/datafusion/sql/unparser/struct.Unparser.html
1216
+ ///
1217
+ /// # Example
1218
+ /// ```
1219
+ /// # use datafusion_expr::{col, lit};
1220
+ /// let expr = col("foo") + lit(42);
1221
+ /// // For EXPLAIN output:
1222
+ /// // "foo + 42"
1223
+ /// println!("{}", expr.human_display());
1224
+ /// ```
1225
+ pub fn human_display ( & self ) -> impl Display + ' _ {
1226
+ SqlDisplay ( self )
1227
+ }
1228
+
1150
1229
/// Returns the qualifier and the schema name of this expression.
1151
1230
///
1152
1231
/// Used when the expression forms the output field of a certain plan.
@@ -2596,6 +2675,187 @@ impl Display for SchemaDisplay<'_> {
2596
2675
}
2597
2676
}
2598
2677
2678
+ /// A helper struct for displaying an `Expr` as an SQL-like string.
2679
+ struct SqlDisplay < ' a > ( & ' a Expr ) ;
2680
+
2681
+ impl Display for SqlDisplay < ' _ > {
2682
+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
2683
+ match self . 0 {
2684
+ Expr :: Literal ( scalar) => scalar. fmt ( f) ,
2685
+ Expr :: Alias ( Alias { name, .. } ) => write ! ( f, "{name}" ) ,
2686
+ Expr :: Between ( Between {
2687
+ expr,
2688
+ negated,
2689
+ low,
2690
+ high,
2691
+ } ) => {
2692
+ if * negated {
2693
+ write ! (
2694
+ f,
2695
+ "{} NOT BETWEEN {} AND {}" ,
2696
+ SqlDisplay ( expr) ,
2697
+ SqlDisplay ( low) ,
2698
+ SqlDisplay ( high) ,
2699
+ )
2700
+ } else {
2701
+ write ! (
2702
+ f,
2703
+ "{} BETWEEN {} AND {}" ,
2704
+ SqlDisplay ( expr) ,
2705
+ SqlDisplay ( low) ,
2706
+ SqlDisplay ( high) ,
2707
+ )
2708
+ }
2709
+ }
2710
+ Expr :: BinaryExpr ( BinaryExpr { left, op, right } ) => {
2711
+ write ! ( f, "{} {op} {}" , SqlDisplay ( left) , SqlDisplay ( right) , )
2712
+ }
2713
+ Expr :: Case ( Case {
2714
+ expr,
2715
+ when_then_expr,
2716
+ else_expr,
2717
+ } ) => {
2718
+ write ! ( f, "CASE " ) ?;
2719
+
2720
+ if let Some ( e) = expr {
2721
+ write ! ( f, "{} " , SqlDisplay ( e) ) ?;
2722
+ }
2723
+
2724
+ for ( when, then) in when_then_expr {
2725
+ write ! ( f, "WHEN {} THEN {} " , SqlDisplay ( when) , SqlDisplay ( then) , ) ?;
2726
+ }
2727
+
2728
+ if let Some ( e) = else_expr {
2729
+ write ! ( f, "ELSE {} " , SqlDisplay ( e) ) ?;
2730
+ }
2731
+
2732
+ write ! ( f, "END" )
2733
+ }
2734
+ Expr :: Cast ( Cast { expr, .. } ) | Expr :: TryCast ( TryCast { expr, .. } ) => {
2735
+ write ! ( f, "{}" , SqlDisplay ( expr) )
2736
+ }
2737
+ Expr :: InList ( InList {
2738
+ expr,
2739
+ list,
2740
+ negated,
2741
+ } ) => {
2742
+ write ! (
2743
+ f,
2744
+ "{}{} IN {}" ,
2745
+ SqlDisplay ( expr) ,
2746
+ if * negated { " NOT" } else { "" } ,
2747
+ ExprListDisplay :: comma_separated( list. as_slice( ) )
2748
+ )
2749
+ }
2750
+ Expr :: GroupingSet ( GroupingSet :: Cube ( exprs) ) => {
2751
+ write ! (
2752
+ f,
2753
+ "ROLLUP ({})" ,
2754
+ ExprListDisplay :: comma_separated( exprs. as_slice( ) )
2755
+ )
2756
+ }
2757
+ Expr :: GroupingSet ( GroupingSet :: GroupingSets ( lists_of_exprs) ) => {
2758
+ write ! ( f, "GROUPING SETS (" ) ?;
2759
+ for exprs in lists_of_exprs. iter ( ) {
2760
+ write ! (
2761
+ f,
2762
+ "({})" ,
2763
+ ExprListDisplay :: comma_separated( exprs. as_slice( ) )
2764
+ ) ?;
2765
+ }
2766
+ write ! ( f, ")" )
2767
+ }
2768
+ Expr :: GroupingSet ( GroupingSet :: Rollup ( exprs) ) => {
2769
+ write ! (
2770
+ f,
2771
+ "ROLLUP ({})" ,
2772
+ ExprListDisplay :: comma_separated( exprs. as_slice( ) )
2773
+ )
2774
+ }
2775
+ Expr :: IsNull ( expr) => write ! ( f, "{} IS NULL" , SqlDisplay ( expr) ) ,
2776
+ Expr :: IsNotNull ( expr) => {
2777
+ write ! ( f, "{} IS NOT NULL" , SqlDisplay ( expr) )
2778
+ }
2779
+ Expr :: IsUnknown ( expr) => {
2780
+ write ! ( f, "{} IS UNKNOWN" , SqlDisplay ( expr) )
2781
+ }
2782
+ Expr :: IsNotUnknown ( expr) => {
2783
+ write ! ( f, "{} IS NOT UNKNOWN" , SqlDisplay ( expr) )
2784
+ }
2785
+ Expr :: IsTrue ( expr) => write ! ( f, "{} IS TRUE" , SqlDisplay ( expr) ) ,
2786
+ Expr :: IsFalse ( expr) => write ! ( f, "{} IS FALSE" , SqlDisplay ( expr) ) ,
2787
+ Expr :: IsNotTrue ( expr) => {
2788
+ write ! ( f, "{} IS NOT TRUE" , SqlDisplay ( expr) )
2789
+ }
2790
+ Expr :: IsNotFalse ( expr) => {
2791
+ write ! ( f, "{} IS NOT FALSE" , SqlDisplay ( expr) )
2792
+ }
2793
+ Expr :: Like ( Like {
2794
+ negated,
2795
+ expr,
2796
+ pattern,
2797
+ escape_char,
2798
+ case_insensitive,
2799
+ } ) => {
2800
+ write ! (
2801
+ f,
2802
+ "{} {}{} {}" ,
2803
+ SqlDisplay ( expr) ,
2804
+ if * negated { "NOT " } else { "" } ,
2805
+ if * case_insensitive { "ILIKE" } else { "LIKE" } ,
2806
+ SqlDisplay ( pattern) ,
2807
+ ) ?;
2808
+
2809
+ if let Some ( char) = escape_char {
2810
+ write ! ( f, " CHAR '{char}'" ) ?;
2811
+ }
2812
+
2813
+ Ok ( ( ) )
2814
+ }
2815
+ Expr :: Negative ( expr) => write ! ( f, "(- {})" , SqlDisplay ( expr) ) ,
2816
+ Expr :: Not ( expr) => write ! ( f, "NOT {}" , SqlDisplay ( expr) ) ,
2817
+ Expr :: Unnest ( Unnest { expr } ) => {
2818
+ write ! ( f, "UNNEST({})" , SqlDisplay ( expr) )
2819
+ }
2820
+ Expr :: SimilarTo ( Like {
2821
+ negated,
2822
+ expr,
2823
+ pattern,
2824
+ escape_char,
2825
+ ..
2826
+ } ) => {
2827
+ write ! (
2828
+ f,
2829
+ "{} {} {}" ,
2830
+ SqlDisplay ( expr) ,
2831
+ if * negated {
2832
+ "NOT SIMILAR TO"
2833
+ } else {
2834
+ "SIMILAR TO"
2835
+ } ,
2836
+ SqlDisplay ( pattern) ,
2837
+ ) ?;
2838
+ if let Some ( char) = escape_char {
2839
+ write ! ( f, " CHAR '{char}'" ) ?;
2840
+ }
2841
+
2842
+ Ok ( ( ) )
2843
+ }
2844
+ Expr :: AggregateFunction ( AggregateFunction { func, params } ) => {
2845
+ match func. human_display ( params) {
2846
+ Ok ( name) => {
2847
+ write ! ( f, "{name}" )
2848
+ }
2849
+ Err ( e) => {
2850
+ write ! ( f, "got error from schema_name {}" , e)
2851
+ }
2852
+ }
2853
+ }
2854
+ _ => write ! ( f, "{}" , self . 0 ) ,
2855
+ }
2856
+ }
2857
+ }
2858
+
2599
2859
/// Get schema_name for Vector of expressions
2600
2860
///
2601
2861
/// Internal usage. Please call `schema_name_from_exprs` instead
@@ -2607,6 +2867,38 @@ pub(crate) fn schema_name_from_exprs_comma_separated_without_space(
2607
2867
schema_name_from_exprs_inner ( exprs, "," )
2608
2868
}
2609
2869
2870
+ /// Formats a list of `&Expr` with a custom separator using SQL display format
2871
+ pub struct ExprListDisplay < ' a > {
2872
+ exprs : & ' a [ Expr ] ,
2873
+ sep : & ' a str ,
2874
+ }
2875
+
2876
+ impl < ' a > ExprListDisplay < ' a > {
2877
+ /// Create a new display struct with the given expressions and separator
2878
+ pub fn new ( exprs : & ' a [ Expr ] , sep : & ' a str ) -> Self {
2879
+ Self { exprs, sep }
2880
+ }
2881
+
2882
+ /// Create a new display struct with comma-space separator
2883
+ pub fn comma_separated ( exprs : & ' a [ Expr ] ) -> Self {
2884
+ Self :: new ( exprs, ", " )
2885
+ }
2886
+ }
2887
+
2888
+ impl Display for ExprListDisplay < ' _ > {
2889
+ fn fmt ( & self , f : & mut Formatter ) -> fmt:: Result {
2890
+ let mut first = true ;
2891
+ for expr in self . exprs {
2892
+ if !first {
2893
+ write ! ( f, "{}" , self . sep) ?;
2894
+ }
2895
+ write ! ( f, "{}" , SqlDisplay ( expr) ) ?;
2896
+ first = false ;
2897
+ }
2898
+ Ok ( ( ) )
2899
+ }
2900
+ }
2901
+
2610
2902
/// Get schema_name for Vector of expressions
2611
2903
pub fn schema_name_from_exprs ( exprs : & [ Expr ] ) -> Result < String , fmt:: Error > {
2612
2904
schema_name_from_exprs_inner ( exprs, ", " )
0 commit comments