use std::sync::Arc;
use futures::StreamExt;
use crate::err::Error;
use crate::exec::{
AccessMode, CardinalityHint, ContextLevel, ExecOperator, ExecutionContext, FlowResult,
OperatorMetrics, ValueBatch, ValueBatchStream, buffer_stream, monitor_stream,
};
use crate::expr::ControlFlow;
use crate::val::Value;
#[derive(Debug, Clone)]
pub struct UnwrapExactlyOne {
pub(crate) input: Arc<dyn ExecOperator>,
pub(crate) none_on_empty: bool,
pub(crate) metrics: Arc<OperatorMetrics>,
}
impl UnwrapExactlyOne {
pub(crate) fn new(input: Arc<dyn ExecOperator>, none_on_empty: bool) -> Self {
Self {
input,
none_on_empty,
metrics: Arc::new(OperatorMetrics::new()),
}
}
}
impl ExecOperator for UnwrapExactlyOne {
fn name(&self) -> &'static str {
"UnwrapExactlyOne"
}
fn required_context(&self) -> ContextLevel {
self.input.required_context()
}
fn access_mode(&self) -> AccessMode {
self.input.access_mode()
}
fn cardinality_hint(&self) -> CardinalityHint {
CardinalityHint::AtMostOne
}
fn children(&self) -> Vec<&Arc<dyn ExecOperator>> {
vec![&self.input]
}
fn metrics(&self) -> Option<&OperatorMetrics> {
Some(&self.metrics)
}
fn is_scalar(&self) -> bool {
true
}
fn output_ordering(&self) -> crate::exec::OutputOrdering {
self.input.output_ordering()
}
fn execute(&self, ctx: &ExecutionContext) -> FlowResult<ValueBatchStream> {
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 none_on_empty = self.none_on_empty;
let unwrap_stream = async_stream::try_stream! {
futures::pin_mut!(input_stream);
let mut collected: Vec<Value> = Vec::new();
while let Some(batch_result) = input_stream.next().await {
let batch = batch_result?;
collected.extend(batch.values);
if collected.len() > 1 {
Err(ControlFlow::Err(anyhow::anyhow!(Error::SingleOnlyOutput)))?;
}
}
if collected.is_empty() {
if none_on_empty {
yield ValueBatch {
values: vec![Value::None],
};
} else {
Err(ControlFlow::Err(anyhow::anyhow!(Error::SingleOnlyOutput)))?;
}
} else {
let result = collected.pop().expect("collected has exactly one element");
yield ValueBatch {
values: vec![result],
};
}
};
Ok(monitor_stream(Box::pin(unwrap_stream), "UnwrapExactlyOne", &self.metrics))
}
}