1use serde::{Deserialize, Serialize};
8
9use super::{EnumFunctionOperation, 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 Operator(Box<OperatorResolutionError>),
36}
37
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct OperatorResolutionError {
40 pub sqlstate: String,
41 pub message: String,
42}
43
44impl FunctionResolutionError {
45 #[must_use]
46 pub fn sql_error(&self) -> crate::SQLError {
47 match self {
48 Self::UndefinedFunction { signature } => {
49 crate::SQLError::undefined_function_call(signature)
50 }
51 Self::Operator(error) => crate::SQLError::Routine {
52 sqlstate: error.sqlstate.clone(),
53 message: error.message.clone(),
54 },
55 }
56 }
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61pub enum FunctionDispatch {
62 NumericOperator(NumericOperator),
63 NamedArgument,
64 VariadicArgument,
65 ArraySubscripts,
66 ArraySlices,
67 Subscript,
68 Slice,
69 FieldSelect,
71 AnyOperator,
72 AllOperator,
73 IsDistinct,
74 BetweenSymmetric,
75 ToBinInt4,
76 ToBinInt8,
77 ToHexInt4,
78 ToHexInt8,
79 ToOctInt4,
80 ToOctInt8,
81 RandomInt4Range,
82 RandomInt8Range,
83 RandomNumericRange,
84 ArraySortJson,
85 ArrayConcat,
87 ArrayAppend,
88 ArrayPrepend,
89 JsonExtract {
90 as_text: bool,
91 path: bool,
92 },
93 Range {
94 operation: RangeFunctionOperation,
95 subtype: RangeSubtype,
96 multirange: bool,
97 },
98 Enum {
100 operation: EnumFunctionOperation,
101 type_oid: u32,
102 },
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
107pub enum RangeFunctionOperation {
108 Lower,
109 Upper,
110 IsEmpty,
111 LowerInclusive,
112 UpperInclusive,
113 LowerInfinite,
114 UpperInfinite,
115 Merge,
116 Multirange,
117 Overlap,
118 Contains,
119 ContainedBy,
120 Adjacent,
121}
122
123impl FunctionDispatch {
124 #[must_use]
126 pub const fn label(self) -> &'static str {
127 match self {
128 Self::NumericOperator(operator) => operator.symbol(),
129 Self::NamedArgument => "named argument",
130 Self::VariadicArgument => "VARIADIC argument",
131 Self::ArraySubscripts | Self::Subscript => "subscript",
132 Self::ArraySlices | Self::Slice => "slice",
133 Self::FieldSelect => "field selection",
134 Self::AnyOperator => "ANY operator",
135 Self::AllOperator => "ALL operator",
136 Self::IsDistinct => "IS DISTINCT FROM",
137 Self::BetweenSymmetric => "BETWEEN SYMMETRIC",
138 Self::ToBinInt4 | Self::ToBinInt8 => "pg_catalog.to_bin",
139 Self::ToHexInt4 | Self::ToHexInt8 => "pg_catalog.to_hex",
140 Self::ToOctInt4 | Self::ToOctInt8 => "pg_catalog.to_oct",
141 Self::RandomInt4Range | Self::RandomInt8Range | Self::RandomNumericRange => {
142 "pg_catalog.random"
143 }
144 Self::ArraySortJson => "pg_catalog.array_sort",
145 Self::ArrayConcat => "pg_catalog.array_cat",
146 Self::ArrayAppend => "pg_catalog.array_append",
147 Self::ArrayPrepend => "pg_catalog.array_prepend",
148 Self::JsonExtract { as_text: false, .. } => "JSON extraction operator",
149 Self::JsonExtract { as_text: true, .. } => "JSON text extraction operator",
150 Self::Range { operation, .. } => operation.label(),
151 Self::Enum { operation, .. } => operation.label(),
152 }
153 }
154
155 #[must_use]
156 pub const fn is_call_argument_marker(self) -> bool {
157 matches!(self, Self::NamedArgument | Self::VariadicArgument)
158 }
159
160 #[doc(hidden)]
162 #[must_use]
163 pub fn from_legacy_serialized_name(name: &str) -> Option<Self> {
164 let fixed = match name {
165 "__named_arg" => Self::NamedArgument,
166 "__variadic_arg" => Self::VariadicArgument,
167 "__array_subscripts" => Self::ArraySubscripts,
168 "__array_slices" => Self::ArraySlices,
169 "__subscript" => Self::Subscript,
170 "__slice" => Self::Slice,
171 "__any_op" => Self::AnyOperator,
172 "__all_op" => Self::AllOperator,
173 "__is_distinct" => Self::IsDistinct,
174 "__between_symmetric" => Self::BetweenSymmetric,
175 "__to_bin_int4" => Self::ToBinInt4,
176 "__to_bin_int8" => Self::ToBinInt8,
177 "__to_hex_int4" => Self::ToHexInt4,
178 "__to_hex_int8" => Self::ToHexInt8,
179 "__to_oct_int4" => Self::ToOctInt4,
180 "__to_oct_int8" => Self::ToOctInt8,
181 "__random_int4_range" => Self::RandomInt4Range,
182 "__random_int8_range" => Self::RandomInt8Range,
183 "__random_numeric_range" => Self::RandomNumericRange,
184 "__array_sort_json" => Self::ArraySortJson,
185 _ => return Self::legacy_range_dispatch(name),
186 };
187 Some(fixed)
188 }
189
190 fn legacy_range_dispatch(name: &str) -> Option<Self> {
191 let encoded = name.strip_prefix("__range_")?;
192 let subtypes = [
193 RangeSubtype::Integer,
194 RangeSubtype::BigInteger,
195 RangeSubtype::Numeric,
196 RangeSubtype::Date,
197 RangeSubtype::Timestamp,
198 RangeSubtype::TimestampTz,
199 ];
200 for subtype in subtypes {
201 for (type_name, multirange) in [
202 (subtype.multirange_name(), true),
203 (subtype.range_name(), false),
204 ] {
205 let Some(operation) = encoded.strip_suffix(type_name) else {
206 continue;
207 };
208 let operation = match operation.trim_end_matches('_') {
209 "lower" => RangeFunctionOperation::Lower,
210 "upper" => RangeFunctionOperation::Upper,
211 "isempty" => RangeFunctionOperation::IsEmpty,
212 "lower_inc" => RangeFunctionOperation::LowerInclusive,
213 "upper_inc" => RangeFunctionOperation::UpperInclusive,
214 "lower_inf" => RangeFunctionOperation::LowerInfinite,
215 "upper_inf" => RangeFunctionOperation::UpperInfinite,
216 "merge" => RangeFunctionOperation::Merge,
217 "multirange" => RangeFunctionOperation::Multirange,
218 "overlap" => RangeFunctionOperation::Overlap,
219 "contains" => RangeFunctionOperation::Contains,
220 "contained_by" => RangeFunctionOperation::ContainedBy,
221 "adjacent" => RangeFunctionOperation::Adjacent,
222 _ => continue,
223 };
224 return Some(Self::Range {
225 operation,
226 subtype,
227 multirange,
228 });
229 }
230 }
231 None
232 }
233}
234
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
237pub enum NumericOperator {
238 Modulo,
239 Power,
240 Plus,
241 SquareRoot,
242 CubeRoot,
243 Absolute,
244}
245
246impl NumericOperator {
247 #[must_use]
248 pub const fn symbol(self) -> &'static str {
249 match self {
250 Self::Modulo => "%",
251 Self::Power => "^",
252 Self::Plus => "+",
253 Self::SquareRoot => "|/",
254 Self::CubeRoot => "||/",
255 Self::Absolute => "@",
256 }
257 }
258
259 #[must_use]
260 pub const fn arity(self) -> usize {
261 match self {
262 Self::Modulo | Self::Power => 2,
263 Self::Plus | Self::SquareRoot | Self::CubeRoot | Self::Absolute => 1,
264 }
265 }
266}
267
268impl RangeFunctionOperation {
269 #[must_use]
270 pub const fn label(self) -> &'static str {
271 match self {
272 Self::Lower => "pg_catalog.lower",
273 Self::Upper => "pg_catalog.upper",
274 Self::IsEmpty => "pg_catalog.isempty",
275 Self::LowerInclusive => "pg_catalog.lower_inc",
276 Self::UpperInclusive => "pg_catalog.upper_inc",
277 Self::LowerInfinite => "pg_catalog.lower_inf",
278 Self::UpperInfinite => "pg_catalog.upper_inf",
279 Self::Merge => "pg_catalog.range_merge",
280 Self::Multirange => "pg_catalog.multirange",
281 Self::Overlap => "range overlap operator",
282 Self::Contains => "range contains operator",
283 Self::ContainedBy => "range contained-by operator",
284 Self::Adjacent => "range adjacent operator",
285 }
286 }
287}
288
289#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
291pub struct RoutineInvocationBinding {
292 pub argument_positions: Vec<usize>,
294 pub argument_targets: Vec<String>,
296 #[serde(default, skip_serializing_if = "Vec::is_empty")]
298 pub argument_sources: Vec<Option<String>>,
299 pub parameter_types: Vec<String>,
301 pub return_type: Option<String>,
303 pub variadic_mode: RoutineVariadicMode,
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
309pub enum RoutineVariadicMode {
310 #[default]
312 None,
313 Expanded {
315 parameter_index: usize,
317 },
318 Explicit {
320 parameter_index: usize,
322 },
323}
324
325impl FunctionBinding {
326 #[must_use]
328 pub fn polymorphic_builtin_syntax(name: &str) -> Self {
329 assert!(Self::is_polymorphic_builtin_syntax_name(name));
330 Self {
331 object_id: None,
332 name: name.into(),
333 argument_types: Vec::new(),
334 builtin: true,
335 dispatch: None,
336 invocation: None,
337 resolution_error: None,
338 }
339 }
340
341 #[must_use]
343 pub fn dispatched(dispatch: FunctionDispatch) -> Self {
344 Self::dispatched_with_control(
345 dispatch,
346 &uqa_core::memory::ProductionControl::uncontrolled(),
347 )
348 .expect("ordinary dispatch constructor cannot be cancelled or limited")
349 .into_uncontrolled()
350 .expect("ordinary dispatch owner")
351 }
352
353 pub fn dispatched_with_control(
354 dispatch: FunctionDispatch,
355 control: &uqa_core::memory::ProductionControl<'_>,
356 ) -> Result<uqa_core::memory::Produced<Self>, uqa_core::ValueRetentionError> {
357 let (name, memory) = control.copy_text(dispatch.label())?.into_parts();
358 control.finish(
359 Self {
360 object_id: None,
361 name,
362 argument_types: Vec::new(),
363 builtin: true,
364 dispatch: Some(dispatch),
365 invocation: None,
366 resolution_error: None,
367 },
368 memory,
369 )
370 }
371
372 #[must_use]
374 pub fn undefined_function(name: impl Into<String>, signature: impl Into<String>) -> Self {
375 Self {
376 object_id: None,
377 name: name.into(),
378 argument_types: Vec::new(),
379 builtin: false,
380 dispatch: None,
381 invocation: None,
382 resolution_error: Some(FunctionResolutionError::UndefinedFunction {
383 signature: signature.into(),
384 }),
385 }
386 }
387
388 #[doc(hidden)]
390 pub fn upgrade_legacy_serialized_dispatch(
391 display_name: &mut String,
392 binding: &mut Option<Self>,
393 ) -> bool {
394 if binding
395 .as_ref()
396 .is_some_and(|binding| binding.dispatch.is_some() || !binding.builtin)
397 {
398 return false;
399 }
400 let Some(dispatch) = FunctionDispatch::from_legacy_serialized_name(display_name) else {
401 return false;
402 };
403 if let Some(binding) = binding {
404 binding.dispatch = Some(dispatch);
405 display_name.clone_from(&binding.name);
406 } else {
407 let upgraded = Self::dispatched(dispatch);
408 display_name.clone_from(&upgraded.name);
409 *binding = Some(upgraded);
410 }
411 true
412 }
413
414 #[must_use]
416 pub fn is_polymorphic_builtin_syntax(&self) -> bool {
417 self.builtin
418 && self.argument_types.is_empty()
419 && Self::is_polymorphic_builtin_syntax_name(&self.name)
420 }
421
422 #[must_use]
424 pub fn is_polymorphic_builtin_syntax_name(name: &str) -> bool {
425 matches!(name, "coalesce" | "greatest" | "least" | "nullif")
426 }
427}
428
429pub type GeneratedFunctionDependency = FunctionBinding;