Skip to main content

reifydb_sub_flow/operator/stateful/
raw.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_codec::{
5	encoded::row::EncodedRow,
6	key::encoded::{EncodedKey, EncodedKeyRange},
7};
8use reifydb_value::Result;
9
10use super::{StateIterator, utils};
11use crate::{Operator, transaction::FlowTransaction};
12
13pub trait RawStatefulOperator: Operator {
14	fn state_get(&self, txn: &mut FlowTransaction, key: &EncodedKey) -> Result<Option<EncodedRow>> {
15		utils::state_get(self.id(), txn, key)
16	}
17
18	fn state_set(&self, txn: &mut FlowTransaction, key: &EncodedKey, value: EncodedRow) -> Result<()> {
19		utils::state_set(self.id(), txn, key, value)
20	}
21
22	fn state_remove(&self, txn: &mut FlowTransaction, key: &EncodedKey) -> Result<()> {
23		utils::state_remove(self.id(), txn, key)
24	}
25
26	fn state_drop(&self, txn: &mut FlowTransaction, key: &EncodedKey) -> Result<()> {
27		utils::state_drop(self.id(), txn, key)
28	}
29
30	fn state_scan_all(&self, txn: &mut FlowTransaction) -> Result<Vec<(EncodedKey, EncodedRow)>> {
31		utils::state_scan_all(self.id(), txn)
32	}
33
34	fn state_range<'a>(&self, txn: &'a mut FlowTransaction, range: EncodedKeyRange) -> StateIterator<'a> {
35		utils::state_range(self.id(), txn, range)
36	}
37
38	fn state_clear(&self, txn: &mut FlowTransaction) -> Result<()> {
39		utils::state_clear(self.id(), txn)
40	}
41
42	fn internal_state_get(&self, txn: &mut FlowTransaction, key: &EncodedKey) -> Result<Option<EncodedRow>> {
43		utils::internal_state_get(self.id(), txn, key)
44	}
45
46	fn internal_state_set(&self, txn: &mut FlowTransaction, key: &EncodedKey, value: EncodedRow) -> Result<()> {
47		utils::internal_state_set(self.id(), txn, key, value)
48	}
49
50	fn internal_state_remove(&self, txn: &mut FlowTransaction, key: &EncodedKey) -> Result<()> {
51		utils::internal_state_remove(self.id(), txn, key)
52	}
53
54	fn internal_state_drop(&self, txn: &mut FlowTransaction, key: &EncodedKey) -> Result<()> {
55		utils::internal_state_drop(self.id(), txn, key)
56	}
57}
58
59#[cfg(test)]
60pub mod tests {
61	use std::ops::Bound::{Excluded, Included};
62
63	use reifydb_catalog::catalog::Catalog;
64	use reifydb_core::{common::CommitVersion, interface::catalog::flow::FlowNodeId};
65	use reifydb_runtime::context::clock::{Clock, MockClock};
66	use reifydb_transaction::interceptor::interceptors::Interceptors;
67	use reifydb_value::util::cowvec::CowVec;
68
69	use super::*;
70	use crate::{operator::stateful::test_utils::test::*, transaction::FlowTransaction};
71
72	impl RawStatefulOperator for TestOperator {}
73
74	#[test]
75	fn test_simple_state_get_set() {
76		let mut txn = create_test_transaction();
77		let mut txn = FlowTransaction::deferred(
78			&mut txn,
79			CommitVersion(1),
80			Catalog::testing(),
81			Interceptors::new(),
82			Clock::Mock(MockClock::from_millis(1000)),
83		);
84		let operator = TestOperator::simple(FlowNodeId(1));
85		let key = test_key("simple_test");
86		let value = test_row();
87
88		// Initially should be None
89		assert!(operator.state_get(&mut txn, &key).unwrap().is_none());
90
91		// Set and verify
92		operator.state_set(&mut txn, &key, value.clone()).unwrap();
93		let result = operator.state_get(&mut txn, &key).unwrap();
94		assert!(result.is_some());
95		assert_row_eq(&result.unwrap(), &value);
96	}
97
98	#[test]
99	fn test_simple_state_remove() {
100		let mut txn = create_test_transaction();
101		let mut txn = FlowTransaction::deferred(
102			&mut txn,
103			CommitVersion(1),
104			Catalog::testing(),
105			Interceptors::new(),
106			Clock::Mock(MockClock::from_millis(1000)),
107		);
108		let operator = TestOperator::simple(FlowNodeId(1));
109		let key = test_key("remove_test");
110		let value = test_row();
111
112		// Set, verify, remove, verify
113		operator.state_set(&mut txn, &key, value).unwrap();
114		assert!(operator.state_get(&mut txn, &key).unwrap().is_some());
115
116		operator.state_remove(&mut txn, &key).unwrap();
117		assert!(operator.state_get(&mut txn, &key).unwrap().is_none());
118	}
119
120	#[test]
121	fn test_simple_state_scan_all() {
122		let mut txn = create_test_transaction();
123		let mut txn = FlowTransaction::deferred(
124			&mut txn,
125			CommitVersion(1),
126			Catalog::testing(),
127			Interceptors::new(),
128			Clock::Mock(MockClock::from_millis(1000)),
129		);
130		let operator = TestOperator::simple(FlowNodeId(1));
131
132		// Add multiple entries
133		let entries = vec![("key_a", vec![1, 2]), ("key_b", vec![3, 4]), ("key_c", vec![5, 6])];
134		for (key_suffix, data) in &entries {
135			let key = test_key(key_suffix);
136			let value = EncodedRow(CowVec::new(data.clone()));
137			operator.state_set(&mut txn, &key, value).unwrap();
138		}
139
140		// Scan and verify count
141		let scanned: Vec<_> = operator.state_scan_all(&mut txn).unwrap();
142		assert_eq!(scanned.len(), 3);
143	}
144
145	#[test]
146	fn test_simple_state_range() {
147		let mut txn = create_test_transaction();
148		let mut txn = FlowTransaction::deferred(
149			&mut txn,
150			CommitVersion(1),
151			Catalog::testing(),
152			Interceptors::new(),
153			Clock::Mock(MockClock::from_millis(1000)),
154		);
155		let operator = TestOperator::simple(FlowNodeId(2));
156
157		// Add ordered entries
158		for i in 0..10 {
159			let key = test_key(&format!("{:02}", i)); // Ensures lexical ordering
160			let value = EncodedRow(CowVec::new(vec![i as u8]));
161			operator.state_set(&mut txn, &key, value).unwrap();
162		}
163
164		let range = EncodedKeyRange::new(Included(test_key("02")), Excluded(test_key("05")));
165		let range_result: Vec<_> = operator.state_range(&mut txn, range).collect::<Result<Vec<_>>>().unwrap();
166
167		// Should get keys 02, 03, 04 (not 05 as end is exclusive)
168		assert_eq!(range_result.len(), 3);
169		assert_eq!(range_result[0].1.as_slice()[0], 2);
170		assert_eq!(range_result[1].1.as_slice()[0], 3);
171		assert_eq!(range_result[2].1.as_slice()[0], 4);
172	}
173
174	#[test]
175	fn test_simple_state_clear() {
176		let mut txn = create_test_transaction();
177		let mut txn = FlowTransaction::deferred(
178			&mut txn,
179			CommitVersion(1),
180			Catalog::testing(),
181			Interceptors::new(),
182			Clock::Mock(MockClock::from_millis(1000)),
183		);
184		let operator = TestOperator::simple(FlowNodeId(3));
185
186		// Add multiple entries
187		for i in 0..5 {
188			let key = test_key(&format!("clear_{}", i));
189			let value = EncodedRow(CowVec::new(vec![i as u8]));
190			operator.state_set(&mut txn, &key, value).unwrap();
191		}
192
193		// Verify entries exist
194		let count = operator.state_scan_all(&mut txn).unwrap().len();
195		assert_eq!(count, 5);
196
197		// Clear all
198		operator.state_clear(&mut txn).unwrap();
199
200		// Verify all cleared
201		let count = operator.state_scan_all(&mut txn).unwrap().len();
202		assert_eq!(count, 0);
203	}
204
205	#[test]
206	fn test_operator_isolation() {
207		let mut txn = create_test_transaction();
208		let mut txn = FlowTransaction::deferred(
209			&mut txn,
210			CommitVersion(1),
211			Catalog::testing(),
212			Interceptors::new(),
213			Clock::Mock(MockClock::from_millis(1000)),
214		);
215		let operator1 = TestOperator::simple(FlowNodeId(10));
216		let operator2 = TestOperator::simple(FlowNodeId(20));
217		let shared_key = test_key("shared");
218
219		let value1 = EncodedRow(CowVec::new(vec![1]));
220		let value2 = EncodedRow(CowVec::new(vec![2]));
221
222		// Set different values for same key in different operators
223		operator1.state_set(&mut txn, &shared_key, value1.clone()).unwrap();
224		operator2.state_set(&mut txn, &shared_key, value2.clone()).unwrap();
225
226		// Each operator should have its own value
227		let result1 = operator1.state_get(&mut txn, &shared_key).unwrap().unwrap();
228		let result2 = operator2.state_get(&mut txn, &shared_key).unwrap().unwrap();
229
230		assert_row_eq(&result1, &value1);
231		assert_row_eq(&result2, &value2);
232	}
233
234	#[test]
235	fn test_empty_range() {
236		let mut txn = create_test_transaction();
237		let mut txn = FlowTransaction::deferred(
238			&mut txn,
239			CommitVersion(1),
240			Catalog::testing(),
241			Interceptors::new(),
242			Clock::Mock(MockClock::from_millis(1000)),
243		);
244		let operator = TestOperator::simple(FlowNodeId(4));
245
246		// Add some entries
247		for i in 0..5 {
248			let key = test_key(&format!("item_{}", i));
249			let value = test_row();
250			operator.state_set(&mut txn, &key, value).unwrap();
251		}
252
253		// Query range that doesn't exist (after all "item_*" entries)
254		let range = EncodedKeyRange::new(Included(test_key("z_aaa")), Excluded(test_key("z_zzz")));
255		let range_result: Vec<_> = operator.state_range(&mut txn, range).collect::<Result<Vec<_>>>().unwrap();
256
257		assert_eq!(range_result.len(), 0);
258	}
259
260	#[test]
261	fn test_overwrite_existing_key() {
262		let mut txn = create_test_transaction();
263		let mut txn = FlowTransaction::deferred(
264			&mut txn,
265			CommitVersion(1),
266			Catalog::testing(),
267			Interceptors::new(),
268			Clock::Mock(MockClock::from_millis(1000)),
269		);
270		let operator = TestOperator::simple(FlowNodeId(5));
271		let key = test_key("overwrite");
272
273		let value1 = EncodedRow(CowVec::new(vec![1, 1, 1]));
274		let value2 = EncodedRow(CowVec::new(vec![2, 2, 2]));
275
276		// Set initial value
277		operator.state_set(&mut txn, &key, value1).unwrap();
278
279		// Overwrite with new value
280		operator.state_set(&mut txn, &key, value2.clone()).unwrap();
281
282		// Should have the new value
283		let result = operator.state_get(&mut txn, &key).unwrap().unwrap();
284		assert_row_eq(&result, &value2);
285	}
286
287	#[test]
288	fn test_remove_non_existent_key() {
289		let mut txn = create_test_transaction();
290		let mut txn = FlowTransaction::deferred(
291			&mut txn,
292			CommitVersion(1),
293			Catalog::testing(),
294			Interceptors::new(),
295			Clock::Mock(MockClock::from_millis(1000)),
296		);
297		let operator = TestOperator::simple(FlowNodeId(6));
298		let key = test_key("non_existent");
299
300		// Remove non-existent key should not error
301		operator.state_remove(&mut txn, &key).unwrap();
302
303		// Should still be None
304		assert!(operator.state_get(&mut txn, &key).unwrap().is_none());
305	}
306
307	#[test]
308	fn test_scan_after_partial_removal() {
309		let mut txn = create_test_transaction();
310		let mut txn = FlowTransaction::deferred(
311			&mut txn,
312			CommitVersion(1),
313			Catalog::testing(),
314			Interceptors::new(),
315			Clock::Mock(MockClock::from_millis(1000)),
316		);
317		let operator = TestOperator::simple(FlowNodeId(7));
318
319		// Add 5 entries
320		for i in 0..5 {
321			let key = test_key(&format!("partial_{}", i));
322			let value = EncodedRow(CowVec::new(vec![i as u8]));
323			operator.state_set(&mut txn, &key, value).unwrap();
324		}
325
326		// Remove some entries
327		operator.state_remove(&mut txn, &test_key("partial_1")).unwrap();
328		operator.state_remove(&mut txn, &test_key("partial_3")).unwrap();
329
330		// Should have 3 entries left (0, 2, 4)
331		let remaining: Vec<_> = operator.state_scan_all(&mut txn).unwrap();
332		assert_eq!(remaining.len(), 3);
333	}
334}