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; 53] = [
"NVL",
"NVL2",
"DECODE",
"TO_CHAR",
"TO_DATE",
"TO_NUMBER",
"TO_TIMESTAMP",
"TO_CLOB",
"SUBSTR",
"INSTR",
"INITCAP",
"LPAD",
"RPAD",
"TRANSLATE",
"TRUNC",
"SIGN",
"POWER",
"SQRT",
"SYSDATE",
"SYSTIMESTAMP",
"ADD_MONTHS",
"MONTHS_BETWEEN",
"LAST_DAY",
"NEXT_DAY",
"EXTRACT",
"NUMTODSINTERVAL",
"NUMTOYMINTERVAL",
"LISTAGG",
"MEDIAN",
"PERCENTILE_CONT",
"PERCENTILE_DISC",
"CUME_DIST",
"PERCENT_RANK",
"REGEXP_LIKE",
"REGEXP_REPLACE",
"REGEXP_SUBSTR",
"REGEXP_COUNT",
"REGEXP_INSTR",
"SYS_CONNECT_BY_PATH",
"JSON_VALUE",
"JSON_QUERY",
"JSON_TABLE",
"JSON_OBJECT",
"JSON_ARRAY",
"XMLELEMENT",
"XMLAGG",
"XMLFOREST",
"GREATEST",
"LEAST",
"EMPTY_CLOB",
"EMPTY_BLOB",
"ROWNUM",
"ROWID",
];
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; 44] = [
"GREATEST",
"LEAST",
"NOW",
"CURRENT_DATE",
"CURRENT_TIMESTAMP",
"CLOCK_TIMESTAMP",
"EXTRACT",
"AGE",
"DATE_TRUNC",
"DATE_PART",
"MAKE_DATE",
"MAKE_INTERVAL",
"TO_TIMESTAMP",
"TO_CHAR",
"TO_NUMBER",
"TO_DATE",
"STRING_AGG",
"INITCAP",
"LEFT",
"RIGHT",
"LPAD",
"RPAD",
"SPLIT_PART",
"REGEXP_REPLACE",
"REGEXP_MATCHES",
"ARRAY_AGG",
"ARRAY_LENGTH",
"UNNEST",
"JSON_BUILD_OBJECT",
"JSON_BUILD_ARRAY",
"JSONB_AGG",
"JSONB_EACH",
"JSONB_EXTRACT_PATH_TEXT",
"JSONB_PRETTY",
"ROW_TO_JSON",
"TO_JSONB",
"BOOL_AND",
"BOOL_OR",
"PERCENTILE_CONT",
"PERCENTILE_DISC",
"GENERATE_SERIES",
"PG_SLEEP",
"TO_TSVECTOR",
"TO_TSQUERY",
];
const PG_KEYWORDS: [&str; 8] = [
"ILIKE",
"RETURNING",
"LATERAL",
"MATERIALIZED",
"CONCURRENTLY",
"SERIAL",
"BIGSERIAL",
"BOOLEAN",
];
const MYSQL_FUNCTIONS: [&str; 38] = [
"IFNULL",
"IF",
"GREATEST",
"LEAST",
"ELT",
"FIELD",
"CONCAT_WS",
"GROUP_CONCAT",
"LPAD",
"RPAD",
"LEFT",
"RIGHT",
"LOCATE",
"INSTR",
"REVERSE",
"REGEXP_REPLACE",
"REGEXP_LIKE",
"DATE_FORMAT",
"STR_TO_DATE",
"DATE_ADD",
"DATE_SUB",
"DATEDIFF",
"TIMESTAMPDIFF",
"NOW",
"CURDATE",
"CURTIME",
"YEAR",
"MONTH",
"DAY",
"EXTRACT",
"JSON_EXTRACT",
"JSON_OBJECT",
"JSON_ARRAY",
"JSON_UNQUOTE",
"JSON_CONTAINS",
"UUID",
"LAST_INSERT_ID",
"SHA2",
];
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());
}
}