reifydb_engine/vm/volcano/
map.rs1use std::{mem, sync::Arc};
5
6use reifydb_core::{
7 interface::{evaluate::TargetColumn, resolved::ResolvedColumn},
8 value::column::{ColumnWithName, columns::Columns, headers::ColumnHeaders},
9};
10use reifydb_extension::transform::{Transform, context::TransformContext};
11use reifydb_rql::expression::{Expression, name::display_label};
12use reifydb_transaction::transaction::Transaction;
13use reifydb_value::{fragment::Fragment, reifydb_assertions, util::cowvec::CowVec};
14use tracing::instrument;
15
16use super::NoopNode;
17use crate::{
18 Result,
19 expression::{
20 cast::cast_column_data,
21 compile::{CompiledExpr, compile_expression},
22 context::{CompileContext, EvalContext},
23 },
24 vm::volcano::{
25 query::{QueryContext, QueryNode},
26 udf::{UdfEvalNode, evaluate_udfs_no_input, strip_udf_columns},
27 },
28};
29
30pub(crate) struct MapNode {
31 input: Box<dyn QueryNode>,
32 expressions: Vec<Expression>,
33 udf_names: Vec<String>,
34 headers: Option<ColumnHeaders>,
35 context: Option<(Arc<QueryContext>, Vec<CompiledExpr>)>,
36}
37
38impl MapNode {
39 pub fn new(input: Box<dyn QueryNode>, expressions: Vec<Expression>) -> Self {
40 Self {
41 input,
42 expressions,
43 udf_names: Vec::new(),
44 headers: None,
45 context: None,
46 }
47 }
48}
49
50impl QueryNode for MapNode {
51 #[instrument(name = "volcano::map::initialize", level = "trace", skip_all)]
52 fn initialize<'a>(&mut self, rx: &mut Transaction<'a>, ctx: &QueryContext) -> Result<()> {
53 let (input, expressions, udf_names) = UdfEvalNode::wrap_if_needed(
54 mem::replace(&mut self.input, Box::new(NoopNode)),
55 &self.expressions,
56 &ctx.symbols,
57 );
58 self.input = input;
59 self.expressions = expressions;
60 self.udf_names = udf_names;
61
62 let compile_ctx = CompileContext {
63 symbols: &ctx.symbols,
64 };
65 let compiled = self
66 .expressions
67 .iter()
68 .map(|e| compile_expression(&compile_ctx, e).expect("compile"))
69 .collect();
70 self.context = Some((Arc::new(ctx.clone()), compiled));
71 let column_names = self.expressions.iter().map(display_label).collect();
72 self.headers = Some(ColumnHeaders {
73 columns: column_names,
74 });
75 self.input.initialize(rx, ctx)?;
76 Ok(())
77 }
78
79 #[instrument(name = "volcano::map::next", level = "trace", skip_all)]
80 fn next<'a>(&mut self, rx: &mut Transaction<'a>, ctx: &mut QueryContext) -> Result<Option<Columns>> {
81 reifydb_assertions! {
82 assert!(self.context.is_some(), "MapNode::next() called before initialize()");
83 }
84
85 if let Some(columns) = self.input.next(rx, ctx)? {
86 let stored_ctx = &self.context.as_ref().unwrap().0;
87 let transform_ctx = TransformContext {
88 routines: &ctx.services.routines,
89 runtime_context: &stored_ctx.services.runtime_context,
90 params: &stored_ctx.params,
91 };
92 let mut result = self.apply(&transform_ctx, columns)?;
93 strip_udf_columns(&mut result, &self.udf_names);
94
95 Ok(Some(result))
96 } else {
97 Ok(None)
98 }
99 }
100
101 fn headers(&self) -> Option<ColumnHeaders> {
102 self.headers.clone().or(self.input.headers())
103 }
104}
105
106impl Transform for MapNode {
107 fn apply(&self, ctx: &TransformContext, input: Columns) -> Result<Columns> {
108 let (stored_ctx, compiled) =
109 self.context.as_ref().expect("MapNode::apply() called before initialize()");
110
111 let row_count = input.row_count();
112 let session = EvalContext::from_transform(ctx, stored_ctx);
113 let mut new_columns = Vec::with_capacity(compiled.len());
114
115 for (expr, compiled_expr) in self.expressions.iter().zip(compiled.iter()) {
116 let mut exec_ctx = session.with_eval(input.clone(), row_count);
117
118 if let (Expression::Alias(alias_expr), Some(source)) = (expr, &stored_ctx.source) {
119 let alias_name = alias_expr.alias.name();
120 if let Some(table_column) = source.columns().iter().find(|col| col.name == alias_name) {
121 let column_ident = Fragment::internal(&table_column.name);
122 let resolved_column =
123 ResolvedColumn::new(column_ident, source.clone(), table_column.clone());
124 exec_ctx.target = Some(TargetColumn::Resolved(resolved_column));
125 }
126 }
127
128 let mut column = compiled_expr.execute(&exec_ctx)?;
129
130 if let Some(target_type) = exec_ctx.target.as_ref().map(|t| t.column_type())
131 && column.data.get_type() != target_type
132 {
133 let data =
134 cast_column_data(&exec_ctx, &column.data, target_type, &expr.lazy_fragment())?;
135 column = ColumnWithName {
136 name: column.name,
137 data,
138 };
139 }
140
141 new_columns.push(column);
142 }
143
144 let mut names_vec = Vec::with_capacity(new_columns.len());
145 let mut buffers_vec = Vec::with_capacity(new_columns.len());
146 for c in new_columns {
147 names_vec.push(c.name);
148 buffers_vec.push(c.data);
149 }
150 Ok(Columns {
151 row_numbers: input.row_numbers,
152 created_at: input.created_at,
153 updated_at: input.updated_at,
154 columns: CowVec::new(buffers_vec),
155 names: CowVec::new(names_vec),
156 })
157 }
158}
159
160pub(crate) struct MapWithoutInputNode {
161 expressions: Vec<Expression>,
162 headers: Option<ColumnHeaders>,
163
164 udf_columns: Option<Columns>,
165 context: Option<(Arc<QueryContext>, Vec<CompiledExpr>)>,
166}
167
168impl MapWithoutInputNode {
169 pub fn new(expressions: Vec<Expression>) -> Self {
170 Self {
171 expressions,
172 headers: None,
173 udf_columns: None,
174 context: None,
175 }
176 }
177}
178
179impl QueryNode for MapWithoutInputNode {
180 #[instrument(name = "volcano::map::noinput::initialize", level = "trace", skip_all)]
181 fn initialize<'a>(&mut self, rx: &mut Transaction<'a>, ctx: &QueryContext) -> Result<()> {
182 if let Some((rewritten, udf_cols)) = evaluate_udfs_no_input(&self.expressions, ctx, rx)? {
183 self.expressions = rewritten;
184 self.udf_columns = Some(udf_cols);
185 }
186
187 let compile_ctx = CompileContext {
188 symbols: &ctx.symbols,
189 };
190 let compiled = self
191 .expressions
192 .iter()
193 .map(|e| compile_expression(&compile_ctx, e).expect("compile"))
194 .collect();
195 self.context = Some((Arc::new(ctx.clone()), compiled));
196 Ok(())
197 }
198
199 #[instrument(name = "volcano::map::noinput::next", level = "trace", skip_all)]
200 fn next<'a>(&mut self, _rx: &mut Transaction<'a>, _ctx: &mut QueryContext) -> Result<Option<Columns>> {
201 reifydb_assertions! {
202 assert!(self.context.is_some(), "MapWithoutInputNode::next() called before initialize()");
203 }
204 let (stored_ctx, compiled) = self.context.as_ref().unwrap();
205
206 if self.headers.is_some() {
207 return Ok(None);
208 }
209
210 let session = EvalContext::from_query(stored_ctx);
211 let mut columns = vec![];
212
213 for compiled_expr in compiled {
214 let exec_ctx = match &self.udf_columns {
215 Some(udf_cols) => session.with_eval(udf_cols.clone(), 1),
216 None => session.with_eval_empty(),
217 };
218
219 let column = compiled_expr.execute(&exec_ctx)?;
220
221 columns.push(column);
222 }
223
224 let columns = Columns::new(columns);
225 self.headers = Some(ColumnHeaders::from_columns(&columns));
226 Ok(Some(columns))
227 }
228
229 fn headers(&self) -> Option<ColumnHeaders> {
230 self.headers.clone()
231 }
232}