LucaCappelletti94 commented on code in PR #2507:
URL:
https://github.com/apache/datafusion-sqlparser-rs/pull/2507#discussion_r4093776267
##########
src/ast/mod.rs:
##########
Review Comment:
You should export the new enum next to `XmlPassingArgument`.
```suggestion
XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause,
XmlPassingMechanism,
XmlTableColumn, XmlTableColumnOption,
```
##########
tests/sqlparser_common.rs:
##########
@@ -19843,6 +19843,36 @@ fn parse_xmlparse() {
.is_err());
}
+#[test]
+fn parse_xml_functions() {
+ let dialects = all_dialects_where(|d| d.supports_xml_expressions());
+
+ dialects.verified_stmt("SELECT XMLELEMENT(NAME foo, 'bar')");
+ dialects.verified_stmt("SELECT XMLELEMENT(NAME foo, 'bar'), * FROM
customers");
+ dialects.verified_stmt("SELECT XMLELEMENT(NAME foo, XMLATTRIBUTES('v' AS
attr), 'bar')");
+ dialects.verified_stmt(r#"SELECT XMLELEMENT(NAME "foo$bar",
XMLATTRIBUTES('xyz' AS "a&b"))"#);
+ dialects.verified_stmt(r#"SELECT XMLPI(NAME php, 'echo "hello world";')"#);
+ dialects.verified_stmt("SELECT XMLPI(NAME php)");
+ dialects.verified_stmt("SELECT XMLROOT('<a/>'::xml, VERSION '1.0')");
+ dialects.verified_stmt("SELECT XMLROOT('<a/>'::xml, VERSION '1.0',
STANDALONE YES)");
+ dialects.verified_stmt("SELECT XMLROOT('<a/>'::xml, VERSION NO VALUE,
STANDALONE NO VALUE)");
+ dialects.verified_stmt("SELECT XMLSERIALIZE(DOCUMENT '<a/>'::xml AS
TEXT)");
+ dialects.verified_stmt("SELECT XMLSERIALIZE(CONTENT '<a/>'::xml AS
VARCHAR(100) INDENT)");
+ dialects.verified_stmt("SELECT XMLSERIALIZE(DOCUMENT '<a/>'::xml AS TEXT
NO INDENT)");
+ dialects.verified_stmt("SELECT XMLEXISTS('/a' PASSING BY REF '<a/>')");
+ dialects.verified_stmt("SELECT XMLEXISTS('/a' PASSING '<a/>')");
+ dialects.verified_stmt("SELECT XMLEXISTS('/a' PASSING BY VALUE '<a/>')");
Review Comment:
These are the red tests I found and mentioned in the other notes.
```suggestion
dialects.verified_stmt("SELECT XMLEXISTS('/a' PASSING BY VALUE '<a/>')");
dialects.verified_stmt("SELECT XMLEXISTS('/a' PASSING BY VALUE '<a/>' BY
REF)");
for sql in [
"SELECT XMLEXISTS('/a')",
"SELECT XMLEXISTS('/a' PASSING '<a/>' BY)",
] {
assert!(dialects.parse_sql_statements(sql).is_err(), "{sql}");
}
```
##########
src/parser/mod.rs:
##########
@@ -2546,6 +2546,102 @@ impl<'a> Parser<'a> {
})
}
+ /// Parse the argument list of `XMLELEMENT(NAME name [, XMLATTRIBUTES(...)
] [, content [, ...]])`
+ /// or `XMLPI(NAME name [, content ])`.
+ fn parse_xmlelement_or_xmlpi_argument_list(
+ &mut self,
+ ) -> Result<FunctionArgumentList, ParserError> {
+ let name_kw = self.parse_identifier()?;
+ let name_val = self.parse_identifier()?;
+ let mut args = vec![FunctionArg::Named {
+ name: name_kw,
+ arg: FunctionArgExpr::Expr(Expr::Identifier(name_val)),
+ operator: FunctionArgOperator::Space,
+ }];
+ if self.consume_token(&Token::Comma) {
+
args.extend(self.parse_comma_separated(Parser::parse_function_args)?);
+ }
+ self.expect_token(&Token::RParen)?;
+ Ok(FunctionArgumentList {
+ duplicate_treatment: None,
+ args,
+ clauses: vec![],
+ })
+ }
+
+ /// Parse the argument list of `XMLROOT(xml, VERSION {text | NO VALUE} [,
STANDALONE {YES | NO | NO VALUE} ])`.
+ fn parse_xmlroot_argument_list(&mut self) -> Result<FunctionArgumentList,
ParserError> {
+ let xml_expr = self.parse_expr()?;
+ let mut args =
vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(xml_expr))];
+ self.expect_token(&Token::Comma)?;
+ let version_kw = self.parse_identifier()?;
+ let version_val = if self.parse_keywords(&[Keyword::NO,
Keyword::VALUE]) {
+ Expr::Identifier(Ident::new("NO VALUE"))
+ } else {
+ self.parse_expr()?
+ };
+ args.push(FunctionArg::Named {
+ name: version_kw,
+ arg: FunctionArgExpr::Expr(version_val),
+ operator: FunctionArgOperator::Space,
+ });
+ if self.consume_token(&Token::Comma) {
+ let standalone_kw = self.parse_identifier()?;
+ let standalone_val = if self.parse_keywords(&[Keyword::NO,
Keyword::VALUE]) {
+ Expr::Identifier(Ident::new("NO VALUE"))
+ } else {
+ Expr::Identifier(self.parse_identifier()?)
+ };
+ args.push(FunctionArg::Named {
+ name: standalone_kw,
+ arg: FunctionArgExpr::Expr(standalone_val),
+ operator: FunctionArgOperator::Space,
+ });
+ }
+ self.expect_token(&Token::RParen)?;
+ Ok(FunctionArgumentList {
+ duplicate_treatment: None,
+ args,
+ clauses: vec![],
+ })
+ }
+
+ /// Parse the argument list of `XMLSERIALIZE({ DOCUMENT | CONTENT } value
AS type [ [ NO ] INDENT ])`.
+ fn parse_xmlserialize_argument_list(&mut self) ->
Result<FunctionArgumentList, ParserError> {
+ let mode = self.parse_identifier()?;
+ let value = self.parse_expr()?;
+ self.expect_keyword_is(Keyword::AS)?;
+ let data_type = self.parse_data_type()?;
+ let mut clauses = vec![FunctionArgumentClause::As(data_type)];
+ if self.parse_keywords(&[Keyword::NO, Keyword::INDENT]) {
+ clauses.push(FunctionArgumentClause::NoIndent);
+ } else if self.parse_keyword(Keyword::INDENT) {
+ clauses.push(FunctionArgumentClause::Indent);
+ }
+ self.expect_token(&Token::RParen)?;
+ Ok(FunctionArgumentList {
+ duplicate_treatment: None,
+ args: vec![FunctionArg::Named {
+ name: mode,
+ arg: FunctionArgExpr::Expr(value),
+ operator: FunctionArgOperator::Space,
+ }],
+ clauses,
+ })
+ }
+
+ /// Parse the argument list of `XMLEXISTS(text PASSING [BY {REF|VALUE}]
xml [BY {REF|VALUE}])`.
+ fn parse_xmlexists_argument_list(&mut self) ->
Result<FunctionArgumentList, ParserError> {
+ let xpath_expr = self.parse_expr()?;
+ let passing = self.parse_xml_passing_clause()?;
Review Comment:
You should require `PASSING`, which `parse_xml_passing_clause` treats as
optional. `SELECT XMLEXISTS('/a')` currently parses and renders as `SELECT
XMLEXISTS('/a' )`, while PostgreSQL rejects it.
```suggestion
if !self.peek_keyword(Keyword::PASSING) {
return self.expected_ref("PASSING", self.peek_token_ref());
}
let passing = self.parse_xml_passing_clause()?;
```
##########
src/parser/mod.rs:
##########
Review Comment:
You should consume `BY` only together with `REF` or `VALUE`, as a lone `BY`
is currently dropped, so `XMLEXISTS('/a' PASSING '<a/>' BY)` parses and renders
without it, while PostgreSQL rejects it.
```suggestion
let mechanism = self.parse_optional_xml_passing_mechanism();
let expr = self.parse_expr()?;
let alias = if self.parse_keyword(Keyword::AS) {
Some(self.parse_identifier()?)
} else {
None
};
let trailing_mechanism =
self.parse_optional_xml_passing_mechanism();
arguments.push(XmlPassingArgument {
expr,
alias,
mechanism,
trailing_mechanism,
});
if !self.consume_token(&Token::Comma) {
break;
}
}
}
Ok(XmlPassingClause { arguments })
}
fn parse_optional_xml_passing_mechanism(&mut self) ->
Option<XmlPassingMechanism> {
if self.parse_keywords(&[Keyword::BY, Keyword::REF]) {
Some(XmlPassingMechanism::ByRef)
} else if self.parse_keywords(&[Keyword::BY, Keyword::VALUE]) {
Some(XmlPassingMechanism::ByValue)
} else {
None
}
}
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]