use sqlparser::dialect::{Dialect, GenericDialect, MySqlDialect, PostgreSqlDialect};
use crate::core::models::DatabaseType;
pub trait SqlDialect: Send + Sync {
fn parser_dialect(&self) -> Box<dyn Dialect>;
fn normalize_identifier(&self, ident: &str) -> String;
fn has_schemas(&self) -> bool;
fn has_packages(&self) -> bool {
false
}
#[allow(dead_code)]
fn supports_procedural_parsing(&self) -> bool {
false
}
fn builtin_functions(&self) -> &[&str];
fn dialect_keywords(&self) -> &[&str];
#[allow(dead_code)]
fn bind_prefix(&self) -> &str;
}
pub struct OracleDialect;
impl SqlDialect for OracleDialect {
fn parser_dialect(&self) -> Box<dyn Dialect> {
Box::new(GenericDialect {})
}
fn normalize_identifier(&self, ident: &str) -> String {
ident.to_uppercase()
}
fn has_schemas(&self) -> bool {
true
}
fn has_packages(&self) -> bool {
true
}
fn builtin_functions(&self) -> &[&str] {
&ORACLE_FUNCTIONS
}
fn dialect_keywords(&self) -> &[&str] {
&ORACLE_KEYWORDS
}
fn bind_prefix(&self) -> &str {
":"
}
}
pub struct PostgresDialect;
impl SqlDialect for PostgresDialect {
fn parser_dialect(&self) -> Box<dyn Dialect> {
Box::new(PostgreSqlDialect {})
}
fn normalize_identifier(&self, ident: &str) -> String {
ident.to_lowercase()
}
fn has_schemas(&self) -> bool {
true
}
fn builtin_functions(&self) -> &[&str] {
&PG_FUNCTIONS
}
fn dialect_keywords(&self) -> &[&str] {
&PG_KEYWORDS
}
fn bind_prefix(&self) -> &str {
"$"
}
}
pub struct MysqlDialect;
impl SqlDialect for MysqlDialect {
fn parser_dialect(&self) -> Box<dyn Dialect> {
Box::new(MySqlDialect {})
}
fn normalize_identifier(&self, ident: &str) -> String {
ident.to_lowercase()
}
fn has_schemas(&self) -> bool {
false
}
fn builtin_functions(&self) -> &[&str] {
&MYSQL_FUNCTIONS
}
fn dialect_keywords(&self) -> &[&str] {
&MYSQL_KEYWORDS
}
fn bind_prefix(&self) -> &str {
"?"
}
}
pub fn dialect_for(db_type: DatabaseType) -> Box<dyn SqlDialect> {
match db_type {
DatabaseType::Oracle => Box::new(OracleDialect),
DatabaseType::PostgreSQL => Box::new(PostgresDialect),
DatabaseType::MySQL => Box::new(MysqlDialect),
}
}
const ORACLE_FUNCTIONS: [&str; 42] = [
"NVL",
"NVL2",
"DECODE",
"COALESCE",
"TO_CHAR",
"TO_DATE",
"TO_NUMBER",
"TO_TIMESTAMP",
"TO_CLOB",
"CAST",
"SUBSTR",
"INSTR",
"LENGTH",
"TRIM",
"LTRIM",
"RTRIM",
"UPPER",
"LOWER",
"INITCAP",
"LPAD",
"RPAD",
"REPLACE",
"TRANSLATE",
"CONCAT",
"ROUND",
"TRUNC",
"MOD",
"ABS",
"CEIL",
"FLOOR",
"SYSDATE",
"SYSTIMESTAMP",
"ADD_MONTHS",
"MONTHS_BETWEEN",
"LISTAGG",
"ROW_NUMBER",
"RANK",
"DENSE_RANK",
"REGEXP_LIKE",
"REGEXP_REPLACE",
"REGEXP_SUBSTR",
"ROWNUM",
];
const ORACLE_KEYWORDS: [&str; 20] = [
"CONNECT",
"PRIOR",
"START",
"LEVEL",
"NOCYCLE",
"ROWID",
"MINUS",
"PACKAGE",
"BODY",
"SYNONYM",
"TABLESPACE",
"PIPELINED",
"DETERMINISTIC",
"RESULT_CACHE",
"PARALLEL_ENABLE",
"AUTHID",
"DEFINER",
"CURRENT_USER",
"AUTONOMOUS_TRANSACTION",
"SERIALLY_REUSABLE",
];
const PG_FUNCTIONS: [&str; 22] = [
"COALESCE",
"NULLIF",
"GREATEST",
"LEAST",
"NOW",
"CURRENT_DATE",
"CURRENT_TIMESTAMP",
"EXTRACT",
"AGE",
"STRING_AGG",
"ARRAY_AGG",
"JSON_BUILD_OBJECT",
"JSONB_AGG",
"GENERATE_SERIES",
"PG_SLEEP",
"TO_TIMESTAMP",
"TO_CHAR",
"TO_NUMBER",
"ROW_NUMBER",
"RANK",
"DENSE_RANK",
"LAG",
];
const PG_KEYWORDS: [&str; 8] = [
"ILIKE",
"RETURNING",
"LATERAL",
"MATERIALIZED",
"CONCURRENTLY",
"SERIAL",
"BIGSERIAL",
"BOOLEAN",
];
const MYSQL_FUNCTIONS: [&str; 17] = [
"IFNULL",
"IF",
"CONCAT_WS",
"GROUP_CONCAT",
"DATE_FORMAT",
"STR_TO_DATE",
"NOW",
"CURDATE",
"CURTIME",
"JSON_EXTRACT",
"JSON_OBJECT",
"JSON_ARRAY",
"UUID",
"LAST_INSERT_ID",
"ROW_NUMBER",
"RANK",
"DENSE_RANK",
];
const MYSQL_KEYWORDS: [&str; 8] = [
"AUTO_INCREMENT",
"ENGINE",
"CHARSET",
"COLLATE",
"UNSIGNED",
"ENUM",
"SHOW",
"DESCRIBE",
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn oracle_normalizes_to_uppercase() {
let d = OracleDialect;
assert_eq!(d.normalize_identifier("employees"), "EMPLOYEES");
}
#[test]
fn postgres_normalizes_to_lowercase() {
let d = PostgresDialect;
assert_eq!(d.normalize_identifier("EMPLOYEES"), "employees");
}
#[test]
fn mysql_normalizes_to_lowercase() {
let d = MysqlDialect;
assert_eq!(d.normalize_identifier("Employees"), "employees");
}
#[test]
fn dialect_for_factory() {
let d = dialect_for(DatabaseType::Oracle);
assert!(d.has_packages());
assert!(d.has_schemas());
let d = dialect_for(DatabaseType::PostgreSQL);
assert!(!d.has_packages());
assert!(d.has_schemas());
let d = dialect_for(DatabaseType::MySQL);
assert!(!d.has_packages());
assert!(!d.has_schemas());
}
}