1use datafusion_common::DataFusionError;
25use datafusion_common::display::{PlanType, StringifiedPlan};
26use datafusion_common::{
27 JoinConstraint, JoinType, NullEquality, TableReference, UnnestOptions,
28};
29
30use crate::generated::datafusion_common::EmptyMessage;
31use crate::protobuf::{
32 self, AnalyzedLogicalPlanType, OptimizedLogicalPlanType, OptimizedPhysicalPlanType,
33 RecursionUnnestOption,
34 plan_type::PlanTypeEnum::{
35 AnalyzedLogicalPlan, FinalAnalyzedLogicalPlan, FinalLogicalPlan,
36 FinalPhysicalPlan, FinalPhysicalPlanWithSchema, FinalPhysicalPlanWithStats,
37 InitialLogicalPlan, InitialPhysicalPlan, InitialPhysicalPlanWithSchema,
38 InitialPhysicalPlanWithStats, OptimizedLogicalPlan, OptimizedPhysicalPlan,
39 PhysicalPlanError,
40 },
41};
42
43impl From<&UnnestOptions> for protobuf::UnnestOptions {
44 fn from(opts: &UnnestOptions) -> Self {
45 use datafusion_common::NullHandling;
46 use protobuf::unnest_options::NullHandling as ProtoNullHandling;
47 let null_handling = match opts.null_handling {
48 NullHandling::Preserve => ProtoNullHandling::Preserve,
49 NullHandling::Drop => ProtoNullHandling::Drop,
50 NullHandling::PreserveAndExpandEmpty => {
51 ProtoNullHandling::PreserveAndExpandEmpty
52 }
53 } as i32;
54 Self {
55 null_handling,
56 recursions: opts
57 .recursions
58 .iter()
59 .map(|r| RecursionUnnestOption {
60 input_column: Some((&r.input_column).into()),
61 output_column: Some((&r.output_column).into()),
62 depth: r.depth as u32,
63 })
64 .collect(),
65 }
66 }
67}
68
69impl From<&StringifiedPlan> for protobuf::StringifiedPlan {
70 fn from(stringified_plan: &StringifiedPlan) -> Self {
71 Self {
72 plan_type: match stringified_plan.clone().plan_type {
73 PlanType::InitialLogicalPlan => Some(protobuf::PlanType {
74 plan_type_enum: Some(InitialLogicalPlan(EmptyMessage {})),
75 }),
76 PlanType::AnalyzedLogicalPlan { analyzer_name } => {
77 Some(protobuf::PlanType {
78 plan_type_enum: Some(AnalyzedLogicalPlan(
79 AnalyzedLogicalPlanType { analyzer_name },
80 )),
81 })
82 }
83 PlanType::FinalAnalyzedLogicalPlan => Some(protobuf::PlanType {
84 plan_type_enum: Some(FinalAnalyzedLogicalPlan(EmptyMessage {})),
85 }),
86 PlanType::OptimizedLogicalPlan { optimizer_name } => {
87 Some(protobuf::PlanType {
88 plan_type_enum: Some(OptimizedLogicalPlan(
89 OptimizedLogicalPlanType { optimizer_name },
90 )),
91 })
92 }
93 PlanType::FinalLogicalPlan => Some(protobuf::PlanType {
94 plan_type_enum: Some(FinalLogicalPlan(EmptyMessage {})),
95 }),
96 PlanType::InitialPhysicalPlan => Some(protobuf::PlanType {
97 plan_type_enum: Some(InitialPhysicalPlan(EmptyMessage {})),
98 }),
99 PlanType::OptimizedPhysicalPlan { optimizer_name } => {
100 Some(protobuf::PlanType {
101 plan_type_enum: Some(OptimizedPhysicalPlan(
102 OptimizedPhysicalPlanType { optimizer_name },
103 )),
104 })
105 }
106 PlanType::FinalPhysicalPlan => Some(protobuf::PlanType {
107 plan_type_enum: Some(FinalPhysicalPlan(EmptyMessage {})),
108 }),
109 PlanType::InitialPhysicalPlanWithStats => Some(protobuf::PlanType {
110 plan_type_enum: Some(InitialPhysicalPlanWithStats(EmptyMessage {})),
111 }),
112 PlanType::InitialPhysicalPlanWithSchema => Some(protobuf::PlanType {
113 plan_type_enum: Some(InitialPhysicalPlanWithSchema(EmptyMessage {})),
114 }),
115 PlanType::FinalPhysicalPlanWithStats => Some(protobuf::PlanType {
116 plan_type_enum: Some(FinalPhysicalPlanWithStats(EmptyMessage {})),
117 }),
118 PlanType::FinalPhysicalPlanWithSchema => Some(protobuf::PlanType {
119 plan_type_enum: Some(FinalPhysicalPlanWithSchema(EmptyMessage {})),
120 }),
121 PlanType::PhysicalPlanError => Some(protobuf::PlanType {
122 plan_type_enum: Some(PhysicalPlanError(EmptyMessage {})),
123 }),
124 },
125 plan: stringified_plan.plan.to_string(),
126 }
127 }
128}
129
130impl From<TableReference> for protobuf::TableReference {
131 fn from(t: TableReference) -> Self {
132 use protobuf::table_reference::TableReferenceEnum;
133 let table_reference_enum = match t {
134 TableReference::Bare { table } => {
135 TableReferenceEnum::Bare(protobuf::BareTableReference {
136 table: table.to_string(),
137 })
138 }
139 TableReference::Partial { schema, table } => {
140 TableReferenceEnum::Partial(protobuf::PartialTableReference {
141 schema: schema.to_string(),
142 table: table.to_string(),
143 })
144 }
145 TableReference::Full {
146 catalog,
147 schema,
148 table,
149 } => TableReferenceEnum::Full(protobuf::FullTableReference {
150 catalog: catalog.to_string(),
151 schema: schema.to_string(),
152 table: table.to_string(),
153 }),
154 };
155
156 protobuf::TableReference {
157 table_reference_enum: Some(table_reference_enum),
158 }
159 }
160}
161
162impl From<JoinType> for protobuf::JoinType {
163 fn from(t: JoinType) -> Self {
164 match t {
165 JoinType::Inner => protobuf::JoinType::Inner,
166 JoinType::Left => protobuf::JoinType::Left,
167 JoinType::Right => protobuf::JoinType::Right,
168 JoinType::Full => protobuf::JoinType::Full,
169 JoinType::LeftSemi => protobuf::JoinType::Leftsemi,
170 JoinType::RightSemi => protobuf::JoinType::Rightsemi,
171 JoinType::LeftAnti => protobuf::JoinType::Leftanti,
172 JoinType::RightAnti => protobuf::JoinType::Rightanti,
173 JoinType::LeftMark => protobuf::JoinType::Leftmark,
174 JoinType::RightMark => protobuf::JoinType::Rightmark,
175 }
176 }
177}
178
179impl From<JoinConstraint> for protobuf::JoinConstraint {
180 fn from(t: JoinConstraint) -> Self {
181 match t {
182 JoinConstraint::On => protobuf::JoinConstraint::On,
183 JoinConstraint::Using => protobuf::JoinConstraint::Using,
184 }
185 }
186}
187
188impl From<NullEquality> for protobuf::NullEquality {
189 fn from(t: NullEquality) -> Self {
190 match t {
191 NullEquality::NullEqualsNothing => protobuf::NullEquality::NullEqualsNothing,
192 NullEquality::NullEqualsNull => protobuf::NullEquality::NullEqualsNull,
193 }
194 }
195}
196
197impl<T> TryFrom<&[T]> for protobuf::FileGroup
210where
211 for<'a> &'a T: TryInto<protobuf::PartitionedFile, Error = DataFusionError>,
212{
213 type Error = DataFusionError;
214
215 fn try_from(files: &[T]) -> Result<Self, Self::Error> {
216 Ok(protobuf::FileGroup {
217 files: files
218 .iter()
219 .map(TryInto::try_into)
220 .collect::<Result<Vec<_>, _>>()?,
221 })
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use datafusion_common::{NullHandling, RecursionUnnestOption};
228
229 use super::*;
230
231 #[test]
232 fn table_reference_roundtrip() {
233 for reference in [
234 TableReference::bare("t"),
235 TableReference::partial("s", "t"),
236 TableReference::full("c", "s", "t"),
237 ] {
238 let encoded = protobuf::TableReference::from(reference.clone());
239 let decoded = TableReference::try_from(encoded).unwrap();
240 assert_eq!(decoded, reference);
241 }
242 }
243
244 #[test]
245 fn table_reference_from_proto_rejects_missing_oneof() {
246 let proto = protobuf::TableReference {
247 table_reference_enum: None,
248 };
249 let err = TableReference::try_from(proto).unwrap_err();
250 assert!(
251 err.to_string().contains("table_reference_enum"),
252 "unexpected error: {err}"
253 );
254 }
255
256 #[test]
257 fn join_enums_roundtrip() {
258 for join_type in [
259 JoinType::Inner,
260 JoinType::Left,
261 JoinType::Right,
262 JoinType::Full,
263 JoinType::LeftSemi,
264 JoinType::RightSemi,
265 JoinType::LeftAnti,
266 JoinType::RightAnti,
267 JoinType::LeftMark,
268 JoinType::RightMark,
269 ] {
270 assert_eq!(
271 JoinType::from(protobuf::JoinType::from(join_type)),
272 join_type
273 );
274 }
275 for constraint in [JoinConstraint::On, JoinConstraint::Using] {
276 assert_eq!(
277 JoinConstraint::from(protobuf::JoinConstraint::from(constraint)),
278 constraint
279 );
280 }
281 for null_equality in [
282 NullEquality::NullEqualsNothing,
283 NullEquality::NullEqualsNull,
284 ] {
285 assert_eq!(
286 NullEquality::from(protobuf::NullEquality::from(null_equality)),
287 null_equality
288 );
289 }
290 }
291
292 #[test]
293 fn unnest_options_roundtrip() {
294 let options = UnnestOptions {
295 null_handling: NullHandling::Drop,
296 recursions: vec![RecursionUnnestOption {
297 input_column: "a".into(),
298 output_column: "b".into(),
299 depth: 2,
300 }],
301 };
302
303 let encoded = protobuf::UnnestOptions::from(&options);
304 let decoded = UnnestOptions::from(&encoded);
305
306 assert_eq!(decoded.null_handling, options.null_handling);
307 assert_eq!(decoded.recursions, options.recursions);
308 }
309
310 #[test]
311 fn stringified_plan_roundtrip() {
312 let plan = StringifiedPlan::new(
313 PlanType::OptimizedLogicalPlan {
314 optimizer_name: "push_down_filter".to_string(),
315 },
316 "some plan",
317 );
318
319 let encoded = protobuf::StringifiedPlan::from(&plan);
320 let decoded = StringifiedPlan::from(&encoded);
321
322 assert_eq!(decoded.plan_type, plan.plan_type);
323 assert_eq!(decoded.plan, plan.plan);
324 }
325}