#![forbid(unsafe_code)]
#[cfg(feature = "dialect-clickhouse")]
mod clickhouse;
#[cfg(feature = "dialect-duckdb")]
mod duckdb;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FunctionNameCase {
#[default]
Insensitive,
Sensitive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FunctionSignature {
pub min_arity: usize,
pub max_arity: Option<usize>,
}
impl FunctionSignature {
pub const fn exact(arity: usize) -> Self {
Self {
min_arity: arity,
max_arity: Some(arity),
}
}
pub const fn range(min_arity: usize, max_arity: usize) -> Self {
Self {
min_arity,
max_arity: Some(max_arity),
}
}
pub const fn variadic(min_arity: usize) -> Self {
Self {
min_arity,
max_arity: None,
}
}
}
pub trait CatalogSink {
fn set_dialect_name_case(&mut self, dialect: &'static str, name_case: FunctionNameCase);
fn set_function_name_case(
&mut self,
dialect: &'static str,
function_name: &str,
name_case: FunctionNameCase,
);
fn register(
&mut self,
dialect: &'static str,
function_name: &str,
signatures: Vec<FunctionSignature>,
);
}
#[allow(unused_variables)]
pub fn register_enabled_catalogs<S: CatalogSink>(sink: &mut S) {
#[cfg(feature = "dialect-clickhouse")]
clickhouse::register(sink);
#[cfg(feature = "dialect-duckdb")]
duckdb::register(sink);
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[derive(Default)]
struct TestSink {
dialect_name_case: HashMap<&'static str, FunctionNameCase>,
entries: HashMap<&'static str, HashMap<String, Vec<FunctionSignature>>>,
}
impl CatalogSink for TestSink {
fn set_dialect_name_case(&mut self, dialect: &'static str, name_case: FunctionNameCase) {
self.dialect_name_case.insert(dialect, name_case);
}
fn set_function_name_case(
&mut self,
_dialect: &'static str,
_function_name: &str,
_name_case: FunctionNameCase,
) {
}
fn register(
&mut self,
dialect: &'static str,
function_name: &str,
signatures: Vec<FunctionSignature>,
) {
self.entries
.entry(dialect)
.or_default()
.insert(function_name.to_string(), signatures);
}
}
#[cfg(feature = "dialect-clickhouse")]
#[test]
fn clickhouse_catalog_exposes_common_functions() {
let mut sink = TestSink::default();
register_enabled_catalogs(&mut sink);
assert_eq!(
sink.dialect_name_case.get("clickhouse"),
Some(&FunctionNameCase::Insensitive)
);
let if_signatures = sink
.entries
.get("clickhouse")
.and_then(|entries| entries.get("if"))
.expect("expected clickhouse function 'if'");
assert!(if_signatures
.iter()
.any(|sig| sig.min_arity == 3 && sig.max_arity == Some(3)));
}
#[cfg(feature = "dialect-duckdb")]
#[test]
fn duckdb_catalog_exposes_common_functions() {
let mut sink = TestSink::default();
register_enabled_catalogs(&mut sink);
assert_eq!(
sink.dialect_name_case.get("duckdb"),
Some(&FunctionNameCase::Insensitive)
);
let abs_signatures = sink
.entries
.get("duckdb")
.and_then(|entries| entries.get("abs"))
.expect("expected duckdb function 'abs'");
assert!(abs_signatures
.iter()
.any(|sig| sig.min_arity == 1 && sig.max_arity == Some(1)));
}
}