datafusion_sql/unparser/
dialect.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
18use std::{collections::HashMap, sync::Arc};
19
20use super::{
21    utils::character_length_to_sql, utils::date_part_to_sql,
22    utils::sqlite_date_trunc_to_sql, utils::sqlite_from_unixtime_to_sql, Unparser,
23};
24use arrow::datatypes::TimeUnit;
25use datafusion_common::Result;
26use datafusion_expr::Expr;
27use regex::Regex;
28use sqlparser::tokenizer::Span;
29use sqlparser::{
30    ast::{
31        self, BinaryOperator, Function, Ident, ObjectName, TimezoneInfo, WindowFrameBound,
32    },
33    keywords::ALL_KEYWORDS,
34};
35
36pub type ScalarFnToSqlHandler =
37    Box<dyn Fn(&Unparser, &[Expr]) -> Result<Option<ast::Expr>> + Send + Sync>;
38
39/// `Dialect` to use for Unparsing
40///
41/// The default dialect tries to avoid quoting identifiers unless necessary (e.g. `a` instead of `"a"`)
42/// but this behavior can be overridden as needed
43///
44/// **Note**: This trait will eventually be replaced by the Dialect in the SQLparser package
45///
46/// See <https://github.com/sqlparser-rs/sqlparser-rs/pull/1170>
47/// See also the discussion in <https://github.com/apache/datafusion/pull/10625>
48pub trait Dialect: Send + Sync {
49    /// Return the character used to quote identifiers.
50    fn identifier_quote_style(&self, _identifier: &str) -> Option<char>;
51
52    /// Does the dialect support specifying `NULLS FIRST/LAST` in `ORDER BY` clauses?
53    fn supports_nulls_first_in_sort(&self) -> bool {
54        true
55    }
56
57    /// Does the dialect use TIMESTAMP to represent Date64 rather than DATETIME?
58    /// E.g. Trino, Athena and Dremio does not have DATETIME data type
59    fn use_timestamp_for_date64(&self) -> bool {
60        false
61    }
62
63    fn interval_style(&self) -> IntervalStyle {
64        IntervalStyle::PostgresVerbose
65    }
66
67    /// Does the dialect use DOUBLE PRECISION to represent Float64 rather than DOUBLE?
68    /// E.g. Postgres uses DOUBLE PRECISION instead of DOUBLE
69    fn float64_ast_dtype(&self) -> ast::DataType {
70        ast::DataType::Double(ast::ExactNumberInfo::None)
71    }
72
73    /// The SQL type to use for Arrow Utf8 unparsing
74    /// Most dialects use VARCHAR, but some, like MySQL, require CHAR
75    fn utf8_cast_dtype(&self) -> ast::DataType {
76        ast::DataType::Varchar(None)
77    }
78
79    /// The SQL type to use for Arrow LargeUtf8 unparsing
80    /// Most dialects use TEXT, but some, like MySQL, require CHAR
81    fn large_utf8_cast_dtype(&self) -> ast::DataType {
82        ast::DataType::Text
83    }
84
85    /// The date field extract style to use: `DateFieldExtractStyle`
86    fn date_field_extract_style(&self) -> DateFieldExtractStyle {
87        DateFieldExtractStyle::DatePart
88    }
89
90    /// The character length extraction style to use: `CharacterLengthStyle`
91    fn character_length_style(&self) -> CharacterLengthStyle {
92        CharacterLengthStyle::CharacterLength
93    }
94
95    /// The SQL type to use for Arrow Int64 unparsing
96    /// Most dialects use BigInt, but some, like MySQL, require SIGNED
97    fn int64_cast_dtype(&self) -> ast::DataType {
98        ast::DataType::BigInt(None)
99    }
100
101    /// The SQL type to use for Arrow Int32 unparsing
102    /// Most dialects use Integer, but some, like MySQL, require SIGNED
103    fn int32_cast_dtype(&self) -> ast::DataType {
104        ast::DataType::Integer(None)
105    }
106
107    /// The SQL type to use for Timestamp unparsing
108    /// Most dialects use Timestamp, but some, like MySQL, require Datetime
109    /// Some dialects like Dremio does not support WithTimeZone and requires always Timestamp
110    fn timestamp_cast_dtype(
111        &self,
112        _time_unit: &TimeUnit,
113        tz: &Option<Arc<str>>,
114    ) -> ast::DataType {
115        let tz_info = match tz {
116            Some(_) => TimezoneInfo::WithTimeZone,
117            None => TimezoneInfo::None,
118        };
119
120        ast::DataType::Timestamp(None, tz_info)
121    }
122
123    /// The SQL type to use for Arrow Date32 unparsing
124    /// Most dialects use Date, but some, like SQLite require TEXT
125    fn date32_cast_dtype(&self) -> ast::DataType {
126        ast::DataType::Date
127    }
128
129    /// Does the dialect support specifying column aliases as part of alias table definition?
130    /// (SELECT col1, col2 from my_table) AS my_table_alias(col1_alias, col2_alias)
131    fn supports_column_alias_in_table_alias(&self) -> bool {
132        true
133    }
134
135    /// Whether the dialect requires a table alias for any subquery in the FROM clause
136    /// This affects behavior when deriving logical plans for Sort, Limit, etc.
137    fn requires_derived_table_alias(&self) -> bool {
138        false
139    }
140
141    /// The division operator for the dialect
142    /// Most dialect uses ` BinaryOperator::Divide` (/)
143    /// But DuckDB dialect uses `BinaryOperator::DuckIntegerDivide` (//)
144    fn division_operator(&self) -> BinaryOperator {
145        BinaryOperator::Divide
146    }
147
148    /// Allows the dialect to override scalar function unparsing if the dialect has specific rules.
149    /// Returns None if the default unparsing should be used, or Some(ast::Expr) if there is
150    /// a custom implementation for the function.
151    fn scalar_function_to_sql_overrides(
152        &self,
153        _unparser: &Unparser,
154        _func_name: &str,
155        _args: &[Expr],
156    ) -> Result<Option<ast::Expr>> {
157        Ok(None)
158    }
159
160    /// Allows the dialect to choose to omit window frame in unparsing
161    /// based on function name and window frame bound
162    /// Returns false if specific function name / window frame bound indicates no window frame is needed in unparsing
163    fn window_func_support_window_frame(
164        &self,
165        _func_name: &str,
166        _start_bound: &WindowFrameBound,
167        _end_bound: &WindowFrameBound,
168    ) -> bool {
169        true
170    }
171
172    /// Extends the dialect's default rules for unparsing scalar functions.
173    /// This is useful for supporting application-specific UDFs or custom engine extensions.
174    fn with_custom_scalar_overrides(
175        self,
176        _handlers: Vec<(&str, ScalarFnToSqlHandler)>,
177    ) -> Self
178    where
179        Self: Sized,
180    {
181        unimplemented!("Custom scalar overrides are not supported by this dialect yet");
182    }
183
184    /// Allow to unparse a qualified column with a full qualified name
185    /// (e.g. catalog_name.schema_name.table_name.column_name)
186    /// Otherwise, the column will be unparsed with only the table name and column name
187    /// (e.g. table_name.column_name)
188    fn full_qualified_col(&self) -> bool {
189        false
190    }
191
192    /// Allow to unparse the unnest plan as [ast::TableFactor::UNNEST].
193    ///
194    /// Some dialects like BigQuery require UNNEST to be used in the FROM clause but
195    /// the LogicalPlan planner always puts UNNEST in the SELECT clause. This flag allows
196    /// to unparse the UNNEST plan as [ast::TableFactor::UNNEST] instead of a subquery.
197    fn unnest_as_table_factor(&self) -> bool {
198        false
199    }
200}
201
202/// `IntervalStyle` to use for unparsing
203///
204/// <https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-INTERVAL-INPUT>
205/// different DBMS follows different standards, popular ones are:
206/// postgres_verbose: '2 years 15 months 100 weeks 99 hours 123456789 milliseconds' which is
207/// compatible with arrow display format, as well as duckdb
208/// sql standard format is '1-2' for year-month, or '1 10:10:10.123456' for day-time
209/// <https://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt>
210#[derive(Clone, Copy)]
211pub enum IntervalStyle {
212    PostgresVerbose,
213    SQLStandard,
214    MySQL,
215}
216
217/// Datetime subfield extraction style for unparsing
218///
219/// `<https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT>`
220/// Different DBMSs follow different standards; popular ones are:
221/// date_part('YEAR', date '2001-02-16')
222/// EXTRACT(YEAR from date '2001-02-16')
223/// Some DBMSs, like Postgres, support both, whereas others like MySQL require EXTRACT.
224#[derive(Clone, Copy, PartialEq)]
225pub enum DateFieldExtractStyle {
226    DatePart,
227    Extract,
228    Strftime,
229}
230
231/// `CharacterLengthStyle` to use for unparsing
232///
233/// Different DBMSs uses different names for function calculating the number of characters in the string
234/// `Length` style uses length(x)
235/// `SQLStandard` style uses character_length(x)
236#[derive(Clone, Copy, PartialEq)]
237pub enum CharacterLengthStyle {
238    Length,
239    CharacterLength,
240}
241
242pub struct DefaultDialect {}
243
244impl Dialect for DefaultDialect {
245    fn identifier_quote_style(&self, identifier: &str) -> Option<char> {
246        let identifier_regex = Regex::new(r"^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap();
247        let id_upper = identifier.to_uppercase();
248        // Special case ignore "ID", see https://github.com/sqlparser-rs/sqlparser-rs/issues/1382
249        // ID is a keyword in ClickHouse, but we don't want to quote it when unparsing SQL here
250        if (id_upper != "ID" && ALL_KEYWORDS.contains(&id_upper.as_str()))
251            || !identifier_regex.is_match(identifier)
252        {
253            Some('"')
254        } else {
255            None
256        }
257    }
258}
259
260pub struct PostgreSqlDialect {}
261
262impl Dialect for PostgreSqlDialect {
263    fn identifier_quote_style(&self, _: &str) -> Option<char> {
264        Some('"')
265    }
266
267    fn interval_style(&self) -> IntervalStyle {
268        IntervalStyle::PostgresVerbose
269    }
270
271    fn float64_ast_dtype(&self) -> ast::DataType {
272        ast::DataType::DoublePrecision
273    }
274
275    fn scalar_function_to_sql_overrides(
276        &self,
277        unparser: &Unparser,
278        func_name: &str,
279        args: &[Expr],
280    ) -> Result<Option<ast::Expr>> {
281        if func_name == "round" {
282            return Ok(Some(
283                self.round_to_sql_enforce_numeric(unparser, func_name, args)?,
284            ));
285        }
286
287        Ok(None)
288    }
289}
290
291impl PostgreSqlDialect {
292    fn round_to_sql_enforce_numeric(
293        &self,
294        unparser: &Unparser,
295        func_name: &str,
296        args: &[Expr],
297    ) -> Result<ast::Expr> {
298        let mut args = unparser.function_args_to_sql(args)?;
299
300        // Enforce the first argument to be Numeric
301        if let Some(ast::FunctionArg::Unnamed(ast::FunctionArgExpr::Expr(expr))) =
302            args.first_mut()
303        {
304            if let ast::Expr::Cast { data_type, .. } = expr {
305                // Don't create an additional cast wrapper if we can update the existing one
306                *data_type = ast::DataType::Numeric(ast::ExactNumberInfo::None);
307            } else {
308                // Wrap the expression in a new cast
309                *expr = ast::Expr::Cast {
310                    kind: ast::CastKind::Cast,
311                    expr: Box::new(expr.clone()),
312                    data_type: ast::DataType::Numeric(ast::ExactNumberInfo::None),
313                    format: None,
314                };
315            }
316        }
317
318        Ok(ast::Expr::Function(Function {
319            name: ObjectName::from(vec![Ident {
320                value: func_name.to_string(),
321                quote_style: None,
322                span: Span::empty(),
323            }]),
324            args: ast::FunctionArguments::List(ast::FunctionArgumentList {
325                duplicate_treatment: None,
326                args,
327                clauses: vec![],
328            }),
329            filter: None,
330            null_treatment: None,
331            over: None,
332            within_group: vec![],
333            parameters: ast::FunctionArguments::None,
334            uses_odbc_syntax: false,
335        }))
336    }
337}
338
339#[derive(Default)]
340pub struct DuckDBDialect {
341    custom_scalar_fn_overrides: HashMap<String, ScalarFnToSqlHandler>,
342}
343
344impl DuckDBDialect {
345    #[must_use]
346    pub fn new() -> Self {
347        Self {
348            custom_scalar_fn_overrides: HashMap::new(),
349        }
350    }
351}
352
353impl Dialect for DuckDBDialect {
354    fn identifier_quote_style(&self, _: &str) -> Option<char> {
355        Some('"')
356    }
357
358    fn character_length_style(&self) -> CharacterLengthStyle {
359        CharacterLengthStyle::Length
360    }
361
362    fn division_operator(&self) -> BinaryOperator {
363        BinaryOperator::DuckIntegerDivide
364    }
365
366    fn with_custom_scalar_overrides(
367        mut self,
368        handlers: Vec<(&str, ScalarFnToSqlHandler)>,
369    ) -> Self {
370        for (func_name, handler) in handlers {
371            self.custom_scalar_fn_overrides
372                .insert(func_name.to_string(), handler);
373        }
374        self
375    }
376
377    fn scalar_function_to_sql_overrides(
378        &self,
379        unparser: &Unparser,
380        func_name: &str,
381        args: &[Expr],
382    ) -> Result<Option<ast::Expr>> {
383        if let Some(handler) = self.custom_scalar_fn_overrides.get(func_name) {
384            return handler(unparser, args);
385        }
386
387        if func_name == "character_length" {
388            return character_length_to_sql(
389                unparser,
390                self.character_length_style(),
391                args,
392            );
393        }
394
395        Ok(None)
396    }
397}
398
399pub struct MySqlDialect {}
400
401impl Dialect for MySqlDialect {
402    fn identifier_quote_style(&self, _: &str) -> Option<char> {
403        Some('`')
404    }
405
406    fn supports_nulls_first_in_sort(&self) -> bool {
407        false
408    }
409
410    fn interval_style(&self) -> IntervalStyle {
411        IntervalStyle::MySQL
412    }
413
414    fn utf8_cast_dtype(&self) -> ast::DataType {
415        ast::DataType::Char(None)
416    }
417
418    fn large_utf8_cast_dtype(&self) -> ast::DataType {
419        ast::DataType::Char(None)
420    }
421
422    fn date_field_extract_style(&self) -> DateFieldExtractStyle {
423        DateFieldExtractStyle::Extract
424    }
425
426    fn int64_cast_dtype(&self) -> ast::DataType {
427        ast::DataType::Custom(ObjectName::from(vec![Ident::new("SIGNED")]), vec![])
428    }
429
430    fn int32_cast_dtype(&self) -> ast::DataType {
431        ast::DataType::Custom(ObjectName::from(vec![Ident::new("SIGNED")]), vec![])
432    }
433
434    fn timestamp_cast_dtype(
435        &self,
436        _time_unit: &TimeUnit,
437        _tz: &Option<Arc<str>>,
438    ) -> ast::DataType {
439        ast::DataType::Datetime(None)
440    }
441
442    fn requires_derived_table_alias(&self) -> bool {
443        true
444    }
445
446    fn scalar_function_to_sql_overrides(
447        &self,
448        unparser: &Unparser,
449        func_name: &str,
450        args: &[Expr],
451    ) -> Result<Option<ast::Expr>> {
452        if func_name == "date_part" {
453            return date_part_to_sql(unparser, self.date_field_extract_style(), args);
454        }
455
456        Ok(None)
457    }
458}
459
460pub struct SqliteDialect {}
461
462impl Dialect for SqliteDialect {
463    fn identifier_quote_style(&self, _: &str) -> Option<char> {
464        Some('`')
465    }
466
467    fn date_field_extract_style(&self) -> DateFieldExtractStyle {
468        DateFieldExtractStyle::Strftime
469    }
470
471    fn date32_cast_dtype(&self) -> ast::DataType {
472        ast::DataType::Text
473    }
474
475    fn character_length_style(&self) -> CharacterLengthStyle {
476        CharacterLengthStyle::Length
477    }
478
479    fn supports_column_alias_in_table_alias(&self) -> bool {
480        false
481    }
482
483    fn scalar_function_to_sql_overrides(
484        &self,
485        unparser: &Unparser,
486        func_name: &str,
487        args: &[Expr],
488    ) -> Result<Option<ast::Expr>> {
489        match func_name {
490            "date_part" => {
491                date_part_to_sql(unparser, self.date_field_extract_style(), args)
492            }
493            "character_length" => {
494                character_length_to_sql(unparser, self.character_length_style(), args)
495            }
496            "from_unixtime" => sqlite_from_unixtime_to_sql(unparser, args),
497            "date_trunc" => sqlite_date_trunc_to_sql(unparser, args),
498            _ => Ok(None),
499        }
500    }
501}
502
503pub struct CustomDialect {
504    identifier_quote_style: Option<char>,
505    supports_nulls_first_in_sort: bool,
506    use_timestamp_for_date64: bool,
507    interval_style: IntervalStyle,
508    float64_ast_dtype: ast::DataType,
509    utf8_cast_dtype: ast::DataType,
510    large_utf8_cast_dtype: ast::DataType,
511    date_field_extract_style: DateFieldExtractStyle,
512    character_length_style: CharacterLengthStyle,
513    int64_cast_dtype: ast::DataType,
514    int32_cast_dtype: ast::DataType,
515    timestamp_cast_dtype: ast::DataType,
516    timestamp_tz_cast_dtype: ast::DataType,
517    date32_cast_dtype: ast::DataType,
518    supports_column_alias_in_table_alias: bool,
519    requires_derived_table_alias: bool,
520    division_operator: BinaryOperator,
521    window_func_support_window_frame: bool,
522    full_qualified_col: bool,
523    unnest_as_table_factor: bool,
524}
525
526impl Default for CustomDialect {
527    fn default() -> Self {
528        Self {
529            identifier_quote_style: None,
530            supports_nulls_first_in_sort: true,
531            use_timestamp_for_date64: false,
532            interval_style: IntervalStyle::SQLStandard,
533            float64_ast_dtype: ast::DataType::Double(ast::ExactNumberInfo::None),
534            utf8_cast_dtype: ast::DataType::Varchar(None),
535            large_utf8_cast_dtype: ast::DataType::Text,
536            date_field_extract_style: DateFieldExtractStyle::DatePart,
537            character_length_style: CharacterLengthStyle::CharacterLength,
538            int64_cast_dtype: ast::DataType::BigInt(None),
539            int32_cast_dtype: ast::DataType::Integer(None),
540            timestamp_cast_dtype: ast::DataType::Timestamp(None, TimezoneInfo::None),
541            timestamp_tz_cast_dtype: ast::DataType::Timestamp(
542                None,
543                TimezoneInfo::WithTimeZone,
544            ),
545            date32_cast_dtype: ast::DataType::Date,
546            supports_column_alias_in_table_alias: true,
547            requires_derived_table_alias: false,
548            division_operator: BinaryOperator::Divide,
549            window_func_support_window_frame: true,
550            full_qualified_col: false,
551            unnest_as_table_factor: false,
552        }
553    }
554}
555
556impl CustomDialect {
557    // Create a CustomDialect
558    #[deprecated(since = "41.0.0", note = "please use `CustomDialectBuilder` instead")]
559    pub fn new(identifier_quote_style: Option<char>) -> Self {
560        Self {
561            identifier_quote_style,
562            ..Default::default()
563        }
564    }
565}
566
567impl Dialect for CustomDialect {
568    fn identifier_quote_style(&self, _: &str) -> Option<char> {
569        self.identifier_quote_style
570    }
571
572    fn supports_nulls_first_in_sort(&self) -> bool {
573        self.supports_nulls_first_in_sort
574    }
575
576    fn use_timestamp_for_date64(&self) -> bool {
577        self.use_timestamp_for_date64
578    }
579
580    fn interval_style(&self) -> IntervalStyle {
581        self.interval_style
582    }
583
584    fn float64_ast_dtype(&self) -> ast::DataType {
585        self.float64_ast_dtype.clone()
586    }
587
588    fn utf8_cast_dtype(&self) -> ast::DataType {
589        self.utf8_cast_dtype.clone()
590    }
591
592    fn large_utf8_cast_dtype(&self) -> ast::DataType {
593        self.large_utf8_cast_dtype.clone()
594    }
595
596    fn date_field_extract_style(&self) -> DateFieldExtractStyle {
597        self.date_field_extract_style
598    }
599
600    fn character_length_style(&self) -> CharacterLengthStyle {
601        self.character_length_style
602    }
603
604    fn int64_cast_dtype(&self) -> ast::DataType {
605        self.int64_cast_dtype.clone()
606    }
607
608    fn int32_cast_dtype(&self) -> ast::DataType {
609        self.int32_cast_dtype.clone()
610    }
611
612    fn timestamp_cast_dtype(
613        &self,
614        _time_unit: &TimeUnit,
615        tz: &Option<Arc<str>>,
616    ) -> ast::DataType {
617        if tz.is_some() {
618            self.timestamp_tz_cast_dtype.clone()
619        } else {
620            self.timestamp_cast_dtype.clone()
621        }
622    }
623
624    fn date32_cast_dtype(&self) -> ast::DataType {
625        self.date32_cast_dtype.clone()
626    }
627
628    fn supports_column_alias_in_table_alias(&self) -> bool {
629        self.supports_column_alias_in_table_alias
630    }
631
632    fn scalar_function_to_sql_overrides(
633        &self,
634        unparser: &Unparser,
635        func_name: &str,
636        args: &[Expr],
637    ) -> Result<Option<ast::Expr>> {
638        match func_name {
639            "date_part" => {
640                date_part_to_sql(unparser, self.date_field_extract_style(), args)
641            }
642            "character_length" => {
643                character_length_to_sql(unparser, self.character_length_style(), args)
644            }
645            _ => Ok(None),
646        }
647    }
648
649    fn requires_derived_table_alias(&self) -> bool {
650        self.requires_derived_table_alias
651    }
652
653    fn division_operator(&self) -> BinaryOperator {
654        self.division_operator.clone()
655    }
656
657    fn window_func_support_window_frame(
658        &self,
659        _func_name: &str,
660        _start_bound: &WindowFrameBound,
661        _end_bound: &WindowFrameBound,
662    ) -> bool {
663        self.window_func_support_window_frame
664    }
665
666    fn full_qualified_col(&self) -> bool {
667        self.full_qualified_col
668    }
669
670    fn unnest_as_table_factor(&self) -> bool {
671        self.unnest_as_table_factor
672    }
673}
674
675/// `CustomDialectBuilder` to build `CustomDialect` using builder pattern
676///
677///
678/// # Examples
679///
680/// Building a custom dialect with all default options set in CustomDialectBuilder::new()
681/// but with `use_timestamp_for_date64` overridden to `true`
682///
683/// ```
684/// use datafusion_sql::unparser::dialect::CustomDialectBuilder;
685/// let dialect = CustomDialectBuilder::new()
686///     .with_use_timestamp_for_date64(true)
687///     .build();
688/// ```
689pub struct CustomDialectBuilder {
690    identifier_quote_style: Option<char>,
691    supports_nulls_first_in_sort: bool,
692    use_timestamp_for_date64: bool,
693    interval_style: IntervalStyle,
694    float64_ast_dtype: ast::DataType,
695    utf8_cast_dtype: ast::DataType,
696    large_utf8_cast_dtype: ast::DataType,
697    date_field_extract_style: DateFieldExtractStyle,
698    character_length_style: CharacterLengthStyle,
699    int64_cast_dtype: ast::DataType,
700    int32_cast_dtype: ast::DataType,
701    timestamp_cast_dtype: ast::DataType,
702    timestamp_tz_cast_dtype: ast::DataType,
703    date32_cast_dtype: ast::DataType,
704    supports_column_alias_in_table_alias: bool,
705    requires_derived_table_alias: bool,
706    division_operator: BinaryOperator,
707    window_func_support_window_frame: bool,
708    full_qualified_col: bool,
709    unnest_as_table_factor: bool,
710}
711
712impl Default for CustomDialectBuilder {
713    fn default() -> Self {
714        Self::new()
715    }
716}
717
718impl CustomDialectBuilder {
719    pub fn new() -> Self {
720        Self {
721            identifier_quote_style: None,
722            supports_nulls_first_in_sort: true,
723            use_timestamp_for_date64: false,
724            interval_style: IntervalStyle::PostgresVerbose,
725            float64_ast_dtype: ast::DataType::Double(ast::ExactNumberInfo::None),
726            utf8_cast_dtype: ast::DataType::Varchar(None),
727            large_utf8_cast_dtype: ast::DataType::Text,
728            date_field_extract_style: DateFieldExtractStyle::DatePart,
729            character_length_style: CharacterLengthStyle::CharacterLength,
730            int64_cast_dtype: ast::DataType::BigInt(None),
731            int32_cast_dtype: ast::DataType::Integer(None),
732            timestamp_cast_dtype: ast::DataType::Timestamp(None, TimezoneInfo::None),
733            timestamp_tz_cast_dtype: ast::DataType::Timestamp(
734                None,
735                TimezoneInfo::WithTimeZone,
736            ),
737            date32_cast_dtype: ast::DataType::Date,
738            supports_column_alias_in_table_alias: true,
739            requires_derived_table_alias: false,
740            division_operator: BinaryOperator::Divide,
741            window_func_support_window_frame: true,
742            full_qualified_col: false,
743            unnest_as_table_factor: false,
744        }
745    }
746
747    pub fn build(self) -> CustomDialect {
748        CustomDialect {
749            identifier_quote_style: self.identifier_quote_style,
750            supports_nulls_first_in_sort: self.supports_nulls_first_in_sort,
751            use_timestamp_for_date64: self.use_timestamp_for_date64,
752            interval_style: self.interval_style,
753            float64_ast_dtype: self.float64_ast_dtype,
754            utf8_cast_dtype: self.utf8_cast_dtype,
755            large_utf8_cast_dtype: self.large_utf8_cast_dtype,
756            date_field_extract_style: self.date_field_extract_style,
757            character_length_style: self.character_length_style,
758            int64_cast_dtype: self.int64_cast_dtype,
759            int32_cast_dtype: self.int32_cast_dtype,
760            timestamp_cast_dtype: self.timestamp_cast_dtype,
761            timestamp_tz_cast_dtype: self.timestamp_tz_cast_dtype,
762            date32_cast_dtype: self.date32_cast_dtype,
763            supports_column_alias_in_table_alias: self
764                .supports_column_alias_in_table_alias,
765            requires_derived_table_alias: self.requires_derived_table_alias,
766            division_operator: self.division_operator,
767            window_func_support_window_frame: self.window_func_support_window_frame,
768            full_qualified_col: self.full_qualified_col,
769            unnest_as_table_factor: self.unnest_as_table_factor,
770        }
771    }
772
773    /// Customize the dialect with a specific identifier quote style, e.g. '`', '"'
774    pub fn with_identifier_quote_style(mut self, identifier_quote_style: char) -> Self {
775        self.identifier_quote_style = Some(identifier_quote_style);
776        self
777    }
778
779    /// Customize the dialect to support `NULLS FIRST` in `ORDER BY` clauses
780    pub fn with_supports_nulls_first_in_sort(
781        mut self,
782        supports_nulls_first_in_sort: bool,
783    ) -> Self {
784        self.supports_nulls_first_in_sort = supports_nulls_first_in_sort;
785        self
786    }
787
788    /// Customize the dialect to uses TIMESTAMP when casting Date64 rather than DATETIME
789    pub fn with_use_timestamp_for_date64(
790        mut self,
791        use_timestamp_for_date64: bool,
792    ) -> Self {
793        self.use_timestamp_for_date64 = use_timestamp_for_date64;
794        self
795    }
796
797    /// Customize the dialect with a specific interval style listed in `IntervalStyle`
798    pub fn with_interval_style(mut self, interval_style: IntervalStyle) -> Self {
799        self.interval_style = interval_style;
800        self
801    }
802
803    /// Customize the dialect with a specific character_length_style listed in `CharacterLengthStyle`
804    pub fn with_character_length_style(
805        mut self,
806        character_length_style: CharacterLengthStyle,
807    ) -> Self {
808        self.character_length_style = character_length_style;
809        self
810    }
811
812    /// Customize the dialect with a specific SQL type for Float64 casting: DOUBLE, DOUBLE PRECISION, etc.
813    pub fn with_float64_ast_dtype(mut self, float64_ast_dtype: ast::DataType) -> Self {
814        self.float64_ast_dtype = float64_ast_dtype;
815        self
816    }
817
818    /// Customize the dialect with a specific SQL type for Utf8 casting: VARCHAR, CHAR, etc.
819    pub fn with_utf8_cast_dtype(mut self, utf8_cast_dtype: ast::DataType) -> Self {
820        self.utf8_cast_dtype = utf8_cast_dtype;
821        self
822    }
823
824    /// Customize the dialect with a specific SQL type for LargeUtf8 casting: TEXT, CHAR, etc.
825    pub fn with_large_utf8_cast_dtype(
826        mut self,
827        large_utf8_cast_dtype: ast::DataType,
828    ) -> Self {
829        self.large_utf8_cast_dtype = large_utf8_cast_dtype;
830        self
831    }
832
833    /// Customize the dialect with a specific date field extract style listed in `DateFieldExtractStyle`
834    pub fn with_date_field_extract_style(
835        mut self,
836        date_field_extract_style: DateFieldExtractStyle,
837    ) -> Self {
838        self.date_field_extract_style = date_field_extract_style;
839        self
840    }
841
842    /// Customize the dialect with a specific SQL type for Int64 casting: BigInt, SIGNED, etc.
843    pub fn with_int64_cast_dtype(mut self, int64_cast_dtype: ast::DataType) -> Self {
844        self.int64_cast_dtype = int64_cast_dtype;
845        self
846    }
847
848    /// Customize the dialect with a specific SQL type for Int32 casting: Integer, SIGNED, etc.
849    pub fn with_int32_cast_dtype(mut self, int32_cast_dtype: ast::DataType) -> Self {
850        self.int32_cast_dtype = int32_cast_dtype;
851        self
852    }
853
854    /// Customize the dialect with a specific SQL type for Timestamp casting: Timestamp, Datetime, etc.
855    pub fn with_timestamp_cast_dtype(
856        mut self,
857        timestamp_cast_dtype: ast::DataType,
858        timestamp_tz_cast_dtype: ast::DataType,
859    ) -> Self {
860        self.timestamp_cast_dtype = timestamp_cast_dtype;
861        self.timestamp_tz_cast_dtype = timestamp_tz_cast_dtype;
862        self
863    }
864
865    pub fn with_date32_cast_dtype(mut self, date32_cast_dtype: ast::DataType) -> Self {
866        self.date32_cast_dtype = date32_cast_dtype;
867        self
868    }
869
870    /// Customize the dialect to support column aliases as part of alias table definition
871    pub fn with_supports_column_alias_in_table_alias(
872        mut self,
873        supports_column_alias_in_table_alias: bool,
874    ) -> Self {
875        self.supports_column_alias_in_table_alias = supports_column_alias_in_table_alias;
876        self
877    }
878
879    pub fn with_requires_derived_table_alias(
880        mut self,
881        requires_derived_table_alias: bool,
882    ) -> Self {
883        self.requires_derived_table_alias = requires_derived_table_alias;
884        self
885    }
886
887    pub fn with_division_operator(mut self, division_operator: BinaryOperator) -> Self {
888        self.division_operator = division_operator;
889        self
890    }
891
892    pub fn with_window_func_support_window_frame(
893        mut self,
894        window_func_support_window_frame: bool,
895    ) -> Self {
896        self.window_func_support_window_frame = window_func_support_window_frame;
897        self
898    }
899
900    /// Customize the dialect to allow full qualified column names
901    pub fn with_full_qualified_col(mut self, full_qualified_col: bool) -> Self {
902        self.full_qualified_col = full_qualified_col;
903        self
904    }
905
906    pub fn with_unnest_as_table_factor(mut self, unnest_as_table_factor: bool) -> Self {
907        self.unnest_as_table_factor = unnest_as_table_factor;
908        self
909    }
910}