use crate::model::*;
use crate::pg::sqlgen::strip_ident_quotes;
use crate::util::{Result, SchemaError};
use sqlparser::ast::{
ArgMode as SqlArgMode, CreateFunctionBody, DataType, FunctionBehavior,
FunctionDefinitionSetParam, FunctionSecurity, FunctionSetValue, Ident, OperateFunctionArg,
};
use crate::util::strip_dollar_quotes;
#[allow(clippy::too_many_arguments)]
pub(super) fn parse_create_function(
schema: &str,
name: &str,
args: Option<&[OperateFunctionArg]>,
return_type: Option<&DataType>,
function_body: Option<&CreateFunctionBody>,
language: Option<&Ident>,
behavior: Option<&FunctionBehavior>,
security: Option<&FunctionSecurity>,
set_params: &[FunctionDefinitionSetParam],
) -> Result<Function> {
let return_type_str = return_type
.map(|rt| normalize_pg_type(&rt.to_string()))
.ok_or_else(|| {
SchemaError::ParseError(format!(
"Function {schema}.{name} is missing RETURNS clause"
))
})?;
let language_str = language
.map(|l| l.to_string().to_lowercase())
.unwrap_or_else(|| "sql".to_string());
let body = function_body
.map(|fb| match fb {
CreateFunctionBody::AsBeforeOptions { body, .. } => body.to_string(),
CreateFunctionBody::AsAfterOptions(expr) => expr.to_string(),
CreateFunctionBody::Return(expr) => expr.to_string(),
CreateFunctionBody::AsBeginEnd(stmts) => stmts.to_string(),
CreateFunctionBody::AsReturnExpr(expr) => expr.to_string(),
CreateFunctionBody::AsReturnSelect(sel) => sel.to_string(),
})
.map(|b| strip_dollar_quotes(&b).trim().to_string())
.ok_or_else(|| {
SchemaError::ParseError(format!("Function {schema}.{name} is missing body"))
})?;
let volatility = behavior
.map(|b| match b {
FunctionBehavior::Immutable => Volatility::Immutable,
FunctionBehavior::Stable => Volatility::Stable,
FunctionBehavior::Volatile => Volatility::Volatile,
})
.unwrap_or_default();
let security_type = security
.map(|s| match s {
FunctionSecurity::Definer => SecurityType::Definer,
FunctionSecurity::Invoker => SecurityType::Invoker,
})
.unwrap_or_default();
let arguments: Vec<FunctionArg> = args
.map(|arg_list| {
arg_list
.iter()
.map(|arg| {
let mode = match arg.mode {
Some(SqlArgMode::In) => ArgMode::In,
Some(SqlArgMode::Out) => ArgMode::Out,
Some(SqlArgMode::InOut) => ArgMode::InOut,
None => ArgMode::In,
};
FunctionArg {
name: arg.name.as_ref().map(|n| strip_ident_quotes(&n.value)),
data_type: normalize_pg_type(&arg.data_type.to_string()),
mode,
default: arg.default_expr.as_ref().map(|e| e.to_string()),
}
})
.collect()
})
.unwrap_or_default();
let config_params: Vec<(String, String)> = set_params
.iter()
.map(|param| {
let key = param.name.to_string().to_lowercase();
let value = match ¶m.value {
FunctionSetValue::Values(exprs) => exprs
.iter()
.map(|e| e.to_string())
.collect::<Vec<_>>()
.join(", "),
FunctionSetValue::FromCurrent => "FROM CURRENT".to_string(),
};
(key, value)
})
.collect();
Ok(Function {
schema: schema.to_string(),
name: name.to_string(),
arguments,
return_type: return_type_str,
language: language_str,
body,
volatility,
security: security_type,
config_params,
owner: None,
grants: Vec::new(),
comment: None,
})
}