Skip to main content

reifydb_engine/vm/volcano/scan/
table.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::sync::Arc;
5
6use reifydb_codec::{
7	encoded::{row::EncodedRow, shape::RowShape},
8	key::encoded::EncodedKey,
9};
10use reifydb_core::{
11	common::CommitVersion,
12	error::diagnostic,
13	interface::{catalog::dictionary::Dictionary, resolved::ResolvedTable},
14	key::{
15		EncodableKey,
16		row::{RowKey, RowKeyRange},
17	},
18	value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns, headers::ColumnHeaders},
19};
20use reifydb_transaction::{multi::RangeScope, transaction::Transaction};
21use reifydb_value::{
22	error, fragment::Fragment, reifydb_assertions, util::cowvec::CowVec, value::value_type::ValueType,
23};
24use tracing::instrument;
25
26use super::super::decode_dictionary_columns;
27use crate::{
28	Result,
29	vm::volcano::query::{QueryContext, QueryNode},
30};
31
32pub struct TableScanNode {
33	table: ResolvedTable,
34	context: Option<Arc<QueryContext>>,
35	headers: ColumnHeaders,
36
37	storage_types: Vec<ValueType>,
38
39	dictionaries: Vec<Option<Dictionary>>,
40
41	shape: Option<RowShape>,
42	last_key: Option<EncodedKey>,
43	exhausted: bool,
44
45	min_commit_version: Option<CommitVersion>,
46}
47
48impl TableScanNode {
49	pub fn with_min_commit_version(mut self, min_commit_version: Option<CommitVersion>) -> Self {
50		self.min_commit_version = min_commit_version;
51		self
52	}
53
54	pub fn new(table: ResolvedTable, context: Arc<QueryContext>, rx: &mut Transaction<'_>) -> Result<Self> {
55		let mut storage_types = Vec::with_capacity(table.columns().len());
56		let mut dictionaries = Vec::with_capacity(table.columns().len());
57
58		for col in table.columns() {
59			if let Some(dict_id) = col.dictionary_id {
60				if let Some(dict) = context.services.catalog.find_dictionary(rx, dict_id)? {
61					storage_types.push(ValueType::DictionaryId);
62					dictionaries.push(Some(dict));
63				} else {
64					storage_types.push(col.constraint.get_type());
65					dictionaries.push(None);
66				}
67			} else {
68				storage_types.push(col.constraint.get_type());
69				dictionaries.push(None);
70			}
71		}
72
73		let headers = ColumnHeaders {
74			columns: table.columns().iter().map(|col| Fragment::internal(&col.name)).collect(),
75		};
76
77		Ok(Self {
78			table,
79			context: Some(context),
80			headers,
81			storage_types,
82			dictionaries,
83			shape: None,
84			last_key: None,
85			exhausted: false,
86			min_commit_version: None,
87		})
88	}
89
90	fn get_or_load_shape<'a>(&mut self, rx: &mut Transaction<'a>, first_row: &EncodedRow) -> Result<RowShape> {
91		if let Some(shape) = &self.shape {
92			return Ok(shape.clone());
93		}
94
95		let fingerprint = first_row.fingerprint();
96
97		let stored_ctx = self.context.as_ref().expect("TableScanNode context not set");
98		let shape = stored_ctx.services.catalog.get_or_load_row_shape(fingerprint, rx)?.ok_or_else(|| {
99			error!(diagnostic::internal::internal(format!(
100				"RowShape with fingerprint {:?} not found for table {}",
101				fingerprint,
102				self.table.def().name
103			)))
104		})?;
105
106		self.shape = Some(shape.clone());
107
108		Ok(shape)
109	}
110}
111
112impl QueryNode for TableScanNode {
113	#[instrument(level = "trace", skip_all, name = "volcano::scan::table::initialize")]
114	fn initialize<'a>(&mut self, _rx: &mut Transaction<'a>, _ctx: &QueryContext) -> Result<()> {
115		Ok(())
116	}
117
118	#[instrument(level = "trace", skip_all, name = "volcano::scan::table::next")]
119	fn next<'a>(&mut self, rx: &mut Transaction<'a>, _ctx: &mut QueryContext) -> Result<Option<Columns>> {
120		reifydb_assertions! {
121			assert!(self.context.is_some(), "TableScanNode::next() called before initialize()");
122		}
123		let stored_ctx = self.context.as_ref().unwrap();
124
125		if self.exhausted {
126			return Ok(None);
127		}
128
129		let batch_size = stored_ctx.batch_size;
130
131		let range = RowKeyRange::scan_range(self.table.def().id.into(), self.last_key.as_ref());
132
133		let mut batch_rows = Vec::new();
134		let mut row_numbers = Vec::new();
135		let mut new_last_key = None;
136
137		let scope = match self.min_commit_version {
138			Some(v) => RangeScope::After(v),
139			None => RangeScope::All,
140		};
141
142		let mut stream = rx.range(range, scope, batch_size as usize)?;
143
144		for _ in 0..batch_size {
145			match stream.next() {
146				Some(Ok(multi)) => {
147					if let Some(key) = RowKey::decode(&multi.key) {
148						batch_rows.push(multi.row);
149						row_numbers.push(key.row);
150						new_last_key = Some(multi.key);
151					}
152				}
153				Some(Err(e)) => return Err(e),
154				None => {
155					self.exhausted = true;
156					break;
157				}
158			}
159		}
160
161		drop(stream);
162
163		if batch_rows.is_empty() {
164			self.exhausted = true;
165			if self.last_key.is_none() {
166				let columns: Vec<ColumnWithName> = self
167					.table
168					.columns()
169					.iter()
170					.map(|col| ColumnWithName {
171						name: Fragment::internal(&col.name),
172						data: ColumnBuffer::none_typed(col.constraint.get_type(), 0),
173					})
174					.collect();
175				return Ok(Some(Columns::new(columns)));
176			}
177			return Ok(None);
178		}
179
180		self.last_key = new_last_key;
181
182		let storage_columns: Vec<ColumnWithName> = {
183			self.table
184				.columns()
185				.iter()
186				.enumerate()
187				.map(|(idx, col)| ColumnWithName {
188					name: Fragment::internal(&col.name),
189					data: ColumnBuffer::with_capacity(self.storage_types[idx].clone(), 0),
190				})
191				.collect()
192		};
193
194		let mut columns = Columns::with_system_columns(storage_columns, Vec::new(), Vec::new(), Vec::new());
195		{
196			let shape = self.get_or_load_shape(rx, &batch_rows[0])?;
197			columns.append_rows(&shape, batch_rows.into_iter(), row_numbers.clone())?;
198		}
199
200		columns.row_numbers = CowVec::new(row_numbers);
201
202		decode_dictionary_columns(&mut columns, &self.dictionaries, rx)?;
203
204		Ok(Some(columns))
205	}
206
207	fn headers(&self) -> Option<ColumnHeaders> {
208		Some(self.headers.clone())
209	}
210}