Skip to content
Open
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
35 changes: 31 additions & 4 deletions datafusion/spark/src/function/url/parse_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,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 @@ -213,8 +216,14 @@ 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 is an error only in ANSI mode; otherwise it
/// yields NULL, as `try_parse_url` does.
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 @@ -612,6 +621,24 @@ mod tests {
Ok(())
}

#[test]
fn test_malformed_url_is_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"));
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 @@ -343,9 +343,29 @@ NULL
statement error 'parse_url' does not support zero arguments
SELECT parse_url();

# A malformed URL is NULL unless ANSI mode is on, as in 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