Skip to content

Commit 4a2bdc2

Browse files
test(parser): add XML dialect-boundary and AST-shape regression coverage
1 parent 3409b43 commit 4a2bdc2

File tree

2 files changed

+430
-0
lines changed

2 files changed

+430
-0
lines changed

tests/sqlparser_common.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6816,6 +6816,205 @@ fn parse_typed_strings() {
68166816
}
68176817
}
68186818

6819+
#[test]
6820+
fn parse_generic_xml_special_expressions() {
6821+
let generic = TestedDialects::new(vec![Box::new(GenericDialect {})]);
6822+
6823+
let select = generic
6824+
.verified_only_select_with_canonical("SELECT xmlconcat(1, 2)", "SELECT XMLCONCAT(1, 2)");
6825+
match &select.projection[0] {
6826+
SelectItem::UnnamedExpr(Expr::XmlConcat(items)) => assert_eq!(items.len(), 2),
6827+
item => panic!("expected XmlConcat expression, got {item:?}"),
6828+
}
6829+
6830+
let select = generic.verified_only_select_with_canonical(
6831+
"SELECT xmlelement(name foo, 'bar')",
6832+
"SELECT XMLELEMENT(NAME foo, 'bar')",
6833+
);
6834+
match &select.projection[0] {
6835+
SelectItem::UnnamedExpr(Expr::XmlElement(XmlElementExpr {
6836+
name,
6837+
attributes,
6838+
content,
6839+
})) => {
6840+
assert_eq!(name.value, "foo");
6841+
assert!(attributes.is_none());
6842+
assert_eq!(content.len(), 1);
6843+
}
6844+
item => panic!("expected XmlElement expression, got {item:?}"),
6845+
}
6846+
6847+
let select = generic.verified_only_select_with_canonical(
6848+
"SELECT xmlforest(1 as one, 2)",
6849+
"SELECT XMLFOREST(1 AS one, 2)",
6850+
);
6851+
match &select.projection[0] {
6852+
SelectItem::UnnamedExpr(Expr::XmlForest(items)) => {
6853+
assert_eq!(items.len(), 2);
6854+
assert_eq!(items[0].alias.as_ref().unwrap().value, "one");
6855+
assert!(items[1].alias.is_none());
6856+
}
6857+
item => panic!("expected XmlForest expression, got {item:?}"),
6858+
}
6859+
6860+
let select = generic.verified_only_select_with_canonical(
6861+
"SELECT xmlparse(content '<a/>')",
6862+
"SELECT XMLPARSE(CONTENT '<a/>')",
6863+
);
6864+
match &select.projection[0] {
6865+
SelectItem::UnnamedExpr(Expr::XmlParse(XmlParseExpr { mode, .. })) => {
6866+
assert_eq!(*mode, XmlParseMode::Content);
6867+
}
6868+
item => panic!("expected XmlParse expression, got {item:?}"),
6869+
}
6870+
6871+
let select = generic.verified_only_select_with_canonical(
6872+
"SELECT xmlpi(name foo, 'bar')",
6873+
"SELECT XMLPI(NAME foo, 'bar')",
6874+
);
6875+
match &select.projection[0] {
6876+
SelectItem::UnnamedExpr(Expr::XmlPi(XmlPiExpr { name, content })) => {
6877+
assert_eq!(name.value, "foo");
6878+
assert!(content.is_some());
6879+
}
6880+
item => panic!("expected XmlPi expression, got {item:?}"),
6881+
}
6882+
6883+
let select = generic.verified_only_select_with_canonical(
6884+
"SELECT xmlserialize(document '<a/>' as text no indent)",
6885+
"SELECT XMLSERIALIZE(DOCUMENT '<a/>' AS TEXT NO INDENT)",
6886+
);
6887+
match &select.projection[0] {
6888+
SelectItem::UnnamedExpr(Expr::XmlSerialize(XmlSerializeExpr { mode, indent, .. })) => {
6889+
assert_eq!(*mode, XmlParseMode::Document);
6890+
assert_eq!(*indent, Some(XmlIndentOption::NoIndent));
6891+
}
6892+
item => panic!("expected XmlSerialize expression, got {item:?}"),
6893+
}
6894+
6895+
let select = generic.verified_only_select_with_canonical(
6896+
"SELECT xmlroot(xml '<foo/>', version no value, standalone yes)",
6897+
"SELECT XMLROOT(xml '<foo/>', VERSION NO VALUE, STANDALONE YES)",
6898+
);
6899+
match &select.projection[0] {
6900+
SelectItem::UnnamedExpr(Expr::XmlRoot(XmlRootExpr {
6901+
version,
6902+
standalone,
6903+
..
6904+
})) => {
6905+
assert!(matches!(version, XmlRootVersion::NoValue));
6906+
assert_eq!(*standalone, Some(XmlStandalone::Yes));
6907+
}
6908+
item => panic!("expected XmlRoot expression, got {item:?}"),
6909+
}
6910+
}
6911+
6912+
#[test]
6913+
fn parse_generic_xml_special_expressions_reject_invalid_forms() {
6914+
let generic = TestedDialects::new(vec![Box::new(GenericDialect {})]);
6915+
6916+
assert!(
6917+
generic.parse_sql_statements("SELECT xmlparse(1)").is_err(),
6918+
"xmlparse requires DOCUMENT|CONTENT mode"
6919+
);
6920+
assert!(
6921+
generic
6922+
.parse_sql_statements("SELECT xmlroot(xml '<foo/>', standalone yes)")
6923+
.is_err(),
6924+
"xmlroot requires VERSION clause"
6925+
);
6926+
assert!(
6927+
generic
6928+
.parse_sql_statements("SELECT xmlserialize(document '<foo/>' text)")
6929+
.is_err(),
6930+
"xmlserialize requires AS <type>"
6931+
);
6932+
}
6933+
6934+
#[test]
6935+
fn parse_non_pg_dialects_keep_xml_names_as_regular_functions() {
6936+
let cases = [
6937+
("SELECT xmlparse(1)", "xmlparse", 1usize),
6938+
("SELECT xmlelement(1, 2)", "xmlelement", 2usize),
6939+
("SELECT xmlroot(1, 2)", "xmlroot", 2usize),
6940+
("SELECT xmlserialize(1, 2)", "xmlserialize", 2usize),
6941+
];
6942+
6943+
let non_pg_dialects =
6944+
all_dialects_except(|d| d.is::<PostgreSqlDialect>() || d.is::<GenericDialect>()).dialects;
6945+
6946+
for dialect in non_pg_dialects {
6947+
let dialect_name = format!("{dialect:?}");
6948+
for (sql, expected_name, expected_arg_count) in cases {
6949+
let statements = Parser::parse_sql(&*dialect, sql)
6950+
.unwrap_or_else(|e| panic!("dialect {dialect_name} failed to parse `{sql}`: {e}"));
6951+
match statements.as_slice() {
6952+
[Statement::Query(query)] => match query.body.as_ref() {
6953+
SetExpr::Select(select) => match select.projection.as_slice() {
6954+
[SelectItem::UnnamedExpr(Expr::Function(function))] => {
6955+
match function.name.0.as_slice() {
6956+
[ObjectNamePart::Identifier(ident)] => {
6957+
assert!(
6958+
ident.value.eq_ignore_ascii_case(expected_name),
6959+
"dialect {dialect_name} parsed `{sql}` as function `{}` instead of `{expected_name}`",
6960+
ident.value
6961+
);
6962+
}
6963+
name_parts => {
6964+
panic!("dialect {dialect_name} expected simple function name, got {name_parts:?}")
6965+
}
6966+
}
6967+
match &function.args {
6968+
FunctionArguments::List(list) => {
6969+
assert_eq!(
6970+
list.args.len(),
6971+
expected_arg_count,
6972+
"dialect {dialect_name} parsed `{sql}` with unexpected argument count"
6973+
);
6974+
}
6975+
args => panic!(
6976+
"dialect {dialect_name} expected positional argument list, got {args:?}"
6977+
),
6978+
}
6979+
}
6980+
projection => panic!(
6981+
"dialect {dialect_name} expected single function projection for `{sql}`, got {projection:?}"
6982+
),
6983+
},
6984+
body => panic!(
6985+
"dialect {dialect_name} expected SELECT query body for `{sql}`, got {body:?}"
6986+
),
6987+
},
6988+
parsed => panic!(
6989+
"dialect {dialect_name} expected a single query statement for `{sql}`, got {parsed:?}"
6990+
),
6991+
}
6992+
}
6993+
}
6994+
}
6995+
6996+
#[test]
6997+
fn parse_non_pg_dialects_reject_xml_special_syntax() {
6998+
let xml_special_forms = [
6999+
"SELECT xmlparse(content '<a/>')",
7000+
"SELECT xmlelement(name foo, 'bar')",
7001+
"SELECT xmlserialize(document '<a/>' as text)",
7002+
];
7003+
7004+
let non_pg_dialects =
7005+
all_dialects_except(|d| d.is::<PostgreSqlDialect>() || d.is::<GenericDialect>()).dialects;
7006+
7007+
for dialect in non_pg_dialects {
7008+
let dialect_name = format!("{dialect:?}");
7009+
for sql in xml_special_forms {
7010+
assert!(
7011+
Parser::parse_sql(&*dialect, sql).is_err(),
7012+
"dialect {dialect_name} unexpectedly accepted XML special syntax: `{sql}`"
7013+
);
7014+
}
7015+
}
7016+
}
7017+
68197018
#[test]
68207019
fn parse_bignumeric_keyword() {
68217020
let sql = r#"SELECT BIGNUMERIC '0'"#;

0 commit comments

Comments
 (0)