1use std::sync::Arc;
5
6use reifydb_codec::row::{series::EncodedSeriesRow, shape::RowShape};
7use reifydb_core::{
8 common::CommitVersion,
9 interface::{catalog::storage::StorageId, resolved::ResolvedSeries, store::MultiVersionRow},
10 key::{
11 any::TaggedKey,
12 bound::TaggedKeyBoundRange,
13 series::{PartitionedSeriesRowKeyRange, SeriesRowKeyRange},
14 },
15 value::column::{ColumnWithName, buffer::ColumnBuffer, columns::Columns, headers::ColumnHeaders},
16};
17use reifydb_transaction::{multi::RangeScope, transaction::Transaction};
18use reifydb_value::{
19 fragment::Fragment,
20 reifydb_assertions,
21 value::{
22 Value, datetime::DateTime, dictionary::DictionaryEntryId, partition::Partition, row_number::RowNumber,
23 system_columns::SystemColumns, value_type::ValueType,
24 },
25};
26use tracing::instrument;
27
28use crate::{
29 Result,
30 transaction::operation::dictionary::DictionaryOperations,
31 vm::{
32 instruction::dml::shape::get_or_create_series_shape,
33 volcano::query::{QueryContext, QueryNode},
34 },
35};
36
37pub struct SeriesScanNode {
38 series: ResolvedSeries,
39 key_range_start: Option<u64>,
40 key_range_end: Option<u64>,
41 variant_tag: Option<u8>,
42 partition: Option<Partition>,
43 context: Option<Arc<QueryContext>>,
44 headers: ColumnHeaders,
45 last_key: Option<TaggedKey>,
46 exhausted: bool,
47
48 min_commit_version: Option<CommitVersion>,
49}
50
51impl SeriesScanNode {
52 pub fn with_min_commit_version(mut self, min_commit_version: Option<CommitVersion>) -> Self {
53 self.min_commit_version = min_commit_version;
54 self
55 }
56
57 pub fn new(
58 series: ResolvedSeries,
59 key_range_start: Option<u64>,
60 key_range_end: Option<u64>,
61 variant_tag: Option<u8>,
62 partition: Option<Partition>,
63 context: Arc<QueryContext>,
64 ) -> Result<Self> {
65 let mut columns = vec![Fragment::internal(series.def().key.column())];
66 if series.def().tag.is_some() {
67 columns.push(Fragment::internal("tag"));
68 }
69 for col in series.columns() {
70 columns.push(Fragment::internal(&col.name));
71 }
72 let headers = ColumnHeaders {
73 columns,
74 };
75
76 Ok(Self {
77 series,
78 key_range_start,
79 key_range_end,
80 variant_tag,
81 partition,
82 context: Some(context),
83 headers,
84 last_key: None,
85 exhausted: false,
86 min_commit_version: None,
87 })
88 }
89
90 #[instrument(level = "trace", skip_all, name = "volcano::scan::series::range_open")]
91 fn open_range<'rx, 'tx>(
92 rx: &'rx mut Transaction<'tx>,
93 range: TaggedKeyBoundRange,
94 scope: RangeScope,
95 batch_size: u64,
96 ) -> Result<Box<dyn Iterator<Item = Result<MultiVersionRow<TaggedKey>>> + Send + 'rx>> {
97 rx.range(range, scope, batch_size as usize)
98 }
99
100 #[instrument(level = "trace", skip_all, name = "volcano::scan::series::drain")]
101 fn drain_batch(
102 stream: &mut dyn Iterator<Item = Result<MultiVersionRow<TaggedKey>>>,
103 batch_size: u64,
104 partitioned: bool,
105 has_tag: bool,
106 data_column_count: usize,
107 read_shape: &RowShape,
108 ) -> Result<SeriesBatch> {
109 let mut batch = SeriesBatch::default();
110 let mut count = 0;
111
112 for entry in stream {
113 let entry = entry?;
114
115 let decoded: Option<(u64, u64, Option<u8>, Option<Partition>)> = if partitioned {
116 match &entry.key {
117 TaggedKey::PartitionedSeriesRow(pk) => {
118 Some((pk.key, pk.sequence, pk.variant_tag, Some(pk.partition)))
119 }
120 _ => None,
121 }
122 } else {
123 match &entry.key {
124 TaggedKey::SeriesRow(k) => Some((k.key, k.sequence, k.variant_tag, None)),
125 _ => None,
126 }
127 };
128
129 if let Some((key_val, sequence, variant_tag, partition)) = decoded {
130 batch.key_values.push(key_val);
131 batch.sequences.push(sequence);
132 if let Some(p) = partition {
133 batch.partitions.push(p);
134 }
135 let row = EncodedSeriesRow::view(&entry.bytes);
136 batch.created_at_values.push(row.created_at());
137 if let Some(time) = row.time() {
138 batch.time_values.push(time);
139 }
140 batch.updated_at_values.push(row.updated_at());
141 if has_tag {
142 batch.tags.push(variant_tag.unwrap_or(0));
143 }
144
145 let mut values = Vec::with_capacity(data_column_count);
146 for i in 0..data_column_count {
147 values.push(read_shape.get_value(&entry.bytes, i + 1));
148 }
149 batch.data_rows.push(values);
150
151 batch.last_key = Some(entry.key.clone());
152 count += 1;
153 if count >= batch_size as usize {
154 break;
155 }
156 }
157 }
158
159 Ok(batch)
160 }
161
162 #[instrument(level = "trace", skip_all, name = "volcano::scan::series::empty_columns")]
163 fn empty_columns(&self, has_tag: bool) -> Vec<ColumnWithName> {
164 let series = self.series.def();
165 let key_type = series
166 .columns
167 .iter()
168 .find(|c| c.name == series.key.column())
169 .map(|c| c.constraint.get_type())
170 .unwrap_or(ValueType::Int8);
171
172 let mut result_columns = Vec::new();
173 result_columns.push(ColumnWithName {
174 name: Fragment::internal(series.key.column()),
175 data: ColumnBuffer::none_typed(key_type, 0),
176 });
177 if has_tag {
178 result_columns.push(ColumnWithName {
179 name: Fragment::internal("tag"),
180 data: ColumnBuffer::none_typed(ValueType::Uint1, 0),
181 });
182 }
183 for col_def in series.data_columns() {
184 result_columns.push(ColumnWithName {
185 name: Fragment::internal(&col_def.name),
186 data: ColumnBuffer::none_typed(col_def.constraint.get_type(), 0),
187 });
188 }
189 result_columns
190 }
191
192 #[instrument(level = "trace", skip_all, name = "volcano::scan::series::assemble")]
193 fn assemble<'a>(
194 &self,
195 rx: &mut Transaction<'a>,
196 stored_ctx: &QueryContext,
197 batch: SeriesBatch,
198 has_tag: bool,
199 partitioned: bool,
200 ) -> Result<Option<Columns>> {
201 let series = self.series.def();
202 let mut result_columns = Vec::new();
203
204 result_columns.push(ColumnWithName::new(
205 Fragment::internal(series.key.column()),
206 series.key_column_data(batch.key_values),
207 ));
208
209 if has_tag {
210 result_columns
211 .push(ColumnWithName::new(Fragment::internal("tag"), ColumnBuffer::uint1(batch.tags)));
212 }
213
214 for (col_idx, col_def) in series.data_columns().enumerate() {
215 let col_type = col_def.constraint.get_type();
216 let mut col_values: Vec<Value> = batch
217 .data_rows
218 .iter()
219 .map(|row| row.get(col_idx).cloned().unwrap_or(Value::none()))
220 .collect();
221
222 if let Some(dict_id) = col_def.dictionary_id
223 && let Some(dictionary) = stored_ctx.services.catalog.find_dictionary(rx, dict_id)?
224 {
225 for value in col_values.iter_mut() {
226 if let Some(entry_id) = DictionaryEntryId::from_value(value) {
227 *value = rx
228 .get_from_dictionary(&dictionary, entry_id)?
229 .unwrap_or_else(Value::none);
230 }
231 }
232 }
233
234 result_columns.push(build_data_column(&col_def.name, &col_values, col_type)?);
235 }
236
237 let row_numbers: Vec<RowNumber> = batch.sequences.into_iter().map(RowNumber::from).collect();
238 let mut result = Columns::with_system(
239 result_columns,
240 SystemColumns::new(
241 row_numbers,
242 Vec::new(),
243 batch.created_at_values,
244 batch.updated_at_values,
245 batch.time_values,
246 ),
247 );
248 if partitioned {
249 result.system.set_partitions(batch.partitions);
250 }
251 Ok(Some(result))
252 }
253}
254
255#[derive(Default)]
256struct SeriesBatch {
257 key_values: Vec<u64>,
258 tags: Vec<u8>,
259 sequences: Vec<u64>,
260 partitions: Vec<Partition>,
261 created_at_values: Vec<DateTime>,
262 time_values: Vec<DateTime>,
263 updated_at_values: Vec<DateTime>,
264 data_rows: Vec<Vec<Value>>,
265 last_key: Option<TaggedKey>,
266}
267
268impl QueryNode for SeriesScanNode {
269 #[instrument(name = "volcano::scan::series::initialize", level = "trace", skip_all)]
270 fn initialize<'a>(&mut self, _rx: &mut Transaction<'a>, _ctx: &QueryContext) -> Result<()> {
271 Ok(())
272 }
273
274 #[instrument(name = "volcano::scan::series::next", level = "trace", skip_all)]
275 fn next<'a>(&mut self, rx: &mut Transaction<'a>, _ctx: &mut QueryContext) -> Result<Option<Columns>> {
276 reifydb_assertions! {
277 assert!(self.context.is_some(), "SeriesScanNode::next() called before initialize()");
278 }
279 let stored_ctx = self.context.as_ref().unwrap();
280
281 if self.exhausted {
282 return Ok(None);
283 }
284
285 let batch_size = stored_ctx.batch_size;
286 let series = self.series.def();
287 let has_tag = series.tag.is_some();
288
289 let partitioned = !series.partition_by.is_empty();
290 let storage = StorageId::series(series.id);
291 let range = if partitioned {
292 match self.partition {
293 Some(partition) => PartitionedSeriesRowKeyRange::scan_range(
294 storage,
295 partition,
296 self.variant_tag,
297 self.key_range_start,
298 self.key_range_end,
299 self.last_key.as_ref(),
300 ),
301 None => PartitionedSeriesRowKeyRange::full_scan_range(storage, self.last_key.as_ref()),
302 }
303 } else {
304 SeriesRowKeyRange::scan_range(
305 storage,
306 self.variant_tag,
307 self.key_range_start,
308 self.key_range_end,
309 self.last_key.as_ref(),
310 )
311 };
312
313 let read_shape = get_or_create_series_shape(&stored_ctx.services.catalog, self.series.def(), rx)?;
314 let stored_ctx = stored_ctx.clone();
315
316 let scope = match self.min_commit_version {
317 Some(v) => RangeScope::After(v),
318 None => RangeScope::All,
319 };
320
321 let data_column_count = series.data_columns().count();
322 let batch = {
323 let mut stream = Self::open_range(rx, range, scope, batch_size)?;
324 Self::drain_batch(
325 &mut stream,
326 batch_size,
327 partitioned,
328 has_tag,
329 data_column_count,
330 &read_shape,
331 )?
332 };
333
334 if batch.key_values.is_empty() {
335 self.exhausted = true;
336 if self.last_key.is_none() {
337 return Ok(Some(Columns::new(self.empty_columns(has_tag))));
338 }
339 return Ok(None);
340 }
341
342 self.last_key = batch.last_key.clone();
343
344 self.assemble(rx, &stored_ctx, batch, has_tag, partitioned)
345 }
346
347 fn headers(&self) -> Option<ColumnHeaders> {
348 Some(self.headers.clone())
349 }
350}
351
352pub(crate) fn build_data_column(name: &str, values: &[Value], col_type: ValueType) -> Result<ColumnWithName> {
353 let data = match col_type {
354 ValueType::Boolean => {
355 let vals: Vec<bool> = values
356 .iter()
357 .map(|v| match v {
358 Value::Boolean(b) => *b,
359 _ => false,
360 })
361 .collect();
362 ColumnBuffer::bool(vals)
363 }
364 ValueType::Int1 => {
365 let vals: Vec<i8> = values
366 .iter()
367 .map(|v| match v {
368 Value::Int1(n) => *n,
369 _ => 0,
370 })
371 .collect();
372 ColumnBuffer::int1(vals)
373 }
374 ValueType::Int2 => {
375 let vals: Vec<i16> = values
376 .iter()
377 .map(|v| match v {
378 Value::Int2(n) => *n,
379 _ => 0,
380 })
381 .collect();
382 ColumnBuffer::int2(vals)
383 }
384 ValueType::Int4 => {
385 let vals: Vec<i32> = values
386 .iter()
387 .map(|v| match v {
388 Value::Int4(n) => *n,
389 _ => 0,
390 })
391 .collect();
392 ColumnBuffer::int4(vals)
393 }
394 ValueType::Int8 => {
395 let vals: Vec<i64> = values
396 .iter()
397 .map(|v| match v {
398 Value::Int8(n) => *n,
399 _ => 0,
400 })
401 .collect();
402 ColumnBuffer::int8(vals)
403 }
404 ValueType::Uint1 => {
405 let vals: Vec<u8> = values
406 .iter()
407 .map(|v| match v {
408 Value::Uint1(n) => *n,
409 _ => 0,
410 })
411 .collect();
412 ColumnBuffer::uint1(vals)
413 }
414 ValueType::Uint2 => {
415 let vals: Vec<u16> = values
416 .iter()
417 .map(|v| match v {
418 Value::Uint2(n) => *n,
419 _ => 0,
420 })
421 .collect();
422 ColumnBuffer::uint2(vals)
423 }
424 ValueType::Uint4 => {
425 let vals: Vec<u32> = values
426 .iter()
427 .map(|v| match v {
428 Value::Uint4(n) => *n,
429 _ => 0,
430 })
431 .collect();
432 ColumnBuffer::uint4(vals)
433 }
434 ValueType::Uint8 => {
435 let vals: Vec<u64> = values
436 .iter()
437 .map(|v| match v {
438 Value::Uint8(n) => *n,
439 _ => 0,
440 })
441 .collect();
442 ColumnBuffer::uint8(vals)
443 }
444 ValueType::Float4 => {
445 let vals: Vec<f32> = values
446 .iter()
447 .map(|v| match v {
448 Value::Float4(n) => n.value(),
449 _ => 0.0,
450 })
451 .collect();
452 ColumnBuffer::float4(vals)
453 }
454 ValueType::Float8 => {
455 let vals: Vec<f64> = values
456 .iter()
457 .map(|v| match v {
458 Value::Float8(n) => n.value(),
459 _ => 0.0,
460 })
461 .collect();
462 ColumnBuffer::float8(vals)
463 }
464 ValueType::Utf8 => {
465 let vals: Vec<String> = values
466 .iter()
467 .map(|v| match v {
468 Value::Utf8(s) => s.clone(),
469 _ => String::new(),
470 })
471 .collect();
472 ColumnBuffer::utf8(vals)
473 }
474 _ => {
475 let vals: Vec<String> = values.iter().map(|v| format!("{:?}", v)).collect();
476 ColumnBuffer::utf8(vals)
477 }
478 };
479
480 Ok(ColumnWithName {
481 name: Fragment::internal(name),
482 data,
483 })
484}