Parses a URL and extracts the specified component. This function takes a URL string and extracts different parts of it based on the `part` parameter. For query parameters, an optional `key` can be specified to extract a specific query parameter value. # Arguments `value` - The URL string to parse `part` - The component of the URL to extract. Valid values are: - `"HOST"` - The hostname (e.g., "e
(value: &str, part: &str, key: Option<&str>)
| 80 | /// * `Ok(None)` - If the requested component doesn't exist |
| 81 | /// * `Err(DataFusionError)` - If the URL is malformed and cannot be parsed |
| 82 | fn parse(value: &str, part: &str, key: Option<&str>) -> Result<Option<String>> { |
| 83 | let url: std::result::Result<Url, ParseError> = Url::parse(value); |
| 84 | if let Err(ParseError::RelativeUrlWithoutBase) = url { |
| 85 | return if !value.contains("://") { |
| 86 | // Schemeless URLs are treated as relative URIs (like java.net.URI). |
| 87 | // Manually parse path, query, and fragment components. |
| 88 | let (without_fragment, fragment) = match value.split_once('#') { |
| 89 | Some((before, frag)) => (before, Some(frag)), |
| 90 | None => (value, None), |
| 91 | }; |
| 92 | let (path, query) = match without_fragment.split_once('?') { |
| 93 | Some((p, q)) => (p, Some(q)), |
| 94 | None => (without_fragment, None), |
| 95 | }; |
| 96 | Ok(match part { |
| 97 | "PATH" => Some(path.to_string()), |
| 98 | "QUERY" => match key { |
| 99 | None => query.map(String::from), |
| 100 | Some(key) => Self::query_value(query, key).map(String::from), |
| 101 | }, |
| 102 | "REF" => fragment.map(String::from), |
| 103 | "FILE" => { |
| 104 | // FILE = path + query (without fragment) |
| 105 | Some(without_fragment.to_string()) |
| 106 | } |
| 107 | // HOST, PROTOCOL, AUTHORITY, USERINFO → NULL |
| 108 | _ => None, |
| 109 | }) |
| 110 | } else { |
| 111 | Err(exec_datafusion_err!( |
| 112 | "The url is invalid: {value}. Use `try_parse_url` to tolerate invalid URL and return NULL instead. SQLSTATE: 22P02" |
| 113 | )) |
| 114 | }; |
| 115 | }; |
| 116 | url.map_err(|e| exec_datafusion_err!("{e:?}")) |
| 117 | .map(|url| match part { |
| 118 | "HOST" => url.host_str().map(String::from), |
| 119 | "PATH" => { |
| 120 | let path = Self::path(value, &url); |
| 121 | Some(path.to_string()) |
| 122 | } |
| 123 | "QUERY" => match key { |
| 124 | None => url.query().map(String::from), |
| 125 | Some(key) => Self::query_value(url.query(), key).map(String::from), |
| 126 | }, |
| 127 | "REF" => url.fragment().map(String::from), |
| 128 | "PROTOCOL" => Some(url.scheme().to_string()), |
| 129 | "FILE" => { |
| 130 | let path = Self::path(value, &url); |
| 131 | match url.query() { |
| 132 | Some(query) => Some(format!("{path}?{query}")), |
| 133 | None => Some(path.to_string()), |
| 134 | } |
| 135 | } |
| 136 | "AUTHORITY" => Some(url.authority().to_string()), |
| 137 | "USERINFO" => { |
| 138 | let username = url.username(); |
| 139 | if username.is_empty() { |