use std::cell::Cell;
use std::sync::Arc;
use arrow::datatypes::Schema;
use datafusion_common::{DataFusionError, Result};
use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
use datafusion_physical_expr_common::physical_expr::proto_decode::PhysicalExprDecode;
use datafusion_physical_expr_common::physical_expr::proto_encode::PhysicalExprEncode;
use datafusion_proto_models::protobuf::{self, PhysicalExprNode, physical_expr_node};
use crate::expressions::Column;
pub(crate) fn column_node(name: &str) -> PhysicalExprNode {
PhysicalExprNode {
expr_id: None,
expr_type: Some(physical_expr_node::ExprType::Column(
protobuf::PhysicalColumn {
name: name.to_string(),
index: 0,
},
)),
}
}
pub(crate) struct StubDecoder {
fail_on_call: Option<usize>,
calls: Cell<usize>,
}
impl StubDecoder {
pub(crate) fn ok() -> Self {
Self {
fail_on_call: None,
calls: Cell::new(0),
}
}
pub(crate) fn failing_on(call: usize) -> Self {
Self {
fail_on_call: Some(call),
calls: Cell::new(0),
}
}
}
impl PhysicalExprDecode for StubDecoder {
fn decode(
&self,
_node: &PhysicalExprNode,
_schema: &Schema,
) -> Result<Arc<dyn PhysicalExpr>> {
let call = self.calls.get() + 1;
self.calls.set(call);
if Some(call) == self.fail_on_call {
return Err(DataFusionError::Internal(format!(
"stub decode failure on call {call}"
)));
}
Ok(Arc::new(Column::new("decoded", 0)))
}
}
pub(crate) struct UnreachableDecoder;
impl PhysicalExprDecode for UnreachableDecoder {
fn decode(
&self,
_node: &PhysicalExprNode,
_schema: &Schema,
) -> Result<Arc<dyn PhysicalExpr>> {
unreachable!("decode must not be reached when the node is rejected")
}
}
pub(crate) struct StubEncoder {
fail_on_call: Option<usize>,
calls: Cell<usize>,
}
impl StubEncoder {
pub(crate) fn ok() -> Self {
Self {
fail_on_call: None,
calls: Cell::new(0),
}
}
pub(crate) fn failing_on(call: usize) -> Self {
Self {
fail_on_call: Some(call),
calls: Cell::new(0),
}
}
}
impl PhysicalExprEncode for StubEncoder {
fn encode(&self, _expr: &Arc<dyn PhysicalExpr>) -> Result<PhysicalExprNode> {
let call = self.calls.get() + 1;
self.calls.set(call);
if Some(call) == self.fail_on_call {
return Err(DataFusionError::Internal(format!(
"stub encode failure on call {call}"
)));
}
Ok(column_node("child"))
}
}