1use std::{
5 collections::{BTreeSet, HashMap},
6 mem,
7 sync::Arc,
8};
9
10use arrow_array::{Array, ArrayRef, RecordBatch};
11use arrow_schema::FieldRef;
12use reifydb_catalog::catalog::Catalog;
13use reifydb_core::{
14 error::diagnostic::{
15 catalog::{column_not_sumtype, namespace_not_found, sumtype_not_found, sumtype_variant_not_found},
16 query::{column_not_found, duplicate_field},
17 },
18 expression::{
19 AliasExpression, CastExpression, ConstantExpression, Expression, IdentExpression,
20 SumTypeConstructorExpression, TypeExpression, name::display_label,
21 },
22 interface::{catalog::sumtype::SumType, evaluate::TargetColumn, resolved::ResolvedObject},
23 value::{
24 batch::{batch, empty_batch},
25 column::{
26 builder::ColumnBuilder,
27 cast::cast_column_data,
28 factory::{from_one, none, none_typed},
29 headers::ColumnHeaders,
30 view::group_by::common_key_type,
31 },
32 },
33};
34use reifydb_evaluate::expression::{context::EvalContext, eval::evaluate};
35use reifydb_rql::expression::variant::for_each_is_variant;
36use reifydb_transaction::transaction::Transaction;
37use reifydb_value::{
38 error::Error,
39 fragment::Fragment,
40 reifydb_assertions, return_error,
41 value::{Value, column_view::ColumnView, constraint::Constraint, sumtype::SumTypeId, value_type::ValueType},
42};
43use tracing::instrument;
44
45use crate::{
46 Result,
47 vm::{
48 instruction::dml::series_insert::resolve_variant_tag,
49 volcano::query::{QueryContext, QueryNode, eval_context_from_query},
50 },
51};
52
53pub(crate) struct InlineDataNode {
54 rows: Vec<Vec<AliasExpression>>,
55 headers: Option<ColumnHeaders>,
56 context: Option<Arc<QueryContext>>,
57 series_tag: Option<SumType>,
58 executed: bool,
59}
60
61impl InlineDataNode {
62 pub fn new(rows: Vec<Vec<AliasExpression>>, context: Arc<QueryContext>) -> Self {
63 let cloned_context = context.clone();
64 let headers = cloned_context.source.as_ref().map(|source| {
65 let mut layout = Self::create_columns_layout_from_source(source);
66 if Self::series_tag_id(source).is_some()
67 && !layout.columns.iter().any(|c| c.text() == "tag")
68 && rows.iter().flatten().any(|alias| alias.alias.0.text() == "tag")
69 {
70 layout.columns.push(Fragment::internal("tag"));
71 }
72 layout
73 });
74
75 Self {
76 rows,
77 headers,
78 context: Some(context),
79 series_tag: None,
80 executed: false,
81 }
82 }
83
84 fn create_columns_layout_from_source(source: &ResolvedObject) -> ColumnHeaders {
85 ColumnHeaders {
86 columns: source.columns().iter().map(|col| Fragment::internal(&col.name)).collect(),
87 }
88 }
89
90 fn series_tag_id(source: &ResolvedObject) -> Option<SumTypeId> {
91 match source {
92 ResolvedObject::Series(series) => series.def().tag,
93 _ => None,
94 }
95 }
96
97 fn load_series_tag<'a>(&mut self, txn: &mut Transaction<'a>) -> Result<()> {
98 let Some(ctx) = self.context.as_ref() else {
99 return Ok(());
100 };
101 if let Some(tag_id) = ctx.source.as_ref().and_then(Self::series_tag_id) {
102 self.series_tag = Some(ctx.services.catalog.get_sumtype(txn, tag_id)?);
103 }
104 Ok(())
105 }
106
107 #[instrument(level = "trace", skip_all, name = "volcano::inline::expand_sumtypes")]
108 fn expand_sumtype_constructors<'a>(&mut self, txn: &mut Transaction<'a>) -> Result<()> {
109 let Some(ctx) = self.context.as_ref().cloned() else {
110 return Ok(());
111 };
112 if !rows_need_sumtype_expansion(&self.rows) {
113 return Ok(());
114 }
115 for row in &mut self.rows {
116 let original = mem::take(row);
117 let mut expanded = Vec::with_capacity(original.len());
118 let mut written = Vec::with_capacity(original.len());
119 for alias_expr in original {
120 let name = alias_expr.alias.0.clone();
121 if let Some(sumtype) = self.series_tag.as_ref()
122 && alias_expr.alias.0.text() == "tag"
123 {
124 expanded.push(resolve_series_tag_name(&ctx, txn, sumtype, alias_expr)?);
125 written.push(name);
126 continue;
127 }
128 match alias_expr.expression.as_ref() {
129 Expression::SumTypeConstructor(_) => {
130 expand_sumtype_ctor(
131 &ctx,
132 txn,
133 ctx.source.as_ref(),
134 alias_expr,
135 &mut expanded,
136 )?;
137 }
138 Expression::Column(_) => {
139 expand_unit_variant_column(
140 &ctx,
141 txn,
142 ctx.source.as_ref(),
143 alias_expr,
144 &mut expanded,
145 )?;
146 }
147 _ => {
148 expand_sumtype_none(
149 &ctx,
150 txn,
151 ctx.source.as_ref(),
152 alias_expr,
153 &mut expanded,
154 )?;
155 }
156 }
157 written.resize(expanded.len(), name);
158 }
159 let names: Vec<Fragment> = expanded.iter().map(|alias| alias.alias.0.clone()).collect();
160 reject_variant_column_clashes(&names, &written)?;
161 *row = expanded;
162 }
163 Ok(())
164 }
165}
166
167#[inline]
168fn rows_need_sumtype_expansion(rows: &[Vec<AliasExpression>]) -> bool {
169 for row in rows {
170 for alias_expr in row {
171 if matches!(
172 alias_expr.expression.as_ref(),
173 Expression::SumTypeConstructor(_)
174 | Expression::Column(_) | Expression::Constant(ConstantExpression::None { .. })
175 ) {
176 return true;
177 }
178 }
179 }
180 false
181}
182
183pub(crate) fn expand_aliases(
184 expressions: Vec<Expression>,
185 mut expand: impl FnMut(AliasExpression, &mut Vec<AliasExpression>) -> Result<()>,
186) -> Result<(Vec<Expression>, Vec<Fragment>)> {
187 let mut result = Vec::with_capacity(expressions.len());
188 let mut written = Vec::with_capacity(expressions.len());
189 let mut expanded = Vec::new();
190 for expr in expressions {
191 let name = display_label(&expr);
192 let Expression::Alias(alias_expr) = expr else {
193 result.push(expr);
194 written.push(name);
195 continue;
196 };
197 expand(alias_expr, &mut expanded)?;
198 written.extend(expanded.iter().map(|_| name.clone()));
199 result.extend(expanded.drain(..).map(Expression::Alias));
200 }
201 Ok((result, written))
202}
203
204pub(crate) fn reject_variant_column_clashes(names: &[Fragment], written: &[Fragment]) -> Result<()> {
205 for (index, name) in names.iter().enumerate() {
206 let expanded = name.text() != written[index].text();
207 if names[..index].iter().zip(written).any(|(earlier, earlier_written)| {
208 earlier.text() == name.text() && (expanded || earlier.text() != earlier_written.text())
209 }) {
210 return_error!(duplicate_field(written[index].clone(), name.text()));
211 }
212 }
213 Ok(())
214}
215
216pub(crate) fn expand_sumtype_ctor(
217 ctx: &QueryContext,
218 txn: &mut Transaction<'_>,
219 source: Option<&ResolvedObject>,
220 alias_expr: AliasExpression,
221 expanded: &mut Vec<AliasExpression>,
222) -> Result<()> {
223 let Expression::SumTypeConstructor(ctor) = alias_expr.expression.as_ref() else {
224 expanded.push(alias_expr);
225 return Ok(());
226 };
227 let (sumtype, variant) = resolve_sumtype_ctor(&ctx.services.catalog, txn, source, &alias_expr.alias.0, ctor)?;
228 let col_name = alias_expr.alias.0.text();
229 let variant_name = sumtype.variants[variant].name.to_lowercase();
230
231 expanded.push(tag_alias(source, col_name, sumtype.variants[variant].tag, &alias_expr.fragment));
232 for (field_name, field_expr) in &ctor.columns {
233 let phys_col_name = format!("{}_{}_{}", col_name, variant_name, field_name.text().to_lowercase());
234 expanded.push(AliasExpression {
235 alias: IdentExpression(Fragment::internal(phys_col_name)),
236 expression: Box::new(field_expr.clone()),
237 fragment: alias_expr.fragment.clone(),
238 });
239 }
240
241 Ok(())
242}
243
244fn expand_sumtype_none(
245 ctx: &QueryContext,
246 txn: &mut Transaction<'_>,
247 source: Option<&ResolvedObject>,
248 alias_expr: AliasExpression,
249 expanded: &mut Vec<AliasExpression>,
250) -> Result<()> {
251 let col_name = alias_expr.alias.0.text();
252 let is_none = matches!(alias_expr.expression.as_ref(), Expression::Constant(ConstantExpression::None { .. }));
253 let declared = source.and_then(|source| declared_sumtype_id(source, col_name));
254
255 let (true, Some(id)) = (is_none, declared) else {
256 expanded.push(alias_expr);
257 return Ok(());
258 };
259
260 let sumtype = ctx.services.catalog.get_sumtype(txn, id)?;
261 let fragment = alias_expr.fragment.clone();
262 let none = || {
263 Box::new(Expression::Constant(ConstantExpression::None {
264 fragment: fragment.clone(),
265 }))
266 };
267
268 expanded.push(AliasExpression {
269 alias: IdentExpression(Fragment::internal(format!("{col_name}_tag"))),
270 expression: none(),
271 fragment: fragment.clone(),
272 });
273 for variant in &sumtype.variants {
274 for field in &variant.fields {
275 let phys_col_name =
276 format!("{}_{}_{}", col_name, variant.name.to_lowercase(), field.name.to_lowercase());
277 expanded.push(AliasExpression {
278 alias: IdentExpression(Fragment::internal(phys_col_name)),
279 expression: none(),
280 fragment: fragment.clone(),
281 });
282 }
283 }
284
285 Ok(())
286}
287
288pub(crate) fn expand_sumtype_assignment(
289 ctx: &QueryContext,
290 txn: &mut Transaction<'_>,
291 source: &ResolvedObject,
292 alias_expr: AliasExpression,
293 expanded: &mut Vec<AliasExpression>,
294) -> Result<()> {
295 match alias_expr.expression.as_ref() {
296 Expression::SumTypeConstructor(ctor) => {
297 let (sumtype, variant) = resolve_sumtype_ctor(
298 &ctx.services.catalog,
299 txn,
300 Some(source),
301 &alias_expr.alias.0,
302 ctor,
303 )?;
304 let given = ctor
305 .columns
306 .iter()
307 .map(|(name, expr)| (name.text().to_lowercase(), expr.clone()))
308 .collect();
309 push_all_variant_columns(
310 source,
311 ctx.source.is_none().then_some(source),
312 alias_expr.alias.0.text(),
313 &sumtype,
314 variant,
315 given,
316 &alias_expr.fragment,
317 expanded,
318 );
319 Ok(())
320 }
321 Expression::Column(_) => expand_unit_variant_column(ctx, txn, Some(source), alias_expr, expanded),
322 _ => {
323 expanded.push(alias_expr);
324 Ok(())
325 }
326 }
327}
328
329pub(crate) fn resolve_sumtype_ctor(
330 catalog: &Catalog,
331 txn: &mut Transaction<'_>,
332 source: Option<&ResolvedObject>,
333 column: &Fragment,
334 ctor: &SumTypeConstructorExpression,
335) -> Result<(SumType, usize)> {
336 let col_name = column.text();
337 let declared = source.and_then(|source| declared_sumtype_id(source, col_name));
338 let sumtype = if is_unresolved_ctor(ctor) {
339 let Some(id) = declared else {
340 return_error!(column_not_sumtype(ctor.variant_name.clone(), col_name));
341 };
342 catalog.get_sumtype(txn, id)?
343 } else {
344 let sumtype = find_ctor_sumtype(catalog, txn, ctor)?;
345 if let Some(source) = source {
346 match declared {
347 None if source.columns().iter().any(|c| c.name == col_name) => {
348 return_error!(column_not_sumtype(ctor.variant_name.clone(), col_name));
349 }
350 None => return_error!(column_not_found(column.clone())),
351 Some(id) if id != sumtype.id => {
352 let declared_name = catalog.get_sumtype(txn, id)?.name;
353 return_error!(sumtype_variant_not_found(
354 qualified_ctor_fragment(ctor),
355 &declared_name
356 ));
357 }
358 Some(_) => {}
359 }
360 }
361 sumtype
362 };
363
364 let variant_name_lower = ctor.variant_name.text().to_lowercase();
365 let Some(variant) = sumtype.variants.iter().position(|v| v.name == variant_name_lower) else {
366 return_error!(sumtype_variant_not_found(ctor.variant_name.clone(), &sumtype.name));
367 };
368 if let Some((unknown, _)) = ctor.columns.iter().find(|(field_name, _)| {
369 !sumtype.variants[variant].fields.iter().any(|f| f.name.eq_ignore_ascii_case(field_name.text()))
370 }) {
371 return_error!(column_not_found(unknown.clone()));
372 }
373
374 Ok((sumtype, variant))
375}
376
377pub(crate) fn resolve_is_variants(
378 catalog: &Catalog,
379 txn: &mut Transaction<'_>,
380 source: &ResolvedObject,
381 expr: &mut Expression,
382) -> Result<()> {
383 for_each_is_variant(expr, &mut |is| {
384 let column = match is.expression.as_ref() {
385 Expression::Column(column) => column.0.name.clone(),
386 other => display_label(other),
387 };
388 let ctor = SumTypeConstructorExpression {
389 namespace: is.namespace.clone().unwrap_or_else(|| is.sumtype_name.clone()),
390 sumtype_name: is.sumtype_name.clone(),
391 variant_name: is.variant_name.clone(),
392 columns: Vec::new(),
393 fragment: is.fragment.clone(),
394 };
395 let (sumtype, variant) = resolve_sumtype_ctor(catalog, txn, Some(source), &column, &ctor)?;
396 is.tag = Some(sumtype.variants[variant].tag);
397 Ok(())
398 })
399}
400
401#[allow(clippy::too_many_arguments)]
402fn push_all_variant_columns(
403 source: &ResolvedObject,
404 field_types: Option<&ResolvedObject>,
405 col_name: &str,
406 sumtype: &SumType,
407 variant: usize,
408 mut given: HashMap<String, Expression>,
409 fragment: &Fragment,
410 expanded: &mut Vec<AliasExpression>,
411) {
412 expanded.push(tag_alias(Some(source), col_name, sumtype.variants[variant].tag, fragment));
413 for (index, v) in sumtype.variants.iter().enumerate() {
414 for field in &v.fields {
415 let field_name = field.name.to_lowercase();
416 let value = match given.remove(&field_name) {
417 Some(expr) if index == variant => expr,
418 _ => Expression::Constant(ConstantExpression::None {
419 fragment: fragment.clone(),
420 }),
421 };
422 let phys_col_name = format!("{}_{}_{}", col_name, v.name.to_lowercase(), field_name);
423 expanded.push(AliasExpression {
424 expression: Box::new(cast_to_stored_type(field_types, &phys_col_name, value, fragment)),
425 alias: IdentExpression(Fragment::internal(phys_col_name)),
426 fragment: fragment.clone(),
427 });
428 }
429 }
430}
431
432fn tag_alias(source: Option<&ResolvedObject>, col_name: &str, tag: u8, fragment: &Fragment) -> AliasExpression {
433 let tag_col_name = format!("{}_tag", col_name);
434 let constant = Expression::Constant(ConstantExpression::Number {
435 fragment: Fragment::internal(tag.to_string()),
436 });
437 AliasExpression {
438 expression: Box::new(cast_to_stored_type(source, &tag_col_name, constant, fragment)),
439 alias: IdentExpression(Fragment::internal(tag_col_name)),
440 fragment: fragment.clone(),
441 }
442}
443
444fn cast_to_stored_type(
445 source: Option<&ResolvedObject>,
446 col_name: &str,
447 expression: Expression,
448 fragment: &Fragment,
449) -> Expression {
450 match source.and_then(|source| source.columns().iter().find(|c| c.name == col_name)) {
451 Some(column) => Expression::Cast(CastExpression {
452 fragment: fragment.clone(),
453 expression: Box::new(expression),
454 to: TypeExpression {
455 fragment: Fragment::internal(column.constraint.get_type().to_string()),
456 ty: column.constraint.get_type(),
457 constraint: None,
458 },
459 }),
460 None => expression,
461 }
462}
463
464fn resolve_series_tag_name<'a>(
465 ctx: &Arc<QueryContext>,
466 txn: &mut Transaction<'a>,
467 sumtype: &SumType,
468 alias_expr: AliasExpression,
469) -> Result<AliasExpression> {
470 let variant = match alias_expr.expression.as_ref() {
471 Expression::Column(column) => column.0.name.clone(),
472 Expression::SumTypeConstructor(ctor) => {
473 if let Some((field, _)) = ctor.columns.first() {
474 return_error!(column_not_found(field.clone()));
475 }
476 if !is_unresolved_ctor(ctor)
477 && find_ctor_sumtype(&ctx.services.catalog, txn, ctor)?.id != sumtype.id
478 {
479 return_error!(sumtype_variant_not_found(qualified_ctor_fragment(ctor), &sumtype.name));
480 }
481 ctor.variant_name.clone()
482 }
483 _ => return Ok(alias_expr),
484 };
485 let Some(tag) = sumtype.variants.iter().find(|v| v.name.eq_ignore_ascii_case(variant.text())).map(|v| v.tag)
486 else {
487 return_error!(sumtype_variant_not_found(variant, &sumtype.name));
488 };
489 Ok(AliasExpression {
490 alias: alias_expr.alias,
491 expression: Box::new(Expression::Constant(ConstantExpression::Number {
492 fragment: Fragment::internal(tag.to_string()),
493 })),
494 fragment: alias_expr.fragment,
495 })
496}
497
498fn is_unresolved_ctor(ctor: &SumTypeConstructorExpression) -> bool {
499 ctor.namespace.text() == ctor.variant_name.text() && ctor.sumtype_name.text() == ctor.variant_name.text()
500}
501
502fn qualified_ctor_fragment(ctor: &SumTypeConstructorExpression) -> Fragment {
503 ctor.namespace.with_text(format!(
504 "{}::{}::{}",
505 ctor.namespace.text(),
506 ctor.sumtype_name.text(),
507 ctor.variant_name.text()
508 ))
509}
510
511fn find_ctor_sumtype(
512 catalog: &Catalog,
513 txn: &mut Transaction<'_>,
514 ctor: &SumTypeConstructorExpression,
515) -> Result<SumType> {
516 let ns_name = ctor.namespace.text();
517 let Some(ns) = catalog.find_namespace_by_name(txn, ns_name)? else {
518 return_error!(namespace_not_found(ctor.namespace.clone(), ns_name));
519 };
520 let sumtype_name = ctor.sumtype_name.text();
521 let Some(sumtype) = catalog.find_sumtype_by_name(txn, ns.id(), sumtype_name)? else {
522 return_error!(sumtype_not_found(ctor.sumtype_name.clone(), ns_name, sumtype_name));
523 };
524 Ok(sumtype)
525}
526
527fn declared_sumtype_id(source: &ResolvedObject, col_name: &str) -> Option<SumTypeId> {
528 let tag_col_name = format!("{}_tag", col_name);
529 let tag_col = source.columns().iter().find(|c| c.name == tag_col_name)?;
530 match tag_col.constraint.constraint() {
531 Some(Constraint::SumType(id)) => Some(*id),
532 _ => None,
533 }
534}
535
536fn expand_unit_variant_column(
537 ctx: &QueryContext,
538 txn: &mut Transaction<'_>,
539 source: Option<&ResolvedObject>,
540 alias_expr: AliasExpression,
541 expanded: &mut Vec<AliasExpression>,
542) -> Result<()> {
543 let (Some(source), Expression::Column(col)) = (source, alias_expr.expression.as_ref()) else {
544 expanded.push(alias_expr);
545 return Ok(());
546 };
547 let Some((sumtype, variant)) = try_resolve_unit_variant(
548 ctx,
549 txn,
550 source,
551 alias_expr.alias.0.text(),
552 col.0.name.text(),
553 alias_expr.expression.full_fragment_owned(),
554 )?
555 else {
556 expanded.push(alias_expr);
557 return Ok(());
558 };
559
560 push_all_variant_columns(
561 source,
562 ctx.source.is_none().then_some(source),
563 alias_expr.alias.0.text(),
564 &sumtype,
565 variant,
566 HashMap::new(),
567 &alias_expr.fragment,
568 expanded,
569 );
570 Ok(())
571}
572
573#[inline]
574fn try_resolve_unit_variant(
575 ctx: &QueryContext,
576 txn: &mut Transaction<'_>,
577 source: &ResolvedObject,
578 col_name: &str,
579 alias_text: &str,
580 variant: Fragment,
581) -> Result<Option<(SumType, usize)>> {
582 let Some(id) = declared_sumtype_id(source, col_name) else {
583 return Ok(None);
584 };
585 let sumtype = ctx.services.catalog.get_sumtype(txn, id)?;
586 let variant_name_lower = alias_text.to_lowercase();
587 let Some(index) = sumtype.variants.iter().position(|v| v.name.to_lowercase() == variant_name_lower) else {
588 return_error!(sumtype_variant_not_found(variant, &sumtype.name));
589 };
590 Ok(Some((sumtype, index)))
591}
592
593impl QueryNode for InlineDataNode {
594 #[instrument(level = "trace", skip_all, name = "volcano::inline::initialize")]
595 fn initialize<'a>(&mut self, rx: &mut Transaction<'a>, _ctx: &QueryContext) -> Result<()> {
596 self.load_series_tag(rx)?;
597 self.expand_sumtype_constructors(rx)?;
598 Ok(())
599 }
600
601 #[instrument(level = "trace", skip_all, name = "volcano::inline::next")]
602 fn next<'a>(&mut self, _rx: &mut Transaction<'a>, _ctx: &mut QueryContext) -> Result<Option<RecordBatch>> {
603 reifydb_assertions! {
604 assert!(self.context.is_some(), "InlineDataNode::next() called before initialize()");
605 }
606 let stored_ctx = self.context.as_ref().unwrap().clone();
607
608 if self.executed {
609 return Ok(None);
610 }
611
612 self.executed = true;
613
614 if self.rows.is_empty() {
615 let columns = empty_batch();
616 if self.headers.is_none() {
617 self.headers = Some(ColumnHeaders::from_batch(&columns));
618 }
619 return Ok(Some(columns));
620 }
621
622 if self.headers.is_some() {
623 self.next_with_source(&stored_ctx)
624 } else {
625 self.next_infer_namespace(&stored_ctx)
626 }
627 }
628
629 fn headers(&self) -> Option<ColumnHeaders> {
630 self.headers.clone()
631 }
632}
633
634type EvaluatedColumnValues = (Vec<(Value, ValueType, Fragment)>, Option<ValueType>);
635
636impl InlineDataNode {
637 fn find_optimal_integer_type(column: &ColumnView<'_>) -> ValueType {
638 let mut min_val = i128::MAX;
639 let mut max_val = i128::MIN;
640 let mut has_values = false;
641
642 for value in column.iter() {
643 match value {
644 Value::Int16(v) => {
645 has_values = true;
646 min_val = min_val.min(v);
647 max_val = max_val.max(v);
648 }
649 Value::None {
650 ..
651 } => {}
652 _ => {
653 return ValueType::Int16;
654 }
655 }
656 }
657
658 if !has_values {
659 return ValueType::Int1;
660 }
661
662 if min_val >= i8::MIN as i128 && max_val <= i8::MAX as i128 {
663 ValueType::Int1
664 } else if min_val >= i16::MIN as i128 && max_val <= i16::MAX as i128 {
665 ValueType::Int2
666 } else if min_val >= i32::MIN as i128 && max_val <= i32::MAX as i128 {
667 ValueType::Int4
668 } else if min_val >= i64::MIN as i128 && max_val <= i64::MAX as i128 {
669 ValueType::Int8
670 } else {
671 ValueType::Int16
672 }
673 }
674
675 fn widen_numeric(wide: ValueType, fractional: ValueType) -> ValueType {
676 match wide {
677 ValueType::Decimal {
678 ..
679 } if matches!(fractional, ValueType::Decimal { .. }) => common_key_type(&wide, &fractional).unwrap_or(wide),
680 _ if wide.is_number() => ValueType::promote(wide, fractional),
681 _ => wide,
682 }
683 }
684
685 #[instrument(level = "trace", skip_all, name = "volcano::inline::column_names")]
686 fn collect_column_names(rows: &[Vec<AliasExpression>]) -> BTreeSet<String> {
687 let mut all_columns: BTreeSet<String> = BTreeSet::new();
688
689 for row in rows {
690 for keyed_expr in row {
691 let column_name = keyed_expr.alias.0.text().to_string();
692 all_columns.insert(column_name);
693 }
694 }
695
696 all_columns
697 }
698
699 #[instrument(level = "trace", skip_all, name = "volcano::inline::row_maps")]
700 fn build_row_maps(rows: &[Vec<AliasExpression>]) -> Vec<HashMap<String, &AliasExpression>> {
701 let mut rows_data: Vec<HashMap<String, &AliasExpression>> = Vec::new();
702
703 for row in rows {
704 let mut row_map: HashMap<String, &AliasExpression> = HashMap::new();
705 for alias_expr in row {
706 let column_name = alias_expr.alias.0.text().to_string();
707 row_map.insert(column_name, alias_expr);
708 }
709 rows_data.push(row_map);
710 }
711
712 rows_data
713 }
714
715 #[instrument(level = "trace", skip_all, name = "volcano::inline::eval")]
716 fn eval_column_values(
717 session: &EvalContext<'_>,
718 rows_data: &[HashMap<String, &AliasExpression>],
719 column_name: &str,
720 ) -> Result<EvaluatedColumnValues> {
721 let mut all_values = Vec::new();
722 let mut first_value_type: Option<ValueType> = None;
723
724 for row_data in rows_data {
725 if let Some(alias_expr) = row_data.get(column_name) {
726 let eval_ctx = session.with_eval_empty();
727
728 let evaluated = evaluate(&eval_ctx, &alias_expr.expression)?;
729 let evaluated = ColumnView::try_from(&evaluated)?;
730
731 let evaluated_type = evaluated.get_type().inner_type().clone();
732 let mut iter = evaluated.iter();
733 if let Some(value) = iter.next() {
734 if first_value_type.is_none() && !matches!(value, Value::None { .. }) {
735 first_value_type = Some(evaluated_type.clone());
736 }
737 all_values.push((
738 value,
739 evaluated_type,
740 alias_expr.expression.full_fragment_owned(),
741 ));
742 } else {
743 all_values.push((Value::none(), ValueType::Any, Fragment::none()));
744 }
745 } else {
746 all_values.push((Value::none(), ValueType::Any, Fragment::none()));
747 }
748 }
749
750 Ok((all_values, first_value_type))
751 }
752
753 #[instrument(level = "trace", skip_all, name = "volcano::inline::materialize")]
754 fn materialize_inferred_column(
755 session: &EvalContext<'_>,
756 all_values: &[(Value, ValueType, Fragment)],
757 first_value_type: Option<ValueType>,
758 name: &str,
759 ) -> Result<(FieldRef, ArrayRef)> {
760 let wide_type = first_value_type.map(|fvt| {
761 let first = match fvt {
762 _ if fvt.is_integer() => ValueType::Int16,
763 _ if fvt.is_floating_point() => ValueType::Float8,
764 _ => fvt,
765 };
766 all_values
767 .iter()
768 .filter(|(value, _, _)| !matches!(value, Value::None { .. }))
769 .map(|(_, ty, _)| ty.clone())
770 .filter(|ty| {
771 ty.is_floating_point()
772 || ty.is_integer() || matches!(ty, ValueType::Decimal { .. })
773 })
774 .fold(first, |wide, ty| match wide {
775 ValueType::Decimal {
776 ..
777 } => Self::widen_numeric(wide, ty),
778 _ if ty.is_integer() => wide,
779 _ => Self::widen_numeric(wide, ty),
780 })
781 });
782
783 let mut column_data = if wide_type.is_none() {
784 let none_type = all_values.iter().find_map(|(value, _, _)| match value {
785 Value::None {
786 inner,
787 } if *inner != ValueType::Any => Some(inner.clone()),
788 _ => None,
789 });
790 match none_type {
791 Some(none_type) => none_typed(name, none_type, all_values.len()),
792 None => none(name, all_values.len()),
793 }
794 } else {
795 let wide = wide_type.clone().unwrap();
796 let mut casts = Self::cast_value_groups(session, all_values, &wide, name)?;
797 let mut data = ColumnBuilder::with_capacity(wide.clone(), 0);
798
799 for (row, (value, value_type, _)) in all_values.iter().enumerate() {
800 if matches!(value, Value::None { .. }) {
801 data.push_none();
802 } else if *value_type == wide {
803 data.push_value(value.clone());
804 } else if let Some(casted_value) = casts[row].take() {
805 data.push_value(casted_value);
806 } else {
807 data.push_none();
808 }
809 }
810
811 data.finish(name)
812 };
813
814 if wide_type == Some(ValueType::Int16) {
815 let optimal_type = Self::find_optimal_integer_type(&ColumnView::try_from(&column_data)?);
816 if optimal_type != ValueType::Int16 {
817 let eval_ctx = session.with_eval(empty_batch(), column_data.1.len());
818 let casted = cast_column_data(
819 &eval_ctx,
820 &ColumnView::try_from(&column_data)?,
821 optimal_type,
822 Fragment::none,
823 )?;
824 column_data = casted;
825 }
826 }
827
828 Ok(column_data)
829 }
830
831 fn cast_value_groups(
832 session: &EvalContext<'_>,
833 all_values: &[(Value, ValueType, Fragment)],
834 wide: &ValueType,
835 name: &str,
836 ) -> Result<Vec<Option<Value>>> {
837 let mut groups: Vec<(ValueType, Vec<usize>)> = Vec::new();
838 let mut singles: Vec<usize> = Vec::new();
839 for (row, (value, value_type, _)) in all_values.iter().enumerate() {
840 if matches!(value, Value::None { .. }) || value_type == wide {
841 continue;
842 }
843 if !Self::groups_by_type(value_type) {
844 singles.push(row);
845 continue;
846 }
847 match groups.iter_mut().find(|(ty, _)| ty == value_type) {
848 Some((_, rows)) => rows.push(row),
849 None => groups.push((value_type.clone(), vec![row])),
850 }
851 }
852
853 let mut casts: Vec<Option<Value>> = vec![None; all_values.len()];
854 let mut first_error: Option<(usize, Error)> = None;
855 for (value_type, rows) in groups {
856 let mut data = ColumnBuilder::with_capacity(value_type, rows.len());
857 for &row in &rows {
858 data.push_value(all_values[row].0.clone());
859 }
860 let data = data.finish(name);
861 let eval_ctx = session.with_eval_empty();
862 let fragment = all_values[rows[0]].2.clone();
863 match cast_column_data(&eval_ctx, &ColumnView::try_from(&data)?, wide.clone(), fragment) {
864 Ok(casted) => {
865 let casted = ColumnView::try_from(&casted)?;
866 for (position, &row) in rows.iter().enumerate() {
867 casts[row] = Some(casted.get_value(position));
868 }
869 }
870 Err(_) => Self::cast_each(
871 session,
872 all_values,
873 &rows,
874 wide,
875 name,
876 &mut casts,
877 &mut first_error,
878 )?,
879 }
880 }
881 Self::cast_each(session, all_values, &singles, wide, name, &mut casts, &mut first_error)?;
882
883 match first_error {
884 Some((_, error)) => Err(error),
885 None => Ok(casts),
886 }
887 }
888
889 fn groups_by_type(value_type: &ValueType) -> bool {
890 value_type.is_number()
891 || value_type.is_bool()
892 || value_type.is_utf8()
893 || value_type.is_blob()
894 || value_type.is_temporal()
895 || value_type.is_uuid()
896 || *value_type == ValueType::IdentityId
897 }
898
899 fn cast_each(
900 session: &EvalContext<'_>,
901 all_values: &[(Value, ValueType, Fragment)],
902 rows: &[usize],
903 wide: &ValueType,
904 name: &str,
905 casts: &mut [Option<Value>],
906 first_error: &mut Option<(usize, Error)>,
907 ) -> Result<()> {
908 for &row in rows {
909 if first_error.as_ref().is_some_and(|(failed, _)| *failed < row) {
910 break;
911 }
912 let (value, _, fragment) = &all_values[row];
913 let temp_data = from_one(name, value.clone());
914 let eval_ctx = session.with_eval_empty();
915 match cast_column_data(
916 &eval_ctx,
917 &ColumnView::try_from(&temp_data)?,
918 wide.clone(),
919 fragment.clone(),
920 ) {
921 Ok(casted) => casts[row] = ColumnView::try_from(&casted)?.iter().next(),
922 Err(error) => {
923 *first_error = Some((row, error));
924 break;
925 }
926 }
927 }
928 Ok(())
929 }
930
931 fn next_infer_namespace(&mut self, ctx: &QueryContext) -> Result<Option<RecordBatch>> {
932 let all_columns = Self::collect_column_names(&self.rows);
933 let rows_data = Self::build_row_maps(&self.rows);
934
935 let session = eval_context_from_query(ctx);
936
937 let mut columns = Vec::new();
938
939 for column_name in all_columns {
940 let (all_values, first_value_type) =
941 Self::eval_column_values(&session, &rows_data, &column_name)?;
942
943 columns.push(Self::materialize_inferred_column(
944 &session,
945 &all_values,
946 first_value_type,
947 &column_name,
948 )?);
949 }
950
951 let columns = batch(columns)?;
952 self.headers = Some(ColumnHeaders::from_batch(&columns));
953
954 Ok(Some(columns))
955 }
956
957 fn next_with_source(&mut self, ctx: &QueryContext) -> Result<Option<RecordBatch>> {
958 let source = ctx.source.as_ref().unwrap();
959 let headers = self.headers.as_ref().unwrap();
960 let session = eval_context_from_query(ctx);
961
962 if let Some(unknown) = self
963 .rows
964 .iter()
965 .flatten()
966 .find(|alias| !headers.columns.iter().any(|c| c.text() == alias.alias.0.text()))
967 {
968 return_error!(column_not_found(unknown.fragment.clone()));
969 }
970
971 let rows_data = Self::build_row_maps(&self.rows);
972
973 let mut columns = Vec::new();
974
975 for column_name in &headers.columns {
976 columns.push(Self::build_source_column(
977 &session,
978 source,
979 self.series_tag.as_ref(),
980 &rows_data,
981 column_name,
982 )?);
983 }
984
985 let columns = batch(columns)?;
986
987 Ok(Some(columns))
988 }
989
990 #[instrument(level = "trace", skip_all, name = "volcano::inline::source_column")]
991 fn build_source_column(
992 session: &EvalContext<'_>,
993 source: &ResolvedObject,
994 series_tag: Option<&SumType>,
995 rows_data: &[HashMap<String, &AliasExpression>],
996 column_name: &Fragment,
997 ) -> Result<(FieldRef, ArrayRef)> {
998 let table_column = source.columns().iter().find(|col| col.name == column_name.text());
999
1000 let mut column_data = if let Some(tc) = table_column {
1001 ColumnBuilder::with_capacity(tc.constraint.get_type(), rows_data.len())
1002 } else {
1003 ColumnBuilder::with_capacity(ValueType::Uint1, 0)
1004 };
1005
1006 for row_data in rows_data {
1007 if let Some(alias_expr) = row_data.get(column_name.text()) {
1008 let mut eval_ctx = session.with_eval_empty();
1009 eval_ctx.target = table_column.map(|tc| TargetColumn::Partial {
1010 source_name: Some(source.identifier().text().to_string()),
1011 column_name: Some(tc.name.clone()),
1012 column_type: tc.constraint.get_type(),
1013 properties: tc.properties.iter().map(|cp| cp.property.clone()).collect(),
1014 });
1015
1016 let evaluated = evaluate(&eval_ctx, &alias_expr.expression)?;
1017 let evaluated = ColumnView::try_from(&evaluated)?;
1018
1019 let eval_len = evaluated.len();
1020 if table_column.is_some() {
1021 if eval_len == 1 {
1022 column_data.extend(&evaluated)?;
1023 } else if eval_len == 0 {
1024 column_data.push_value(Value::none());
1025 } else {
1026 let first_value = evaluated.iter().next().unwrap_or(Value::none());
1027 column_data.push_value(first_value);
1028 }
1029 } else {
1030 let value = if eval_len > 0 {
1031 evaluated.iter().next().unwrap_or(Value::none())
1032 } else {
1033 Value::none()
1034 };
1035 if matches!(value, Value::None { .. }) {
1036 column_data.push_none();
1037 } else {
1038 let sumtype = series_tag.expect(
1039 "the only inline column outside the schema is the series tag",
1040 );
1041 column_data.push_value(Value::Uint1(resolve_variant_tag(
1042 sumtype,
1043 &value,
1044 alias_expr.expression.full_fragment_owned(),
1045 )?));
1046 }
1047 }
1048 } else {
1049 column_data.push_value(Value::none());
1050 }
1051 }
1052
1053 Ok(column_data.finish(column_name.text()))
1054 }
1055}