(&self, f: &mut AstFormatter<W>, allow_special_form: bool)
| 1307 | } |
| 1308 | |
| 1309 | fn fmt_call<W: fmt::Write>(&self, f: &mut AstFormatter<W>, allow_special_form: bool) { |
| 1310 | // This block handles printing function calls that have special parsing. In stable mode, the |
| 1311 | // name is quoted and so won't get the special parsing. We only need to print the special |
| 1312 | // formats in non-stable mode. |
| 1313 | // |
| 1314 | // The special forms (`position(a IN b)`, `extract(field FROM source)`) |
| 1315 | // have no syntax for `DISTINCT`, a within-group `ORDER BY`, a `FILTER`, |
| 1316 | // or an `OVER` window. A call literally named `"position"`/`"extract"` |
| 1317 | // that carries any of those modifiers (only reachable via the quoted |
| 1318 | // name — the real special grammar doesn't accept them) must therefore |
| 1319 | // fall through to the plain quoted-call form, or the special form |
| 1320 | // silently drops them on display. |
| 1321 | let has_call_modifiers = self.distinct |
| 1322 | || self.filter.is_some() |
| 1323 | || self.over.is_some() |
| 1324 | || matches!(&self.args, FunctionArgs::Args { order_by, .. } if !order_by.is_empty()); |
| 1325 | if allow_special_form && !f.stable() && !has_call_modifiers { |
| 1326 | let special: Option<(&str, &[Option<Keyword>])> = |
| 1327 | match self.name.to_ast_string_stable().as_str() { |
| 1328 | // `extract(field FROM source)` parses `field` into a string |
| 1329 | // literal, so the special form only round-trips when arg0 is |
| 1330 | // a string. A generic `"extract"(a, b)` with a non-string |
| 1331 | // first arg must use the plain (quoted) call form. |
| 1332 | r#""extract""# |
| 1333 | if self.args.len() == Some(2) |
| 1334 | && matches!(self.args.first(), Some(Expr::Value(Value::String(_)))) => |
| 1335 | { |
| 1336 | Some(("extract", &[None, Some(FROM)])) |
| 1337 | } |
| 1338 | // `position(<needle> IN <haystack>)` parses the needle at |
| 1339 | // `Precedence::Like`, so a low-precedence needle (`NOT`, a |
| 1340 | // comparison, `IS`, a boolean connective, a quantified |
| 1341 | // comparison, ...) printed bare before the `IN` would swallow |
| 1342 | // or stop short of the delimiter. Only use the special form |
| 1343 | // with a needle that's safe to sit left of `IN`. |
| 1344 | r#""position""# |
| 1345 | if self.args.len() == Some(2) |
| 1346 | && self.args.first().is_some_and(prints_self_delimiting) => |
| 1347 | { |
| 1348 | Some(("position", &[None, Some(IN)])) |
| 1349 | } |
| 1350 | |
| 1351 | // "trim" doesn't need to appear here because it changes the function name (to |
| 1352 | // "btrim", "ltrim", or "rtrim"), but only "trim" is parsed specially. "substring" |
| 1353 | // supports comma-delimited arguments, so doesn't need to be here. |
| 1354 | _ => None, |
| 1355 | }; |
| 1356 | if let Some((name, kws)) = special { |
| 1357 | f.write_str(name); |
| 1358 | f.write_str("("); |
| 1359 | self.args.intersperse_function_argument_keywords(f, kws); |
| 1360 | f.write_str(")"); |
| 1361 | return; |
| 1362 | } |
| 1363 | } |
| 1364 | |
| 1365 | // If the function name clashes with a keyword that has its own special |
| 1366 | // parser form, an unquoted name on reparse would trigger the |
no test coverage detected