Skip to main content

reifydb_core/interface/
store.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, util::cowvec::CowVec};
9
10use crate::{
11	common::CommitVersion,
12	delta::Delta,
13	interface::catalog::{flow::FlowNodeId, shape::ShapeId},
14	key::{
15		EncodableKeyRange, Key, flow_node_internal_state::FlowNodeInternalStateKeyRange,
16		flow_node_state::FlowNodeStateKeyRange, kind::KeyKind, partitioned_row::PartitionedRowKeyRange,
17		row::RowKeyRange,
18	},
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum Tier {
23	Buffer,
24	Persistent,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
28pub enum EntryKind {
29	Multi,
30
31	Source(ShapeId),
32
33	PartitionedSource(ShapeId),
34
35	Operator(FlowNodeId),
36
37	OperatorInternal(FlowNodeId),
38}
39
40pub fn classify_key(key: &EncodedKey) -> EntryKind {
41	match Key::decode(key) {
42		Some(Key::Row(row_key)) => EntryKind::Source(row_key.shape),
43		Some(Key::PartitionedRow(partitioned_key)) => EntryKind::PartitionedSource(partitioned_key.shape),
44		Some(Key::FlowNodeState(state_key)) => EntryKind::Operator(state_key.node),
45		Some(Key::FlowNodeInternalState(internal_key)) => EntryKind::OperatorInternal(internal_key.node),
46		_ => EntryKind::Multi,
47	}
48}
49
50pub fn is_single_version_semantics_key(key: &EncodedKey) -> bool {
51	Key::kind(key).is_some_and(|kind| matches!(kind, KeyKind::FlowNodeState | KeyKind::FlowNodeInternalState))
52}
53
54pub fn classify_range(range: &EncodedKeyRange) -> Option<EntryKind> {
55	if let (Some(start), Some(_end)) = RowKeyRange::decode(range) {
56		return Some(EntryKind::Source(start.shape));
57	}
58
59	if let (Some(start), Some(_end)) = PartitionedRowKeyRange::decode(range) {
60		return Some(EntryKind::PartitionedSource(start.shape));
61	}
62
63	if let (Some(start), Some(_end)) = FlowNodeStateKeyRange::decode(range) {
64		return Some(EntryKind::Operator(start.node));
65	}
66
67	if let (Some(start), Some(_end)) = FlowNodeInternalStateKeyRange::decode(range) {
68		return Some(EntryKind::OperatorInternal(start.node));
69	}
70
71	None
72}
73
74#[derive(Debug, Clone)]
75pub struct MultiVersionRow {
76	pub key: EncodedKey,
77	pub row: EncodedRow,
78	pub version: CommitVersion,
79}
80
81#[derive(Debug, Clone)]
82pub struct SingleVersionRow {
83	pub key: EncodedKey,
84	pub row: EncodedRow,
85}
86
87#[derive(Debug, Clone)]
88pub struct MultiVersionBatch {
89	pub items: Vec<MultiVersionRow>,
90
91	pub has_more: bool,
92}
93
94impl MultiVersionBatch {
95	pub fn empty() -> Self {
96		Self {
97			items: Vec::new(),
98			has_more: false,
99		}
100	}
101
102	pub fn is_empty(&self) -> bool {
103		self.items.is_empty()
104	}
105}
106
107pub trait MultiVersionCommit: Send + Sync {
108	fn commit(&self, deltas: CowVec<Delta>, version: CommitVersion) -> Result<()>;
109}
110
111#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
112pub struct ReadOptions {
113	pub bypass_buffer: bool,
114}
115
116pub trait MultiVersionGet: Send + Sync {
117	fn get(&self, key: &EncodedKey, version: CommitVersion) -> Result<Option<MultiVersionRow>>;
118
119	fn get_with_options(
120		&self,
121		key: &EncodedKey,
122		version: CommitVersion,
123		_options: ReadOptions,
124	) -> Result<Option<MultiVersionRow>> {
125		self.get(key, version)
126	}
127}
128
129pub trait MultiVersionContains: Send + Sync {
130	fn contains(&self, key: &EncodedKey, version: CommitVersion) -> Result<bool>;
131
132	fn contains_with_options(
133		&self,
134		key: &EncodedKey,
135		version: CommitVersion,
136		_options: ReadOptions,
137	) -> Result<bool> {
138		self.contains(key, version)
139	}
140}
141
142pub trait MultiVersionGetPrevious: Send + Sync {
143	fn get_previous_version(
144		&self,
145		key: &EncodedKey,
146		before_version: CommitVersion,
147	) -> Result<Option<MultiVersionRow>>;
148}
149
150pub trait MultiVersionStore:
151	Send + Sync + Clone + MultiVersionCommit + MultiVersionGet + MultiVersionGetPrevious + MultiVersionContains + 'static
152{
153}
154
155#[derive(Debug, Clone)]
156pub struct SingleVersionBatch {
157	pub items: Vec<SingleVersionRow>,
158
159	pub has_more: bool,
160}
161
162impl SingleVersionBatch {
163	pub fn empty() -> Self {
164		Self {
165			items: Vec::new(),
166			has_more: false,
167		}
168	}
169
170	pub fn is_empty(&self) -> bool {
171		self.items.is_empty()
172	}
173}
174
175pub trait SingleVersionCommit: Send + Sync {
176	fn commit(&mut self, deltas: CowVec<Delta>) -> Result<()>;
177}
178
179pub trait SingleVersionGet: Send + Sync {
180	fn get(&self, key: &EncodedKey) -> Result<Option<SingleVersionRow>>;
181}
182
183pub trait SingleVersionContains: Send + Sync {
184	fn contains(&self, key: &EncodedKey) -> Result<bool>;
185}
186
187pub trait SingleVersionSet: SingleVersionCommit {
188	fn set(&mut self, key: &EncodedKey, row: EncodedRow) -> Result<()> {
189		Self::commit(
190			self,
191			CowVec::new(vec![Delta::Set {
192				key: key.clone(),
193				row: row.clone(),
194			}]),
195		)
196	}
197}
198
199pub trait SingleVersionRemove: SingleVersionCommit {
200	fn unset(&mut self, key: &EncodedKey, row: EncodedRow) -> Result<()> {
201		Self::commit(
202			self,
203			CowVec::new(vec![Delta::Unset {
204				key: key.clone(),
205				row,
206			}]),
207		)
208	}
209
210	fn remove(&mut self, key: &EncodedKey) -> Result<()> {
211		Self::commit(
212			self,
213			CowVec::new(vec![Delta::Remove {
214				key: key.clone(),
215			}]),
216		)
217	}
218}
219
220pub trait SingleVersionRange: Send + Sync {
221	fn range_batch(&self, range: EncodedKeyRange, batch_size: u64) -> Result<SingleVersionBatch>;
222
223	fn range(&self, range: EncodedKeyRange) -> Result<SingleVersionBatch> {
224		self.range_batch(range, 1024)
225	}
226
227	fn prefix(&self, prefix: &EncodedKey) -> Result<SingleVersionBatch> {
228		self.range(EncodedKeyRange::prefix(prefix))
229	}
230}
231
232pub trait SingleVersionRangeRev: Send + Sync {
233	fn range_rev_batch(&self, range: EncodedKeyRange, batch_size: u64) -> Result<SingleVersionBatch>;
234
235	fn range_rev(&self, range: EncodedKeyRange) -> Result<SingleVersionBatch> {
236		self.range_rev_batch(range, 1024)
237	}
238
239	fn prefix_rev(&self, prefix: &EncodedKey) -> Result<SingleVersionBatch> {
240		self.range_rev(EncodedKeyRange::prefix(prefix))
241	}
242}
243
244pub trait SingleVersionStore:
245	Send
246	+ Sync
247	+ Clone
248	+ SingleVersionCommit
249	+ SingleVersionGet
250	+ SingleVersionContains
251	+ SingleVersionSet
252	+ SingleVersionRemove
253	+ SingleVersionRange
254	+ SingleVersionRangeRev
255	+ 'static
256{
257}
258
259#[cfg(test)]
260mod tests {
261	use reifydb_value::value::{Value, partition::Partition, row_number::RowNumber};
262
263	use super::{EntryKind, classify_key, classify_range};
264	use crate::{
265		interface::catalog::{id::TableId, shape::ShapeId},
266		key::{
267			partitioned_row::{PartitionedRowKey, RowLocator},
268			row::RowKey,
269		},
270	};
271
272	fn part(v: &str) -> Partition {
273		Partition::of(&[Value::Utf8(v.to_string())])
274	}
275
276	#[test]
277	fn classify_key_partitioned_row_is_partitioned_source() {
278		let shape = ShapeId::Table(TableId(7));
279		let key = PartitionedRowKey::encoded(shape, part("us"), RowLocator::Row(RowNumber(1)));
280		assert_eq!(classify_key(&key), EntryKind::PartitionedSource(shape));
281	}
282
283	#[test]
284	fn classify_key_row_is_still_source() {
285		let shape = ShapeId::Table(TableId(7));
286		let key = RowKey::encoded(shape, RowNumber(1));
287		assert_eq!(classify_key(&key), EntryKind::Source(shape));
288	}
289
290	#[test]
291	fn classify_range_all_partition_forms_are_partitioned_source() {
292		let shape = ShapeId::Table(TableId(9));
293		let p = part("us");
294		let last = PartitionedRowKey::encoded(shape, p, RowLocator::Row(RowNumber(5)));
295		assert_eq!(
296			classify_range(&PartitionedRowKey::partition_range(shape, p)),
297			Some(EntryKind::PartitionedSource(shape))
298		);
299		assert_eq!(
300			classify_range(&PartitionedRowKey::partition_scan_range(shape, p, Some(&last))),
301			Some(EntryKind::PartitionedSource(shape))
302		);
303		assert_eq!(
304			classify_range(&PartitionedRowKey::scan_range(shape, None)),
305			Some(EntryKind::PartitionedSource(shape))
306		);
307		assert_eq!(
308			classify_range(&PartitionedRowKey::full_scan(shape)),
309			Some(EntryKind::PartitionedSource(shape))
310		);
311	}
312
313	#[test]
314	fn classify_range_row_range_is_still_source() {
315		let shape = ShapeId::Table(TableId(9));
316		assert_eq!(classify_range(&RowKey::full_scan(shape)), Some(EntryKind::Source(shape)));
317	}
318}