This is an automated email from the ASF dual-hosted git repository.
agrove pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-comet.git
The following commit(s) were added to refs/heads/main by this push:
new b04baa55 chore: move scalar_funcs into spark-expr (#712)
b04baa55 is described below
commit b04baa55e548961fe641c6e385e65956880249f3
Author: Arttu <[email protected]>
AuthorDate: Sun Jul 28 15:44:32 2024 +0200
chore: move scalar_funcs into spark-expr (#712)
---
native/Cargo.lock | 5 +-
native/Cargo.toml | 1 +
native/core/Cargo.toml | 4 +-
native/core/benches/hash.rs | 2 +-
.../datafusion/expressions/comet_scalar_funcs.rs | 185 +++++++++++++++++++++
.../src/execution/datafusion/expressions/mod.rs | 3 +-
native/core/src/execution/datafusion/mod.rs | 1 -
native/core/src/execution/datafusion/planner.rs | 2 +-
.../src/execution/datafusion/shuffle_writer.rs | 20 ++-
.../datafusion/util/spark_bloom_filter.rs | 5 +-
native/spark-expr/Cargo.toml | 7 +-
native/spark-expr/src/lib.rs | 3 +
.../expressions => spark-expr/src}/scalar_funcs.rs | 177 ++------------------
.../src}/scalar_funcs/chr.rs | 28 ++--
.../src}/scalar_funcs/hash_expressions.rs | 32 +++-
.../src}/scalar_funcs/hex.rs | 3 +-
.../src}/scalar_funcs/unhex.rs | 3 +-
.../datafusion => spark-expr/src}/spark_hash.rs | 29 +---
.../expressions => spark-expr/src}/xxhash64.rs | 0
19 files changed, 283 insertions(+), 227 deletions(-)
diff --git a/native/Cargo.lock b/native/Cargo.lock
index 58061074..a6748fa5 100644
--- a/native/Cargo.lock
+++ b/native/Cargo.lock
@@ -896,8 +896,6 @@ dependencies = [
"tempfile",
"thiserror",
"tokio",
- "twox-hash",
- "unicode-segmentation",
"zstd",
]
@@ -915,6 +913,7 @@ version = "0.2.0"
dependencies = [
"arrow",
"arrow-array",
+ "arrow-data",
"arrow-schema",
"chrono",
"chrono-tz 0.8.6",
@@ -929,6 +928,8 @@ dependencies = [
"rand",
"regex",
"thiserror",
+ "twox-hash",
+ "unicode-segmentation",
]
[[package]]
diff --git a/native/Cargo.toml b/native/Cargo.toml
index 2b07b1e5..fc9b68c4 100644
--- a/native/Cargo.toml
+++ b/native/Cargo.toml
@@ -51,6 +51,7 @@ datafusion-comet-proto = { path = "proto", version = "0.2.0" }
chrono = { version = "0.4", default-features = false, features = ["clock"] }
chrono-tz = { version = "0.8" }
num = "0.4"
+rand = "0.8"
regex = "1.9.6"
thiserror = "1"
diff --git a/native/core/Cargo.toml b/native/core/Cargo.toml
index e396976d..5b5c237c 100644
--- a/native/core/Cargo.toml
+++ b/native/core/Cargo.toml
@@ -58,7 +58,7 @@ brotli = "3.3"
flate2 = "1.0"
lz4 = "1.24"
zstd = "0.11"
-rand = "0.8"
+rand = { workspace = true}
num = { workspace = true }
bytes = "1.5.0"
tempfile = "3.8.0"
@@ -70,7 +70,6 @@ datafusion = { workspace = true }
datafusion-expr = { workspace = true }
datafusion-physical-expr-common = { workspace = true }
datafusion-physical-expr = { workspace = true }
-unicode-segmentation = "^1.10.1"
once_cell = "1.18.0"
regex = { workspace = true }
crc32fast = "1.3.2"
@@ -85,7 +84,6 @@ jni = { version = "0.21", features = ["invocation"] }
lazy_static = "1.4"
assertables = "7"
hex = "0.4.3"
-twox-hash = "1.6.3"
[features]
default = []
diff --git a/native/core/benches/hash.rs b/native/core/benches/hash.rs
index d66d5892..039a6d5d 100644
--- a/native/core/benches/hash.rs
+++ b/native/core/benches/hash.rs
@@ -19,10 +19,10 @@
mod common;
use arrow_array::ArrayRef;
-use
comet::execution::datafusion::expressions::scalar_funcs::{spark_murmur3_hash,
spark_xxhash64};
use comet::execution::kernels::hash;
use common::*;
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
+use datafusion_comet_spark_expr::scalar_funcs::{spark_murmur3_hash,
spark_xxhash64};
use datafusion_common::ScalarValue;
use datafusion_expr::ColumnarValue;
use std::sync::Arc;
diff --git
a/native/core/src/execution/datafusion/expressions/comet_scalar_funcs.rs
b/native/core/src/execution/datafusion/expressions/comet_scalar_funcs.rs
new file mode 100644
index 00000000..70cbdeba
--- /dev/null
+++ b/native/core/src/execution/datafusion/expressions/comet_scalar_funcs.rs
@@ -0,0 +1,185 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use arrow_schema::DataType;
+use datafusion_comet_spark_expr::scalar_funcs::hash_expressions::{
+ spark_sha224, spark_sha256, spark_sha384, spark_sha512,
+};
+use datafusion_comet_spark_expr::scalar_funcs::{
+ spark_ceil, spark_decimal_div, spark_floor, spark_hex, spark_isnan,
spark_make_decimal,
+ spark_murmur3_hash, spark_round, spark_rpad, spark_unhex,
spark_unscaled_value, spark_xxhash64,
+ SparkChrFunc,
+};
+use datafusion_common::{DataFusionError, Result as DataFusionResult};
+use datafusion_expr::registry::FunctionRegistry;
+use datafusion_expr::{
+ ColumnarValue, ScalarFunctionImplementation, ScalarUDF, ScalarUDFImpl,
Signature, Volatility,
+};
+use std::any::Any;
+use std::fmt::Debug;
+use std::sync::Arc;
+
+macro_rules! make_comet_scalar_udf {
+ ($name:expr, $func:ident, $data_type:ident) => {{
+ let scalar_func = CometScalarFunction::new(
+ $name.to_string(),
+ Signature::variadic_any(Volatility::Immutable),
+ $data_type.clone(),
+ Arc::new(move |args| $func(args, &$data_type)),
+ );
+ Ok(Arc::new(ScalarUDF::new_from_impl(scalar_func)))
+ }};
+ ($name:expr, $func:expr, without $data_type:ident) => {{
+ let scalar_func = CometScalarFunction::new(
+ $name.to_string(),
+ Signature::variadic_any(Volatility::Immutable),
+ $data_type,
+ $func,
+ );
+ Ok(Arc::new(ScalarUDF::new_from_impl(scalar_func)))
+ }};
+}
+
+/// Create a physical scalar function.
+pub fn create_comet_physical_fun(
+ fun_name: &str,
+ data_type: DataType,
+ registry: &dyn FunctionRegistry,
+) -> Result<Arc<ScalarUDF>, DataFusionError> {
+ match fun_name {
+ "ceil" => {
+ make_comet_scalar_udf!("ceil", spark_ceil, data_type)
+ }
+ "floor" => {
+ make_comet_scalar_udf!("floor", spark_floor, data_type)
+ }
+ "rpad" => {
+ let func = Arc::new(spark_rpad);
+ make_comet_scalar_udf!("rpad", func, without data_type)
+ }
+ "round" => {
+ make_comet_scalar_udf!("round", spark_round, data_type)
+ }
+ "unscaled_value" => {
+ let func = Arc::new(spark_unscaled_value);
+ make_comet_scalar_udf!("unscaled_value", func, without data_type)
+ }
+ "make_decimal" => {
+ make_comet_scalar_udf!("make_decimal", spark_make_decimal,
data_type)
+ }
+ "hex" => {
+ let func = Arc::new(spark_hex);
+ make_comet_scalar_udf!("hex", func, without data_type)
+ }
+ "unhex" => {
+ let func = Arc::new(spark_unhex);
+ make_comet_scalar_udf!("unhex", func, without data_type)
+ }
+ "decimal_div" => {
+ make_comet_scalar_udf!("decimal_div", spark_decimal_div, data_type)
+ }
+ "murmur3_hash" => {
+ let func = Arc::new(spark_murmur3_hash);
+ make_comet_scalar_udf!("murmur3_hash", func, without data_type)
+ }
+ "xxhash64" => {
+ let func = Arc::new(spark_xxhash64);
+ make_comet_scalar_udf!("xxhash64", func, without data_type)
+ }
+ "chr" =>
Ok(Arc::new(ScalarUDF::new_from_impl(SparkChrFunc::default()))),
+ "isnan" => {
+ let func = Arc::new(spark_isnan);
+ make_comet_scalar_udf!("isnan", func, without data_type)
+ }
+ "sha224" => {
+ let func = Arc::new(spark_sha224);
+ make_comet_scalar_udf!("sha224", func, without data_type)
+ }
+ "sha256" => {
+ let func = Arc::new(spark_sha256);
+ make_comet_scalar_udf!("sha256", func, without data_type)
+ }
+ "sha384" => {
+ let func = Arc::new(spark_sha384);
+ make_comet_scalar_udf!("sha384", func, without data_type)
+ }
+ "sha512" => {
+ let func = Arc::new(spark_sha512);
+ make_comet_scalar_udf!("sha512", func, without data_type)
+ }
+ _ => registry.udf(fun_name).map_err(|e| {
+ DataFusionError::Execution(format!(
+ "Function {fun_name} not found in the registry: {e}",
+ ))
+ }),
+ }
+}
+
+struct CometScalarFunction {
+ name: String,
+ signature: Signature,
+ data_type: DataType,
+ func: ScalarFunctionImplementation,
+}
+
+impl Debug for CometScalarFunction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CometScalarFunction")
+ .field("name", &self.name)
+ .field("signature", &self.signature)
+ .field("data_type", &self.data_type)
+ .finish()
+ }
+}
+
+impl CometScalarFunction {
+ fn new(
+ name: String,
+ signature: Signature,
+ data_type: DataType,
+ func: ScalarFunctionImplementation,
+ ) -> Self {
+ Self {
+ name,
+ signature,
+ data_type,
+ func,
+ }
+ }
+}
+
+impl ScalarUDFImpl for CometScalarFunction {
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+
+ fn name(&self) -> &str {
+ self.name.as_str()
+ }
+
+ fn signature(&self) -> &Signature {
+ &self.signature
+ }
+
+ fn return_type(&self, _: &[DataType]) -> DataFusionResult<DataType> {
+ Ok(self.data_type.clone())
+ }
+
+ fn invoke(&self, args: &[ColumnarValue]) ->
DataFusionResult<ColumnarValue> {
+ (self.func)(args)
+ }
+}
diff --git a/native/core/src/execution/datafusion/expressions/mod.rs
b/native/core/src/execution/datafusion/expressions/mod.rs
index 3c0a5b26..ecd04956 100644
--- a/native/core/src/execution/datafusion/expressions/mod.rs
+++ b/native/core/src/execution/datafusion/expressions/mod.rs
@@ -20,13 +20,13 @@
pub mod bitwise_not;
pub mod checkoverflow;
mod normalize_nan;
-pub mod scalar_funcs;
pub use normalize_nan::NormalizeNaNAndZero;
use crate::errors::CometError;
pub mod avg;
pub mod avg_decimal;
pub mod bloom_filter_might_contain;
+pub mod comet_scalar_funcs;
pub mod correlation;
pub mod covariance;
pub mod create_named_struct;
@@ -39,7 +39,6 @@ pub mod sum_decimal;
pub mod unbound;
mod utils;
pub mod variance;
-pub mod xxhash64;
pub use datafusion_comet_spark_expr::{EvalMode, SparkError};
diff --git a/native/core/src/execution/datafusion/mod.rs
b/native/core/src/execution/datafusion/mod.rs
index e98e4ed4..6f81ee91 100644
--- a/native/core/src/execution/datafusion/mod.rs
+++ b/native/core/src/execution/datafusion/mod.rs
@@ -21,5 +21,4 @@ pub mod expressions;
mod operators;
pub mod planner;
pub mod shuffle_writer;
-pub mod spark_hash;
mod util;
diff --git a/native/core/src/execution/datafusion/planner.rs
b/native/core/src/execution/datafusion/planner.rs
index f3bc146d..c283cebb 100644
--- a/native/core/src/execution/datafusion/planner.rs
+++ b/native/core/src/execution/datafusion/planner.rs
@@ -76,7 +76,6 @@ use crate::{
correlation::Correlation,
covariance::Covariance,
negative,
- scalar_funcs::create_comet_physical_fun,
stats::StatsType,
stddev::Stddev,
strings::{Contains, EndsWith, Like, StartsWith,
StringSpaceExpr, SubstringExpr},
@@ -95,6 +94,7 @@ use crate::{
};
use super::expressions::{create_named_struct::CreateNamedStruct, EvalMode};
+use
crate::execution::datafusion::expressions::comet_scalar_funcs::create_comet_physical_fun;
use datafusion_comet_proto::{
spark_expression::{
self, agg_expr::ExprStruct as AggExprStruct, expr::ExprStruct,
literal::Value, AggExpr,
diff --git a/native/core/src/execution/datafusion/shuffle_writer.rs
b/native/core/src/execution/datafusion/shuffle_writer.rs
index 1a1149da..f7c97bea 100644
--- a/native/core/src/execution/datafusion/shuffle_writer.rs
+++ b/native/core/src/execution/datafusion/shuffle_writer.rs
@@ -62,8 +62,8 @@ use tokio::task;
use crate::{
common::bit::ceil,
errors::{CometError, CometResult},
- execution::datafusion::spark_hash::{create_murmur3_hashes, pmod},
};
+use datafusion_comet_spark_expr::spark_hash::create_murmur3_hashes;
/// The shuffle writer operator maps each input partition to M output
partitions based on a
/// partitioning scheme. No guarantees are made about the order of the
resulting partitions.
@@ -1413,6 +1413,14 @@ impl RecordBatchStream for EmptyStream {
}
}
+fn pmod(hash: u32, n: usize) -> usize {
+ let hash = hash as i32;
+ let n = n as i32;
+ let r = hash % n;
+ let result = if r < 0 { (r + n) % n } else { r };
+ result as usize
+}
+
#[cfg(test)]
mod test {
use super::*;
@@ -1477,4 +1485,14 @@ mod test {
let rt = Runtime::new().unwrap();
rt.block_on(collect(stream)).unwrap();
}
+
+ #[test]
+ fn test_pmod() {
+ let i: Vec<u32> = vec![0x99f0149d, 0x9c67b85d, 0xc8008529, 0xa05b5d7b,
0xcd1e64fb];
+ let result = i.into_iter().map(|i| pmod(i,
200)).collect::<Vec<usize>>();
+
+ // expected partition from Spark with n=200
+ let expected = vec![69, 5, 193, 171, 115];
+ assert_eq!(result, expected);
+ }
}
diff --git a/native/core/src/execution/datafusion/util/spark_bloom_filter.rs
b/native/core/src/execution/datafusion/util/spark_bloom_filter.rs
index 22957a14..00f71767 100644
--- a/native/core/src/execution/datafusion/util/spark_bloom_filter.rs
+++ b/native/core/src/execution/datafusion/util/spark_bloom_filter.rs
@@ -15,10 +15,9 @@
// specific language governing permissions and limitations
// under the License.
-use crate::execution::datafusion::{
- spark_hash::spark_compatible_murmur3_hash,
util::spark_bit_array::SparkBitArray,
-};
+use crate::execution::datafusion::util::spark_bit_array::SparkBitArray;
use arrow_array::{ArrowNativeTypeOp, BooleanArray, Int64Array};
+use datafusion_comet_spark_expr::spark_hash::spark_compatible_murmur3_hash;
const SPARK_BLOOM_FILTER_VERSION_1: i32 = 1;
diff --git a/native/spark-expr/Cargo.toml b/native/spark-expr/Cargo.toml
index aa4fcfc5..a535a2b8 100644
--- a/native/spark-expr/Cargo.toml
+++ b/native/spark-expr/Cargo.toml
@@ -41,10 +41,13 @@ chrono-tz = { workspace = true }
num = { workspace = true }
regex = { workspace = true }
thiserror = { workspace = true }
+unicode-segmentation = "1.11.0"
[dev-dependencies]
+arrow-data = {workspace = true}
criterion = "0.5.1"
-rand = "0.8.5"
+rand = { workspace = true}
+twox-hash = "1.6.3"
[lib]
name = "datafusion_comet_spark_expr"
@@ -60,4 +63,4 @@ harness = false
[[bench]]
name = "conditional"
-harness = false
\ No newline at end of file
+harness = false
diff --git a/native/spark-expr/src/lib.rs b/native/spark-expr/src/lib.rs
index 22628978..14ab080b 100644
--- a/native/spark-expr/src/lib.rs
+++ b/native/spark-expr/src/lib.rs
@@ -20,9 +20,12 @@ mod error;
mod if_expr;
mod kernels;
+pub mod scalar_funcs;
+pub mod spark_hash;
mod temporal;
pub mod timezone;
pub mod utils;
+mod xxhash64;
pub use cast::{spark_cast, Cast};
pub use error::{SparkError, SparkResult};
diff --git a/native/core/src/execution/datafusion/expressions/scalar_funcs.rs
b/native/spark-expr/src/scalar_funcs.rs
similarity index 79%
rename from native/core/src/execution/datafusion/expressions/scalar_funcs.rs
rename to native/spark-expr/src/scalar_funcs.rs
index d47172df..c50b98ba 100644
--- a/native/core/src/execution/datafusion/expressions/scalar_funcs.rs
+++ b/native/spark-expr/src/scalar_funcs.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use std::{any::Any, cmp::min, fmt::Debug, sync::Arc};
+use std::{cmp::min, sync::Arc};
use arrow::{
array::{
@@ -26,17 +26,11 @@ use arrow::{
};
use arrow_array::{Array, ArrowNativeTypeOp, BooleanArray, Decimal128Array};
use arrow_schema::DataType;
-use datafusion::{
- execution::FunctionRegistry,
- functions::math::round::round,
- logical_expr::{ScalarFunctionImplementation, ScalarUDFImpl, Signature,
Volatility},
- physical_plan::ColumnarValue,
-};
+use datafusion::{functions::math::round::round, physical_plan::ColumnarValue};
use datafusion_common::{
cast::as_generic_string_array, exec_err, internal_err, DataFusionError,
Result as DataFusionResult, ScalarValue,
};
-use datafusion_expr::ScalarUDF;
use num::{
integer::{div_ceil, div_floor},
BigInt, Signed, ToPrimitive,
@@ -44,113 +38,18 @@ use num::{
use unicode_segmentation::UnicodeSegmentation;
mod unhex;
-use unhex::spark_unhex;
+pub use unhex::spark_unhex;
mod hex;
-use hex::spark_hex;
+pub use hex::spark_hex;
mod chr;
-use chr::spark_chr;
+pub use chr::SparkChrFunc;
pub mod hash_expressions;
// exposed for benchmark only
-use hash_expressions::wrap_digest_result_as_hex_string;
pub use hash_expressions::{spark_murmur3_hash, spark_xxhash64};
-macro_rules! make_comet_scalar_udf {
- ($name:expr, $func:ident, $data_type:ident) => {{
- let scalar_func = CometScalarFunction::new(
- $name.to_string(),
- Signature::variadic_any(Volatility::Immutable),
- $data_type.clone(),
- Arc::new(move |args| $func(args, &$data_type)),
- );
- Ok(Arc::new(ScalarUDF::new_from_impl(scalar_func)))
- }};
- ($name:expr, $func:expr, without $data_type:ident) => {{
- let scalar_func = CometScalarFunction::new(
- $name.to_string(),
- Signature::variadic_any(Volatility::Immutable),
- $data_type,
- $func,
- );
- Ok(Arc::new(ScalarUDF::new_from_impl(scalar_func)))
- }};
-}
-
-/// Create a physical scalar function.
-pub fn create_comet_physical_fun(
- fun_name: &str,
- data_type: DataType,
- registry: &dyn FunctionRegistry,
-) -> Result<Arc<ScalarUDF>, DataFusionError> {
- let sha2_functions = ["sha224", "sha256", "sha384", "sha512"];
- match fun_name {
- "ceil" => {
- make_comet_scalar_udf!("ceil", spark_ceil, data_type)
- }
- "floor" => {
- make_comet_scalar_udf!("floor", spark_floor, data_type)
- }
- "rpad" => {
- let func = Arc::new(spark_rpad);
- make_comet_scalar_udf!("rpad", func, without data_type)
- }
- "round" => {
- make_comet_scalar_udf!("round", spark_round, data_type)
- }
- "unscaled_value" => {
- let func = Arc::new(spark_unscaled_value);
- make_comet_scalar_udf!("unscaled_value", func, without data_type)
- }
- "make_decimal" => {
- make_comet_scalar_udf!("make_decimal", spark_make_decimal,
data_type)
- }
- "hex" => {
- let func = Arc::new(spark_hex);
- make_comet_scalar_udf!("hex", func, without data_type)
- }
- "unhex" => {
- let func = Arc::new(spark_unhex);
- make_comet_scalar_udf!("unhex", func, without data_type)
- }
- "decimal_div" => {
- make_comet_scalar_udf!("decimal_div", spark_decimal_div, data_type)
- }
- "murmur3_hash" => {
- let func = Arc::new(spark_murmur3_hash);
- make_comet_scalar_udf!("murmur3_hash", func, without data_type)
- }
- "xxhash64" => {
- let func = Arc::new(spark_xxhash64);
- make_comet_scalar_udf!("xxhash64", func, without data_type)
- }
- "chr" => {
- let func = Arc::new(spark_chr);
- make_comet_scalar_udf!("chr", func, without data_type)
- }
- "isnan" => {
- let func = Arc::new(spark_isnan);
- make_comet_scalar_udf!("isnan", func, without data_type)
- }
- sha if sha2_functions.contains(&sha) => {
- // Spark requires hex string as the result of sha2 functions, we
have to wrap the
- // result of digest functions as hex string
- let func = registry.udf(sha)?;
- let wrapped_func = Arc::new(move |args: &[ColumnarValue]| {
- wrap_digest_result_as_hex_string(args, func.fun())
- });
- let spark_func_name = "spark".to_owned() + sha;
- make_comet_scalar_udf!(spark_func_name, wrapped_func, without
data_type)
- }
- _ => registry.udf(fun_name).map_err(|e| {
- DataFusionError::Execution(format!(
- "Function {fun_name} not found in the registry: {e}",
- ))
- }),
- }
-}
-
#[inline]
fn get_precision_scale(data_type: &DataType) -> (u8, i8) {
let DataType::Decimal128(precision, scale) = data_type else {
@@ -176,61 +75,6 @@ macro_rules! downcast_compute_op {
}};
}
-struct CometScalarFunction {
- name: String,
- signature: Signature,
- data_type: DataType,
- func: ScalarFunctionImplementation,
-}
-
-impl Debug for CometScalarFunction {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("CometScalarFunction")
- .field("name", &self.name)
- .field("signature", &self.signature)
- .field("data_type", &self.data_type)
- .finish()
- }
-}
-
-impl CometScalarFunction {
- fn new(
- name: String,
- signature: Signature,
- data_type: DataType,
- func: ScalarFunctionImplementation,
- ) -> Self {
- Self {
- name,
- signature,
- data_type,
- func,
- }
- }
-}
-
-impl ScalarUDFImpl for CometScalarFunction {
- fn as_any(&self) -> &dyn Any {
- self
- }
-
- fn name(&self) -> &str {
- self.name.as_str()
- }
-
- fn signature(&self) -> &Signature {
- &self.signature
- }
-
- fn return_type(&self, _: &[DataType]) -> DataFusionResult<DataType> {
- Ok(self.data_type.clone())
- }
-
- fn invoke(&self, args: &[ColumnarValue]) ->
DataFusionResult<ColumnarValue> {
- (self.func)(args)
- }
-}
-
/// `ceil` function that simulates Spark `ceil` expression
pub fn spark_ceil(
args: &[ColumnarValue],
@@ -333,6 +177,7 @@ pub fn spark_floor(
}
}
+/// Spark-compatible `UnscaledValue` expression (internal to Spark optimizer)
pub fn spark_unscaled_value(args: &[ColumnarValue]) ->
DataFusionResult<ColumnarValue> {
match &args[0] {
ColumnarValue::Scalar(v) => match v {
@@ -352,6 +197,7 @@ pub fn spark_unscaled_value(args: &[ColumnarValue]) ->
DataFusionResult<Columnar
}
}
+/// Spark-compatible `MakeDecimal` expression (internal to Spark optimizer)
pub fn spark_make_decimal(
args: &[ColumnarValue],
data_type: &DataType,
@@ -489,7 +335,7 @@ macro_rules! round_integer_scalar {
}
/// `round` function that simulates Spark `round` expression
-fn spark_round(
+pub fn spark_round(
args: &[ColumnarValue],
data_type: &DataType,
) -> Result<ColumnarValue, DataFusionError> {
@@ -541,7 +387,7 @@ fn spark_round(
}
/// Similar to DataFusion `rpad`, but not to truncate when the string is
already longer than length
-fn spark_rpad(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+pub fn spark_rpad(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
match args {
[ColumnarValue::Array(array),
ColumnarValue::Scalar(ScalarValue::Int32(Some(length)))] => {
match args[0].data_type() {
@@ -594,7 +440,7 @@ fn spark_rpad_internal<T: OffsetSizeTrait>(
// get enough scale that matches with Spark behavior, it requires to widen s1
to s2 + s3 + 1. Since
// both s2 and s3 are 38 at max., s1 is 77 at max. DataFusion division cannot
handle such scale >
// Decimal256Type::MAX_SCALE. Therefore, we need to implement this decimal
division using BigInt.
-fn spark_decimal_div(
+pub fn spark_decimal_div(
args: &[ColumnarValue],
data_type: &DataType,
) -> Result<ColumnarValue, DataFusionError> {
@@ -639,7 +485,8 @@ fn spark_decimal_div(
Ok(ColumnarValue::Array(Arc::new(result)))
}
-fn spark_isnan(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+/// Spark-compatible `isnan` expression
+pub fn spark_isnan(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
fn set_nulls_to_false(is_nan: BooleanArray) -> ColumnarValue {
match is_nan.nulls() {
Some(nulls) => {
diff --git
a/native/core/src/execution/datafusion/expressions/scalar_funcs/chr.rs
b/native/spark-expr/src/scalar_funcs/chr.rs
similarity index 87%
rename from native/core/src/execution/datafusion/expressions/scalar_funcs/chr.rs
rename to native/spark-expr/src/scalar_funcs/chr.rs
index 0421b491..5de59f9f 100644
--- a/native/core/src/execution/datafusion/expressions/scalar_funcs/chr.rs
+++ b/native/spark-expr/src/scalar_funcs/chr.rs
@@ -26,17 +26,9 @@ use arrow::{
};
use datafusion::logical_expr::{ColumnarValue, ScalarUDFImpl, Signature,
Volatility};
-use datafusion_common::{cast::as_int64_array, exec_err, DataFusionError,
Result, ScalarValue};
+use datafusion_common::{cast::as_int64_array, exec_err, Result, ScalarValue};
-/// Returns the ASCII character having the binary equivalent to the input
expression.
-/// E.g., chr(65) = 'A'.
-/// Compatible with Apache Spark's Chr function
-pub fn spark_chr(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
- let chr_func = ChrFunc::default();
- chr_func.invoke(args)
-}
-
-pub fn chr(args: &[ArrayRef]) -> Result<ArrayRef> {
+fn chr(args: &[ArrayRef]) -> Result<ArrayRef> {
let integer_array = as_int64_array(&args[0])?;
// first map is the iterator, second is for the `Option<_>`
@@ -62,18 +54,19 @@ pub fn chr(args: &[ArrayRef]) -> Result<ArrayRef> {
Ok(Arc::new(result) as ArrayRef)
}
+/// Spark-compatible `chr` expression
#[derive(Debug)]
-pub struct ChrFunc {
+pub struct SparkChrFunc {
signature: Signature,
}
-impl Default for ChrFunc {
+impl Default for SparkChrFunc {
fn default() -> Self {
Self::new()
}
}
-impl ChrFunc {
+impl SparkChrFunc {
pub fn new() -> Self {
Self {
signature: Signature::uniform(1, vec![Int64],
Volatility::Immutable),
@@ -81,7 +74,7 @@ impl ChrFunc {
}
}
-impl ScalarUDFImpl for ChrFunc {
+impl ScalarUDFImpl for SparkChrFunc {
fn as_any(&self) -> &dyn Any {
self
}
@@ -99,11 +92,14 @@ impl ScalarUDFImpl for ChrFunc {
}
fn invoke(&self, args: &[ColumnarValue]) -> Result<ColumnarValue> {
- handle_chr_fn(args)
+ spark_chr(args)
}
}
-fn handle_chr_fn(args: &[ColumnarValue]) -> Result<ColumnarValue> {
+/// Returns the ASCII character having the binary equivalent to the input
expression.
+/// E.g., chr(65) = 'A'.
+/// Compatible with Apache Spark's Chr function
+fn spark_chr(args: &[ColumnarValue]) -> Result<ColumnarValue> {
let array = args[0].clone();
match array {
ColumnarValue::Array(array) => {
diff --git
a/native/core/src/execution/datafusion/expressions/scalar_funcs/hash_expressions.rs
b/native/spark-expr/src/scalar_funcs/hash_expressions.rs
similarity index 79%
rename from
native/core/src/execution/datafusion/expressions/scalar_funcs/hash_expressions.rs
rename to native/spark-expr/src/scalar_funcs/hash_expressions.rs
index 67d72816..1a403b9e 100644
---
a/native/core/src/execution/datafusion/expressions/scalar_funcs/hash_expressions.rs
+++ b/native/spark-expr/src/scalar_funcs/hash_expressions.rs
@@ -15,15 +15,17 @@
// specific language governing permissions and limitations
// under the License.
-use crate::execution::datafusion::expressions::scalar_funcs::hex::hex_strings;
-use crate::execution::datafusion::spark_hash::{create_murmur3_hashes,
create_xxhash64_hashes};
+use crate::scalar_funcs::hex::hex_strings;
+use crate::spark_hash::{create_murmur3_hashes, create_xxhash64_hashes};
+
use arrow_array::{ArrayRef, Int32Array, Int64Array, StringArray};
+use datafusion::functions::crypto::{sha224, sha256, sha384, sha512};
use datafusion_common::cast::as_binary_array;
use datafusion_common::{exec_err, internal_err, DataFusionError, ScalarValue};
use datafusion_expr::{ColumnarValue, ScalarFunctionImplementation};
use std::sync::Arc;
-/// Spark compatible murmur3 hash in vectorized execution fashion
+/// Spark compatible murmur3 hash (just `hash` in Spark) in vectorized
execution fashion
pub fn spark_murmur3_hash(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
let length = args.len();
let seed = &args[length - 1];
@@ -111,7 +113,29 @@ pub fn spark_xxhash64(args: &[ColumnarValue]) ->
Result<ColumnarValue, DataFusio
}
}
-pub(super) fn wrap_digest_result_as_hex_string(
+/// `sha224` function that simulates Spark's `sha2` expression with bit width
224
+pub fn spark_sha224(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+ wrap_digest_result_as_hex_string(args, sha224().fun())
+}
+
+/// `sha256` function that simulates Spark's `sha2` expression with bit width
0 or 256
+pub fn spark_sha256(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+ wrap_digest_result_as_hex_string(args, sha256().fun())
+}
+
+/// `sha384` function that simulates Spark's `sha2` expression with bit width
384
+pub fn spark_sha384(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+ wrap_digest_result_as_hex_string(args, sha384().fun())
+}
+
+/// `sha512` function that simulates Spark's `sha2` expression with bit width
512
+pub fn spark_sha512(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+ wrap_digest_result_as_hex_string(args, sha512().fun())
+}
+
+// Spark requires hex string as the result of sha2 functions, we have to wrap
the
+// result of digest functions as hex string
+fn wrap_digest_result_as_hex_string(
args: &[ColumnarValue],
digest: ScalarFunctionImplementation,
) -> Result<ColumnarValue, DataFusionError> {
diff --git
a/native/core/src/execution/datafusion/expressions/scalar_funcs/hex.rs
b/native/spark-expr/src/scalar_funcs/hex.rs
similarity index 98%
rename from native/core/src/execution/datafusion/expressions/scalar_funcs/hex.rs
rename to native/spark-expr/src/scalar_funcs/hex.rs
index e6059818..e572ba5e 100644
--- a/native/core/src/execution/datafusion/expressions/scalar_funcs/hex.rs
+++ b/native/spark-expr/src/scalar_funcs/hex.rs
@@ -62,7 +62,8 @@ fn hex_bytes<T: AsRef<[u8]>>(bytes: T) -> Result<String,
std::fmt::Error> {
Ok(hex_string)
}
-pub(super) fn spark_hex(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+/// Spark-compatible `hex` function
+pub fn spark_hex(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
if args.len() != 1 {
return Err(DataFusionError::Internal(
"hex expects exactly one argument".to_string(),
diff --git
a/native/core/src/execution/datafusion/expressions/scalar_funcs/unhex.rs
b/native/spark-expr/src/scalar_funcs/unhex.rs
similarity index 98%
rename from
native/core/src/execution/datafusion/expressions/scalar_funcs/unhex.rs
rename to native/spark-expr/src/scalar_funcs/unhex.rs
index 38d5c047..9996392b 100644
--- a/native/core/src/execution/datafusion/expressions/scalar_funcs/unhex.rs
+++ b/native/spark-expr/src/scalar_funcs/unhex.rs
@@ -109,7 +109,8 @@ fn spark_unhex_inner<T: OffsetSizeTrait>(
}
}
-pub(super) fn spark_unhex(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
+/// Spark-compatible `unhex` expression
+pub fn spark_unhex(args: &[ColumnarValue]) -> Result<ColumnarValue,
DataFusionError> {
if args.len() > 2 {
return exec_err!("unhex takes at most 2 arguments, but got: {}",
args.len());
}
diff --git a/native/core/src/execution/datafusion/spark_hash.rs
b/native/spark-expr/src/spark_hash.rs
similarity index 96%
rename from native/core/src/execution/datafusion/spark_hash.rs
rename to native/spark-expr/src/spark_hash.rs
index e429f543..66a103a2 100644
--- a/native/core/src/execution/datafusion/spark_hash.rs
+++ b/native/spark-expr/src/spark_hash.rs
@@ -34,10 +34,11 @@ use datafusion::{
error::{DataFusionError, Result},
};
-use
crate::execution::datafusion::expressions::xxhash64::spark_compatible_xxhash64;
+use crate::xxhash64::spark_compatible_xxhash64;
+/// Spark-compatible murmur3 hash function
#[inline]
-pub(crate) fn spark_compatible_murmur3_hash<T: AsRef<[u8]>>(data: T, seed:
u32) -> u32 {
+pub fn spark_compatible_murmur3_hash<T: AsRef<[u8]>>(data: T, seed: u32) ->
u32 {
#[inline]
fn mix_k1(mut k1: i32) -> i32 {
k1 = k1.mul_wrapping(0xcc9e2d51u32 as i32);
@@ -458,7 +459,7 @@ macro_rules! create_hashes_internal {
///
/// The number of rows to hash is determined by `hashes_buffer.len()`.
/// `hashes_buffer` should be pre-sized appropriately
-pub(crate) fn create_murmur3_hashes<'a>(
+pub fn create_murmur3_hashes<'a>(
arrays: &[ArrayRef],
hashes_buffer: &'a mut [u32],
) -> Result<&'a mut [u32]> {
@@ -489,22 +490,12 @@ pub fn create_xxhash64_hashes<'a>(
Ok(hashes_buffer)
}
-pub(crate) fn pmod(hash: u32, n: usize) -> usize {
- let hash = hash as i32;
- let n = n as i32;
- let r = hash % n;
- let result = if r < 0 { (r + n) % n } else { r };
- result as usize
-}
-
#[cfg(test)]
mod tests {
use arrow::array::{Float32Array, Float64Array};
use std::sync::Arc;
- use crate::execution::datafusion::spark_hash::{
- create_murmur3_hashes, create_xxhash64_hashes, pmod,
- };
+ use super::{create_murmur3_hashes, create_xxhash64_hashes};
use datafusion::arrow::array::{ArrayRef, Int32Array, Int64Array,
Int8Array, StringArray};
macro_rules! test_hashes_internal {
@@ -714,14 +705,4 @@ mod tests {
],
)
}
-
- #[test]
- fn test_pmod() {
- let i: Vec<u32> = vec![0x99f0149d, 0x9c67b85d, 0xc8008529, 0xa05b5d7b,
0xcd1e64fb];
- let result = i.into_iter().map(|i| pmod(i,
200)).collect::<Vec<usize>>();
-
- // expected partition from Spark with n=200
- let expected = vec![69, 5, 193, 171, 115];
- assert_eq!(result, expected);
- }
}
diff --git a/native/core/src/execution/datafusion/expressions/xxhash64.rs
b/native/spark-expr/src/xxhash64.rs
similarity index 100%
rename from native/core/src/execution/datafusion/expressions/xxhash64.rs
rename to native/spark-expr/src/xxhash64.rs
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]