From 39186d01db76810a09e97d4be349bab00ebf587c Mon Sep 17 00:00:00 2001 From: saadtajwar Date: Wed, 17 Jun 2026 14:41:53 -0400 Subject: [PATCH 1/6] feat: supporting logical protobuf serialization for range repartitioning --- .../proto-models/proto/datafusion.proto | 10 + .../proto-models/src/generated/pbjson.rs | 213 ++++++++++++++++++ .../proto-models/src/generated/prost.rs | 16 +- 3 files changed, 238 insertions(+), 1 deletion(-) diff --git a/datafusion/proto-models/proto/datafusion.proto b/datafusion/proto-models/proto/datafusion.proto index 322395ab3728c..0c078f97fab8c 100644 --- a/datafusion/proto-models/proto/datafusion.proto +++ b/datafusion/proto-models/proto/datafusion.proto @@ -148,9 +148,19 @@ message RepartitionNode { oneof partition_method { uint64 round_robin = 2; HashRepartition hash = 3; + RangeRepartition range = 4; } } +message RangeSplitPoint { + repeated datafusion_common.ScalarValue value = 1; +} + +message RangeRepartition { + repeated SortExprNode expr = 1; + repeated RangeSplitPoint split_points = 2; +} + message HashRepartition { repeated LogicalExprNode hash_expr = 1; uint64 partition_count = 2; diff --git a/datafusion/proto-models/src/generated/pbjson.rs b/datafusion/proto-models/src/generated/pbjson.rs index 1eb9de00fb362..7a54ca64fd2c2 100644 --- a/datafusion/proto-models/src/generated/pbjson.rs +++ b/datafusion/proto-models/src/generated/pbjson.rs @@ -21555,6 +21555,206 @@ impl<'de> serde::Deserialize<'de> for ProjectionNode { deserializer.deserialize_struct("datafusion.ProjectionNode", FIELDS, GeneratedVisitor) } } +impl serde::Serialize for RangeRepartition { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.expr.is_empty() { + len += 1; + } + if !self.split_points.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("datafusion.RangeRepartition", len)?; + if !self.expr.is_empty() { + struct_ser.serialize_field("expr", &self.expr)?; + } + if !self.split_points.is_empty() { + struct_ser.serialize_field("splitPoints", &self.split_points)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for RangeRepartition { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "expr", + "split_points", + "splitPoints", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Expr, + SplitPoints, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "expr" => Ok(GeneratedField::Expr), + "splitPoints" | "split_points" => Ok(GeneratedField::SplitPoints), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = RangeRepartition; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct datafusion.RangeRepartition") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut expr__ = None; + let mut split_points__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Expr => { + if expr__.is_some() { + return Err(serde::de::Error::duplicate_field("expr")); + } + expr__ = Some(map_.next_value()?); + } + GeneratedField::SplitPoints => { + if split_points__.is_some() { + return Err(serde::de::Error::duplicate_field("splitPoints")); + } + split_points__ = Some(map_.next_value()?); + } + } + } + Ok(RangeRepartition { + expr: expr__.unwrap_or_default(), + split_points: split_points__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("datafusion.RangeRepartition", FIELDS, GeneratedVisitor) + } +} +impl serde::Serialize for RangeSplitPoint { + #[allow(deprecated)] + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + let mut len = 0; + if !self.value.is_empty() { + len += 1; + } + let mut struct_ser = serializer.serialize_struct("datafusion.RangeSplitPoint", len)?; + if !self.value.is_empty() { + struct_ser.serialize_field("value", &self.value)?; + } + struct_ser.end() + } +} +impl<'de> serde::Deserialize<'de> for RangeSplitPoint { + #[allow(deprecated)] + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + const FIELDS: &[&str] = &[ + "value", + ]; + + #[allow(clippy::enum_variant_names)] + enum GeneratedField { + Value, + } + impl<'de> serde::Deserialize<'de> for GeneratedField { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::Deserializer<'de>, + { + struct GeneratedVisitor; + + impl serde::de::Visitor<'_> for GeneratedVisitor { + type Value = GeneratedField; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(formatter, "expected one of: {:?}", &FIELDS) + } + + #[allow(unused_variables)] + fn visit_str(self, value: &str) -> std::result::Result + where + E: serde::de::Error, + { + match value { + "value" => Ok(GeneratedField::Value), + _ => Err(serde::de::Error::unknown_field(value, FIELDS)), + } + } + } + deserializer.deserialize_identifier(GeneratedVisitor) + } + } + struct GeneratedVisitor; + impl<'de> serde::de::Visitor<'de> for GeneratedVisitor { + type Value = RangeSplitPoint; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("struct datafusion.RangeSplitPoint") + } + + fn visit_map(self, mut map_: V) -> std::result::Result + where + V: serde::de::MapAccess<'de>, + { + let mut value__ = None; + while let Some(k) = map_.next_key()? { + match k { + GeneratedField::Value => { + if value__.is_some() { + return Err(serde::de::Error::duplicate_field("value")); + } + value__ = Some(map_.next_value()?); + } + } + } + Ok(RangeSplitPoint { + value: value__.unwrap_or_default(), + }) + } + } + deserializer.deserialize_struct("datafusion.RangeSplitPoint", FIELDS, GeneratedVisitor) + } +} impl serde::Serialize for RecursionUnnestOption { #[allow(deprecated)] fn serialize(&self, serializer: S) -> std::result::Result @@ -21983,6 +22183,9 @@ impl serde::Serialize for RepartitionNode { repartition_node::PartitionMethod::Hash(v) => { struct_ser.serialize_field("hash", v)?; } + repartition_node::PartitionMethod::Range(v) => { + struct_ser.serialize_field("range", v)?; + } } } struct_ser.end() @@ -21999,6 +22202,7 @@ impl<'de> serde::Deserialize<'de> for RepartitionNode { "round_robin", "roundRobin", "hash", + "range", ]; #[allow(clippy::enum_variant_names)] @@ -22006,6 +22210,7 @@ impl<'de> serde::Deserialize<'de> for RepartitionNode { Input, RoundRobin, Hash, + Range, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -22030,6 +22235,7 @@ impl<'de> serde::Deserialize<'de> for RepartitionNode { "input" => Ok(GeneratedField::Input), "roundRobin" | "round_robin" => Ok(GeneratedField::RoundRobin), "hash" => Ok(GeneratedField::Hash), + "range" => Ok(GeneratedField::Range), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -22070,6 +22276,13 @@ impl<'de> serde::Deserialize<'de> for RepartitionNode { return Err(serde::de::Error::duplicate_field("hash")); } partition_method__ = map_.next_value::<::std::option::Option<_>>()?.map(repartition_node::PartitionMethod::Hash) +; + } + GeneratedField::Range => { + if partition_method__.is_some() { + return Err(serde::de::Error::duplicate_field("range")); + } + partition_method__ = map_.next_value::<::std::option::Option<_>>()?.map(repartition_node::PartitionMethod::Range) ; } } diff --git a/datafusion/proto-models/src/generated/prost.rs b/datafusion/proto-models/src/generated/prost.rs index 3ac04a6164db8..810525f0eae40 100644 --- a/datafusion/proto-models/src/generated/prost.rs +++ b/datafusion/proto-models/src/generated/prost.rs @@ -214,7 +214,7 @@ pub struct SortNode { pub struct RepartitionNode { #[prost(message, optional, boxed, tag = "1")] pub input: ::core::option::Option<::prost::alloc::boxed::Box>, - #[prost(oneof = "repartition_node::PartitionMethod", tags = "2, 3")] + #[prost(oneof = "repartition_node::PartitionMethod", tags = "2, 3, 4")] pub partition_method: ::core::option::Option, } /// Nested message and enum types in `RepartitionNode`. @@ -225,9 +225,23 @@ pub mod repartition_node { RoundRobin(u64), #[prost(message, tag = "3")] Hash(super::HashRepartition), + #[prost(message, tag = "4")] + Range(super::RangeRepartition), } } #[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeSplitPoint { + #[prost(message, repeated, tag = "1")] + pub value: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeRepartition { + #[prost(message, repeated, tag = "1")] + pub expr: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub split_points: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] pub struct HashRepartition { #[prost(message, repeated, tag = "1")] pub hash_expr: ::prost::alloc::vec::Vec, From 5e7e9ece201731e68ad052f7ab45275dd1efecb9 Mon Sep 17 00:00:00 2001 From: saadtajwar Date: Wed, 17 Jun 2026 19:18:59 -0400 Subject: [PATCH 2/6] serialization to proto type and used in mod.rs --- datafusion/proto/src/logical_plan/mod.rs | 17 +++++++++++++---- datafusion/proto/src/logical_plan/to_proto.rs | 17 ++++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/datafusion/proto/src/logical_plan/mod.rs b/datafusion/proto/src/logical_plan/mod.rs index 35c2e76d880b9..ed27dcbd76cda 100644 --- a/datafusion/proto/src/logical_plan/mod.rs +++ b/datafusion/proto/src/logical_plan/mod.rs @@ -75,6 +75,7 @@ use datafusion_expr::{ use datafusion_proto_common::protobuf_common; use self::to_proto::{serialize_expr, serialize_exprs}; +use crate::logical_plan::to_proto::serialize_range_split_point; use crate::logical_plan::to_proto::serialize_sorts; use datafusion_catalog::TableProvider; use datafusion_catalog::default_table_source::{provider_as_source, source_as_provider}; @@ -1755,10 +1756,18 @@ impl AsLogicalPlan for LogicalPlanNode { Partitioning::RoundRobinBatch(partition_count) => { PartitionMethod::RoundRobin(*partition_count as u64) } - Partitioning::Range(_) => { - // TODO: Support range repartition protobuf serialization. - // Tracked by https://github.com/apache/datafusion/issues/22787 - return not_impl_err!("Range repartition"); + Partitioning::Range(range_partitioning) => { + let ordering = range_partitioning.ordering(); + let split_points = range_partitioning + .split_points() + .iter() + .map(serialize_range_split_point) + .collect::, ToProtoError>>()?; + + PartitionMethod::Range(protobuf::RangeRepartition { + expr: serialize_sorts(ordering, extension_codec)?, + split_points, + }) } Partitioning::DistributeBy(_) => { return not_impl_err!("DistributeBy"); diff --git a/datafusion/proto/src/logical_plan/to_proto.rs b/datafusion/proto/src/logical_plan/to_proto.rs index 71a6bd824a369..d65ae139c8bea 100644 --- a/datafusion/proto/src/logical_plan/to_proto.rs +++ b/datafusion/proto/src/logical_plan/to_proto.rs @@ -21,7 +21,7 @@ use std::collections::HashMap; -use datafusion_common::{NullEquality, TableReference, UnnestOptions}; +use datafusion_common::{NullEquality, SplitPoint, TableReference, UnnestOptions}; use datafusion_expr::WriteOp; use datafusion_expr::dml::InsertOp; use datafusion_expr::expr::{ @@ -686,6 +686,21 @@ where .collect::, Error>>() } +pub fn serialize_range_split_point( + split_point: &SplitPoint, +) -> Result { + Ok(protobuf::RangeSplitPoint { + value: split_point + .values() + .iter() + .map(|value| { + TryInto::::try_into(value) + .map_err(Into::into) + }) + .collect::>()?, + }) +} + impl FromProto for protobuf::TableReference { fn from_proto(t: TableReference) -> Self { use protobuf::table_reference::TableReferenceEnum; From 66fc0076fa565bdff60f6bf57bc1f7aa8a4a329e Mon Sep 17 00:00:00 2001 From: saadtajwar Date: Wed, 17 Jun 2026 19:53:28 -0400 Subject: [PATCH 3/6] from proto --- datafusion/proto/src/logical_plan/from_proto.rs | 13 ++++++++++++- datafusion/proto/src/logical_plan/mod.rs | 16 +++++++++++++--- datafusion/proto/src/logical_plan/to_proto.rs | 5 +---- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/datafusion/proto/src/logical_plan/from_proto.rs b/datafusion/proto/src/logical_plan/from_proto.rs index c68b83964f4cf..a803d6cefbdd5 100644 --- a/datafusion/proto/src/logical_plan/from_proto.rs +++ b/datafusion/proto/src/logical_plan/from_proto.rs @@ -20,7 +20,7 @@ use std::sync::Arc; use arrow::datatypes::{DataType, Field}; use datafusion_common::datatype::DataTypeExt; use datafusion_common::{ - NullEquality, RecursionUnnestOption, Result, ScalarValue, TableReference, + NullEquality, RecursionUnnestOption, Result, ScalarValue, SplitPoint, TableReference, UnnestOptions, exec_datafusion_err, internal_err, plan_datafusion_err, }; use datafusion_execution::TaskContext; @@ -786,3 +786,14 @@ fn parse_required_expr( fn proto_error>(message: S) -> Error { Error::General(message.into()) } + +pub fn parse_protobuf_range_split_point( + split_point: &protobuf::RangeSplitPoint, +) -> Result { + let values = split_point + .value + .iter() + .map(ScalarValue::try_from) + .collect::>()?; + Ok(SplitPoint::new(values)) +} diff --git a/datafusion/proto/src/logical_plan/mod.rs b/datafusion/proto/src/logical_plan/mod.rs index ed27dcbd76cda..d2c124b86c151 100644 --- a/datafusion/proto/src/logical_plan/mod.rs +++ b/datafusion/proto/src/logical_plan/mod.rs @@ -59,8 +59,8 @@ use datafusion_datasource_json::file_format::{ #[cfg(feature = "parquet")] use datafusion_datasource_parquet::file_format::{ParquetFormat, ParquetFormatFactory}; use datafusion_expr::{ - AggregateUDF, DmlStatement, FetchType, HigherOrderUDF, RecursiveQuery, SkipType, - TableSource, Unnest, WriteOp, + AggregateUDF, DmlStatement, FetchType, HigherOrderUDF, RangePartitioning, + RecursiveQuery, SkipType, TableSource, Unnest, WriteOp, }; use datafusion_expr::{ DistinctOn, DropView, Expr, JoinConstraint, LogicalPlan, LogicalPlanBuilder, @@ -72,7 +72,7 @@ use datafusion_expr::{ builder::project, }, }; -use datafusion_proto_common::protobuf_common; +use datafusion_proto_common::{FromProtoError, protobuf_common}; use self::to_proto::{serialize_expr, serialize_exprs}; use crate::logical_plan::to_proto::serialize_range_split_point; @@ -747,6 +747,16 @@ impl AsLogicalPlan for LogicalPlanNode { PartitionMethod::RoundRobin(partition_count) => { Partitioning::RoundRobinBatch(*partition_count as usize) } + PartitionMethod::Range(protobuf::RangeRepartition { + expr: pb_sort_expr, + split_points, + }) => Partitioning::Range(RangePartitioning::try_new( + from_proto::parse_sorts(pb_sort_expr, ctx, extension_codec)?, + split_points + .iter() + .map(from_proto::parse_protobuf_range_split_point) + .collect::, FromProtoError>>()?, + )?), }; LogicalPlanBuilder::from(input) diff --git a/datafusion/proto/src/logical_plan/to_proto.rs b/datafusion/proto/src/logical_plan/to_proto.rs index d65ae139c8bea..148d92ca8b9b8 100644 --- a/datafusion/proto/src/logical_plan/to_proto.rs +++ b/datafusion/proto/src/logical_plan/to_proto.rs @@ -693,10 +693,7 @@ pub fn serialize_range_split_point( value: split_point .values() .iter() - .map(|value| { - TryInto::::try_into(value) - .map_err(Into::into) - }) + .map(TryInto::::try_into) .collect::>()?, }) } From 7542d1c09514c2a927155331e5aace63ef05db53 Mon Sep 17 00:00:00 2001 From: saadtajwar Date: Thu, 18 Jun 2026 20:23:49 -0400 Subject: [PATCH 4/6] roundtrip test added --- .../tests/cases/roundtrip_logical_plan.rs | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/datafusion/proto/tests/cases/roundtrip_logical_plan.rs b/datafusion/proto/tests/cases/roundtrip_logical_plan.rs index 7f1d0a666fdce..c265edeb5bb7e 100644 --- a/datafusion/proto/tests/cases/roundtrip_logical_plan.rs +++ b/datafusion/proto/tests/cases/roundtrip_logical_plan.rs @@ -73,8 +73,8 @@ use datafusion_common::format::{ }; use datafusion_common::scalar::ScalarStructBuilder; use datafusion_common::{ - DFSchema, DFSchemaRef, DataFusionError, Result, ScalarValue, TableReference, - internal_datafusion_err, internal_err, not_impl_err, plan_err, + DFSchema, DFSchemaRef, DataFusionError, Result, ScalarValue, SplitPoint, + TableReference, internal_datafusion_err, internal_err, not_impl_err, plan_err, }; use datafusion_execution::TaskContext; use datafusion_expr::dml::CopyTo; @@ -88,8 +88,9 @@ use datafusion_expr::logical_plan::{ use datafusion_expr::{ Accumulator, AggregateUDF, ColumnarValue, ExprFunctionExt, ExprSchemable, LimitEffect, Literal, LogicalPlan, LogicalPlanBuilder, Operator, PartitionEvaluator, - ScalarUDF, Signature, TryCast, Volatility, WindowFrame, WindowFrameBound, - WindowFrameUnits, WindowFunctionDefinition, WindowUDF, WindowUDFImpl, + RangePartitioning, Repartition, ScalarUDF, Signature, TryCast, Volatility, + WindowFrame, WindowFrameBound, WindowFrameUnits, WindowFunctionDefinition, WindowUDF, + WindowUDFImpl, }; use datafusion_functions_aggregate::average::avg_udaf; use datafusion_functions_aggregate::expr_fn::{ @@ -111,7 +112,7 @@ use datafusion_proto::logical_plan::to_proto::serialize_expr; use datafusion_proto::logical_plan::{ DefaultLogicalExtensionCodec, LogicalExtensionCodec, from_proto, }; -use datafusion_proto::protobuf; +use datafusion_proto::protobuf::{self}; use crate::cases::{MyAggregateUDF, MyAggregateUdfNode, MyRegexUdf, MyRegexUdfNode}; @@ -3330,3 +3331,33 @@ async fn roundtrip_join_null_equality() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn roundtrip_range_partitioning() -> Result<()> { + let ctx = SessionContext::new(); + + let schema = Schema::new(vec![ + Field::new("a", DataType::Int64, true), + Field::new("b", DataType::Decimal128(15, 2), true), + ]); + + ctx.register_csv( + "t1", + "tests/testdata/test.csv", + CsvReadOptions::default().schema(&schema), + ) + .await?; + + let scan_plan = ctx.sql("SELECT * FROM t1").await?.into_optimized_plan()?; + let plan = LogicalPlan::Repartition(Repartition { + input: Arc::new(scan_plan), + partitioning_scheme: Partitioning::Range(RangePartitioning::try_new( + vec![col("a").sort(true, true)], + vec![SplitPoint::new(vec![ScalarValue::Int32(Some(2))])], + )?), + }); + let bytes = logical_plan_to_bytes(&plan)?; + let logical_round_trip = logical_plan_from_bytes(&bytes, &ctx.task_ctx())?; + assert_eq!(format!("{plan:?}"), format!("{logical_round_trip:?}")); + Ok(()) +} From d3680727de76c2884fe96ac131939c1be40a4181 Mon Sep 17 00:00:00 2001 From: saadtajwar Date: Thu, 18 Jun 2026 20:34:13 -0400 Subject: [PATCH 5/6] quick lint fixes --- datafusion/proto/src/logical_plan/mod.rs | 6 +++--- datafusion/proto/tests/cases/roundtrip_logical_plan.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/datafusion/proto/src/logical_plan/mod.rs b/datafusion/proto/src/logical_plan/mod.rs index d2c124b86c151..5aa8f116dea18 100644 --- a/datafusion/proto/src/logical_plan/mod.rs +++ b/datafusion/proto/src/logical_plan/mod.rs @@ -72,7 +72,7 @@ use datafusion_expr::{ builder::project, }, }; -use datafusion_proto_common::{FromProtoError, protobuf_common}; +use datafusion_proto_common::protobuf_common; use self::to_proto::{serialize_expr, serialize_exprs}; use crate::logical_plan::to_proto::serialize_range_split_point; @@ -755,7 +755,7 @@ impl AsLogicalPlan for LogicalPlanNode { split_points .iter() .map(from_proto::parse_protobuf_range_split_point) - .collect::, FromProtoError>>()?, + .collect::, _>>()?, )?), }; @@ -1772,7 +1772,7 @@ impl AsLogicalPlan for LogicalPlanNode { .split_points() .iter() .map(serialize_range_split_point) - .collect::, ToProtoError>>()?; + .collect::, _>>()?; PartitionMethod::Range(protobuf::RangeRepartition { expr: serialize_sorts(ordering, extension_codec)?, diff --git a/datafusion/proto/tests/cases/roundtrip_logical_plan.rs b/datafusion/proto/tests/cases/roundtrip_logical_plan.rs index c265edeb5bb7e..b092dfc9b608f 100644 --- a/datafusion/proto/tests/cases/roundtrip_logical_plan.rs +++ b/datafusion/proto/tests/cases/roundtrip_logical_plan.rs @@ -3353,7 +3353,7 @@ async fn roundtrip_range_partitioning() -> Result<()> { input: Arc::new(scan_plan), partitioning_scheme: Partitioning::Range(RangePartitioning::try_new( vec![col("a").sort(true, true)], - vec![SplitPoint::new(vec![ScalarValue::Int32(Some(2))])], + vec![SplitPoint::new(vec![ScalarValue::Int64(Some(2))])], )?), }); let bytes = logical_plan_to_bytes(&plan)?; From 12515b0f2b91363f54c935e13b4fa78f1b7164bc Mon Sep 17 00:00:00 2001 From: saadtajwar Date: Thu, 18 Jun 2026 20:37:57 -0400 Subject: [PATCH 6/6] lint again --- datafusion/proto/tests/cases/roundtrip_logical_plan.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datafusion/proto/tests/cases/roundtrip_logical_plan.rs b/datafusion/proto/tests/cases/roundtrip_logical_plan.rs index b092dfc9b608f..0c99ceb5bcde6 100644 --- a/datafusion/proto/tests/cases/roundtrip_logical_plan.rs +++ b/datafusion/proto/tests/cases/roundtrip_logical_plan.rs @@ -112,7 +112,7 @@ use datafusion_proto::logical_plan::to_proto::serialize_expr; use datafusion_proto::logical_plan::{ DefaultLogicalExtensionCodec, LogicalExtensionCodec, from_proto, }; -use datafusion_proto::protobuf::{self}; +use datafusion_proto::protobuf; use crate::cases::{MyAggregateUDF, MyAggregateUdfNode, MyRegexUdf, MyRegexUdfNode};