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
36 changes: 32 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,11 @@ 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)
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 @@ -622,4 +628,26 @@ mod tests {
let msg = format!("{err}");
assert!(msg.contains("expects STRING arguments"));
}

#[test]
fn test_malformed_url_null_unless_ansi() -> Result<()> {
let urls = sa(&[
Some("https://example.com/a"),
Some("inva lid://example.com"),
]);
let parts = sa(&[Some("HOST"), Some("HOST")]);

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

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

// Argument errors are not turned into NULL
let err = spark_parse_url(from_ref(&urls), false).unwrap_err();
assert!(format!("{err}").contains("expects 2 or 3 arguments"));
Ok(())
}
}
21 changes: 21 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,30 @@ NULL
statement error 'parse_url' does not support zero arguments
SELECT parse_url();

# A malformed URL returns NULL when ANSI mode is off (the default)
query T
SELECT parse_url('inva lid://spark.apache.org/path?query=1', 'QUERY');
----
NULL

query T
SELECT parse_url('http://spark.apache.org:99999/path', 'HOST');
----
NULL

# A malformed URL raises an error when ANSI mode is on
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://spark.apache.org:99999/path', '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