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) => {
60 if t.precision > 0 {
61 format!("TIME({})", t.precision)
62 } else {
63 "TIME".to_string()
64 }
65 }
66 DataType::Timestamp(ts) => {
67 if ts.precision > 0 {
68 format!("TIMESTAMP({})", ts.precision)
69 } else {
70 "TIMESTAMP".to_string()
71 }
72 }
73 DataType::TimestampTz(ts) => {
74 let base = "TIMESTAMP WITH TIME ZONE";
75 if ts.precision > 0 { format!("{base}({})", ts.precision) } else { base.to_string() }
76 }
77 DataType::DateTime(dt_inner) => {
78 if dt_inner.precision > 0 {
79 format!("DATETIME({})", dt_inner.precision)
80 } else {
81 "DATETIME".to_string()
82 }
83 }
84 DataType::Interval(_) => "INTERVAL".to_string(),
85 DataType::Uuid(_) => "UUID".to_string(),
86 DataType::Json(j) => match j.storage_format {
87 JsonStorageFormat::Binary => "JSONB".to_string(),
88 JsonStorageFormat::Text => "JSON".to_string(),
89 },
90 DataType::Array(arr) => {
91 let elem = render_data_type_ansi(&arr.element_type);
92 format!("{elem}[]")
93 }
94 DataType::Map(_) | DataType::Struct(_) => "JSON".to_string(),
96 }
97}
98
99#[must_use]
111pub fn render_cast_type_ansi(dt: &DataType) -> String {
112 match dt {
113 DataType::Boolean(_) => "BOOLEAN".to_owned(),
114 DataType::Integer(i) => match i.size {
115 IntegerSize::Tiny | IntegerSize::Small => "SMALLINT".to_owned(),
116 IntegerSize::Standard => "INTEGER".to_owned(),
117 IntegerSize::Big => "BIGINT".to_owned(),
118 },
119 DataType::Decimal(d) => format!("DECIMAL({}, {})", d.precision, d.scale),
120 DataType::Float(f) => match f.precision {
121 FloatPrecision::Double => "DOUBLE PRECISION".to_owned(),
122 FloatPrecision::Single => "REAL".to_owned(),
123 },
124 DataType::Char(c) => format!("CHAR({})", c.length),
125 DataType::VarChar(v) => match v.max_length {
126 Some(n) => format!("VARCHAR({n})"),
127 None => "VARCHAR".to_owned(),
128 },
129 DataType::Date(_) => "DATE".to_owned(),
130 DataType::Time(_) => "TIME".to_owned(),
131 DataType::Timestamp(_) | DataType::DateTime(_) => "TIMESTAMP".to_owned(),
132 DataType::TimestampTz(_) => "TIMESTAMP WITH TIME ZONE".to_owned(),
133 DataType::Binary(_) => "BINARY".to_owned(),
134 DataType::VarBinary(_) => "VARBINARY".to_owned(),
135 DataType::Json(_) => "JSON".to_owned(),
136 DataType::Uuid(_) => "UUID".to_owned(),
137 _ => "TEXT".to_owned(),
138 }
139}
140
141pub struct AnsiDialect;
147
148impl SqlRenderer for AnsiDialect {
149 fn render_data_type(&self, dt: &DataType) -> String {
150 render_data_type_ansi(dt)
151 }
152
153 fn quote_identifier(&self, name: &str) -> String {
154 format!("\"{name}\"")
155 }
156
157 fn qualify_name(&self, schema: Option<&str>, name: &str) -> String {
158 match schema {
159 Some(s) => format!("{}.{}", self.quote_identifier(s), self.quote_identifier(name)),
160 None => self.quote_identifier(name),
161 }
162 }
163
164 fn render_current_timestamp(&self) -> String {
165 "CURRENT_TIMESTAMP".to_string()
166 }
167
168 fn is_agnostic_dialect(&self) -> bool {
169 true
170 }
171
172 fn dialect_name(&self) -> &'static str {
173 "ansi"
174 }
175
176 fn to_formula_renderer(&self) -> Box<dyn FormulaRenderer> {
177 Box::new(Self)
178 }
179
180 fn sid_spelling(&self) -> crate::SidSpelling {
183 crate::ANSI_SID_SPELLING
184 }
185}
186
187impl DdlRenderer for AnsiDialect {}
188
189impl FormulaRenderer for AnsiDialect {}
190
191impl crate::ReplicationRenderer for AnsiDialect {}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn agnostic_is_agnostic() {
199 let d = AnsiDialect;
200 assert!(d.is_agnostic_dialect());
201 assert_eq!(d.dialect_name(), "ansi");
202 }
203
204 #[test]
205 fn agnostic_quotes_with_double_quotes() {
206 let d = AnsiDialect;
207 assert_eq!(d.quote_identifier("my_table"), "\"my_table\"");
208 }
209
210 #[test]
211 fn agnostic_qualifies_name() {
212 let d = AnsiDialect;
213 assert_eq!(d.qualify_name(Some("public"), "users"), "\"public\".\"users\"");
214 assert_eq!(d.qualify_name(None, "users"), "\"users\"");
215 }
216
217 #[test]
218 fn ansi_data_type_integer() {
219 assert_eq!(render_data_type_ansi(&DataType::int()), "INTEGER");
220 assert_eq!(render_data_type_ansi(&DataType::bigint()), "BIGINT");
221 assert_eq!(render_data_type_ansi(&DataType::smallint()), "SMALLINT");
222 }
223
224 #[test]
225 fn ansi_data_type_varchar() {
226 assert_eq!(render_data_type_ansi(&DataType::varchar(100)), "VARCHAR(100)");
227 assert_eq!(render_data_type_ansi(&DataType::text()), "TEXT");
228 }
229
230 #[test]
231 fn ansi_data_type_timestamp() {
232 assert_eq!(render_data_type_ansi(&DataType::timestamp()), "TIMESTAMP(6)");
233 assert_eq!(render_data_type_ansi(&DataType::date()), "DATE");
234 }
235
236 #[test]
239 fn ansi_answers_for_every_nested_type() {
240 use crate::data_type::{ArrayDataType, MapDataType, StructDataType, StructField};
241
242 let array = DataType::Array(ArrayDataType::new(DataType::varchar(100)));
243 let map = DataType::Map(MapDataType::new(DataType::varchar(50), DataType::bigint()));
244 let record = DataType::Struct(StructDataType::new(vec![
245 StructField::new("street", DataType::varchar(200)),
246 StructField::new("zip", DataType::varchar(10)),
247 ]));
248 let array_of_records =
249 DataType::Array(ArrayDataType::new(DataType::Struct(StructDataType::new(vec![
250 StructField::new("key", DataType::varchar(100)),
251 StructField::new("value", DataType::varchar(1000)),
252 ]))));
253
254 assert_eq!(render_data_type_ansi(&array), "VARCHAR(100)[]");
255 assert_eq!(render_data_type_ansi(&map), "JSON");
256 assert_eq!(render_data_type_ansi(&record), "JSON");
257 assert_eq!(render_data_type_ansi(&array_of_records), "JSON[]");
258
259 for dt in [&array, &map, &record, &array_of_records] {
260 assert!(!render_cast_type_ansi(dt).trim().is_empty());
261 }
262 }
263}