Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions datafusion/physical-plan/src/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,20 +673,34 @@ impl ExecutionPlan for ProjectionExec {
metrics: _,
// Derived plan properties, recomputed on decode.
cache: _,
// Derived metadata comparison, recomputed with the projector.
overrides_metadata: _,
overrides_metadata,
} = self;
let projection_exprs = projector.projection().as_ref();
let input = ctx.encode_child(input)?;
let expr = ctx.encode_expressions(projection_exprs.iter().map(|p| &p.expr))?;
let expr_name = projection_exprs.iter().map(|p| p.alias.clone()).collect();
let output_schema = projector.output_schema();
// Keep inherited metadata self-contained, and retain empty overrides
// that explicitly clear metadata from the input.
let schema = if *overrides_metadata
|| !output_schema.metadata().is_empty()
|| output_schema
.fields()
.iter()
.any(|field| !field.metadata().is_empty())
{
Some(output_schema.as_ref().try_into()?)
} else {
None
};
Ok(Some(protobuf::PhysicalPlanNode {
physical_plan_type: Some(
protobuf::physical_plan_node::PhysicalPlanType::Projection(Box::new(
protobuf::ProjectionExecNode {
input: Some(Box::new(input)),
expr,
expr_name,
schema,
},
)),
),
Expand Down Expand Up @@ -722,6 +736,7 @@ impl ProjectionExec {
input,
expr,
expr_name,
schema,
} = &**projection;
let input =
ctx.decode_required_child(input.as_deref(), "ProjectionExec", "input")?;
Expand All @@ -736,7 +751,15 @@ impl ProjectionExec {
})
})
.collect::<Result<Vec<ProjectionExpr>>>()?;
Ok(Arc::new(ProjectionExec::try_new(exprs, input)?))
let projection = match schema {
Some(schema) => ProjectionExec::try_new_with_schema_metadata(
exprs,
input,
&Schema::try_from(schema)?,
)?,
None => ProjectionExec::try_new(exprs, input)?,
};
Ok(Arc::new(projection))
}
}

Expand Down
4 changes: 4 additions & 0 deletions datafusion/proto-models/proto/datafusion.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,10 @@ message ProjectionExecNode {
PhysicalPlanNode input = 1;
repeated PhysicalExprNode expr = 2;
repeated string expr_name = 3;
// Only field and schema metadata are used; output types are derived from expr.
// Present when the projection has field or schema metadata, or explicitly clears
// input metadata. Absent for older plans and metadata-free projections.
datafusion_common.Schema schema = 4;
}

enum AggregateMode {
Expand Down
17 changes: 17 additions & 0 deletions datafusion/proto-models/src/generated/pbjson.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24074,6 +24074,9 @@ impl serde::Serialize for ProjectionExecNode {
if !self.expr_name.is_empty() {
len += 1;
}
if self.schema.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("datafusion.ProjectionExecNode", len)?;
if let Some(v) = self.input.as_ref() {
struct_ser.serialize_field("input", v)?;
Expand All @@ -24084,6 +24087,9 @@ impl serde::Serialize for ProjectionExecNode {
if !self.expr_name.is_empty() {
struct_ser.serialize_field("exprName", &self.expr_name)?;
}
if let Some(v) = self.schema.as_ref() {
struct_ser.serialize_field("schema", v)?;
}
struct_ser.end()
}
}
Expand All @@ -24098,13 +24104,15 @@ impl<'de> serde::Deserialize<'de> for ProjectionExecNode {
"expr",
"expr_name",
"exprName",
"schema",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Input,
Expr,
ExprName,
Schema,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
fn deserialize<D>(deserializer: D) -> std::result::Result<GeneratedField, D::Error>
Expand All @@ -24129,6 +24137,7 @@ impl<'de> serde::Deserialize<'de> for ProjectionExecNode {
"input" => Ok(GeneratedField::Input),
"expr" => Ok(GeneratedField::Expr),
"exprName" | "expr_name" => Ok(GeneratedField::ExprName),
"schema" => Ok(GeneratedField::Schema),
_ => Err(serde::de::Error::unknown_field(value, FIELDS)),
}
}
Expand All @@ -24151,6 +24160,7 @@ impl<'de> serde::Deserialize<'de> for ProjectionExecNode {
let mut input__ = None;
let mut expr__ = None;
let mut expr_name__ = None;
let mut schema__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Input => {
Expand All @@ -24171,12 +24181,19 @@ impl<'de> serde::Deserialize<'de> for ProjectionExecNode {
}
expr_name__ = Some(map_.next_value()?);
}
GeneratedField::Schema => {
if schema__.is_some() {
return Err(serde::de::Error::duplicate_field("schema"));
}
schema__ = map_.next_value()?;
}
}
}
Ok(ProjectionExecNode {
input: input__,
expr: expr__.unwrap_or_default(),
expr_name: expr_name__.unwrap_or_default(),
schema: schema__,
})
}
}
Expand Down
5 changes: 5 additions & 0 deletions datafusion/proto-models/src/generated/prost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2231,6 +2231,11 @@ pub struct ProjectionExecNode {
pub expr: ::prost::alloc::vec::Vec<PhysicalExprNode>,
#[prost(string, repeated, tag = "3")]
pub expr_name: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// Only field and schema metadata are used; output types are derived from expr.
/// Present when the projection has field or schema metadata, or explicitly clears
/// input metadata. Absent for older plans and metadata-free projections.
#[prost(message, optional, tag = "4")]
pub schema: ::core::option::Option<super::datafusion_common::Schema>,
}
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct PartiallySortedInputOrderMode {
Expand Down
Loading