ormdantic-dialects 2.0.1

SQL dialect support for Ormdantic
Documentation
use ormdantic_dialects::{
    Dialect, DialectKind, MariaDbDialect, MsSqlDialect, MySqlDialect, OracleDialect,
    PostgresDialect, ReflectionQuery, ReflectionQueryKind, ReflectionScope, SqliteDialect,
};

struct DefaultReflectionDialect;

impl Dialect for DefaultReflectionDialect {
    fn kind(&self) -> DialectKind {
        DialectKind::Sqlite
    }

    fn name(&self) -> &'static str {
        "default-reflection"
    }

    fn quote_ident(&self, ident: &str) -> String {
        format!("\"{ident}\"")
    }

    fn placeholder(&self, _index: usize) -> String {
        "?".to_string()
    }

    fn supports_returning(&self) -> bool {
        false
    }

    fn supports_native_uuid(&self) -> bool {
        false
    }

    fn supports_json(&self) -> bool {
        false
    }
}

#[test]
fn renders_postgres_catalog_queries_without_scope() {
    let queries = PostgresDialect.reflection_queries(&ReflectionScope::new());
    let kinds = queries.iter().map(|query| query.kind()).collect::<Vec<_>>();

    assert_eq!(
        kinds,
        vec![
            ReflectionQueryKind::Tables,
            ReflectionQueryKind::Columns,
            ReflectionQueryKind::Indexes,
            ReflectionQueryKind::ForeignKeys,
            ReflectionQueryKind::Constraints,
        ]
    );
    assert!(queries[0].sql().contains("information_schema.tables"));
    assert!(queries[1].sql().contains("information_schema.columns"));
    assert!(queries[2].sql().contains("pg_indexes"));
    assert!(queries[3].sql().contains("FOREIGN KEY"));
    assert!(queries[4]
        .sql()
        .contains("information_schema.table_constraints"));
}

#[test]
fn renders_schema_and_table_scoped_postgres_queries() {
    let scope = ReflectionScope::new()
        .schema("pub'lic")
        .tables(vec!["flavor".to_string(), "supplier".to_string()]);
    let queries = PostgresDialect.reflection_queries(&scope);

    assert_eq!(scope.schema_name(), Some("pub'lic"));
    assert_eq!(scope.table_names(), ["flavor", "supplier"]);
    assert!(queries[0].sql().contains("table_schema = 'pub''lic'"));
    assert!(queries[0]
        .sql()
        .contains("table_name IN ('flavor', 'supplier')"));
}

#[test]
fn renders_sqlite_catalog_queries() {
    let queries = SqliteDialect
        .reflection_queries(&ReflectionScope::new().tables(vec!["flavor".to_string()]));

    assert_eq!(
        queries.iter().map(|query| query.kind()).collect::<Vec<_>>(),
        vec![
            ReflectionQueryKind::Tables,
            ReflectionQueryKind::Columns,
            ReflectionQueryKind::Indexes,
            ReflectionQueryKind::ForeignKeys,
            ReflectionQueryKind::Constraints,
        ]
    );
    assert!(queries[0].sql().contains("sqlite_master"));
    assert!(queries[1].sql().contains("pragma_table_xinfo"));
    assert!(queries[2].sql().contains("pragma_index_list"));
    assert!(queries[3].sql().contains("pragma_foreign_key_list"));
    assert!(queries[0].sql().contains("m.name IN ('flavor')"));
}

#[test]
fn renders_mysql_and_mariadb_catalog_queries_with_default_database_scope() {
    let mysql = MySqlDialect.reflection_queries(&ReflectionScope::new());
    let mariadb = MariaDbDialect.reflection_queries(&ReflectionScope::new());

    assert!(mysql[0].sql().contains("table_schema = DATABASE()"));
    assert!(mysql[2].sql().contains("information_schema.statistics"));
    assert!(mysql[3].sql().contains("referential_constraints"));
    assert_eq!(
        mysql.iter().map(|query| query.kind()).collect::<Vec<_>>(),
        mariadb.iter().map(|query| query.kind()).collect::<Vec<_>>()
    );
}

