polyglot_sql/dialects/dremio.rs
1//! Dremio SQL Dialect
2//!
3//! Dremio-specific SQL dialect based on sqlglot patterns.
4//! Reference: https://docs.dremio.com/current/reference/sql/data-types/
5//!
6//! Key characteristics:
7//! - NULLS LAST is default ordering
8//! - DATE_ADD/DATE_SUB with INTERVAL CAST for non-DAY units
9//! - CURRENT_DATE_UTC for current date in UTC
10//! - ARRAY_GENERATE_RANGE for generating series
11//! - No timezone-aware timestamps
12//! - Comments support: --, //, /* */
13//! - Type mappings: SMALLINT→INT, TINYINT→INT, ARRAY→LIST, etc.
14
15use super::{DialectImpl, DialectType};
16use crate::error::Result;
17use crate::expressions::{Expression, Function};
18use crate::generator::GeneratorConfig;
19use crate::tokens::TokenizerConfig;
20
21/// Dremio dialect
22pub struct DremioDialect;
23
24impl DialectImpl for DremioDialect {
25 fn dialect_type(&self) -> DialectType {
26 DialectType::Dremio
27 }
28
29 fn tokenizer_config(&self) -> TokenizerConfig {
30 let mut config = TokenizerConfig::default();
31 // Dremio uses double quotes for identifiers
32 config.identifiers.insert('"', '"');
33 // Dremio supports multiple comment styles: --, //, /* */
34 // Default tokenizer handles -- and /* */
35 config
36 }
37
38 fn generator_config(&self) -> GeneratorConfig {
39 use crate::generator::IdentifierQuoteStyle;
40 GeneratorConfig {
41 identifier_quote: '"',
42 identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
43 dialect: Some(DialectType::Dremio),
44 // Dremio uses singular form for intervals (DAY not DAYS)
45 interval_allows_plural_form: false,
46 // Dremio requires literal values in LIMIT clause
47 limit_only_literals: true,
48 // Dremio doesn't support COUNT(DISTINCT a, b) - needs transformation
49 multi_arg_distinct: false,
50 // Dremio supports BETWEEN SYMMETRIC/ASYMMETRIC
51 supports_between_flags: true,
52 ..Default::default()
53 }
54 }
55
56 fn transform_expr(&self, expr: Expression) -> Result<Expression> {
57 match expr {
58 // Generic function transformations
59 Expression::Function(f) => self.transform_function(*f),
60
61 // Aggregate function transformations
62 Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
63
64 // Pass through everything else
65 _ => Ok(expr),
66 }
67 }
68}
69
70impl DremioDialect {
71 fn transform_function(&self, f: Function) -> Result<Expression> {
72 let name_upper = f.name.to_uppercase();
73 match name_upper.as_str() {
74 // GenerateSeries → ARRAY_GENERATE_RANGE
75 "GENERATE_SERIES" => Ok(Expression::Function(Box::new(Function::new(
76 "ARRAY_GENERATE_RANGE".to_string(),
77 f.args,
78 )))),
79
80 // TimeToStr → TO_CHAR
81 "DATE_FORMAT" | "TIME_TO_STR" | "STRFTIME" => Ok(Expression::Function(Box::new(
82 Function::new("TO_CHAR".to_string(), f.args),
83 ))),
84
85 // TO_DATE is native
86 "TO_DATE" => Ok(Expression::Function(Box::new(f))),
87
88 // DATE_ADD is native (with interval cast for non-day units)
89 "DATE_ADD" => Ok(Expression::Function(Box::new(f))),
90
91 // DATE_SUB is native (with interval cast for non-day units)
92 "DATE_SUB" => Ok(Expression::Function(Box::new(f))),
93
94 // REGEXP_MATCHES → REGEXP_LIKE (Dremio uses REGEXP_LIKE)
95 "REGEXP_MATCHES" => Ok(Expression::Function(Box::new(Function::new(
96 "REGEXP_LIKE".to_string(),
97 f.args,
98 )))),
99
100 // REPEATSTR → REPEAT (Dremio uses REPEAT, not REPEATSTR)
101 "REPEATSTR" => Ok(Expression::Function(Box::new(Function::new(
102 "REPEAT".to_string(),
103 f.args,
104 )))),
105
106 // DATE_PART → DATE_PART (native, same as EXTRACT)
107 "DATE_PART" | "EXTRACT" => Ok(Expression::Function(Box::new(f))),
108
109 // DATETYPE constructor for date literals
110 "DATETYPE" => Ok(Expression::Function(Box::new(f))),
111
112 // Pass through everything else
113 _ => Ok(Expression::Function(Box::new(f))),
114 }
115 }
116
117 fn transform_aggregate_function(
118 &self,
119 f: Box<crate::expressions::AggregateFunction>,
120 ) -> Result<Expression> {
121 let name_upper = f.name.to_uppercase();
122 match name_upper.as_str() {
123 // BitwiseAndAgg → BIT_AND
124 "BITWISE_AND_AGG" | "BIT_AND_AGG" => Ok(Expression::Function(Box::new(Function::new(
125 "BIT_AND".to_string(),
126 f.args,
127 )))),
128
129 // BitwiseOrAgg → BIT_OR
130 "BITWISE_OR_AGG" | "BIT_OR_AGG" => Ok(Expression::Function(Box::new(Function::new(
131 "BIT_OR".to_string(),
132 f.args,
133 )))),
134
135 // Pass through everything else
136 _ => Ok(Expression::AggregateFunction(f)),
137 }
138 }
139}
140
141// Note: Dremio type mappings (handled in generator if needed):
142// - SMALLINT → INT
143// - TINYINT → INT
144// - BINARY → VARBINARY
145// - TEXT → VARCHAR
146// - NCHAR → VARCHAR
147// - CHAR → VARCHAR
148// - TIMESTAMPNTZ → TIMESTAMP
149// - DATETIME → TIMESTAMP
150// - ARRAY → LIST
151// - BIT → BOOLEAN
152//
153// Dremio does not support timezone-aware TIMESTAMP types