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 composite_field: Option<Box<CompositeFieldBinding>>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub resolution_error: Option<Box<FunctionResolutionError>>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct CompositeFieldBinding {
37 pub type_oid: u32,
38 pub number: i16,
39 pub result_type: super::ColumnType,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub collation_oid: Option<u32>,
43 pub dropped: bool,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
46 pub changed_type: Option<super::ColumnType>,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
51pub enum FunctionResolutionError {
52 UndefinedFunction { signature: String },
53 Operator(Box<OperatorResolutionError>),
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57pub struct OperatorResolutionError {
58 pub sqlstate: String,
59 pub message: String,
60}
61
62impl FunctionResolutionError {
63 #[must_use]
64 pub fn sql_error(&self) -> crate::SQLError {
65 match self {
66 Self::UndefinedFunction { signature } => {
67 crate::SQLError::undefined_function_call(signature)
68 }
69 Self::Operator(error) => crate::SQLError::Routine {
70 sqlstate: error.sqlstate.clone(),
71 message: error.message.clone(),
72 },
73 }
74 }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
79pub enum FunctionDispatch {
80 NumericOperator(NumericOperator),
81 Pattern {
83 operator: super::PatternOperator,
84 oid: Option<std::num::NonZeroU32>,
85 },
86 PatternEscape {
88 similar: bool,
89 },
90 BooleanTest(BooleanTest),
92 Collate {
94 oid: Option<u32>,
95 },
96 CollationFor {
98 collation_oid: u32,
99 noncollatable_type_oid: Option<u32>,
100 },
101 NamedArgument,
102 VariadicArgument,
103 ArraySubscripts,
104 ArraySlices,
105 ArraySlicesWithBounds(super::ArraySliceBounds),
107 Subscript,
108 Slice,
109 FieldSelect,
111 AnyOperator,
112 AllOperator,
113 IsDistinct,
114 ValueIsDistinct,
116 ValueNullTest {
118 negated: bool,
119 },
120 BetweenSymmetric,
121 ToBinInt4,
122 ToBinInt8,
123 ToHexInt4,
124 ToHexInt8,
125 ToOctInt4,
126 ToOctInt8,
127 RandomInt4Range,
128 RandomInt8Range,
129 RandomNumericRange,
130 ArraySortJson,
131 Concat {
133 oid: Option<u32>,
134 },
135 ArrayConcat,
137 ArrayAppend,
138 ArrayPrepend,
139 JsonPath {
141 as_text: bool,
142 jsonb: bool,
143 },
144 JsonExtract {
145 as_text: bool,
146 path: bool,
147 },
148 Range {
149 operation: RangeFunctionOperation,
150 subtype: RangeSubtype,
151 multirange: bool,
152 },
153 Enum {
155 operation: EnumFunctionOperation,
156 type_oid: u32,
157 },
158 MergeAction,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
164pub enum BooleanTest {
165 IsTrue,
166 IsNotTrue,
167 IsFalse,
168 IsNotFalse,
169 IsUnknown,
170 IsNotUnknown,
171}
172
173impl BooleanTest {
174 #[must_use]
175 pub const fn label(self) -> &'static str {
176 match self {
177 Self::IsTrue => "IS TRUE",
178 Self::IsNotTrue => "IS NOT TRUE",
179 Self::IsFalse => "IS FALSE",
180 Self::IsNotFalse => "IS NOT FALSE",
181 Self::IsUnknown => "IS UNKNOWN",
182 Self::IsNotUnknown => "IS NOT UNKNOWN",
183 }
184 }
185
186 #[must_use]
188 pub const fn catalog_code(self) -> i64 {
189 match self {
190 Self::IsTrue => 0,
191 Self::IsNotTrue => 1,
192 Self::IsFalse => 2,
193 Self::IsNotFalse => 3,
194 Self::IsUnknown => 4,
195 Self::IsNotUnknown => 5,
196 }
197 }
198
199 #[must_use]
200 pub const fn from_catalog_code(code: i64) -> Option<Self> {
201 match code {
202 0 => Some(Self::IsTrue),
203 1 => Some(Self::IsNotTrue),
204 2 => Some(Self::IsFalse),
205 3 => Some(Self::IsNotFalse),
206 4 => Some(Self::IsUnknown),
207 5 => Some(Self::IsNotUnknown),
208 _ => None,
209 }
210 }
211}
212
213#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
215pub enum RangeFunctionOperation {
216 Lower,
217 Upper,
218 IsEmpty,
219 LowerInclusive,
220 UpperInclusive,
221 LowerInfinite,
222 UpperInfinite,
223 Merge,
224 Multirange,
225 Overlap,
226 Contains,
227 ContainedBy,
228 ContainsElement,
230 ElementContainedBy,
232 Adjacent,
233}
234
235impl FunctionDispatch {
236 #[must_use]
238 pub const fn is_concatenation(self) -> bool {
239 matches!(
240 self,
241 Self::Concat { .. } | Self::ArrayConcat | Self::ArrayAppend | Self::ArrayPrepend
242 )
243 }
244
245 #[must_use]
247 pub const fn label(self) -> &'static str {
248 match self {
249 Self::MergeAction => "merge_action",
250 Self::NumericOperator(operator) => operator.symbol(),
251 Self::Pattern { operator, .. } => operator.syntax(),
252 Self::PatternEscape { similar: false } => "pg_catalog.like_escape",
253 Self::PatternEscape { similar: true } => "pg_catalog.similar_to_escape",
254 Self::BooleanTest(test) => test.label(),
255 Self::Collate { .. } => "COLLATE",
256 Self::CollationFor { .. } => "pg_catalog.pg_collation_for",
257 Self::NamedArgument => "named argument",
258 Self::VariadicArgument => "VARIADIC argument",
259 Self::ArraySubscripts | Self::Subscript => "subscript",
260 Self::ArraySlices | Self::ArraySlicesWithBounds(_) | Self::Slice => "slice",
261 Self::FieldSelect => "field selection",
262 Self::AnyOperator => "ANY operator",
263 Self::AllOperator => "ALL operator",
264 Self::IsDistinct | Self::ValueIsDistinct => "IS DISTINCT FROM",
265 Self::ValueNullTest { negated: false } => "IS NULL",
266 Self::ValueNullTest { negated: true } => "IS NOT NULL",
267 Self::BetweenSymmetric => "BETWEEN SYMMETRIC",
268 Self::ToBinInt4 | Self::ToBinInt8 => "pg_catalog.to_bin",
269 Self::ToHexInt4 | Self::ToHexInt8 => "pg_catalog.to_hex",
270 Self::ToOctInt4 | Self::ToOctInt8 => "pg_catalog.to_oct",
271 Self::RandomInt4Range | Self::RandomInt8Range | Self::RandomNumericRange => {
272 "pg_catalog.random"
273 }
274 Self::ArraySortJson => "pg_catalog.array_sort",
275 Self::Concat { .. } => "||",
276 Self::ArrayConcat => "pg_catalog.array_cat",
277 Self::ArrayAppend => "pg_catalog.array_append",
278 Self::ArrayPrepend => "pg_catalog.array_prepend",
279 Self::JsonPath {
280 as_text: false,
281 jsonb: false,
282 } => "pg_catalog.json_extract_path",
283 Self::JsonPath {
284 as_text: true,
285 jsonb: false,
286 } => "pg_catalog.json_extract_path_text",
287 Self::JsonPath {
288 as_text: false,
289 jsonb: true,
290 } => "pg_catalog.jsonb_extract_path",
291 Self::JsonPath {
292 as_text: true,
293 jsonb: true,
294 } => "pg_catalog.jsonb_extract_path_text",
295 Self::JsonExtract { as_text: false, .. } => "JSON extraction operator",
296 Self::JsonExtract { as_text: true, .. } => "JSON text extraction operator",
297 Self::Range { operation, .. } => operation.label(),
298 Self::Enum { operation, .. } => operation.label(),
299 }
300 }
301
302 #[must_use]
303 pub const fn is_call_argument_marker(self) -> bool {
304 matches!(self, Self::NamedArgument | Self::VariadicArgument)
305 }
306
307 #[must_use]
309 pub const fn json_extraction_symbol(self) -> Option<&'static str> {
310 match self {
311 Self::JsonExtract {
312 as_text: false,
313 path: false,
314 } => Some("->"),
315 Self::JsonExtract {
316 as_text: true,
317 path: false,
318 } => Some("->>"),
319 Self::JsonExtract {
320 as_text: false,
321 path: true,
322 } => Some("#>"),
323 Self::JsonExtract {
324 as_text: true,
325 path: true,
326 } => Some("#>>"),
327 _ => None,
328 }
329 }
330
331 #[doc(hidden)]
333 #[must_use]
334 pub fn from_legacy_serialized_name(name: &str) -> Option<Self> {
335 let fixed = match name {
336 "merge_action" => Self::MergeAction,
337 "__named_arg" => Self::NamedArgument,
338 "__variadic_arg" => Self::VariadicArgument,
339 "__array_subscripts" => Self::ArraySubscripts,
340 "__array_slices" => Self::ArraySlices,
341 "__subscript" => Self::Subscript,
342 "__slice" => Self::Slice,
343 "__any_op" => Self::AnyOperator,
344 "__all_op" => Self::AllOperator,
345 "__is_distinct" => Self::IsDistinct,
346 "__between_symmetric" => Self::BetweenSymmetric,
347 "__to_bin_int4" => Self::ToBinInt4,
348 "__to_bin_int8" => Self::ToBinInt8,
349 "__to_hex_int4" => Self::ToHexInt4,
350 "__to_hex_int8" => Self::ToHexInt8,
351 "__to_oct_int4" => Self::ToOctInt4,
352 "__to_oct_int8" => Self::ToOctInt8,
353 "__random_int4_range" => Self::RandomInt4Range,
354 "__random_int8_range" => Self::RandomInt8Range,
355 "__random_numeric_range" => Self::RandomNumericRange,
356 "__array_sort_json" => Self::ArraySortJson,
357 _ => return Self::legacy_range_dispatch(name),
358 };
359 Some(fixed)
360 }
361
362 fn legacy_range_dispatch(name: &str) -> Option<Self> {
363 let encoded = name.strip_prefix("__range_")?;
364 let subtypes = [
365 RangeSubtype::Integer,
366 RangeSubtype::BigInteger,
367 RangeSubtype::Numeric,
368 RangeSubtype::Date,
369 RangeSubtype::Timestamp,
370 RangeSubtype::TimestampTz,
371 ];
372 for subtype in subtypes {
373 for (type_name, multirange) in [
374 (subtype.multirange_name(), true),
375 (subtype.range_name(), false),
376 ] {
377 let Some(operation) = encoded.strip_suffix(type_name) else {
378 continue;
379 };
380 let operation = match operation.trim_end_matches('_') {
381 "lower" => RangeFunctionOperation::Lower,
382 "upper" => RangeFunctionOperation::Upper,
383 "isempty" => RangeFunctionOperation::IsEmpty,
384 "lower_inc" => RangeFunctionOperation::LowerInclusive,
385 "upper_inc" => RangeFunctionOperation::UpperInclusive,
386 "lower_inf" => RangeFunctionOperation::LowerInfinite,
387 "upper_inf" => RangeFunctionOperation::UpperInfinite,
388 "merge" => RangeFunctionOperation::Merge,
389 "multirange" => RangeFunctionOperation::Multirange,
390 "overlap" => RangeFunctionOperation::Overlap,
391 "contains" => RangeFunctionOperation::Contains,
392 "contained_by" => RangeFunctionOperation::ContainedBy,
393 "adjacent" => RangeFunctionOperation::Adjacent,
394 _ => continue,
395 };
396 return Some(Self::Range {
397 operation,
398 subtype,
399 multirange,
400 });
401 }
402 }
403 None
404 }
405}
406
407#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
409pub enum NumericOperator {
410 Modulo,
411 Power,
412 Plus,
413 SquareRoot,
414 CubeRoot,
415 Absolute,
416}
417
418impl NumericOperator {
419 #[must_use]
420 pub const fn symbol(self) -> &'static str {
421 match self {
422 Self::Modulo => "%",
423 Self::Power => "^",
424 Self::Plus => "+",
425 Self::SquareRoot => "|/",
426 Self::CubeRoot => "||/",
427 Self::Absolute => "@",
428 }
429 }
430
431 #[must_use]
432 pub const fn arity(self) -> usize {
433 match self {
434 Self::Modulo | Self::Power => 2,
435 Self::Plus | Self::SquareRoot | Self::CubeRoot | Self::Absolute => 1,
436 }
437 }
438}
439
440impl RangeFunctionOperation {
441 #[must_use]
442 pub const fn label(self) -> &'static str {
443 match self {
444 Self::Lower => "pg_catalog.lower",
445 Self::Upper => "pg_catalog.upper",
446 Self::IsEmpty => "pg_catalog.isempty",
447 Self::LowerInclusive => "pg_catalog.lower_inc",
448 Self::UpperInclusive => "pg_catalog.upper_inc",
449 Self::LowerInfinite => "pg_catalog.lower_inf",
450 Self::UpperInfinite => "pg_catalog.upper_inf",
451 Self::Merge => "pg_catalog.range_merge",
452 Self::Multirange => "pg_catalog.multirange",
453 Self::Overlap => "range overlap operator",
454 Self::Contains => "range contains operator",
455 Self::ContainedBy => "range contained-by operator",
456 Self::ContainsElement => "range contains element operator",
457 Self::ElementContainedBy => "element contained-by range operator",
458 Self::Adjacent => "range adjacent operator",
459 }
460 }
461}
462
463#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
465pub struct RoutineInvocationBinding {
466 pub argument_positions: Vec<usize>,
468 pub argument_targets: Vec<String>,
470 #[serde(default, skip_serializing_if = "Vec::is_empty")]
472 pub argument_sources: Vec<Option<String>>,
473 pub parameter_types: Vec<String>,
475 pub return_type: Option<String>,
477 pub variadic_mode: RoutineVariadicMode,
479}
480
481#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
483pub enum RoutineVariadicMode {
484 #[default]
486 None,
487 Expanded {
489 parameter_index: usize,
491 },
492 Explicit {
494 parameter_index: usize,
496 },
497}
498
499impl FunctionBinding {
500 #[doc(hidden)]
502 pub fn upgrade_legacy_array_slices(
503 binding: &mut Option<Self>,
504 arguments: usize,
505 is_bare_null: impl Fn(usize) -> bool,
506 ) -> bool {
507 let Some(binding) = binding
508 .as_mut()
509 .filter(|binding| binding.dispatch == Some(FunctionDispatch::ArraySlices))
510 else {
511 return false;
512 };
513 let Some(Ok(access)) = super::ArrayAccess::from_dispatch(
514 FunctionDispatch::ArraySlices,
515 arguments,
516 is_bare_null,
517 ) else {
518 return false;
519 };
520 binding.dispatch = Some(FunctionDispatch::ArraySlicesWithBounds(
521 access.slice_bounds().expect("slice bounds"),
522 ));
523 true
524 }
525
526 #[must_use]
528 pub fn polymorphic_builtin_syntax(name: &str) -> Self {
529 assert!(Self::is_polymorphic_builtin_syntax_name(name));
530 Self {
531 object_id: None,
532 name: name.into(),
533 argument_types: Vec::new(),
534 builtin: true,
535 dispatch: None,
536 invocation: None,
537 composite_field: None,
538 resolution_error: None,
539 }
540 }
541
542 #[must_use]
544 pub fn dispatched(dispatch: FunctionDispatch) -> Self {
545 Self::dispatched_with_control(
546 dispatch,
547 &uqa_core::memory::ProductionControl::uncontrolled(),
548 )
549 .expect("ordinary dispatch constructor cannot be cancelled or limited")
550 .into_uncontrolled()
551 .expect("ordinary dispatch owner")
552 }
553
554 pub fn dispatched_with_control(
555 dispatch: FunctionDispatch,
556 control: &uqa_core::memory::ProductionControl<'_>,
557 ) -> Result<uqa_core::memory::Produced<Self>, uqa_core::ValueRetentionError> {
558 let (name, memory) = control.copy_text(dispatch.label())?.into_parts();
559 control.finish(
560 Self {
561 object_id: None,
562 name,
563 argument_types: Vec::new(),
564 builtin: true,
565 dispatch: Some(dispatch),
566 invocation: None,
567 composite_field: None,
568 resolution_error: None,
569 },
570 memory,
571 )
572 }
573
574 #[must_use]
576 pub fn undefined_function(name: impl Into<String>, signature: impl Into<String>) -> Self {
577 Self {
578 object_id: None,
579 name: name.into(),
580 argument_types: Vec::new(),
581 builtin: false,
582 dispatch: None,
583 invocation: None,
584 composite_field: None,
585 resolution_error: Some(Box::new(FunctionResolutionError::UndefinedFunction {
586 signature: signature.into(),
587 })),
588 }
589 }
590
591 #[doc(hidden)]
593 pub fn upgrade_legacy_serialized_dispatch(
594 display_name: &mut String,
595 binding: &mut Option<Self>,
596 ) -> bool {
597 if binding
598 .as_ref()
599 .is_some_and(|binding| binding.dispatch.is_some() || !binding.builtin)
600 {
601 return false;
602 }
603 let Some(dispatch) = FunctionDispatch::from_legacy_serialized_name(display_name) else {
604 return false;
605 };
606 if let Some(binding) = binding {
607 binding.dispatch = Some(dispatch);
608 display_name.clone_from(&binding.name);
609 } else {
610 let upgraded = Self::dispatched(dispatch);
611 display_name.clone_from(&upgraded.name);
612 *binding = Some(upgraded);
613 }
614 true
615 }
616
617 #[must_use]
619 pub fn is_polymorphic_builtin_syntax(&self) -> bool {
620 self.builtin
621 && self.argument_types.is_empty()
622 && Self::is_polymorphic_builtin_syntax_name(&self.name)
623 }
624
625 #[must_use]
627 pub fn is_polymorphic_builtin_syntax_name(name: &str) -> bool {
628 matches!(name, "coalesce" | "greatest" | "least" | "nullif")
629 }
630}
631
632pub type GeneratedFunctionDependency = FunctionBinding;