use serde::{Deserialize, Serialize};
use super::RangeSubtype;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FunctionBinding {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub object_id: Option<[u8; 16]>,
pub name: String,
pub argument_types: Vec<String>,
#[serde(default)]
pub builtin: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dispatch: Option<FunctionDispatch>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub invocation: Option<Box<RoutineInvocationBinding>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub resolution_error: Option<FunctionResolutionError>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum FunctionResolutionError {
UndefinedFunction { signature: String },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FunctionDispatch {
NamedArgument,
VariadicArgument,
ArraySubscripts,
ArraySlices,
Subscript,
Slice,
AnyOperator,
AllOperator,
IsDistinct,
BetweenSymmetric,
ToBinInt4,
ToBinInt8,
ToHexInt4,
ToHexInt8,
ToOctInt4,
ToOctInt8,
RandomInt4Range,
RandomInt8Range,
RandomNumericRange,
ArraySortJson,
Range {
operation: RangeFunctionOperation,
subtype: RangeSubtype,
multirange: bool,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RangeFunctionOperation {
Lower,
Upper,
IsEmpty,
LowerInclusive,
UpperInclusive,
LowerInfinite,
UpperInfinite,
Merge,
Multirange,
Overlap,
Contains,
ContainedBy,
Adjacent,
}
impl FunctionDispatch {
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::NamedArgument => "named argument",
Self::VariadicArgument => "VARIADIC argument",
Self::ArraySubscripts | Self::Subscript => "subscript",
Self::ArraySlices | Self::Slice => "slice",
Self::AnyOperator => "ANY operator",
Self::AllOperator => "ALL operator",
Self::IsDistinct => "IS DISTINCT FROM",
Self::BetweenSymmetric => "BETWEEN SYMMETRIC",
Self::ToBinInt4 | Self::ToBinInt8 => "pg_catalog.to_bin",
Self::ToHexInt4 | Self::ToHexInt8 => "pg_catalog.to_hex",
Self::ToOctInt4 | Self::ToOctInt8 => "pg_catalog.to_oct",
Self::RandomInt4Range | Self::RandomInt8Range | Self::RandomNumericRange => {
"pg_catalog.random"
}
Self::ArraySortJson => "pg_catalog.array_sort",
Self::Range { operation, .. } => operation.label(),
}
}
#[must_use]
pub const fn is_call_argument_marker(self) -> bool {
matches!(self, Self::NamedArgument | Self::VariadicArgument)
}
#[doc(hidden)]
#[must_use]
pub fn from_legacy_serialized_name(name: &str) -> Option<Self> {
let fixed = match name {
"__named_arg" => Self::NamedArgument,
"__variadic_arg" => Self::VariadicArgument,
"__array_subscripts" => Self::ArraySubscripts,
"__array_slices" => Self::ArraySlices,
"__subscript" => Self::Subscript,
"__slice" => Self::Slice,
"__any_op" => Self::AnyOperator,
"__all_op" => Self::AllOperator,
"__is_distinct" => Self::IsDistinct,
"__between_symmetric" => Self::BetweenSymmetric,
"__to_bin_int4" => Self::ToBinInt4,
"__to_bin_int8" => Self::ToBinInt8,
"__to_hex_int4" => Self::ToHexInt4,
"__to_hex_int8" => Self::ToHexInt8,
"__to_oct_int4" => Self::ToOctInt4,
"__to_oct_int8" => Self::ToOctInt8,
"__random_int4_range" => Self::RandomInt4Range,
"__random_int8_range" => Self::RandomInt8Range,
"__random_numeric_range" => Self::RandomNumericRange,
"__array_sort_json" => Self::ArraySortJson,
_ => return Self::legacy_range_dispatch(name),
};
Some(fixed)
}
fn legacy_range_dispatch(name: &str) -> Option<Self> {
let encoded = name.strip_prefix("__range_")?;
let subtypes = [
RangeSubtype::Integer,
RangeSubtype::BigInteger,
RangeSubtype::Numeric,
RangeSubtype::Date,
RangeSubtype::Timestamp,
RangeSubtype::TimestampTz,
];
for subtype in subtypes {
for (type_name, multirange) in [
(subtype.multirange_name(), true),
(subtype.range_name(), false),
] {
let Some(operation) = encoded.strip_suffix(type_name) else {
continue;
};
let operation = match operation.trim_end_matches('_') {
"lower" => RangeFunctionOperation::Lower,
"upper" => RangeFunctionOperation::Upper,
"isempty" => RangeFunctionOperation::IsEmpty,
"lower_inc" => RangeFunctionOperation::LowerInclusive,
"upper_inc" => RangeFunctionOperation::UpperInclusive,
"lower_inf" => RangeFunctionOperation::LowerInfinite,
"upper_inf" => RangeFunctionOperation::UpperInfinite,
"merge" => RangeFunctionOperation::Merge,
"multirange" => RangeFunctionOperation::Multirange,
"overlap" => RangeFunctionOperation::Overlap,
"contains" => RangeFunctionOperation::Contains,
"contained_by" => RangeFunctionOperation::ContainedBy,
"adjacent" => RangeFunctionOperation::Adjacent,
_ => continue,
};
return Some(Self::Range {
operation,
subtype,
multirange,
});
}
}
None
}
}
impl RangeFunctionOperation {
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Lower => "pg_catalog.lower",
Self::Upper => "pg_catalog.upper",
Self::IsEmpty => "pg_catalog.isempty",
Self::LowerInclusive => "pg_catalog.lower_inc",
Self::UpperInclusive => "pg_catalog.upper_inc",
Self::LowerInfinite => "pg_catalog.lower_inf",
Self::UpperInfinite => "pg_catalog.upper_inf",
Self::Merge => "pg_catalog.range_merge",
Self::Multirange => "pg_catalog.multirange",
Self::Overlap => "range overlap operator",
Self::Contains => "range contains operator",
Self::ContainedBy => "range contained-by operator",
Self::Adjacent => "range adjacent operator",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoutineInvocationBinding {
pub argument_positions: Vec<usize>,
pub argument_targets: Vec<String>,
pub parameter_types: Vec<String>,
pub return_type: Option<String>,
pub variadic_mode: RoutineVariadicMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum RoutineVariadicMode {
#[default]
None,
Expanded {
parameter_index: usize,
},
Explicit {
parameter_index: usize,
},
}
impl FunctionBinding {
#[must_use]
pub fn polymorphic_builtin_syntax(name: &str) -> Self {
assert!(Self::is_polymorphic_builtin_syntax_name(name));
Self {
object_id: None,
name: name.into(),
argument_types: Vec::new(),
builtin: true,
dispatch: None,
invocation: None,
resolution_error: None,
}
}
#[must_use]
pub fn dispatched(dispatch: FunctionDispatch) -> Self {
Self {
object_id: None,
name: dispatch.label().into(),
argument_types: Vec::new(),
builtin: true,
dispatch: Some(dispatch),
invocation: None,
resolution_error: None,
}
}
#[must_use]
pub fn undefined_function(name: impl Into<String>, signature: impl Into<String>) -> Self {
Self {
object_id: None,
name: name.into(),
argument_types: Vec::new(),
builtin: false,
dispatch: None,
invocation: None,
resolution_error: Some(FunctionResolutionError::UndefinedFunction {
signature: signature.into(),
}),
}
}
#[doc(hidden)]
pub fn upgrade_legacy_serialized_dispatch(
display_name: &mut String,
binding: &mut Option<Self>,
) -> bool {
if binding
.as_ref()
.is_some_and(|binding| binding.dispatch.is_some() || !binding.builtin)
{
return false;
}
let Some(dispatch) = FunctionDispatch::from_legacy_serialized_name(display_name) else {
return false;
};
if let Some(binding) = binding {
binding.dispatch = Some(dispatch);
display_name.clone_from(&binding.name);
} else {
let upgraded = Self::dispatched(dispatch);
display_name.clone_from(&upgraded.name);
*binding = Some(upgraded);
}
true
}
#[must_use]
pub fn is_polymorphic_builtin_syntax(&self) -> bool {
self.builtin
&& self.argument_types.is_empty()
&& Self::is_polymorphic_builtin_syntax_name(&self.name)
}
#[must_use]
pub fn is_polymorphic_builtin_syntax_name(name: &str) -> bool {
matches!(name, "coalesce" | "greatest" | "least" | "nullif")
}
}
pub type GeneratedFunctionDependency = FunctionBinding;