You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/07/12 19:36:03 UTC
(datafusion) branch gh-readonly-queue/main/pr-23470-360a56dfe48edfb5b7391b911b75d22959e33c6a created (now f87e8174c1)
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a change to branch gh-readonly-queue/main/pr-23470-360a56dfe48edfb5b7391b911b75d22959e33c6a
in repository https://gitbox.apache.org/repos/asf/datafusion.git
at f87e8174c1 perf: optimize make_date in datafusion-functions (#23470)
This branch includes the following new commits:
new f87e8174c1 perf: optimize make_date in datafusion-functions (#23470)
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org
(datafusion) 01/01: perf: optimize make_date in datafusion-functions (#23470)
Posted by gi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-23470-360a56dfe48edfb5b7391b911b75d22959e33c6a
in repository https://gitbox.apache.org/repos/asf/datafusion.git
commit f87e8174c107f61740606fa8c38b8f446911a1e8
Author: Andy Grove <ag...@apache.org>
AuthorDate: Sun Jul 12 13:35:39 2026 -0600
perf: optimize make_date in datafusion-functions (#23470)
## Which issue does this PR close?
N/A
## Rationale for this change
Optimize existing expression.
## What changes are included in this PR?
Replaced make_date's per-element triple is_null check + PrimitiveBuilder
with a single unioned NullBuffer, raw value-slice reads, and direct
PrimitiveArray::new construction (matching the already-optimized
make_time), cutting per-row overhead on the hot valid-input path.
## Are these changes tested?
Existing tests + new tests.
Benchmark (criterion):
- make_date_scalar_col_col_8192: 19.979% faster (base 31210ns -> cand
24974ns)
- make_date_col_col_col_8192: 16.931% faster (base 30849ns -> cand
25626ns)
- make_date_scalar_scalar_col_8192: 22.999% faster (base 31551ns -> cand
24295ns)
- make_date_scalar_scalar_scalar: 1.271% faster (base 48ns -> cand 48ns)
## Are there any user-facing changes?
No
<!--
If there are user-facing changes then we may require documentation to be
updated before approving the PR.
-->
<!--
If there are any breaking changes to public APIs, please add the `api
change` label.
-->
---
datafusion/functions/src/datetime/make_date.rs | 102 +++++++++++++++++++++++--
1 file changed, 95 insertions(+), 7 deletions(-)
diff --git a/datafusion/functions/src/datetime/make_date.rs b/datafusion/functions/src/datetime/make_date.rs
index dc1328742f..3a6b76ed86 100644
--- a/datafusion/functions/src/datetime/make_date.rs
+++ b/datafusion/functions/src/datetime/make_date.rs
@@ -17,10 +17,10 @@
use std::sync::Arc;
-use arrow::array::builder::PrimitiveBuilder;
use arrow::array::cast::AsArray;
use arrow::array::types::{Date32Type, Int32Type};
use arrow::array::{Array, PrimitiveArray};
+use arrow::buffer::NullBuffer;
use arrow::datatypes::DataType;
use arrow::datatypes::DataType::Date32;
use chrono::prelude::*;
@@ -139,24 +139,27 @@ impl ScalarUDFImpl for MakeDateFunc {
let months = months.as_primitive::<Int32Type>();
let days = days.as_primitive::<Int32Type>();
- let mut builder: PrimitiveBuilder<Date32Type> =
- PrimitiveArray::builder(len);
+ let nulls =
+ NullBuffer::union_many([years.nulls(), months.nulls(), days.nulls()]);
+ let mut values = Vec::with_capacity(len);
for i in 0..len {
// match postgresql behaviour which returns null for any null input
- if years.is_null(i) || months.is_null(i) || days.is_null(i) {
- builder.append_null();
+ if nulls.as_ref().is_some_and(|n| n.is_null(i)) {
+ values.push(0);
} else {
make_date_inner(
years.value(i),
months.value(i),
days.value(i),
- |days: i32| builder.append_value(days),
+ |days: i32| values.push(days),
)?;
}
}
- Ok(ColumnarValue::Array(Arc::new(builder.finish())))
+ Ok(ColumnarValue::Array(Arc::new(
+ PrimitiveArray::<Date32Type>::new(values.into(), nulls),
+ )))
}
}
}
@@ -197,3 +200,88 @@ fn make_date_inner<F: FnMut(i32)>(
exec_err!("Unable to parse date from {year}, {month}, {day}")
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use arrow::array::Int32Array;
+ use arrow::datatypes::Field;
+ use datafusion_common::config::ConfigOptions;
+
+ fn invoke(args: Vec<ColumnarValue>, number_rows: usize) -> Result<ColumnarValue> {
+ let arg_fields = args
+ .iter()
+ .map(|a| Field::new("a", a.data_type(), true).into())
+ .collect::<Vec<_>>();
+ MakeDateFunc::new().invoke_with_args(ScalarFunctionArgs {
+ args,
+ arg_fields,
+ number_rows,
+ return_field: Field::new("f", Date32, true).into(),
+ config_options: Arc::new(ConfigOptions::default()),
+ })
+ }
+
+ #[test]
+ fn test_make_date_array() {
+ let years = ColumnarValue::Array(Arc::new(Int32Array::from(vec![
+ Some(1970),
+ Some(1970),
+ ])));
+ let months =
+ ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(1), Some(1)])));
+ let days =
+ ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(1), Some(2)])));
+
+ let ColumnarValue::Array(arr) = invoke(vec![years, months, days], 2).unwrap()
+ else {
+ panic!("expected array result");
+ };
+ let arr = arr.as_primitive::<Date32Type>();
+ // Days since the unix epoch.
+ assert_eq!(arr.value(0), 0);
+ assert_eq!(arr.value(1), 1);
+ }
+
+ #[test]
+ fn test_make_date_null_propagation() {
+ // A NULL in any component column yields a NULL row.
+ let years =
+ ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(2000), None])));
+ let months =
+ ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(6), Some(6)])));
+ let days = ColumnarValue::Array(Arc::new(Int32Array::from(vec![None, Some(15)])));
+
+ let ColumnarValue::Array(arr) = invoke(vec![years, months, days], 2).unwrap()
+ else {
+ panic!("expected array result");
+ };
+ let arr = arr.as_primitive::<Date32Type>();
+ assert!(arr.is_null(0));
+ assert!(arr.is_null(1));
+ }
+
+ #[test]
+ fn test_make_date_scalar_array_mix() {
+ let year = ColumnarValue::Scalar(ScalarValue::Int32(Some(1970)));
+ let month = ColumnarValue::Scalar(ScalarValue::Int32(Some(1)));
+ let days =
+ ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(1), Some(3)])));
+
+ let ColumnarValue::Array(arr) = invoke(vec![year, month, days], 2).unwrap()
+ else {
+ panic!("expected array result");
+ };
+ let arr = arr.as_primitive::<Date32Type>();
+ assert_eq!(arr.value(0), 0);
+ assert_eq!(arr.value(1), 2);
+ }
+
+ #[test]
+ fn test_make_date_out_of_range_errors() {
+ let years = ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(2000)])));
+ let months = ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(13)])));
+ let days = ColumnarValue::Array(Arc::new(Int32Array::from(vec![Some(1)])));
+ assert!(invoke(vec![years, months, days], 1).is_err());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org