diff --git a/datafusion/spark/src/function/url/parse_url.rs b/datafusion/spark/src/function/url/parse_url.rs index 7d03f9d824c67..ad5bdf0c4e442 100644 --- a/datafusion/spark/src/function/url/parse_url.rs +++ b/datafusion/spark/src/function/url/parse_url.rs @@ -180,8 +180,11 @@ impl ScalarUDFImpl for ParseUrl { } fn invoke_with_args(&self, args: ScalarFunctionArgs) -> Result { - 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) } } @@ -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 { - 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 { + spark_handled_parse_url(args, |x| match x { + Err(_) if !enable_ansi_mode => Ok(None), + result => result, + }) } pub fn spark_handled_parse_url( @@ -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::().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")]); diff --git a/datafusion/sqllogictest/test_files/spark/url/parse_url.slt b/datafusion/sqllogictest/test_files/spark/url/parse_url.slt index 595b264cdcf3e..374d56ff6f975 100644 --- a/datafusion/sqllogictest/test_files/spark/url/parse_url.slt +++ b/datafusion/sqllogictest/test_files/spark/url/parse_url.slt @@ -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