-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simple Functions Preview #14668
base: main
Are you sure you want to change the base?
Simple Functions Preview #14668
Conversation
i now also have support for various numeric #[excalibur_function]
fn add(a: i32, b: u32) -> i64 {
a as i64 + b as i64
} nullable function arguments #[excalibur_function]
fn first_non_null(a: Option<i32>, b: Option<i32>) -> Option<i32> {
a.or(b)
} nullable results #[excalibur_function]
fn try_div(a: i32, b: i32) -> Option<i32> {
if b == 0 {
None
} else {
Some(a / b)
}
} functions that can throw #[excalibur_function]
fn maybe_fail(fail: bool) -> Result<bool> {
if fail {
exec_err!("This test function just failed")
} else {
Ok(true)
}
} and string arguments #[excalibur_function]
fn character_length(s: &str) -> u64 {
s.chars().count() as u64
}
|
55bbf7a
to
d66b8c6
Compare
d66b8c6
to
55a02e6
Compare
#[excalibur_function]
fn add(a: i32, b: u32) -> i64 {
a as i64 + b as i64
} Is it possible to define a function using generics? Otherwise, will we need to duplicate the function for different types? |
currently not.
currently yes; which is easy with a macro. |
i learned a bunch during last few days, being thrown at pages like below, which are good read
The result is a slightly smarter approach to function argument dispatch so that
|
39af19b
to
73efaf3
Compare
73efaf3
to
a277aa0
Compare
Added support for out types: #[excalibur_function]
fn concat(a: &str, b: &str, out: &mut impl std::fmt::Write) -> Result<ValuePresence> {
if a.is_empty() && b.is_empty() {
// Be like Oracle
return Ok(ValuePresence::Null);
}
out.write_str(a)?;
out.write_str(b)?;
Ok(ValuePresence::Value)
} |
Marked this as non-draft. It would be great to have reviews here. |
I bencharked #[excalibur_function]
fn character_length(s: &str) -> i32 {
s.chars().count() as i32
} comparing it with the datafusion/datafusion/functions/src/unicode/character_length.rs Lines 48 to 175 in f31ca5b
I don't have expectations that generic implementation will be faster than 50x1 longer, hand-written and optimized code. Footnotes
|
Interesting, I will review it when I have time |
I don't find it outperform the existing function 🫤 Is this something possible to support but not yet? #[excalibur_function]
fn concat(a: &str, b: &str) -> Result<String> {
if a.is_empty() && b.is_empty() {
// Be like Oracle
return Ok(String::new());
}
Ok(format!("{}{}", a, b))
} When I try concat with null, it doesn't output what I expect. How do we handle null in #[excalibur_function]
fn concat(a: &str, b: &str, out: &mut impl std::fmt::Write) -> Result<ValuePresence> {
if a.is_empty() && b.is_empty() {
// Be like Oracle
return Ok(ValuePresence::Null);
}
out.write_str(a)?;
out.write_str(b)?;
Ok(ValuePresence::Value)
}
#[test]
fn test_concat() {
let udf = concat_udf();
let n_rows = 3;
let invoke_args = vec![
ColumnarValue::Array(Arc::new(StringArray::from(vec![Some("hello"), None, Some("hello")]))),
ColumnarValue::Scalar(ScalarValue::Utf8(Some("world".to_string()))),
];
let time_start = time::Instant::now();
let ColumnarValue::Array(result_array) = udf
.invoke_with_args(ScalarFunctionArgs {
args: invoke_args,
number_rows: n_rows,
return_type: &DataType::Utf8,
})
.unwrap()
else {
panic!("Expected array result");
};
println!(
"Time elapsed in character_length_array() is: {:?}",
time_start.elapsed()
);
assert_eq!(
&*result_array,
&StringArray::from(vec!["helloworld", "world", "helloworld"])
);
} Similar to character_length, if I want to return 0 for null case, is it possible? #[excalibur_function]
fn character_length(s: &str) -> i32 {
s.chars().count() as i32
} |
support for (However, the StringViewArray API is almost imperfect, since it disallows writing to the builder, requiring
Depends on "what i expect". Generally the excalibur functions follow the "return null on null input" convention which is default for SQL.
yes, just use eg |
This is currently a preview PR for #12635 to share status of the work.
Based on
add_one
function, this createsadd_one_udf
(by convention, configurable):The generated
ScalarUDFImpl
is fully functional, with signature derived from the Rust-level signature of the decorated function: