use crate::data_type::DataType;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RoutineKind {
Procedure,
ScalarFunction,
TableFunction,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RoutineSecurity {
#[default]
Caller,
Owner,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ParameterDirection {
#[default]
In,
Out,
InOut,
Variadic,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoutineParameter {
pub name: String,
pub data_type: String,
pub declared_type: Option<DataType>,
pub direction: ParameterDirection,
pub default: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoutineDeclaration {
pub name: String,
pub data_type: String,
pub declared_type: Option<DataType>,
pub default: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RoutineColumn {
pub name: String,
pub data_type: String,
pub declared_type: Option<DataType>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RoutineReturns {
Nothing,
Value(String),
Columns(Vec<RoutineColumn>),
RowsOf(String),
Inferred,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RoutineBody {
Expression(String),
Query(String),
Statements(Vec<String>),
Block(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct RoutineSpec {
pub kind: RoutineKind,
pub schema: Option<String>,
pub name: String,
pub or_replace: bool,
pub parameters: Vec<RoutineParameter>,
pub declarations: Vec<RoutineDeclaration>,
pub returns: RoutineReturns,
pub security: RoutineSecurity,
pub body: RoutineBody,
pub comment: Option<String>,
}
impl RoutineSpec {
#[must_use]
pub fn new(kind: RoutineKind, name: impl Into<String>) -> Self {
Self {
kind,
schema: None,
name: name.into(),
or_replace: false,
parameters: Vec::new(),
declarations: Vec::new(),
returns: match kind {
RoutineKind::Procedure => RoutineReturns::Nothing,
RoutineKind::ScalarFunction => RoutineReturns::Value(String::new()),
RoutineKind::TableFunction => RoutineReturns::Columns(Vec::new()),
},
security: RoutineSecurity::Caller,
body: RoutineBody::Statements(Vec::new()),
comment: None,
}
}
#[must_use]
pub fn qualified_name(&self, quote: impl Fn(&str) -> String) -> String {
match &self.schema {
Some(schema) => format!("{}.{}", quote(schema), quote(&self.name)),
None => quote(&self.name),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_new_spec_returns_what_its_kind_returns() {
assert_eq!(
RoutineSpec::new(RoutineKind::Procedure, "load_customer").returns,
RoutineReturns::Nothing
);
assert_eq!(
RoutineSpec::new(RoutineKind::ScalarFunction, "generate_sid").returns,
RoutineReturns::Value(String::new())
);
assert_eq!(
RoutineSpec::new(RoutineKind::TableFunction, "customer_at").returns,
RoutineReturns::Columns(Vec::new())
);
}
#[test]
fn the_qualified_name_quotes_with_the_engine_and_omits_an_absent_schema() {
let quote = |name: &str| format!("\"{name}\"");
let mut spec = RoutineSpec::new(RoutineKind::Procedure, "load_customer");
assert_eq!(spec.qualified_name(quote), "\"load_customer\"");
spec.schema = Some("retail_core".to_owned());
assert_eq!(spec.qualified_name(quote), "\"retail_core\".\"load_customer\"");
}
}