diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index 9b2ede40d..611ba7310 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1767,6 +1767,18 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if the dialect supports a leading `WITH XMLNAMESPACES (...)` + /// clause in queries. + /// + /// Example: + /// ```sql + /// WITH XMLNAMESPACES ('urn:example' AS ns) + /// SELECT 1 + /// ``` + fn supports_with_xmlnamespaces_clause(&self) -> bool { + false + } + /// Returns true if the dialect supports `USING ` in `CREATE TABLE`. /// /// Example: diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs index 980b63d28..e07a7fc80 100644 --- a/src/dialect/mssql.rs +++ b/src/dialect/mssql.rs @@ -248,6 +248,10 @@ impl Dialect for MsSqlDialect { _ => None, } } + + fn supports_with_xmlnamespaces_clause(&self) -> bool { + true + } } impl MsSqlDialect { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3c6185193..604bf8ed5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -14108,12 +14108,31 @@ impl<'a> Parser<'a> { pub fn parse_query(&mut self) -> Result, ParserError> { let _guard = self.recursion_counter.try_decrease()?; let with = if self.parse_keyword(Keyword::WITH) { - let with_token = self.get_current_token(); - Some(With { - with_token: with_token.clone().into(), - recursive: self.parse_keyword(Keyword::RECURSIVE), - cte_tables: self.parse_comma_separated(Parser::parse_cte)?, - }) + let with_token = self.get_current_token().clone(); + if self.dialect.supports_with_xmlnamespaces_clause() + && self.parse_keyword(Keyword::XMLNAMESPACES) + { + self.expect_token(&Token::LParen)?; + let _namespaces = + self.parse_comma_separated(Parser::parse_xml_namespace_definition)?; + self.expect_token(&Token::RParen)?; + + if self.consume_token(&Token::Comma) { + Some(With { + with_token: with_token.clone().into(), + recursive: self.parse_keyword(Keyword::RECURSIVE), + cte_tables: self.parse_comma_separated(Parser::parse_cte)?, + }) + } else { + None + } + } else { + Some(With { + with_token: with_token.clone().into(), + recursive: self.parse_keyword(Keyword::RECURSIVE), + cte_tables: self.parse_comma_separated(Parser::parse_cte)?, + }) + } } else { None }; diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 6e866746d..2a6eb67b7 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -2923,3 +2923,25 @@ fn parse_mssql_money_constants() { expr_from_projection(only(&select.projection)), ); } + +#[test] +fn parse_xmlnamespaces() { + let sql = r#"WITH XMLNAMESPACES ('urn:test' AS ns) +SELECT 1 AS [ns:Value] +FOR XML PATH('ns:Root');"#; + + ms().parse_sql_statements(sql).unwrap(); + +} + +#[test] +fn parse_xmlnamespaces_with_cte() { + let sql = r#" +WITH XMLNAMESPACES ('urn:example' AS ns), t AS ( + SELECT 1 AS id +) +SELECT id FROM t +"#; + + ms().parse_sql_statements(sql).unwrap(); +} \ No newline at end of file