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
27 changes: 12 additions & 15 deletions datafusion/proto/src/physical_plan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1179,15 +1179,11 @@ pub trait PhysicalPlanNodeExt: Sized {
})?;
let schema: SchemaRef = SchemaRef::new(proto_schema.try_into()?);

let projection = if !scan.projection.is_empty() {
Some(
scan.projection
.iter()
.map(|i| *i as usize)
.collect::<Vec<_>>(),
)
} else {
None
// Preserve the empty-projection sentinel written by `try_from_data_source_exec`.
let projection = match scan.projection.as_slice() {
[] => None,
[u32::MAX] => Some(Vec::new()),
indices => Some(indices.iter().map(|i| *i as usize).collect()),
};

let mut sort_information = vec![];
Expand Down Expand Up @@ -2364,12 +2360,13 @@ pub trait PhysicalPlanNodeExt: Sized {
let proto_schema: protobuf::Schema =
source_conf.original_schema().as_ref().try_into()?;

let proto_projection = source_conf
.projection()
.as_ref()
.map_or_else(Vec::new, |v| {
v.iter().map(|x| *x as u32).collect::<Vec<u32>>()
});
// Proto3 can't tell `None` from `Some(vec![])`; encode the latter
// as the `[u32::MAX]` sentinel, matching the join/filter nodes.
let proto_projection = match source_conf.projection().as_ref() {
None => Vec::new(),
Some(v) if v.is_empty() => vec![u32::MAX],
Some(v) => v.iter().map(|x| *x as u32).collect(),
};

let proto_sort_information = source_conf
.sort_information()
Expand Down
19 changes: 19 additions & 0 deletions datafusion/proto/tests/cases/roundtrip_physical_plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2678,6 +2678,25 @@ async fn roundtrip_empty_projection() -> Result<()> {
roundtrip_test_sql_with_context(sql, &ctx).await
}

#[tokio::test]
async fn roundtrip_memory_source_empty_projection() -> Result<()> {
// Memory scan: `Some(vec![])` must not decode back as `None`
let ctx = SessionContext::new();
let batch = RecordBatch::try_new(
Arc::new(Schema::new(vec![
Field::new("a", DataType::Utf8, false),
Field::new("b", DataType::Int64, false),
])),
vec![
Arc::new(arrow::array::StringArray::from(vec!["Tom"])),
Arc::new(arrow::array::Int64Array::from(vec![18i64])),
],
)?;
ctx.register_batch("tmem", batch)?;
let sql = "select 1 from tmem";
roundtrip_test_sql_with_context(sql, &ctx).await
}

#[tokio::test]
async fn roundtrip_physical_plan_node() {
use datafusion::prelude::*;
Expand Down
Loading