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