Skip to main content

uqa_sql/ast/
enum_functions.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use serde::{Deserialize, Serialize};
8
9/// Operation selected for one call of an `anyenum` support function. The bound call carries the concrete enum type, because `enum_first`, `enum_last` and `enum_range` read only their argument's type.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
11pub enum EnumFunctionOperation {
12    First,
13    Last,
14    Range,
15    BoundedRange,
16    Compare,
17    Equal,
18    NotEqual,
19    Less,
20    Greater,
21    LessEqual,
22    GreaterEqual,
23    Smaller,
24    Larger,
25    Hash,
26    ExtendedHash,
27}
28
29impl EnumFunctionOperation {
30    /// Resolve a local `pg_catalog` routine name and argument count to its operation.
31    #[must_use]
32    pub fn from_call(name: &str, argument_count: usize) -> Option<Self> {
33        let local = match name.split_once('.') {
34            Some((namespace, local)) if namespace.eq_ignore_ascii_case("pg_catalog") => local,
35            Some(_) => return None,
36            None => name,
37        };
38        let operation = [
39            ("enum_first", 1, Self::First),
40            ("enum_last", 1, Self::Last),
41            ("enum_range", 1, Self::Range),
42            ("enum_range", 2, Self::BoundedRange),
43            ("enum_cmp", 2, Self::Compare),
44            ("enum_eq", 2, Self::Equal),
45            ("enum_ne", 2, Self::NotEqual),
46            ("enum_lt", 2, Self::Less),
47            ("enum_gt", 2, Self::Greater),
48            ("enum_le", 2, Self::LessEqual),
49            ("enum_ge", 2, Self::GreaterEqual),
50            ("enum_smaller", 2, Self::Smaller),
51            ("enum_larger", 2, Self::Larger),
52            ("hashenum", 1, Self::Hash),
53            ("hashenumextended", 2, Self::ExtendedHash),
54        ]
55        .into_iter()
56        .find(|(candidate, arity, _)| {
57            *arity == argument_count && local.eq_ignore_ascii_case(candidate)
58        })?
59        .2;
60        Some(operation)
61    }
62
63    /// Whether any overload of this routine name is an enum support function.
64    #[must_use]
65    pub fn is_routine_name(name: &str) -> bool {
66        (1..=2).any(|count| Self::from_call(name, count).is_some())
67    }
68
69    #[must_use]
70    pub const fn label(self) -> &'static str {
71        match self {
72            Self::First => "pg_catalog.enum_first",
73            Self::Last => "pg_catalog.enum_last",
74            Self::Range | Self::BoundedRange => "pg_catalog.enum_range",
75            Self::Compare => "pg_catalog.enum_cmp",
76            Self::Equal => "pg_catalog.enum_eq",
77            Self::NotEqual => "pg_catalog.enum_ne",
78            Self::Less => "pg_catalog.enum_lt",
79            Self::Greater => "pg_catalog.enum_gt",
80            Self::LessEqual => "pg_catalog.enum_le",
81            Self::GreaterEqual => "pg_catalog.enum_ge",
82            Self::Smaller => "pg_catalog.enum_smaller",
83            Self::Larger => "pg_catalog.enum_larger",
84            Self::Hash => "pg_catalog.hashenum",
85            Self::ExtendedHash => "pg_catalog.hashenumextended",
86        }
87    }
88
89    /// `enum_first`, `enum_last` and both `enum_range` forms accept NULL arguments; every other support function is strict.
90    #[must_use]
91    pub const fn is_strict(self) -> bool {
92        !matches!(
93            self,
94            Self::First | Self::Last | Self::Range | Self::BoundedRange
95        )
96    }
97
98    /// The zero-based argument positions declared `anyenum`; `hashenumextended` takes a trailing `int8` seed.
99    #[must_use]
100    pub const fn enum_argument_count(self) -> usize {
101        match self {
102            Self::First | Self::Last | Self::Range | Self::Hash | Self::ExtendedHash => 1,
103            _ => 2,
104        }
105    }
106}