Skip to main content

linkrs_query/executor/expression/functions/builtin/
sequence.rs

1use crate::executor::expression::ExpressionError;
2use linkrs_core::value::NullType;
3use linkrs_core::Value;
4/// Sequence function enumeration
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum SequenceFunction {
7    /// curr_val(seq_name) - get current value without incrementing
8    CurrVal,
9    /// next_val(seq_name) - get next value with atomic increment
10    NextVal,
11}
12
13impl SequenceFunction {
14    pub fn name(&self) -> &str {
15        match self {
16            Self::CurrVal => "curr_val",
17            Self::NextVal => "next_val",
18        }
19    }
20
21    pub fn arity(&self) -> usize {
22        1
23    }
24
25    pub fn is_variadic(&self) -> bool {
26        false
27    }
28
29    pub fn description(&self) -> &str {
30        match self {
31            Self::CurrVal => "Get the current value of a sequence without incrementing",
32            Self::NextVal => "Get the next value of a sequence with atomic increment",
33        }
34    }
35
36    pub fn execute(&self, args: &[Value]) -> Result<Value, ExpressionError> {
37        if !self.is_variadic() && args.len() != self.arity() {
38            return Err(ExpressionError::invalid_arity(
39                self.name(),
40                self.arity(),
41                args.len(),
42            ));
43        }
44
45        let name = match &args[0] {
46            Value::String(s) => s.as_str(),
47            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
48            _ => {
49                return Err(ExpressionError::type_error(format!(
50                    "{} requires a string sequence name",
51                    self.name()
52                )))
53            }
54        };
55
56        // Sequence functions require a SequenceManager from the execution context.
57        // When called without context (e.g. during planning or constant folding),
58        // return an error indicating the function needs execution context.
59        Err(ExpressionError::function_error(format!(
60            "{}('{}') requires execution context with SequenceManager; \
61             use within a query execution context",
62            self.name(),
63            name
64        )))
65    }
66
67    /// Execute against a sequence catalog manager.
68    ///
69    /// Arity and argument validation mirror [`Self::execute`]; a missing
70    /// manager produces the same missing-context error so callers see one
71    /// consistent message whether the function ran bare or unthreaded.
72    pub fn execute_with_manager(
73        &self,
74        args: &[Value],
75        manager: Option<&std::sync::Arc<linkrs_core::metadata::SequenceManager>>,
76    ) -> Result<Value, ExpressionError> {
77        if !self.is_variadic() && args.len() != self.arity() {
78            return Err(ExpressionError::invalid_arity(
79                self.name(),
80                self.arity(),
81                args.len(),
82            ));
83        }
84
85        let name = match &args[0] {
86            Value::String(s) => s.as_str(),
87            Value::Null(_) => return Ok(Value::Null(NullType::Null)),
88            _ => {
89                return Err(ExpressionError::type_error(format!(
90                    "{} requires a string sequence name",
91                    self.name()
92                )))
93            }
94        };
95
96        let manager = manager.ok_or_else(|| {
97            ExpressionError::function_error(format!(
98                "{}('{}') requires execution context with SequenceManager; \
99                 use within a query execution context",
100                self.name(),
101                name
102            ))
103        })?;
104        let value = match self {
105            Self::CurrVal => manager
106                .current_value(name)
107                .map_err(|e| ExpressionError::function_error(e.to_string()))?,
108            Self::NextVal => manager
109                .next_value(name)
110                .map_err(|e| ExpressionError::function_error(e.to_string()))?,
111        };
112        Ok(Value::BigInt(value))
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_curr_val_null_input() {
122        let result = SequenceFunction::CurrVal.execute(&[Value::Null(NullType::Null)]);
123        assert!(result.is_ok());
124        assert!(matches!(result.unwrap(), Value::Null(NullType::Null)));
125    }
126
127    #[test]
128    fn test_next_val_null_input() {
129        let result = SequenceFunction::NextVal.execute(&[Value::Null(NullType::Null)]);
130        assert!(result.is_ok());
131        assert!(matches!(result.unwrap(), Value::Null(NullType::Null)));
132    }
133
134    #[test]
135    fn test_curr_val_wrong_arg_count() {
136        let result = SequenceFunction::CurrVal.execute(&[]);
137        assert!(result.is_err());
138    }
139
140    #[test]
141    fn test_next_val_wrong_arg_type() {
142        let result = SequenceFunction::NextVal.execute(&[Value::Int(42)]);
143        assert!(result.is_err());
144    }
145
146    #[test]
147    fn test_curr_val_needs_context() {
148        let result = SequenceFunction::CurrVal.execute(&[Value::string("seq1")]);
149        assert!(result.is_err());
150        let err = result.unwrap_err();
151        assert!(err.to_string().contains("requires execution context"));
152    }
153
154    #[test]
155    fn test_next_val_needs_context() {
156        let result = SequenceFunction::NextVal.execute(&[Value::string("seq1")]);
157        assert!(result.is_err());
158        let err = result.unwrap_err();
159        assert!(err.to_string().contains("requires execution context"));
160    }
161
162    #[test]
163    fn test_execute_with_manager_next_and_curr() {
164        use std::sync::Arc;
165
166        let manager = Arc::new(linkrs_core::metadata::SequenceManager::new());
167        manager
168            .create_sequence("seq1".into(), 1, 2, 1, 100, false)
169            .expect("create sequence");
170        let name = Value::string("seq1");
171
172        let curr = SequenceFunction::CurrVal
173            .execute_with_manager(std::slice::from_ref(&name), Some(&manager))
174            .expect("curr_val reads start");
175        assert_eq!(curr, Value::BigInt(1));
176
177        let next = SequenceFunction::NextVal
178            .execute_with_manager(std::slice::from_ref(&name), Some(&manager))
179            .expect("next_val steps");
180        assert_eq!(next, Value::BigInt(3));
181
182        let curr = SequenceFunction::CurrVal
183            .execute_with_manager(std::slice::from_ref(&name), Some(&manager))
184            .expect("curr_val follows next");
185        assert_eq!(curr, Value::BigInt(3));
186    }
187
188    #[test]
189    fn test_execute_with_manager_missing_sequence_errors() {
190        use std::sync::Arc;
191
192        let manager = Arc::new(linkrs_core::metadata::SequenceManager::new());
193        let name = Value::string("ghost");
194        let err = SequenceFunction::NextVal
195            .execute_with_manager(std::slice::from_ref(&name), Some(&manager))
196            .expect_err("missing sequence must fail");
197        assert!(err.to_string().contains("does not exist"), "{err}");
198    }
199
200    #[test]
201    fn test_execute_with_manager_without_manager_matches_bare_error() {
202        let name = Value::string("seq1");
203        let err = SequenceFunction::CurrVal
204            .execute_with_manager(std::slice::from_ref(&name), None)
205            .expect_err("missing manager must fail");
206        assert!(
207            err.to_string().contains("requires execution context"),
208            "{err}"
209        );
210    }
211}