1use serde::{Deserialize, Serialize};
8
9use super::RangeSubtype;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct FunctionBinding {
13 pub name: String,
14 pub argument_types: Vec<String>,
15 #[serde(default)]
16 pub builtin: bool,
17 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub dispatch: Option<FunctionDispatch>,
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub invocation: Option<Box<RoutineInvocationBinding>>,
23 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub resolution_error: Option<FunctionResolutionError>,
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub enum FunctionResolutionError {
31 UndefinedFunction { signature: String },
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36pub enum FunctionDispatch {
37 NamedArgument,
38 VariadicArgument,
39 ArraySubscripts,
40 ArraySlices,
41 Subscript,
42 Slice,
43 AnyOperator,
44 AllOperator,
45 IsDistinct,
46 BetweenSymmetric,
47 ToBinInt4,
48 ToBinInt8,
49 ToHexInt4,
50 ToHexInt8,
51 ToOctInt4,
52 ToOctInt8,
53 RandomInt4Range,
54 RandomInt8Range,
55 RandomNumericRange,
56 ArraySortJson,
57 Range {
58 operation: RangeFunctionOperation,
59 subtype: RangeSubtype,
60 multirange: bool,
61 },
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
66pub enum RangeFunctionOperation {
67 Lower,
68 Upper,
69 IsEmpty,
70 LowerInclusive,
71 UpperInclusive,
72 LowerInfinite,
73 UpperInfinite,
74 Merge,
75 Multirange,
76 Overlap,
77 Contains,
78 ContainedBy,
79 Adjacent,
80}
81
82impl FunctionDispatch {
83 #[must_use]
85 pub const fn label(self) -> &'static str {
86 match self {
87 Self::NamedArgument => "named argument",
88 Self::VariadicArgument => "VARIADIC argument",
89 Self::ArraySubscripts | Self::Subscript => "subscript",
90 Self::ArraySlices | Self::Slice => "slice",
91 Self::AnyOperator => "ANY operator",
92 Self::AllOperator => "ALL operator",
93 Self::IsDistinct => "IS DISTINCT FROM",
94 Self::BetweenSymmetric => "BETWEEN SYMMETRIC",
95 Self::ToBinInt4 | Self::ToBinInt8 => "pg_catalog.to_bin",
96 Self::ToHexInt4 | Self::ToHexInt8 => "pg_catalog.to_hex",
97 Self::ToOctInt4 | Self::ToOctInt8 => "pg_catalog.to_oct",
98 Self::RandomInt4Range | Self::RandomInt8Range | Self::RandomNumericRange => {
99 "pg_catalog.random"
100 }
101 Self::ArraySortJson => "pg_catalog.array_sort",
102 Self::Range { operation, .. } => operation.label(),
103 }
104 }
105
106 #[must_use]
107 pub const fn is_call_argument_marker(self) -> bool {
108 matches!(self, Self::NamedArgument | Self::VariadicArgument)
109 }
110
111 #[doc(hidden)]
113 #[must_use]
114 pub fn from_legacy_serialized_name(name: &str) -> Option<Self> {
115 let fixed = match name {
116 "__named_arg" => Self::NamedArgument,
117 "__variadic_arg" => Self::VariadicArgument,
118 "__array_subscripts" => Self::ArraySubscripts,
119 "__array_slices" => Self::ArraySlices,
120 "__subscript" => Self::Subscript,
121 "__slice" => Self::Slice,
122 "__any_op" => Self::AnyOperator,
123 "__all_op" => Self::AllOperator,
124 "__is_distinct" => Self::IsDistinct,
125 "__between_symmetric" => Self::BetweenSymmetric,
126 "__to_bin_int4" => Self::ToBinInt4,
127 "__to_bin_int8" => Self::ToBinInt8,
128 "__to_hex_int4" => Self::ToHexInt4,
129 "__to_hex_int8" => Self::ToHexInt8,
130 "__to_oct_int4" => Self::ToOctInt4,
131 "__to_oct_int8" => Self::ToOctInt8,
132 "__random_int4_range" => Self::RandomInt4Range,
133 "__random_int8_range" => Self::RandomInt8Range,
134 "__random_numeric_range" => Self::RandomNumericRange,
135 "__array_sort_json" => Self::ArraySortJson,
136 _ => return Self::legacy_range_dispatch(name),
137 };
138 Some(fixed)
139 }
140
141 fn legacy_range_dispatch(name: &str) -> Option<Self> {
142 let encoded = name.strip_prefix("__range_")?;
143 let subtypes = [
144 RangeSubtype::Integer,
145 RangeSubtype::BigInteger,
146 RangeSubtype::Numeric,
147 RangeSubtype::Date,
148 RangeSubtype::Timestamp,
149 RangeSubtype::TimestampTz,
150 ];
151 for subtype in subtypes {
152 for (type_name, multirange) in [
153 (subtype.multirange_name(), true),
154 (subtype.range_name(), false),
155 ] {
156 let Some(operation) = encoded.strip_suffix(type_name) else {
157 continue;
158 };
159 let operation = match operation.trim_end_matches('_') {
160 "lower" => RangeFunctionOperation::Lower,
161 "upper" => RangeFunctionOperation::Upper,
162 "isempty" => RangeFunctionOperation::IsEmpty,
163 "lower_inc" => RangeFunctionOperation::LowerInclusive,
164 "upper_inc" => RangeFunctionOperation::UpperInclusive,
165 "lower_inf" => RangeFunctionOperation::LowerInfinite,
166 "upper_inf" => RangeFunctionOperation::UpperInfinite,
167 "merge" => RangeFunctionOperation::Merge,
168 "multirange" => RangeFunctionOperation::Multirange,
169 "overlap" => RangeFunctionOperation::Overlap,
170 "contains" => RangeFunctionOperation::Contains,
171 "contained_by" => RangeFunctionOperation::ContainedBy,
172 "adjacent" => RangeFunctionOperation::Adjacent,
173 _ => continue,
174 };
175 return Some(Self::Range {
176 operation,
177 subtype,
178 multirange,
179 });
180 }
181 }
182 None
183 }
184}
185
186impl RangeFunctionOperation {
187 #[must_use]
188 pub const fn label(self) -> &'static str {
189 match self {
190 Self::Lower => "pg_catalog.lower",
191 Self::Upper => "pg_catalog.upper",
192 Self::IsEmpty => "pg_catalog.isempty",
193 Self::LowerInclusive => "pg_catalog.lower_inc",
194 Self::UpperInclusive => "pg_catalog.upper_inc",
195 Self::LowerInfinite => "pg_catalog.lower_inf",
196 Self::UpperInfinite => "pg_catalog.upper_inf",
197 Self::Merge => "pg_catalog.range_merge",
198 Self::Multirange => "pg_catalog.multirange",
199 Self::Overlap => "range overlap operator",
200 Self::Contains => "range contains operator",
201 Self::ContainedBy => "range contained-by operator",
202 Self::Adjacent => "range adjacent operator",
203 }
204 }
205}
206
207#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209pub struct RoutineInvocationBinding {
210 pub argument_positions: Vec<usize>,
212 pub argument_targets: Vec<String>,
214 pub parameter_types: Vec<String>,
216 pub return_type: Option<String>,
218 pub variadic_mode: RoutineVariadicMode,
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
224pub enum RoutineVariadicMode {
225 #[default]
227 None,
228 Expanded {
230 parameter_index: usize,
232 },
233 Explicit {
235 parameter_index: usize,
237 },
238}
239
240impl FunctionBinding {
241 #[must_use]
243 pub fn polymorphic_builtin_syntax(name: &str) -> Self {
244 assert!(Self::is_polymorphic_builtin_syntax_name(name));
245 Self {
246 name: name.into(),
247 argument_types: Vec::new(),
248 builtin: true,
249 dispatch: None,
250 invocation: None,
251 resolution_error: None,
252 }
253 }
254
255 #[must_use]
257 pub fn dispatched(dispatch: FunctionDispatch) -> Self {
258 Self {
259 name: dispatch.label().into(),
260 argument_types: Vec::new(),
261 builtin: true,
262 dispatch: Some(dispatch),
263 invocation: None,
264 resolution_error: None,
265 }
266 }
267
268 #[must_use]
270 pub fn undefined_function(name: impl Into<String>, signature: impl Into<String>) -> Self {
271 Self {
272 name: name.into(),
273 argument_types: Vec::new(),
274 builtin: false,
275 dispatch: None,
276 invocation: None,
277 resolution_error: Some(FunctionResolutionError::UndefinedFunction {
278 signature: signature.into(),
279 }),
280 }
281 }
282
283 #[doc(hidden)]
285 pub fn upgrade_legacy_serialized_dispatch(
286 display_name: &mut String,
287 binding: &mut Option<Self>,
288 ) -> bool {
289 if binding
290 .as_ref()
291 .is_some_and(|binding| binding.dispatch.is_some() || !binding.builtin)
292 {
293 return false;
294 }
295 let Some(dispatch) = FunctionDispatch::from_legacy_serialized_name(display_name) else {
296 return false;
297 };
298 if let Some(binding) = binding {
299 binding.dispatch = Some(dispatch);
300 display_name.clone_from(&binding.name);
301 } else {
302 let upgraded = Self::dispatched(dispatch);
303 display_name.clone_from(&upgraded.name);
304 *binding = Some(upgraded);
305 }
306 true
307 }
308
309 #[must_use]
311 pub fn is_polymorphic_builtin_syntax(&self) -> bool {
312 self.builtin
313 && self.argument_types.is_empty()
314 && Self::is_polymorphic_builtin_syntax_name(&self.name)
315 }
316
317 #[must_use]
319 pub fn is_polymorphic_builtin_syntax_name(name: &str) -> bool {
320 matches!(name, "coalesce" | "greatest" | "least" | "nullif")
321 }
322}
323
324pub type GeneratedFunctionDependency = FunctionBinding;