use std::sync::Arc;
use futures::StreamExt;
use crate::exec::{
AccessMode, CardinalityHint, CombineAccessModes, ContextLevel, EvalContext, ExecOperator,
ExecutionContext, FlowResult, OperatorMetrics, PhysicalExpr, ValueBatch, ValueBatchStream,
buffer_stream, monitor_stream,
};
use crate::expr::ControlFlow;
use crate::val::{Object, Strand, Value};
#[derive(Debug, Clone)]
pub struct Compute {
pub input: Arc<dyn ExecOperator>,
pub fields: Vec<(Strand, Arc<dyn PhysicalExpr>)>,
pub(crate) metrics: Arc<OperatorMetrics>,
}
impl Compute {
pub(crate) fn new(
input: Arc<dyn ExecOperator>,
fields: Vec<(Strand, Arc<dyn PhysicalExpr>)>,
) -> Self {
Self {
input,
fields,
metrics: Arc::new(OperatorMetrics::new()),
}
}
}
impl ExecOperator for Compute {
fn name(&self) -> &'static str {
"Compute"
}
fn attrs(&self) -> Vec<(String, String)> {
let fields_str = self
.fields
.iter()
.map(|(name, expr)| format!("{} = {}", name, expr.to_sql()))
.collect::<Vec<_>>()
.join(", ");
vec![("fields".to_string(), fields_str)]
}
fn required_context(&self) -> ContextLevel {
let expr_ctx = self
.fields
.iter()
.map(|(_, expr)| expr.required_context())
.max()
.unwrap_or(ContextLevel::Root);
self.input.required_context().max(expr_ctx)
}
fn access_mode(&self) -> AccessMode {
let expr_mode = self.fields.iter().map(|(_, expr)| expr.access_mode()).combine_all();
self.input.access_mode().combine(expr_mode)
}
fn cardinality_hint(&self) -> CardinalityHint {
self.input.cardinality_hint()
}
fn children(&self) -> Vec<&Arc<dyn ExecOperator>> {
vec![&self.input]
}
fn metrics(&self) -> Option<&OperatorMetrics> {
Some(&self.metrics)
}
fn expressions(&self) -> Vec<(&str, &Arc<dyn PhysicalExpr>)> {
self.fields.iter().map(|(name, expr)| (name.as_str(), expr)).collect()
}
fn output_ordering(&self) -> crate::exec::OutputOrdering {
self.input.output_ordering()
}
fn execute(&self, ctx: &ExecutionContext) -> FlowResult<ValueBatchStream> {
if self.fields.is_empty() {
return self.input.execute(ctx);
}
let input_stream = buffer_stream(
self.input.execute(ctx)?,
self.input.access_mode(),
self.input.cardinality_hint(),
ctx.root().ctx.config.operator_buffer_size,
);
let fields = self.fields.clone();
let ctx = ctx.clone();
let computed = input_stream.then(move |batch_result| {
let fields = fields.clone();
let ctx = ctx.clone();
async move {
let batch = batch_result?;
let eval_ctx = EvalContext::from_exec_ctx(&ctx);
compute_batch(batch.values, &fields, eval_ctx).await
}
});
Ok(monitor_stream(Box::pin(computed), "Compute", &self.metrics))
}
}
async fn compute_batch(
values: Vec<Value>,
fields: &[(Strand, Arc<dyn PhysicalExpr>)],
eval_ctx: EvalContext<'_>,
) -> Result<ValueBatch, ControlFlow> {
let mut seeded: Vec<Option<Object>> = Vec::with_capacity(values.len());
for v in values.iter() {
let row = match v {
Value::Object(_) => None,
Value::Geometry(geo) => Some(geo.as_object()),
Value::RecordId(rid) => {
let o = if let Value::Object(v) =
super::fetch::fetch_record(eval_ctx.exec_ctx, rid).await?
{
v
} else {
Object::default()
};
Some(o)
}
_ => Some(Object::default()),
};
seeded.push(row);
}
let mut field_results: Vec<Vec<Value>> = Vec::with_capacity(fields.len());
for (_name, expr) in fields {
let computed = match expr.evaluate_batch(eval_ctx.clone(), &values).await {
Ok(v) => v,
Err(ControlFlow::Return(_)) => {
let mut per_row = Vec::with_capacity(values.len());
for value in values.iter() {
let v = match expr.evaluate(eval_ctx.with_value(value)).await {
Ok(v) => v,
Err(ControlFlow::Return(v)) => v,
Err(e) => return Err(e),
};
per_row.push(v);
}
per_row
}
Err(e) => return Err(e),
};
field_results.push(computed);
}
let mut objects: Vec<Object> = Vec::with_capacity(values.len());
for (v, sr) in values.into_iter().zip(seeded) {
let o = match sr {
Some(o) => o,
None => {
if let Value::Object(o) = v {
o
} else {
Object::default()
}
}
};
objects.push(o);
}
for ((name, _expr), computed_values) in fields.iter().zip(field_results) {
for (i, computed) in computed_values.into_iter().enumerate() {
objects[i].insert(name.clone(), computed);
}
}
Ok(ValueBatch {
values: objects.into_iter().map(Value::Object).collect(),
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::exec::physical_expr::Literal;
use crate::val::Number;
fn literal_expr(value: i64) -> Arc<dyn PhysicalExpr> {
Arc::new(Literal(Value::Number(Number::Int(value))))
}
#[test]
fn test_compute_attrs() {
use crate::exec::operators::SourceExpr;
let source = Arc::new(SourceExpr::new(literal_expr(1)));
let compute = Compute::new(
source,
vec![(Strand::new("a"), literal_expr(42)), (Strand::new("b"), literal_expr(100))],
);
assert_eq!(compute.name(), "Compute");
assert_eq!(compute.fields.len(), 2);
assert!(!compute.fields.is_empty());
let attrs = compute.attrs();
assert_eq!(attrs.len(), 1);
assert!(attrs[0].1.contains("a = 42"));
assert!(attrs[0].1.contains("b = 100"));
}
#[test]
fn test_compute_empty() {
use crate::exec::operators::SourceExpr;
let source = Arc::new(SourceExpr::new(literal_expr(1)));
let compute = Compute::new(source, vec![]);
assert!(compute.fields.is_empty());
assert_eq!(compute.fields.len(), 0);
}
}