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    /// Ordering support calls retain the actual enum type selected on their first slow comparison.
31    #[must_use]
32    pub const fn uses_comparison_state(self) -> bool {
33        matches!(
34            self,
35            Self::Compare
36                | Self::Less
37                | Self::Greater
38                | Self::LessEqual
39                | Self::GreaterEqual
40                | Self::Smaller
41                | Self::Larger
42        )
43    }
44
45    /// Resolve a local `pg_catalog` routine name and argument count to its operation.
46    #[must_use]
47    pub fn from_call(name: &str, argument_count: usize) -> Option<Self> {
48        let local = match name.split_once('.') {
49            Some((namespace, local)) if namespace.eq_ignore_ascii_case("pg_catalog") => local,
50            Some(_) => return None,
51            None => name,
52        };
53        let operation = [
54            ("enum_first", 1, Self::First),
55            ("enum_last", 1, Self::Last),
56            ("enum_range", 1, Self::Range),
57            ("enum_range", 2, Self::BoundedRange),
58            ("enum_cmp", 2, Self::Compare),
59            ("enum_eq", 2, Self::Equal),
60            ("enum_ne", 2, Self::NotEqual),
61            ("enum_lt", 2, Self::Less),
62            ("enum_gt", 2, Self::Greater),
63            ("enum_le", 2, Self::LessEqual),
64            ("enum_ge", 2, Self::GreaterEqual),
65            ("enum_smaller", 2, Self::Smaller),
66            ("enum_larger", 2, Self::Larger),
67            ("hashenum", 1, Self::Hash),
68            ("hashenumextended", 2, Self::ExtendedHash),
69        ]
70        .into_iter()
71        .find(|(candidate, arity, _)| {
72            *arity == argument_count && local.eq_ignore_ascii_case(candidate)
73        })?
74        .2;
75        Some(operation)
76    }
77
78    /// Whether any overload of this routine name is an enum support function.
79    #[must_use]
80    pub fn is_routine_name(name: &str) -> bool {
81        (1..=2).any(|count| Self::from_call(name, count).is_some())
82    }
83
84    #[must_use]
85    pub const fn label(self) -> &'static str {
86        match self {
87            Self::First => "pg_catalog.enum_first",
88            Self::Last => "pg_catalog.enum_last",
89            Self::Range | Self::BoundedRange => "pg_catalog.enum_range",
90            Self::Compare => "pg_catalog.enum_cmp",
91            Self::Equal => "pg_catalog.enum_eq",
92            Self::NotEqual => "pg_catalog.enum_ne",
93            Self::Less => "pg_catalog.enum_lt",
94            Self::Greater => "pg_catalog.enum_gt",
95            Self::LessEqual => "pg_catalog.enum_le",
96            Self::GreaterEqual => "pg_catalog.enum_ge",
97            Self::Smaller => "pg_catalog.enum_smaller",
98            Self::Larger => "pg_catalog.enum_larger",
99            Self::Hash => "pg_catalog.hashenum",
100            Self::ExtendedHash => "pg_catalog.hashenumextended",
101        }
102    }
103
104    /// `enum_first`, `enum_last` and both `enum_range` forms accept NULL arguments; every other support function is strict.
105    #[must_use]
106    pub const fn is_strict(self) -> bool {
107        !matches!(
108            self,
109            Self::First | Self::Last | Self::Range | Self::BoundedRange
110        )
111    }
112
113    /// The zero-based argument positions declared `anyenum`; `hashenumextended` takes a trailing `int8` seed.
114    #[must_use]
115    pub const fn enum_argument_count(self) -> usize {
116        match self {
117            Self::First | Self::Last | Self::Range | Self::Hash | Self::ExtendedHash => 1,
118            _ => 2,
119        }
120    }
121}