Skip to content

Commit 358a9fc

Browse files
Parse XMLCONCAT as a dedicated expression
1 parent 6fa3614 commit 358a9fc

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

src/ast/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,12 @@ pub enum Expr {
13321332
Lambda(LambdaFunction),
13331333
/// Checks membership of a value in a JSON array
13341334
MemberOf(MemberOf),
1335+
/// PostgreSQL `XMLCONCAT(xml[, ...])` — concatenates a list of
1336+
/// individual XML values to create a single value containing an
1337+
/// XML content fragment.
1338+
///
1339+
/// [PostgreSQL](https://www.postgresql.org/docs/current/functions-xml.html#FUNCTIONS-PRODUCING-XML-XMLCONCAT)
1340+
XmlConcat(Vec<Expr>),
13351341
}
13361342

13371343
impl Expr {
@@ -2182,6 +2188,9 @@ impl fmt::Display for Expr {
21822188
Expr::Prior(expr) => write!(f, "PRIOR {expr}"),
21832189
Expr::Lambda(lambda) => write!(f, "{lambda}"),
21842190
Expr::MemberOf(member_of) => write!(f, "{member_of}"),
2191+
Expr::XmlConcat(exprs) => {
2192+
write!(f, "XMLCONCAT({})", display_comma_separated(exprs))
2193+
}
21852194
}
21862195
}
21872196
}

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,6 +1651,7 @@ impl Spanned for Expr {
16511651
Expr::Prior(expr) => expr.span(),
16521652
Expr::Lambda(_) => Span::empty(),
16531653
Expr::MemberOf(member_of) => member_of.value.span().union(&member_of.array.span()),
1654+
Expr::XmlConcat(exprs) => union_spans(exprs.iter().map(|e| e.span())),
16541655
}
16551656
}
16561657
}

src/parser/mod.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,9 +2430,47 @@ impl<'a> Parser<'a> {
24302430

24312431
/// Parse a function call expression named by `name` and return it as an `Expr`.
24322432
pub fn parse_function(&mut self, name: ObjectName) -> Result<Expr, ParserError> {
2433+
if let Some(expr) = self.maybe_parse_xml_function(&name)? {
2434+
return Ok(expr);
2435+
}
24332436
self.parse_function_call(name).map(Expr::Function)
24342437
}
24352438

2439+
/// If `name` is a PostgreSQL XML function and the current dialect
2440+
/// supports XML expressions, parse it as a dedicated [`Expr`]
2441+
/// variant rather than a generic function call.
2442+
///
2443+
/// Returns `Ok(None)` when the name is not an XML function or the
2444+
/// dialect does not support XML expressions, in which case the
2445+
/// caller should fall back to the regular function-call parser.
2446+
fn maybe_parse_xml_function(
2447+
&mut self,
2448+
name: &ObjectName,
2449+
) -> Result<Option<Expr>, ParserError> {
2450+
if !self.dialect.supports_xml_expressions() {
2451+
return Ok(None);
2452+
}
2453+
let [ObjectNamePart::Identifier(ident)] = name.0.as_slice() else {
2454+
return Ok(None);
2455+
};
2456+
if ident.quote_style.is_some() {
2457+
return Ok(None);
2458+
}
2459+
if ident.value.eq_ignore_ascii_case("xmlconcat") {
2460+
return Ok(Some(self.parse_xmlconcat_expr()?));
2461+
}
2462+
Ok(None)
2463+
}
2464+
2465+
/// Parse the argument list of a PostgreSQL `XMLCONCAT` expression:
2466+
/// `(expr [, expr]...)`.
2467+
fn parse_xmlconcat_expr(&mut self) -> Result<Expr, ParserError> {
2468+
self.expect_token(&Token::LParen)?;
2469+
let exprs = self.parse_comma_separated(Parser::parse_expr)?;
2470+
self.expect_token(&Token::RParen)?;
2471+
Ok(Expr::XmlConcat(exprs))
2472+
}
2473+
24362474
fn parse_function_call(&mut self, name: ObjectName) -> Result<Function, ParserError> {
24372475
self.expect_token(&Token::LParen)?;
24382476

0 commit comments

Comments
 (0)