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]

Reply via email to