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