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