use super::types::{CanonicalType, TypeCategory, TypeSupport};
pub trait Dialect: Send + Sync + 'static {
fn name(&self) -> &'static str;
fn ddl_type(&self, t: &CanonicalType) -> String;
fn cast_name(&self, t: &CanonicalType) -> Option<String>;
fn type_category(&self, t: &CanonicalType) -> TypeCategory;
fn type_support(&self, t: &CanonicalType) -> TypeSupport;
fn quote_ident(&self, s: &str) -> String;
fn placeholder(&self, n: usize) -> String;
fn cast_expr(&self, placeholder: &str, cast: &str) -> String;
fn now_fn(&self) -> &'static str;
fn uuid_default_expr(&self) -> &'static str;
fn returning_clause(&self, cols: &str) -> String;
fn upsert_conflict(&self, conflict_cols: &[&str], set_pairs: &str) -> String;
fn to_one_subquery(&self, col_exprs: &[String], from_clause: &str) -> String;
fn to_many_subquery(&self, col_exprs: &[String], from_clause: &str) -> String;
fn json_extract_text(&self, col: &str, key: &str) -> String;
fn json_extract_typed(&self, col: &str, key: &str, t: &CanonicalType) -> String;
fn case_insensitive_like(&self, col: &str, placeholder: &str) -> String;
fn sys_json_type(&self) -> &'static str;
fn sys_timestamp_type(&self) -> &'static str;
fn sys_timestamp_default(&self) -> String {
format!(
"{} NOT NULL DEFAULT {}",
self.sys_timestamp_type(),
self.now_fn()
)
}
fn sys_bigserial_type(&self) -> &'static str;
fn sys_bytes_type(&self) -> &'static str;
fn audit_timestamp_type(&self) -> &'static str;
fn supports_schemas(&self) -> bool {
true
}
fn default_now_plus_hours(&self, hours: u32) -> Option<String> {
Some(format!("NOW() + INTERVAL '{} hours'", hours))
}
fn supports_rls(&self) -> bool;
fn supports_named_enum_types(&self) -> bool;
fn supports_index_include(&self) -> bool;
fn set_tenant_session_sql(&self, tenant_id: &str) -> Option<String>;
fn set_read_only_sql(&self) -> Option<String> {
None
}
fn set_statement_timeout_sql(&self, _ms: u64) -> Option<String> {
None
}
fn set_role_sql(&self, _role: &str) -> Option<String> {
None
}
fn supports_add_column_if_not_exists(&self) -> bool {
false
}
fn is_duplicate_object_code(&self, _code: &str) -> bool {
false
}
fn introspect_columns_sql(&self, schema: &str) -> String {
format!(
"SELECT table_name, column_name, data_type, is_nullable, \
CASE WHEN column_default IS NULL THEN 'NO' ELSE 'YES' END \
FROM information_schema.columns WHERE table_schema = '{}'",
escape_literal(schema)
)
}
fn introspect_indexes_sql(&self, _schema: &str) -> Option<String> {
None
}
fn introspect_constraints_sql(&self, schema: &str) -> Option<String> {
Some(format!(
"SELECT table_name, constraint_name FROM information_schema.table_constraints \
WHERE constraint_schema = '{}'",
escape_literal(schema)
))
}
}
pub fn escape_literal(s: &str) -> String {
s.replace('\'', "''")
}