#[test]
fn renders_mssql_catalog_queries_with_scoped_filters() {
    let queries = MsSqlDialect.reflection_queries(
        &ReflectionScope::new()
            .schema("dbo")
            .tables(vec!["flavor".to_string(), "supplier".to_string()]),
    );

    assert_eq!(
        queries.iter().map(|query| query.kind()).collect::<Vec<_>>(),
        vec![
            ReflectionQueryKind::Tables,
            ReflectionQueryKind::Columns,
            ReflectionQueryKind::Indexes,
            ReflectionQueryKind::ForeignKeys,
            ReflectionQueryKind::Constraints,
        ]
    );
    assert!(queries[0].sql().contains("INFORMATION_SCHEMA.TABLES"));
    assert!(queries[0].sql().contains("TABLE_SCHEMA = 'dbo'"));
    assert!(queries[0]
        .sql()
        .contains("TABLE_NAME IN ('flavor', 'supplier')"));
    assert!(queries[2].sql().contains("sys.indexes"));
    assert!(queries[3].sql().contains("REFERENTIAL_CONSTRAINTS"));
}

#[test]
fn renders_oracle_catalog_queries_without_scope() {
    let queries = OracleDialect.reflection_queries(&ReflectionScope::new());

    assert_eq!(
        queries.iter().map(|query| query.kind()).collect::<Vec<_>>(),
        vec![
            ReflectionQueryKind::Tables,
            ReflectionQueryKind::Columns,
            ReflectionQueryKind::Indexes,
            ReflectionQueryKind::ForeignKeys,
            ReflectionQueryKind::Constraints,
        ]
    );
    assert!(queries[0].sql().contains("FROM user_tables"));
    assert!(queries[1].sql().contains("FROM user_tab_columns"));
    assert!(queries[2].sql().contains("FROM user_indexes"));
    assert!(queries[3].sql().contains("FROM user_constraints c"));
    assert!(queries[4].sql().contains("FROM user_constraints"));
}

#[test]
fn renders_schema_scoped_oracle_catalog_queries() {
    let queries = OracleDialect.reflection_queries(&ReflectionScope::new().schema("app"));

    assert!(queries[0].sql().contains("FROM all_tables"));
    assert!(queries[0].sql().contains("owner = 'APP'"));
    assert!(queries[1].sql().contains("FROM all_tab_columns"));
    assert!(queries[3].sql().contains("c.owner = 'APP'"));
}

#[test]
fn reflection_query_exposes_kind_and_sql() {
    let query = ReflectionQuery::new(ReflectionQueryKind::ForeignKeys, "SELECT 1");

    assert_eq!(query.kind(), ReflectionQueryKind::ForeignKeys);
    assert_eq!(query.sql(), "SELECT 1");
}

#[test]
fn default_reflection_queries_escape_scoped_schema_and_tables() {
    let scope = ReflectionScope::new()
        .schema("app's")
        .tables(vec!["flavor".to_string(), "sup'plier".to_string()]);
    let queries = DefaultReflectionDialect.reflection_queries(&scope);
    let unscoped = DefaultReflectionDialect.reflection_queries(&ReflectionScope::new());

    assert_eq!(
        queries.iter().map(|query| query.kind()).collect::<Vec<_>>(),
        vec![
            ReflectionQueryKind::Tables,
            ReflectionQueryKind::Columns,
            ReflectionQueryKind::Constraints,
        ]
    );
    assert!(queries[0].sql().contains("table_schema = 'app''s'"));
    assert!(queries[0]
        .sql()
        .contains("table_name IN ('flavor', 'sup''plier')"));
    assert!(queries[1].sql().contains("information_schema.columns"));
    assert!(queries[2]
        .sql()
        .contains("information_schema.table_constraints"));
    assert!(!unscoped[0].sql().contains(" WHERE "));
}