use crate::{
DataType,
data_type::{FloatPrecision, IntegerSize, JsonStorageFormat},
};
use crate::{DdlRenderer, FormulaRenderer, SqlRenderer};
#[must_use]
pub fn render_data_type_ansi(dt: &DataType) -> String {
match dt {
DataType::Integer(i) => match i.size {
IntegerSize::Tiny => "TINYINT",
IntegerSize::Small => "SMALLINT",
IntegerSize::Standard => "INTEGER",
IntegerSize::Big => "BIGINT",
}
.to_string(),
DataType::Decimal(d) => format!("DECIMAL({}, {})", d.precision, d.scale),
DataType::Float(f) => match f.precision {
FloatPrecision::Single => "FLOAT",
FloatPrecision::Double => "DOUBLE PRECISION",
}
.to_string(),
DataType::Boolean(_) => "BOOLEAN".to_string(),
DataType::Char(c) => format!("CHAR({})", c.length),
DataType::VarChar(vc) => match vc.max_length {
Some(len) => format!("VARCHAR({len})"),
None => "TEXT".to_string(),
},
DataType::Text(_) => "TEXT".to_string(),
DataType::Binary(b) => format!("BINARY({})", b.length),
DataType::VarBinary(vb) => match vb.max_length {
Some(len) => format!("VARBINARY({len})"),
None => "BLOB".to_string(),
},
DataType::Date(_) => "DATE".to_string(),
DataType::Time(t) => {
if t.precision > 0 {
format!("TIME({})", t.precision)
} else {
"TIME".to_string()
}
}
DataType::Timestamp(ts) => {
if ts.precision > 0 {
format!("TIMESTAMP({})", ts.precision)
} else {
"TIMESTAMP".to_string()
}
}
DataType::TimestampTz(ts) => {
let base = "TIMESTAMP WITH TIME ZONE";
if ts.precision > 0 { format!("{base}({})", ts.precision) } else { base.to_string() }
}
DataType::DateTime(dt_inner) => {
if dt_inner.precision > 0 {
format!("DATETIME({})", dt_inner.precision)
} else {
"DATETIME".to_string()
}
}
DataType::Interval(_) => "INTERVAL".to_string(),
DataType::Uuid(_) => "UUID".to_string(),
DataType::Json(j) => match j.storage_format {
JsonStorageFormat::Binary => "JSONB".to_string(),
JsonStorageFormat::Text => "JSON".to_string(),
},
DataType::Array(arr) => {
let elem = render_data_type_ansi(&arr.element_type);
format!("{elem}[]")
}
DataType::Map(_) | DataType::Struct(_) => "JSON".to_string(),
}
}
#[must_use]
pub fn render_cast_type_ansi(dt: &DataType) -> String {
match dt {
DataType::Boolean(_) => "BOOLEAN".to_owned(),
DataType::Integer(i) => match i.size {
IntegerSize::Tiny | IntegerSize::Small => "SMALLINT".to_owned(),
IntegerSize::Standard => "INTEGER".to_owned(),
IntegerSize::Big => "BIGINT".to_owned(),
},
DataType::Decimal(d) => format!("DECIMAL({}, {})", d.precision, d.scale),
DataType::Float(f) => match f.precision {
FloatPrecision::Double => "DOUBLE PRECISION".to_owned(),
FloatPrecision::Single => "REAL".to_owned(),
},
DataType::Char(c) => format!("CHAR({})", c.length),
DataType::VarChar(v) => match v.max_length {
Some(n) => format!("VARCHAR({n})"),
None => "VARCHAR".to_owned(),
},
DataType::Date(_) => "DATE".to_owned(),
DataType::Time(_) => "TIME".to_owned(),
DataType::Timestamp(_) | DataType::DateTime(_) => "TIMESTAMP".to_owned(),
DataType::TimestampTz(_) => "TIMESTAMP WITH TIME ZONE".to_owned(),
DataType::Binary(_) => "BINARY".to_owned(),
DataType::VarBinary(_) => "VARBINARY".to_owned(),
DataType::Json(_) => "JSON".to_owned(),
DataType::Uuid(_) => "UUID".to_owned(),
_ => "TEXT".to_owned(),
}
}
pub struct AnsiDialect;
impl SqlRenderer for AnsiDialect {
fn render_data_type(&self, dt: &DataType) -> String {
render_data_type_ansi(dt)
}
fn quote_identifier(&self, name: &str) -> String {
format!("\"{name}\"")
}
fn qualify_name(&self, schema: Option<&str>, name: &str) -> String {
match schema {
Some(s) => format!("{}.{}", self.quote_identifier(s), self.quote_identifier(name)),
None => self.quote_identifier(name),
}
}
fn render_current_timestamp(&self) -> String {
"CURRENT_TIMESTAMP".to_string()
}
fn is_agnostic_dialect(&self) -> bool {
true
}
fn dialect_name(&self) -> &'static str {
"ansi"
}
fn to_formula_renderer(&self) -> Box<dyn FormulaRenderer> {
Box::new(Self)
}
fn sid_spelling(&self) -> crate::SidSpelling {
crate::ANSI_SID_SPELLING
}
}
impl DdlRenderer for AnsiDialect {}
impl FormulaRenderer for AnsiDialect {}
impl crate::ReplicationRenderer for AnsiDialect {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn agnostic_is_agnostic() {
let d = AnsiDialect;
assert!(d.is_agnostic_dialect());
assert_eq!(d.dialect_name(), "ansi");
}
#[test]
fn agnostic_quotes_with_double_quotes() {
let d = AnsiDialect;
assert_eq!(d.quote_identifier("my_table"), "\"my_table\"");
}
#[test]
fn agnostic_qualifies_name() {
let d = AnsiDialect;
assert_eq!(d.qualify_name(Some("public"), "users"), "\"public\".\"users\"");
assert_eq!(d.qualify_name(None, "users"), "\"users\"");
}
#[test]
fn ansi_data_type_integer() {
assert_eq!(render_data_type_ansi(&DataType::int()), "INTEGER");
assert_eq!(render_data_type_ansi(&DataType::bigint()), "BIGINT");
assert_eq!(render_data_type_ansi(&DataType::smallint()), "SMALLINT");
}
#[test]
fn ansi_data_type_varchar() {
assert_eq!(render_data_type_ansi(&DataType::varchar(100)), "VARCHAR(100)");
assert_eq!(render_data_type_ansi(&DataType::text()), "TEXT");
}
#[test]
fn ansi_data_type_timestamp() {
assert_eq!(render_data_type_ansi(&DataType::timestamp()), "TIMESTAMP(6)");
assert_eq!(render_data_type_ansi(&DataType::date()), "DATE");
}
#[test]
fn ansi_answers_for_every_nested_type() {
use crate::data_type::{ArrayDataType, MapDataType, StructDataType, StructField};
let array = DataType::Array(ArrayDataType::new(DataType::varchar(100)));
let map = DataType::Map(MapDataType::new(DataType::varchar(50), DataType::bigint()));
let record = DataType::Struct(StructDataType::new(vec![
StructField::new("street", DataType::varchar(200)),
StructField::new("zip", DataType::varchar(10)),
]));
let array_of_records =
DataType::Array(ArrayDataType::new(DataType::Struct(StructDataType::new(vec![
StructField::new("key", DataType::varchar(100)),
StructField::new("value", DataType::varchar(1000)),
]))));
assert_eq!(render_data_type_ansi(&array), "VARCHAR(100)[]");
assert_eq!(render_data_type_ansi(&map), "JSON");
assert_eq!(render_data_type_ansi(&record), "JSON");
assert_eq!(render_data_type_ansi(&array_of_records), "JSON[]");
for dt in [&array, &map, &record, &array_of_records] {
assert!(!render_cast_type_ansi(dt).trim().is_empty());
}
}
}