linkrs_query/executor/expression/functions/builtin/
sequence.rs1use crate::executor::expression::ExpressionError;
2use linkrs_core::value::NullType;
3use linkrs_core::Value;
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum SequenceFunction {
7 CurrVal,
9 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 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 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}