Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions datafusion/spark/src/function/url/parse_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,11 @@ impl ScalarUDFImpl for ParseUrl {
}

fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result<ColumnarValue> {
let ScalarFunctionArgs { args, .. } = args;
make_scalar_function(spark_parse_url, vec![])(&args)
let enable_ansi_mode = args.config_options.execution.enable_ansi_mode;
make_scalar_function(
move |arrays: &[ArrayRef]| spark_parse_url(arrays, enable_ansi_mode),
vec![],
)(&args.args)
}
}

Expand All @@ -200,8 +203,13 @@ impl ScalarUDFImpl for ParseUrl {
/// - A string array with extracted URL components
/// - `None` values where extraction failed or component doesn't exist
/// - The output array type (StringArray or LargeStringArray) is determined by input types
fn spark_parse_url(args: &[ArrayRef]) -> Result<ArrayRef> {
spark_handled_parse_url(args, |x| x)
/// Like Spark, a malformed URL errors only when ANSI mode is on; otherwise
/// the result is NULL (same as `try_parse_url` for parse failures).
fn spark_parse_url(args: &[ArrayRef], enable_ansi_mode: bool) -> Result<ArrayRef> {
spark_handled_parse_url(args, |x| match x {
Err(_) if !enable_ansi_mode => Ok(None),
result => result,
})
}

pub fn spark_handled_parse_url(
Expand Down Expand Up @@ -599,6 +607,27 @@ mod tests {
Ok(())
}

#[test]
fn test_malformed_url_null_unless_ansi() -> Result<()> {
let urls = sa(&[
Some("inva lid://spark.apache.org/path?query=1"),
Some("https://example.com/a"),
]);
let parts = sa(&[Some("QUERY"), Some("PATH")]);

let out = spark_parse_url(&[Arc::clone(&urls), Arc::clone(&parts)], false)?;
let out_sa = out.as_any().downcast_ref::<StringArray>().unwrap();
assert!(out_sa.is_null(0));
assert_eq!(out_sa.value(1), "/a");

let err = spark_parse_url(&[urls, parts], true).unwrap_err();
assert!(
format!("{err}").contains("The url is invalid"),
"unexpected error: {err}"
);
Ok(())
}

#[test]
fn test_invalid_arg_count() {
let urls = sa(&[Some("https://example.com")]);
Expand Down
20 changes: 20 additions & 0 deletions datafusion/sqllogictest/test_files/spark/url/parse_url.slt
Original file line number Diff line number Diff line change
Expand Up @@ -330,9 +330,29 @@ NULL
statement error 'parse_url' does not support zero arguments
SELECT parse_url();

# Malformed URL: NULL when ANSI is off (DataFusion default), error when on — matches Spark.
query T
SELECT parse_url('inva lid://spark.apache.org/path?query=1', 'QUERY');
----
NULL

query T
SELECT parse_url('http://example.com:99999/', 'HOST');
----
NULL

statement ok
set datafusion.execution.enable_ansi_mode = true;

query error DataFusion error: Execution error: The url is invalid: inva lid://spark\.apache\.org/path\?query=1\. Use `try_parse_url` to tolerate invalid URL and return NULL instead\. SQLSTATE: 22P02
SELECT parse_url('inva lid://spark.apache.org/path?query=1', 'QUERY');

query error DataFusion error: Execution error: InvalidPort
SELECT parse_url('http://example.com:99999/', 'HOST');

statement ok
set datafusion.execution.enable_ansi_mode = false;

# NULL argument handling (Sail PR #1393)
# NULL URL should return NULL
query T
Expand Down
Loading