use std::collections::HashMap;
use crate::Value;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SqlType {
Integer,
BigInt,
SmallInt,
Float,
Double,
Decimal,
Text,
VarChar(Option<u32>),
Blob,
Boolean,
Timestamp,
Date,
Time,
Uuid,
Json,
Array(Box<SqlType>),
Unknown(String),
}
impl SqlType {
pub fn as_sql_name(&self) -> String {
match self {
SqlType::Integer => "INTEGER".into(),
SqlType::BigInt => "BIGINT".into(),
SqlType::SmallInt => "SMALLINT".into(),
SqlType::Float => "REAL".into(),
SqlType::Double => "DOUBLE PRECISION".into(),
SqlType::Decimal => "DECIMAL".into(),
SqlType::Text => "TEXT".into(),
SqlType::VarChar(None) => "VARCHAR".into(),
SqlType::VarChar(Some(n)) => format!("VARCHAR({n})"),
SqlType::Blob => "BYTEA".into(),
SqlType::Boolean => "BOOLEAN".into(),
SqlType::Timestamp => "TIMESTAMP".into(),
SqlType::Date => "DATE".into(),
SqlType::Time => "TIME".into(),
SqlType::Uuid => "UUID".into(),
SqlType::Json => "JSON".into(),
SqlType::Array(inner) => format!("{}[]", inner.as_sql_name()),
SqlType::Unknown(s) => s.clone(),
}
}
pub fn default_value(&self) -> Value {
match self {
SqlType::Integer | SqlType::BigInt | SqlType::SmallInt => Value::I64(0),
SqlType::Float | SqlType::Double => Value::F64(0.0),
SqlType::Decimal => Value::Decimal("0".into()),
SqlType::Text | SqlType::VarChar(_) => Value::Text(String::new()),
SqlType::Blob => Value::Blob(Vec::new()),
SqlType::Boolean => Value::Bool(false),
SqlType::Timestamp => Value::Timestamp(0),
SqlType::Date => Value::Date(0),
SqlType::Time => Value::Time(0),
SqlType::Uuid => Value::Uuid(0),
SqlType::Json => Value::Json("{}".into()),
SqlType::Array(_) => Value::Array(Vec::new()),
SqlType::Unknown(_) => Value::Null,
}
}
}
pub struct TypeRegistry {
name_to_type: HashMap<String, SqlType>,
}
impl TypeRegistry {
pub fn new() -> Self {
let mut reg = Self {
name_to_type: HashMap::new(),
};
reg.insert("INTEGER", SqlType::Integer);
reg.insert("INT", SqlType::Integer);
reg.insert("INT4", SqlType::Integer);
reg.insert("BIGINT", SqlType::BigInt);
reg.insert("INT8", SqlType::BigInt);
reg.insert("SMALLINT", SqlType::SmallInt);
reg.insert("INT2", SqlType::SmallInt);
reg.insert("REAL", SqlType::Float);
reg.insert("FLOAT4", SqlType::Float);
reg.insert("FLOAT", SqlType::Float);
reg.insert("DOUBLE PRECISION", SqlType::Double);
reg.insert("FLOAT8", SqlType::Double);
reg.insert("DOUBLE", SqlType::Double);
reg.insert("DECIMAL", SqlType::Decimal);
reg.insert("NUMERIC", SqlType::Decimal);
reg.insert("TEXT", SqlType::Text);
reg.insert("CLOB", SqlType::Text);
reg.insert("VARCHAR", SqlType::VarChar(None));
reg.insert("CHARACTER VARYING", SqlType::VarChar(None));
reg.insert("BYTEA", SqlType::Blob);
reg.insert("BLOB", SqlType::Blob);
reg.insert("BINARY", SqlType::Blob);
reg.insert("BOOLEAN", SqlType::Boolean);
reg.insert("BOOL", SqlType::Boolean);
reg.insert("TIMESTAMP", SqlType::Timestamp);
reg.insert("TIMESTAMPTZ", SqlType::Timestamp);
reg.insert("TIMESTAMP WITH TIME ZONE", SqlType::Timestamp);
reg.insert("DATE", SqlType::Date);
reg.insert("TIME", SqlType::Time);
reg.insert("TIMETZ", SqlType::Time);
reg.insert("UUID", SqlType::Uuid);
reg.insert("JSON", SqlType::Json);
reg.insert("JSONB", SqlType::Json);
reg
}
fn insert(&mut self, name: &str, sql_type: SqlType) {
self.name_to_type
.insert(name.to_ascii_uppercase(), sql_type);
}
pub fn lookup(&self, type_name: &str) -> Option<&SqlType> {
self.name_to_type.get(&type_name.to_ascii_uppercase())
}
pub fn default_value_for(&self, type_name: &str) -> Value {
self.lookup(type_name)
.map(|t| t.default_value())
.unwrap_or(Value::Null)
}
pub fn register(&mut self, name: &str, sql_type: SqlType) {
self.name_to_type
.insert(name.to_ascii_uppercase(), sql_type);
}
pub fn value_matches_type(value: &Value, sql_type: &SqlType) -> bool {
if value.is_null() {
return true;
}
matches!(
(value, sql_type),
(
Value::I64(_),
SqlType::Integer | SqlType::BigInt | SqlType::SmallInt
) | (Value::F64(_), SqlType::Float | SqlType::Double)
| (Value::Decimal(_), SqlType::Decimal)
| (Value::Text(_), SqlType::Text | SqlType::VarChar(_))
| (Value::Blob(_), SqlType::Blob)
| (Value::Bool(_), SqlType::Boolean)
| (Value::Timestamp(_), SqlType::Timestamp)
| (Value::Date(_), SqlType::Date)
| (Value::Time(_), SqlType::Time)
| (Value::Uuid(_), SqlType::Uuid)
| (Value::Json(_), SqlType::Json)
| (Value::Array(_), SqlType::Array(_))
| (Value::TypedArray { .. }, SqlType::Array(_))
)
}
}
impl Default for TypeRegistry {
fn default() -> Self {
Self::new()
}
}