reifydb_engine/vm/volcano/scan/
table.rs1use 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}