1use crate::arrow::util::pretty::pretty_format_batches_with_options;
21use arrow::array::{ArrayRef, RecordBatch};
22use arrow::error::ArrowError;
23use std::fmt::Display;
24use std::{error::Error, path::PathBuf};
25
26pub trait IntoArrayRef {
28 fn into_array_ref(self) -> ArrayRef;
29}
30
31pub fn format_batches(results: &[RecordBatch]) -> Result<impl Display, ArrowError> {
32 let datafusion_format_options = crate::config::FormatOptions::default();
33
34 let arrow_format_options: arrow::util::display::FormatOptions =
35 (&datafusion_format_options).try_into().unwrap();
36
37 pretty_format_batches_with_options(results, &arrow_format_options)
38}
39
40#[macro_export]
72macro_rules! assert_batches_eq {
73 ($EXPECTED_LINES: expr, $CHUNKS: expr) => {
74 let expected_lines: Vec<String> =
75 $EXPECTED_LINES.iter().map(|&s| s.into()).collect();
76
77 let formatted = $crate::test_util::format_batches($CHUNKS)
78 .unwrap()
79 .to_string();
80
81 let actual_lines: Vec<&str> = formatted.trim().lines().collect();
82
83 assert_eq!(
84 expected_lines, actual_lines,
85 "\n\nexpected:\n\n{:#?}\nactual:\n\n{:#?}\n\n",
86 expected_lines, actual_lines
87 );
88 };
89}
90
91pub fn batches_to_string(batches: &[RecordBatch]) -> String {
92 let actual = format_batches(batches).unwrap().to_string();
93
94 actual.trim().to_string()
95}
96
97pub fn batches_to_sort_string(batches: &[RecordBatch]) -> String {
98 let actual_lines = format_batches(batches).unwrap().to_string();
99
100 let mut actual_lines: Vec<&str> = actual_lines.trim().lines().collect();
101
102 let num_lines = actual_lines.len();
104 if num_lines > 3 {
105 actual_lines.as_mut_slice()[2..num_lines - 1].sort_unstable()
106 }
107
108 actual_lines.join("\n")
109}
110
111#[macro_export]
121macro_rules! assert_batches_sorted_eq {
122 ($EXPECTED_LINES: expr, $CHUNKS: expr) => {
123 let mut expected_lines: Vec<String> =
124 $EXPECTED_LINES.iter().map(|&s| s.into()).collect();
125
126 let num_lines = expected_lines.len();
128 if num_lines > 3 {
129 expected_lines.as_mut_slice()[2..num_lines - 1].sort_unstable()
130 }
131
132 let formatted = $crate::test_util::format_batches($CHUNKS)
133 .unwrap()
134 .to_string();
135 let mut actual_lines: Vec<&str> = formatted.trim().lines().collect();
138
139 let num_lines = actual_lines.len();
141 if num_lines > 3 {
142 actual_lines.as_mut_slice()[2..num_lines - 1].sort_unstable()
143 }
144
145 assert_eq!(
146 expected_lines, actual_lines,
147 "\n\nexpected:\n\n{:#?}\nactual:\n\n{:#?}\n\n",
148 expected_lines, actual_lines
149 );
150 };
151}
152
153#[macro_export]
163macro_rules! assert_contains {
164 ($ACTUAL: expr, $EXPECTED: expr) => {
165 let actual_value: String = $ACTUAL.into();
166 let expected_value: String = $EXPECTED.into();
167 assert!(
168 actual_value.contains(&expected_value),
169 "Can not find expected in actual.\n\nExpected:\n{}\n\nActual:\n{}",
170 expected_value,
171 actual_value
172 );
173 };
174}
175
176#[macro_export]
186macro_rules! assert_not_contains {
187 ($ACTUAL: expr, $UNEXPECTED: expr) => {
188 let actual_value: String = $ACTUAL.into();
189 let unexpected_value: String = $UNEXPECTED.into();
190 assert!(
191 !actual_value.contains(&unexpected_value),
192 "Found unexpected in actual.\n\nUnexpected:\n{}\n\nActual:\n{}",
193 unexpected_value,
194 actual_value
195 );
196 };
197}
198
199pub fn datafusion_test_data() -> String {
213 match get_data_dir("DATAFUSION_TEST_DATA", "../../datafusion/core/tests/data") {
214 Ok(pb) => pb.display().to_string(),
215 Err(err) => panic!("failed to get arrow data dir: {err}"),
216 }
217}
218
219pub fn arrow_test_data() -> String {
234 match get_data_dir("ARROW_TEST_DATA", "../../testing/data") {
235 Ok(pb) => pb.display().to_string(),
236 Err(err) => panic!("failed to get arrow data dir: {err}"),
237 }
238}
239
240#[cfg(feature = "parquet")]
256pub fn parquet_test_data() -> String {
257 match get_data_dir("PARQUET_TEST_DATA", "../../parquet-testing/data") {
258 Ok(pb) => {
259 let mut path = pb.display().to_string();
260 if cfg!(target_os = "windows") {
261 path = path.replace("\\", "/");
263 }
264 path
265 }
266 Err(err) => panic!("failed to get parquet data dir: {err}"),
267 }
268}
269
270pub fn get_data_dir(
280 udf_env: &str,
281 submodule_data: &str,
282) -> Result<PathBuf, Box<dyn Error>> {
283 if let Ok(dir) = std::env::var(udf_env) {
285 let trimmed = dir.trim().to_string();
286 if !trimmed.is_empty() {
287 let pb = PathBuf::from(trimmed);
288 if pb.is_dir() {
289 return Ok(pb);
290 } else {
291 return Err(format!(
292 "the data dir `{}` defined by env {} not found",
293 pb.display(),
294 udf_env
295 )
296 .into());
297 }
298 }
299 }
300
301 let dir = env!("CARGO_MANIFEST_DIR");
307
308 let pb = PathBuf::from(dir).join(submodule_data);
309 if pb.is_dir() {
310 Ok(pb)
311 } else {
312 Err(format!(
313 "env `{}` is undefined or has empty value, and the pre-defined data dir `{}` not found\n\
314 HINT: try running `git submodule update --init`",
315 udf_env,
316 pb.display(),
317 ).into())
318 }
319}
320
321#[macro_export]
322macro_rules! create_array {
323 (Boolean, $values: expr) => {
324 std::sync::Arc::new($crate::arrow::array::BooleanArray::from($values))
325 };
326 (Int8, $values: expr) => {
327 std::sync::Arc::new($crate::arrow::array::Int8Array::from($values))
328 };
329 (Int16, $values: expr) => {
330 std::sync::Arc::new($crate::arrow::array::Int16Array::from($values))
331 };
332 (Int32, $values: expr) => {
333 std::sync::Arc::new($crate::arrow::array::Int32Array::from($values))
334 };
335 (Int64, $values: expr) => {
336 std::sync::Arc::new($crate::arrow::array::Int64Array::from($values))
337 };
338 (UInt8, $values: expr) => {
339 std::sync::Arc::new($crate::arrow::array::UInt8Array::from($values))
340 };
341 (UInt16, $values: expr) => {
342 std::sync::Arc::new($crate::arrow::array::UInt16Array::from($values))
343 };
344 (UInt32, $values: expr) => {
345 std::sync::Arc::new($crate::arrow::array::UInt32Array::from($values))
346 };
347 (UInt64, $values: expr) => {
348 std::sync::Arc::new($crate::arrow::array::UInt64Array::from($values))
349 };
350 (Float16, $values: expr) => {
351 std::sync::Arc::new($crate::arrow::array::Float16Array::from($values))
352 };
353 (Float32, $values: expr) => {
354 std::sync::Arc::new($crate::arrow::array::Float32Array::from($values))
355 };
356 (Float64, $values: expr) => {
357 std::sync::Arc::new($crate::arrow::array::Float64Array::from($values))
358 };
359 (Utf8, $values: expr) => {
360 std::sync::Arc::new($crate::arrow::array::StringArray::from($values))
361 };
362}
363
364#[deprecated(since = "55.0.0", note = "Use `arrow::array::record_batch` instead")]
381#[macro_export]
382macro_rules! record_batch {
383 ($(($name: expr, $type: ident, $values: expr)),*) => {
384 {
385 let schema = std::sync::Arc::new($crate::arrow::datatypes::Schema::new(vec![
386 $(
387 $crate::arrow::datatypes::Field::new($name, $crate::arrow::datatypes::DataType::$type, true),
388 )*
389 ]));
390
391 let batch = $crate::arrow::array::RecordBatch::try_new(
392 schema,
393 vec![$(
394 $crate::create_array!($type, $values),
395 )*]
396 );
397
398 batch
399 }
400 }
401}
402
403pub mod array_conversion {
404 use arrow::array::ArrayRef;
405
406 use super::IntoArrayRef;
407
408 impl IntoArrayRef for Vec<bool> {
409 fn into_array_ref(self) -> ArrayRef {
410 create_array!(Boolean, self)
411 }
412 }
413
414 impl IntoArrayRef for Vec<Option<bool>> {
415 fn into_array_ref(self) -> ArrayRef {
416 create_array!(Boolean, self)
417 }
418 }
419
420 impl IntoArrayRef for &[bool] {
421 fn into_array_ref(self) -> ArrayRef {
422 create_array!(Boolean, self.to_vec())
423 }
424 }
425
426 impl IntoArrayRef for &[Option<bool>] {
427 fn into_array_ref(self) -> ArrayRef {
428 create_array!(Boolean, self.to_vec())
429 }
430 }
431
432 impl IntoArrayRef for Vec<i8> {
433 fn into_array_ref(self) -> ArrayRef {
434 create_array!(Int8, self)
435 }
436 }
437
438 impl IntoArrayRef for Vec<Option<i8>> {
439 fn into_array_ref(self) -> ArrayRef {
440 create_array!(Int8, self)
441 }
442 }
443
444 impl IntoArrayRef for &[i8] {
445 fn into_array_ref(self) -> ArrayRef {
446 create_array!(Int8, self.to_vec())
447 }
448 }
449
450 impl IntoArrayRef for &[Option<i8>] {
451 fn into_array_ref(self) -> ArrayRef {
452 create_array!(Int8, self.to_vec())
453 }
454 }
455
456 impl IntoArrayRef for Vec<i16> {
457 fn into_array_ref(self) -> ArrayRef {
458 create_array!(Int16, self)
459 }
460 }
461
462 impl IntoArrayRef for Vec<Option<i16>> {
463 fn into_array_ref(self) -> ArrayRef {
464 create_array!(Int16, self)
465 }
466 }
467
468 impl IntoArrayRef for &[i16] {
469 fn into_array_ref(self) -> ArrayRef {
470 create_array!(Int16, self.to_vec())
471 }
472 }
473
474 impl IntoArrayRef for &[Option<i16>] {
475 fn into_array_ref(self) -> ArrayRef {
476 create_array!(Int16, self.to_vec())
477 }
478 }
479
480 impl IntoArrayRef for Vec<i32> {
481 fn into_array_ref(self) -> ArrayRef {
482 create_array!(Int32, self)
483 }
484 }
485
486 impl IntoArrayRef for Vec<Option<i32>> {
487 fn into_array_ref(self) -> ArrayRef {
488 create_array!(Int32, self)
489 }
490 }
491
492 impl IntoArrayRef for &[i32] {
493 fn into_array_ref(self) -> ArrayRef {
494 create_array!(Int32, self.to_vec())
495 }
496 }
497
498 impl IntoArrayRef for &[Option<i32>] {
499 fn into_array_ref(self) -> ArrayRef {
500 create_array!(Int32, self.to_vec())
501 }
502 }
503
504 impl IntoArrayRef for Vec<i64> {
505 fn into_array_ref(self) -> ArrayRef {
506 create_array!(Int64, self)
507 }
508 }
509
510 impl IntoArrayRef for Vec<Option<i64>> {
511 fn into_array_ref(self) -> ArrayRef {
512 create_array!(Int64, self)
513 }
514 }
515
516 impl IntoArrayRef for &[i64] {
517 fn into_array_ref(self) -> ArrayRef {
518 create_array!(Int64, self.to_vec())
519 }
520 }
521
522 impl IntoArrayRef for &[Option<i64>] {
523 fn into_array_ref(self) -> ArrayRef {
524 create_array!(Int64, self.to_vec())
525 }
526 }
527
528 impl IntoArrayRef for Vec<u8> {
529 fn into_array_ref(self) -> ArrayRef {
530 create_array!(UInt8, self)
531 }
532 }
533
534 impl IntoArrayRef for Vec<Option<u8>> {
535 fn into_array_ref(self) -> ArrayRef {
536 create_array!(UInt8, self)
537 }
538 }
539
540 impl IntoArrayRef for &[u8] {
541 fn into_array_ref(self) -> ArrayRef {
542 create_array!(UInt8, self.to_vec())
543 }
544 }
545
546 impl IntoArrayRef for &[Option<u8>] {
547 fn into_array_ref(self) -> ArrayRef {
548 create_array!(UInt8, self.to_vec())
549 }
550 }
551
552 impl IntoArrayRef for Vec<u16> {
553 fn into_array_ref(self) -> ArrayRef {
554 create_array!(UInt16, self)
555 }
556 }
557
558 impl IntoArrayRef for Vec<Option<u16>> {
559 fn into_array_ref(self) -> ArrayRef {
560 create_array!(UInt16, self)
561 }
562 }
563
564 impl IntoArrayRef for &[u16] {
565 fn into_array_ref(self) -> ArrayRef {
566 create_array!(UInt16, self.to_vec())
567 }
568 }
569
570 impl IntoArrayRef for &[Option<u16>] {
571 fn into_array_ref(self) -> ArrayRef {
572 create_array!(UInt16, self.to_vec())
573 }
574 }
575
576 impl IntoArrayRef for Vec<u32> {
577 fn into_array_ref(self) -> ArrayRef {
578 create_array!(UInt32, self)
579 }
580 }
581
582 impl IntoArrayRef for Vec<Option<u32>> {
583 fn into_array_ref(self) -> ArrayRef {
584 create_array!(UInt32, self)
585 }
586 }
587
588 impl IntoArrayRef for &[u32] {
589 fn into_array_ref(self) -> ArrayRef {
590 create_array!(UInt32, self.to_vec())
591 }
592 }
593
594 impl IntoArrayRef for &[Option<u32>] {
595 fn into_array_ref(self) -> ArrayRef {
596 create_array!(UInt32, self.to_vec())
597 }
598 }
599
600 impl IntoArrayRef for Vec<u64> {
601 fn into_array_ref(self) -> ArrayRef {
602 create_array!(UInt64, self)
603 }
604 }
605
606 impl IntoArrayRef for Vec<Option<u64>> {
607 fn into_array_ref(self) -> ArrayRef {
608 create_array!(UInt64, self)
609 }
610 }
611
612 impl IntoArrayRef for &[u64] {
613 fn into_array_ref(self) -> ArrayRef {
614 create_array!(UInt64, self.to_vec())
615 }
616 }
617
618 impl IntoArrayRef for &[Option<u64>] {
619 fn into_array_ref(self) -> ArrayRef {
620 create_array!(UInt64, self.to_vec())
621 }
622 }
623
624 impl IntoArrayRef for Vec<half::f16> {
625 fn into_array_ref(self) -> ArrayRef {
626 create_array!(Float16, self)
627 }
628 }
629
630 impl IntoArrayRef for Vec<Option<half::f16>> {
631 fn into_array_ref(self) -> ArrayRef {
632 create_array!(Float16, self)
633 }
634 }
635
636 impl IntoArrayRef for &[half::f16] {
637 fn into_array_ref(self) -> ArrayRef {
638 create_array!(Float16, self.to_vec())
639 }
640 }
641
642 impl IntoArrayRef for &[Option<half::f16>] {
643 fn into_array_ref(self) -> ArrayRef {
644 create_array!(Float16, self.to_vec())
645 }
646 }
647
648 impl IntoArrayRef for Vec<f32> {
649 fn into_array_ref(self) -> ArrayRef {
650 create_array!(Float32, self)
651 }
652 }
653
654 impl IntoArrayRef for Vec<Option<f32>> {
655 fn into_array_ref(self) -> ArrayRef {
656 create_array!(Float32, self)
657 }
658 }
659
660 impl IntoArrayRef for &[f32] {
661 fn into_array_ref(self) -> ArrayRef {
662 create_array!(Float32, self.to_vec())
663 }
664 }
665
666 impl IntoArrayRef for &[Option<f32>] {
667 fn into_array_ref(self) -> ArrayRef {
668 create_array!(Float32, self.to_vec())
669 }
670 }
671
672 impl IntoArrayRef for Vec<f64> {
673 fn into_array_ref(self) -> ArrayRef {
674 create_array!(Float64, self)
675 }
676 }
677
678 impl IntoArrayRef for Vec<Option<f64>> {
679 fn into_array_ref(self) -> ArrayRef {
680 create_array!(Float64, self)
681 }
682 }
683
684 impl IntoArrayRef for &[f64] {
685 fn into_array_ref(self) -> ArrayRef {
686 create_array!(Float64, self.to_vec())
687 }
688 }
689
690 impl IntoArrayRef for &[Option<f64>] {
691 fn into_array_ref(self) -> ArrayRef {
692 create_array!(Float64, self.to_vec())
693 }
694 }
695
696 impl IntoArrayRef for Vec<&str> {
697 fn into_array_ref(self) -> ArrayRef {
698 create_array!(Utf8, self)
699 }
700 }
701
702 impl IntoArrayRef for Vec<Option<&str>> {
703 fn into_array_ref(self) -> ArrayRef {
704 create_array!(Utf8, self)
705 }
706 }
707
708 impl IntoArrayRef for &[&str] {
709 fn into_array_ref(self) -> ArrayRef {
710 create_array!(Utf8, self.to_vec())
711 }
712 }
713
714 impl IntoArrayRef for &[Option<&str>] {
715 fn into_array_ref(self) -> ArrayRef {
716 create_array!(Utf8, self.to_vec())
717 }
718 }
719
720 impl IntoArrayRef for Vec<String> {
721 fn into_array_ref(self) -> ArrayRef {
722 create_array!(Utf8, self)
723 }
724 }
725
726 impl IntoArrayRef for Vec<Option<String>> {
727 fn into_array_ref(self) -> ArrayRef {
728 create_array!(Utf8, self)
729 }
730 }
731
732 impl IntoArrayRef for &[String] {
733 fn into_array_ref(self) -> ArrayRef {
734 create_array!(Utf8, self.to_vec())
735 }
736 }
737
738 impl IntoArrayRef for &[Option<String>] {
739 fn into_array_ref(self) -> ArrayRef {
740 create_array!(Utf8, self.to_vec())
741 }
742 }
743}
744
745#[cfg(test)]
746mod tests {
747 use crate::cast::{as_float64_array, as_int32_array, as_string_array};
748 use crate::error::Result;
749
750 use super::*;
751 use std::env;
752
753 #[test]
754 fn test_data_dir() {
755 let udf_env = "get_data_dir";
756 let cwd = env::current_dir().unwrap();
757
758 let existing_pb = cwd.join("..");
759 let existing = existing_pb.display().to_string();
760 let existing_str = existing.as_str();
761
762 let non_existing = cwd.join("non-existing-dir").display().to_string();
763 let non_existing_str = non_existing.as_str();
764
765 unsafe {
766 env::set_var(udf_env, non_existing_str);
767 let res = get_data_dir(udf_env, existing_str);
768 assert!(res.is_err());
769
770 env::set_var(udf_env, "");
771 let res = get_data_dir(udf_env, existing_str);
772 assert!(res.is_ok());
773 assert_eq!(res.unwrap(), existing_pb);
774
775 env::set_var(udf_env, " ");
776 let res = get_data_dir(udf_env, existing_str);
777 assert!(res.is_ok());
778 assert_eq!(res.unwrap(), existing_pb);
779
780 env::set_var(udf_env, existing_str);
781 let res = get_data_dir(udf_env, existing_str);
782 assert!(res.is_ok());
783 assert_eq!(res.unwrap(), existing_pb);
784
785 env::remove_var(udf_env);
786 let res = get_data_dir(udf_env, non_existing_str);
787 assert!(res.is_err());
788
789 let res = get_data_dir(udf_env, existing_str);
790 assert!(res.is_ok());
791 assert_eq!(res.unwrap(), existing_pb);
792 }
793 }
794
795 #[test]
796 #[cfg(feature = "parquet")]
797 fn test_happy() {
798 let res = arrow_test_data();
799 assert!(PathBuf::from(res).is_dir());
800
801 let res = parquet_test_data();
802 assert!(PathBuf::from(res).is_dir());
803 }
804
805 #[test]
806 #[expect(
807 deprecated,
808 reason = "testing the deprecated record_batch! macro itself"
809 )]
810 fn test_create_record_batch() -> Result<()> {
811 use arrow::array::Array;
812
813 let batch = record_batch!(
814 ("a", Int32, vec![1, 2, 3, 4]),
815 ("b", Float64, vec![Some(4.0), None, Some(5.0), None]),
816 ("c", Utf8, vec!["alpha", "beta", "gamma", "delta"])
817 )?;
818
819 assert_eq!(3, batch.num_columns());
820 assert_eq!(4, batch.num_rows());
821
822 let values: Vec<_> = as_int32_array(batch.column(0))?
823 .values()
824 .iter()
825 .map(|v| v.to_owned())
826 .collect();
827 assert_eq!(values, vec![1, 2, 3, 4]);
828
829 let values: Vec<_> = as_float64_array(batch.column(1))?
830 .values()
831 .iter()
832 .map(|v| v.to_owned())
833 .collect();
834 assert_eq!(values, vec![4.0, 0.0, 5.0, 0.0]);
835
836 let nulls: Vec<_> = as_float64_array(batch.column(1))?
837 .nulls()
838 .unwrap()
839 .iter()
840 .collect();
841 assert_eq!(nulls, vec![true, false, true, false]);
842
843 let values: Vec<_> = as_string_array(batch.column(2))?.iter().flatten().collect();
844 assert_eq!(values, vec!["alpha", "beta", "gamma", "delta"]);
845
846 Ok(())
847 }
848}