1use crate::{
16 DataType,
17 data_type::{FloatPrecision, IntegerSize, JsonStorageFormat},
18};
19
20use crate::{DdlRenderer, FormulaRenderer, SqlRenderer};
21
22#[must_use]
31pub fn render_data_type_ansi(dt: &DataType) -> String {
32 match dt {
33 DataType::Integer(i) => match i.size {
34 IntegerSize::Tiny => "TINYINT",
35 IntegerSize::Small => "SMALLINT",
36 IntegerSize::Standard => "INTEGER",
37 IntegerSize::Big => "BIGINT",
38 }
39 .to_string(),
40 DataType::Decimal(d) => format!("DECIMAL({}, {})", d.precision, d.scale),
41 DataType::Float(f) => match f.precision {
42 FloatPrecision::Single => "FLOAT",
43 FloatPrecision::Double => "DOUBLE PRECISION",
44 }
45 .to_string(),
46 DataType::Boolean(_) => "BOOLEAN".to_string(),
47 DataType::Char(c) => format!("CHAR({})", c.length),
48 DataType::VarChar(vc) => match vc.max_length {
49 Some(len) => format!("VARCHAR({len})"),
50 None => "TEXT".to_string(),
51 },
52 DataType::Text(_) => "TEXT".to_string(),
53 DataType::Binary(b) => format!("BINARY({})", b.length),
54 DataType::VarBinary(vb) => match vb.max_length {
55 Some(len) => format!("VARBINARY({len})"),
56 None => "BLOB".to_string(),
57 },
58 DataType::Date(_) => "DATE".to_string(),
59 DataType::Time(t) => match t.precision {
60 Some(digits) => format!("TIME({digits})"),
61 None => "TIME".to_string(),
62 },
63 DataType::Timestamp(ts) => format!("TIMESTAMP({})", ts.precision),
65 DataType::TimestampTz(ts) => {
66 let base = "TIMESTAMP WITH TIME ZONE";
67 if ts.precision > 0 { format!("{base}({})", ts.precision) } else { base.to_string() }
68 }
69 DataType::DateTime(dt_inner) => {
70 if dt_inner.precision > 0 {
71 format!("DATETIME({})", dt_inner.precision)
72 } else {
73 "DATETIME".to_string()
74 }
75 }
76 DataType::Interval(_) => "INTERVAL".to_string(),
77 DataType::Uuid(_) => "UUID".to_string(),
78 DataType::Json(j) => match j.storage_format {
79 JsonStorageFormat::Binary => "JSONB".to_string(),
80 JsonStorageFormat::Text => "JSON".to_string(),
81 },
82 DataType::Array(arr) => {
83 let elem = render_data_type_ansi(&arr.element_type);
84 format!("{elem}[]")
85 }
86 DataType::Map(_) | DataType::Struct(_) => "JSON".to_string(),
88 }
89}
90
91#[must_use]
103pub fn render_cast_type_ansi(dt: &DataType) -> String {
104 match dt {
105 DataType::Boolean(_) => "BOOLEAN".to_owned(),
106 DataType::Integer(i) => match i.size {
107 IntegerSize::Tiny | IntegerSize::Small => "SMALLINT".to_owned(),
108 IntegerSize::Standard => "INTEGER".to_owned(),
109 IntegerSize::Big => "BIGINT".to_owned(),
110 },
111 DataType::Decimal(d) => format!("DECIMAL({}, {})", d.precision, d.scale),
112 DataType::Float(f) => match f.precision {
113 FloatPrecision::Double => "DOUBLE PRECISION".to_owned(),
114 FloatPrecision::Single => "REAL".to_owned(),
115 },
116 DataType::Char(c) => format!("CHAR({})", c.length),
117 DataType::VarChar(v) => match v.max_length {
118 Some(n) => format!("VARCHAR({n})"),
119 None => "VARCHAR".to_owned(),
120 },
121 DataType::Date(_) => "DATE".to_owned(),
122 DataType::Time(_) => "TIME".to_owned(),
123 DataType::Timestamp(_) | DataType::DateTime(_) => "TIMESTAMP".to_owned(),
124 DataType::TimestampTz(_) => "TIMESTAMP WITH TIME ZONE".to_owned(),
125 DataType::Binary(_) => "BINARY".to_owned(),
126 DataType::VarBinary(_) => "VARBINARY".to_owned(),
127 DataType::Json(_) => "JSON".to_owned(),
128 DataType::Uuid(_) => "UUID".to_owned(),
129 _ => "TEXT".to_owned(),
130 }
131}
132
133pub struct AnsiDialect;
139
140impl SqlRenderer for AnsiDialect {
141 fn render_data_type(&self, dt: &DataType) -> String {
142 render_data_type_ansi(dt)
143 }
144
145 fn quote_identifier(&self, name: &str) -> String {
146 format!("\"{name}\"")
147 }
148
149 fn qualify_name(&self, schema: Option<&str>, name: &str) -> String {
150 match schema {
151 Some(s) => format!("{}.{}", self.quote_identifier(s), self.quote_identifier(name)),
152 None => self.quote_identifier(name),
153 }
154 }
155
156 fn render_current_timestamp(&self) -> String {
157 "CURRENT_TIMESTAMP".to_string()
158 }
159
160 fn is_agnostic_dialect(&self) -> bool {
161 true
162 }
163
164 fn dialect_name(&self) -> &'static str {
165 "ansi"
166 }
167
168 fn to_formula_renderer(&self) -> Box<dyn FormulaRenderer> {
169 Box::new(Self)
170 }
171
172 fn sid_spelling(&self) -> crate::SidSpelling {
175 crate::ANSI_SID_SPELLING
176 }
177}
178
179impl DdlRenderer for AnsiDialect {}
180
181impl FormulaRenderer for AnsiDialect {}
182
183impl crate::ReplicationRenderer for AnsiDialect {}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn agnostic_is_agnostic() {
191 let d = AnsiDialect;
192 assert!(d.is_agnostic_dialect());
193 assert_eq!(d.dialect_name(), "ansi");
194 }
195
196 #[test]
197 fn agnostic_quotes_with_double_quotes() {
198 let d = AnsiDialect;
199 assert_eq!(d.quote_identifier("my_table"), "\"my_table\"");
200 }
201
202 #[test]
203 fn agnostic_qualifies_name() {
204 let d = AnsiDialect;
205 assert_eq!(d.qualify_name(Some("public"), "users"), "\"public\".\"users\"");
206 assert_eq!(d.qualify_name(None, "users"), "\"users\"");
207 }
208
209 #[test]
210 fn ansi_data_type_integer() {
211 assert_eq!(render_data_type_ansi(&DataType::int()), "INTEGER");
212 assert_eq!(render_data_type_ansi(&DataType::bigint()), "BIGINT");
213 assert_eq!(render_data_type_ansi(&DataType::smallint()), "SMALLINT");
214 }
215
216 #[test]
217 fn ansi_data_type_varchar() {
218 assert_eq!(render_data_type_ansi(&DataType::varchar(100)), "VARCHAR(100)");
219 assert_eq!(render_data_type_ansi(&DataType::text()), "TEXT");
220 }
221
222 #[test]
223 fn ansi_data_type_timestamp() {
224 assert_eq!(render_data_type_ansi(&DataType::timestamp()), "TIMESTAMP(6)");
225 assert_eq!(render_data_type_ansi(&DataType::date()), "DATE");
226 }
227
228 #[test]
231 fn ansi_answers_for_every_nested_type() {
232 use crate::data_type::{ArrayDataType, MapDataType, StructDataType, StructField};
233
234 let array = DataType::Array(ArrayDataType::new(DataType::varchar(100)));
235 let map = DataType::Map(MapDataType::new(DataType::varchar(50), DataType::bigint()));
236 let record = DataType::Struct(StructDataType::new(vec![
237 StructField::new("street", DataType::varchar(200)),
238 StructField::new("zip", DataType::varchar(10)),
239 ]));
240 let array_of_records =
241 DataType::Array(ArrayDataType::new(DataType::Struct(StructDataType::new(vec![
242 StructField::new("key", DataType::varchar(100)),
243 StructField::new("value", DataType::varchar(1000)),
244 ]))));
245
246 assert_eq!(render_data_type_ansi(&array), "VARCHAR(100)[]");
247 assert_eq!(render_data_type_ansi(&map), "JSON");
248 assert_eq!(render_data_type_ansi(&record), "JSON");
249 assert_eq!(render_data_type_ansi(&array_of_records), "JSON[]");
250
251 for dt in [&array, &map, &record, &array_of_records] {
252 assert!(!render_cast_type_ansi(dt).trim().is_empty());
253 }
254 }
255}