datafusion_proto/logical_plan/
file_formats.rs1use std::sync::Arc;
19
20use super::LogicalExtensionCodec;
21use crate::protobuf::{CsvOptions as CsvOptionsProto, JsonOptions as JsonOptionsProto};
22use datafusion_common::config::{CsvOptions, JsonOptions};
23use datafusion_common::{TableReference, exec_datafusion_err, exec_err, not_impl_err};
24use datafusion_datasource::file_format::FileFormatFactory;
25use datafusion_datasource_arrow::file_format::ArrowFormatFactory;
26use datafusion_datasource_csv::file_format::CsvFormatFactory;
27use datafusion_datasource_json::file_format::JsonFormatFactory;
28use datafusion_execution::TaskContext;
29use prost::Message;
30
31#[derive(Debug)]
32pub struct CsvLogicalExtensionCodec;
33
34impl LogicalExtensionCodec for CsvLogicalExtensionCodec {
36 fn try_decode(
37 &self,
38 _buf: &[u8],
39 _inputs: &[datafusion_expr::LogicalPlan],
40 _ctx: &TaskContext,
41 ) -> datafusion_common::Result<datafusion_expr::Extension> {
42 not_impl_err!("Method not implemented")
43 }
44
45 fn try_encode(
46 &self,
47 _node: &datafusion_expr::Extension,
48 _buf: &mut Vec<u8>,
49 ) -> datafusion_common::Result<()> {
50 not_impl_err!("Method not implemented")
51 }
52
53 fn try_decode_table_provider(
54 &self,
55 _buf: &[u8],
56 _table_ref: &TableReference,
57 _schema: arrow::datatypes::SchemaRef,
58 _ctx: &TaskContext,
59 ) -> datafusion_common::Result<Arc<dyn datafusion_catalog::TableProvider>> {
60 not_impl_err!("Method not implemented")
61 }
62
63 fn try_encode_table_provider(
64 &self,
65 _table_ref: &TableReference,
66 _node: Arc<dyn datafusion_catalog::TableProvider>,
67 _buf: &mut Vec<u8>,
68 ) -> datafusion_common::Result<()> {
69 not_impl_err!("Method not implemented")
70 }
71
72 fn try_decode_file_format(
73 &self,
74 buf: &[u8],
75 _ctx: &TaskContext,
76 ) -> datafusion_common::Result<Arc<dyn FileFormatFactory>> {
77 let proto = CsvOptionsProto::decode(buf).map_err(|e| {
78 exec_datafusion_err!("Failed to decode CsvOptionsProto: {e:?}")
79 })?;
80 let options = CsvOptions::from(&proto);
81 Ok(Arc::new(CsvFormatFactory {
82 options: Some(options),
83 }))
84 }
85
86 fn try_encode_file_format(
87 &self,
88 buf: &mut Vec<u8>,
89 node: Arc<dyn FileFormatFactory>,
90 ) -> datafusion_common::Result<()> {
91 let options = if let Some(csv_factory) = node.downcast_ref::<CsvFormatFactory>() {
92 csv_factory.options.clone().unwrap_or_default()
93 } else {
94 return exec_err!("{}", "Unsupported FileFormatFactory type".to_string());
95 };
96
97 let proto = CsvOptionsProto::from(&CsvFormatFactory {
98 options: Some(options),
99 });
100
101 proto
102 .encode(buf)
103 .map_err(|e| exec_datafusion_err!("Failed to encode CsvOptions: {e:?}"))?;
104
105 Ok(())
106 }
107}
108
109#[derive(Debug)]
110pub struct JsonLogicalExtensionCodec;
111
112impl LogicalExtensionCodec for JsonLogicalExtensionCodec {
114 fn try_decode(
115 &self,
116 _buf: &[u8],
117 _inputs: &[datafusion_expr::LogicalPlan],
118 _ctx: &TaskContext,
119 ) -> datafusion_common::Result<datafusion_expr::Extension> {
120 not_impl_err!("Method not implemented")
121 }
122
123 fn try_encode(
124 &self,
125 _node: &datafusion_expr::Extension,
126 _buf: &mut Vec<u8>,
127 ) -> datafusion_common::Result<()> {
128 not_impl_err!("Method not implemented")
129 }
130
131 fn try_decode_table_provider(
132 &self,
133 _buf: &[u8],
134 _table_ref: &TableReference,
135 _schema: arrow::datatypes::SchemaRef,
136 _ctx: &TaskContext,
137 ) -> datafusion_common::Result<Arc<dyn datafusion_catalog::TableProvider>> {
138 not_impl_err!("Method not implemented")
139 }
140
141 fn try_encode_table_provider(
142 &self,
143 _table_ref: &TableReference,
144 _node: Arc<dyn datafusion_catalog::TableProvider>,
145 _buf: &mut Vec<u8>,
146 ) -> datafusion_common::Result<()> {
147 not_impl_err!("Method not implemented")
148 }
149
150 fn try_decode_file_format(
151 &self,
152 buf: &[u8],
153 _ctx: &TaskContext,
154 ) -> datafusion_common::Result<Arc<dyn FileFormatFactory>> {
155 let proto = JsonOptionsProto::decode(buf).map_err(|e| {
156 exec_datafusion_err!("Failed to decode JsonOptionsProto: {e:?}")
157 })?;
158 let options = JsonOptions::from(&proto);
159 Ok(Arc::new(JsonFormatFactory {
160 options: Some(options),
161 }))
162 }
163
164 fn try_encode_file_format(
165 &self,
166 buf: &mut Vec<u8>,
167 node: Arc<dyn FileFormatFactory>,
168 ) -> datafusion_common::Result<()> {
169 let options = if let Some(json_factory) = node.downcast_ref::<JsonFormatFactory>()
170 {
171 json_factory.options.clone().unwrap_or_default()
172 } else {
173 return exec_err!("Unsupported FileFormatFactory type");
174 };
175
176 let proto = JsonOptionsProto::from(&JsonFormatFactory {
177 options: Some(options),
178 });
179
180 proto
181 .encode(buf)
182 .map_err(|e| exec_datafusion_err!("Failed to encode JsonOptions: {e:?}"))?;
183
184 Ok(())
185 }
186}
187
188#[cfg(feature = "parquet")]
189mod parquet {
190 use super::*;
191
192 use crate::protobuf::TableParquetOptions as TableParquetOptionsProto;
193 use datafusion_common::config::TableParquetOptions;
194 use datafusion_datasource_parquet::file_format::ParquetFormatFactory;
195
196 #[derive(Debug)]
197 pub struct ParquetLogicalExtensionCodec;
198
199 impl LogicalExtensionCodec for ParquetLogicalExtensionCodec {
201 fn try_decode(
202 &self,
203 _buf: &[u8],
204 _inputs: &[datafusion_expr::LogicalPlan],
205 _ctx: &TaskContext,
206 ) -> datafusion_common::Result<datafusion_expr::Extension> {
207 not_impl_err!("Method not implemented")
208 }
209
210 fn try_encode(
211 &self,
212 _node: &datafusion_expr::Extension,
213 _buf: &mut Vec<u8>,
214 ) -> datafusion_common::Result<()> {
215 not_impl_err!("Method not implemented")
216 }
217
218 fn try_decode_table_provider(
219 &self,
220 _buf: &[u8],
221 _table_ref: &TableReference,
222 _schema: arrow::datatypes::SchemaRef,
223 _ctx: &TaskContext,
224 ) -> datafusion_common::Result<Arc<dyn datafusion_catalog::TableProvider>>
225 {
226 not_impl_err!("Method not implemented")
227 }
228
229 fn try_encode_table_provider(
230 &self,
231 _table_ref: &TableReference,
232 _node: Arc<dyn datafusion_catalog::TableProvider>,
233 _buf: &mut Vec<u8>,
234 ) -> datafusion_common::Result<()> {
235 not_impl_err!("Method not implemented")
236 }
237
238 fn try_decode_file_format(
239 &self,
240 buf: &[u8],
241 _ctx: &TaskContext,
242 ) -> datafusion_common::Result<Arc<dyn FileFormatFactory>> {
243 let proto = TableParquetOptionsProto::decode(buf).map_err(|e| {
244 exec_datafusion_err!("Failed to decode TableParquetOptionsProto: {e:?}")
245 })?;
246 let options = TableParquetOptions::try_from(&proto)?;
247 Ok(Arc::new(ParquetFormatFactory {
248 options: Some(options),
249 }))
250 }
251
252 fn try_encode_file_format(
253 &self,
254 buf: &mut Vec<u8>,
255 node: Arc<dyn FileFormatFactory>,
256 ) -> datafusion_common::Result<()> {
257 use datafusion_datasource_parquet::file_format::ParquetFormatFactory;
258
259 let options = if let Some(parquet_factory) =
260 node.downcast_ref::<ParquetFormatFactory>()
261 {
262 parquet_factory.options.clone().unwrap_or_default()
263 } else {
264 return exec_err!("Unsupported FileFormatFactory type");
265 };
266
267 let proto = TableParquetOptionsProto::from(&ParquetFormatFactory {
268 options: Some(options),
269 });
270
271 proto.encode(buf).map_err(|e| {
272 exec_datafusion_err!("Failed to encode TableParquetOptionsProto: {e:?}")
273 })?;
274
275 Ok(())
276 }
277 }
278
279 #[cfg(test)]
280 mod tests {
281 use super::*;
282 use crate::protobuf::ParquetOptions as ParquetOptionsProto;
283 use datafusion_common::config::ParquetOptions;
284
285 fn encode_table_options(proto: TableParquetOptionsProto) -> Vec<u8> {
286 let mut buf = Vec::new();
287 proto.encode(&mut buf).expect("encode parquet options");
288 buf
289 }
290
291 #[test]
292 fn try_decode_file_format_errors_on_invalid_writer_version() {
293 let proto = TableParquetOptionsProto {
294 global: Some(ParquetOptionsProto {
295 writer_version: "3.0".to_string(),
296 ..Default::default()
297 }),
298 ..Default::default()
299 };
300
301 let result = ParquetLogicalExtensionCodec.try_decode_file_format(
302 &encode_table_options(proto),
303 &TaskContext::default(),
304 );
305
306 let err = result.expect_err("invalid writer version should error");
307 assert!(
308 err.to_string()
309 .contains("Invalid parquet writer version: 3.0"),
310 "{err}"
311 );
312 }
313
314 #[test]
315 fn try_decode_file_format_defaults_empty_writer_version() {
316 let proto = TableParquetOptionsProto {
317 global: Some(ParquetOptionsProto::default()),
318 ..Default::default()
319 };
320
321 let factory = ParquetLogicalExtensionCodec
322 .try_decode_file_format(
323 &encode_table_options(proto),
324 &TaskContext::default(),
325 )
326 .expect("decode parquet options");
327 let parquet_factory = factory
328 .downcast_ref::<ParquetFormatFactory>()
329 .expect("parquet format factory");
330 let options = parquet_factory.options.as_ref().expect("parquet options");
331
332 assert_eq!(
333 options.global.writer_version,
334 ParquetOptions::default().writer_version
335 );
336 }
337 }
338}
339#[cfg(feature = "parquet")]
340pub use parquet::ParquetLogicalExtensionCodec;
341
342#[derive(Debug)]
343pub struct ArrowLogicalExtensionCodec;
344
345impl LogicalExtensionCodec for ArrowLogicalExtensionCodec {
347 fn try_decode(
348 &self,
349 _buf: &[u8],
350 _inputs: &[datafusion_expr::LogicalPlan],
351 _ctx: &TaskContext,
352 ) -> datafusion_common::Result<datafusion_expr::Extension> {
353 not_impl_err!("Method not implemented")
354 }
355
356 fn try_encode(
357 &self,
358 _node: &datafusion_expr::Extension,
359 _buf: &mut Vec<u8>,
360 ) -> datafusion_common::Result<()> {
361 not_impl_err!("Method not implemented")
362 }
363
364 fn try_decode_table_provider(
365 &self,
366 _buf: &[u8],
367 _table_ref: &TableReference,
368 _schema: arrow::datatypes::SchemaRef,
369 _ctx: &TaskContext,
370 ) -> datafusion_common::Result<Arc<dyn datafusion_catalog::TableProvider>> {
371 not_impl_err!("Method not implemented")
372 }
373
374 fn try_encode_table_provider(
375 &self,
376 _table_ref: &TableReference,
377 _node: Arc<dyn datafusion_catalog::TableProvider>,
378 _buf: &mut Vec<u8>,
379 ) -> datafusion_common::Result<()> {
380 not_impl_err!("Method not implemented")
381 }
382
383 fn try_decode_file_format(
384 &self,
385 __buf: &[u8],
386 __ctx: &TaskContext,
387 ) -> datafusion_common::Result<Arc<dyn FileFormatFactory>> {
388 Ok(Arc::new(ArrowFormatFactory::new()))
389 }
390
391 fn try_encode_file_format(
392 &self,
393 __buf: &mut Vec<u8>,
394 __node: Arc<dyn FileFormatFactory>,
395 ) -> datafusion_common::Result<()> {
396 Ok(())
397 }
398}
399
400#[derive(Debug)]
401pub struct AvroLogicalExtensionCodec;
402
403impl LogicalExtensionCodec for AvroLogicalExtensionCodec {
405 fn try_decode(
406 &self,
407 _buf: &[u8],
408 _inputs: &[datafusion_expr::LogicalPlan],
409 _ctx: &TaskContext,
410 ) -> datafusion_common::Result<datafusion_expr::Extension> {
411 not_impl_err!("Method not implemented")
412 }
413
414 fn try_encode(
415 &self,
416 _node: &datafusion_expr::Extension,
417 _buf: &mut Vec<u8>,
418 ) -> datafusion_common::Result<()> {
419 not_impl_err!("Method not implemented")
420 }
421
422 fn try_decode_table_provider(
423 &self,
424 _buf: &[u8],
425 _table_ref: &TableReference,
426 _schema: arrow::datatypes::SchemaRef,
427 _cts: &TaskContext,
428 ) -> datafusion_common::Result<Arc<dyn datafusion_catalog::TableProvider>> {
429 not_impl_err!("Method not implemented")
430 }
431
432 fn try_encode_table_provider(
433 &self,
434 _table_ref: &TableReference,
435 _node: Arc<dyn datafusion_catalog::TableProvider>,
436 _buf: &mut Vec<u8>,
437 ) -> datafusion_common::Result<()> {
438 not_impl_err!("Method not implemented")
439 }
440
441 fn try_decode_file_format(
442 &self,
443 __buf: &[u8],
444 __ctx: &TaskContext,
445 ) -> datafusion_common::Result<Arc<dyn FileFormatFactory>> {
446 Ok(Arc::new(ArrowFormatFactory::new()))
447 }
448
449 fn try_encode_file_format(
450 &self,
451 __buf: &mut Vec<u8>,
452 __node: Arc<dyn FileFormatFactory>,
453 ) -> datafusion_common::Result<()> {
454 Ok(())
455 }
456}