Skip to content

Commit 2b4ea76

Browse files
feat(ast): add XML expression nodes and span coverage
1 parent bd7f70e commit 2b4ea76

File tree

2 files changed

+292
-0
lines changed

2 files changed

+292
-0
lines changed

src/ast/mod.rs

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,247 @@ impl fmt::Display for CaseWhen {
792792
}
793793
}
794794

795+
/// Modes accepted by XML parsing/serialization clauses.
796+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
797+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
798+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
799+
pub enum XmlParseMode {
800+
/// `CONTENT`
801+
Content,
802+
/// `DOCUMENT`
803+
Document,
804+
}
805+
806+
impl fmt::Display for XmlParseMode {
807+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808+
match self {
809+
XmlParseMode::Content => write!(f, "CONTENT"),
810+
XmlParseMode::Document => write!(f, "DOCUMENT"),
811+
}
812+
}
813+
}
814+
815+
/// A named XML argument (for XMLFOREST entries).
816+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
817+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
818+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
819+
pub struct XmlNamedExpr {
820+
/// Value expression.
821+
pub expr: Expr,
822+
/// Optional explicit XML name.
823+
pub alias: Option<Ident>,
824+
}
825+
826+
impl fmt::Display for XmlNamedExpr {
827+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
828+
write!(f, "{}", self.expr)?;
829+
if let Some(alias) = &self.alias {
830+
write!(f, " AS {alias}")?;
831+
}
832+
Ok(())
833+
}
834+
}
835+
836+
/// A single XML attribute expression, optionally named.
837+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
838+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
839+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
840+
pub struct XmlAttribute {
841+
/// Attribute value expression.
842+
pub expr: Expr,
843+
/// Optional explicit attribute name.
844+
pub alias: Option<Ident>,
845+
}
846+
847+
impl fmt::Display for XmlAttribute {
848+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
849+
write!(f, "{}", self.expr)?;
850+
if let Some(alias) = &self.alias {
851+
write!(f, " AS {alias}")?;
852+
}
853+
Ok(())
854+
}
855+
}
856+
857+
/// `XMLELEMENT(NAME ..., [XMLATTRIBUTES(...)], [content ...])`.
858+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
859+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
860+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
861+
pub struct XmlElementExpr {
862+
/// Element name.
863+
pub name: Ident,
864+
/// Optional XML attributes.
865+
pub attributes: Option<Vec<XmlAttribute>>,
866+
/// Optional content expressions.
867+
pub content: Vec<Expr>,
868+
}
869+
870+
impl fmt::Display for XmlElementExpr {
871+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872+
write!(f, "XMLELEMENT(NAME {}", self.name)?;
873+
if let Some(attrs) = &self.attributes {
874+
write!(f, ", XMLATTRIBUTES({})", display_comma_separated(attrs))?;
875+
}
876+
if !self.content.is_empty() {
877+
write!(f, ", {}", display_comma_separated(&self.content))?;
878+
}
879+
write!(f, ")")
880+
}
881+
}
882+
883+
/// `XMLPARSE(<mode> <expr>)`.
884+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
885+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
886+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
887+
pub struct XmlParseExpr {
888+
/// Parsing mode.
889+
pub mode: XmlParseMode,
890+
/// Expression to parse as XML.
891+
pub expr: Box<Expr>,
892+
}
893+
894+
impl fmt::Display for XmlParseExpr {
895+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
896+
write!(f, "XMLPARSE({} {})", self.mode, self.expr)
897+
}
898+
}
899+
900+
/// `XMLPI(NAME ..., [content])`.
901+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
902+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
903+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
904+
pub struct XmlPiExpr {
905+
/// Processing instruction target name.
906+
pub name: Ident,
907+
/// Optional processing instruction content.
908+
pub content: Option<Box<Expr>>,
909+
}
910+
911+
impl fmt::Display for XmlPiExpr {
912+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
913+
write!(f, "XMLPI(NAME {}", self.name)?;
914+
if let Some(content) = &self.content {
915+
write!(f, ", {content}")?;
916+
}
917+
write!(f, ")")
918+
}
919+
}
920+
921+
/// Version argument in XMLROOT.
922+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
923+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
924+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
925+
pub enum XmlRootVersion {
926+
/// `VERSION NO VALUE`
927+
NoValue,
928+
/// `VERSION <expr>`
929+
Value(Box<Expr>),
930+
}
931+
932+
impl fmt::Display for XmlRootVersion {
933+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
934+
match self {
935+
XmlRootVersion::NoValue => write!(f, "NO VALUE"),
936+
XmlRootVersion::Value(expr) => write!(f, "{expr}"),
937+
}
938+
}
939+
}
940+
941+
/// Standalone option in XMLROOT.
942+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
943+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
944+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
945+
pub enum XmlStandalone {
946+
/// `YES`
947+
Yes,
948+
/// `NO`
949+
No,
950+
/// `NO VALUE`
951+
NoValue,
952+
}
953+
954+
impl fmt::Display for XmlStandalone {
955+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
956+
match self {
957+
XmlStandalone::Yes => write!(f, "YES"),
958+
XmlStandalone::No => write!(f, "NO"),
959+
XmlStandalone::NoValue => write!(f, "NO VALUE"),
960+
}
961+
}
962+
}
963+
964+
/// `XMLROOT(...)` expression.
965+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
966+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
967+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
968+
pub struct XmlRootExpr {
969+
/// XML expression to rewrite.
970+
pub expr: Box<Expr>,
971+
/// Required version argument.
972+
pub version: XmlRootVersion,
973+
/// Optional standalone option.
974+
pub standalone: Option<XmlStandalone>,
975+
}
976+
977+
impl fmt::Display for XmlRootExpr {
978+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
979+
write!(f, "XMLROOT({}, VERSION {}", self.expr, self.version)?;
980+
if let Some(standalone) = &self.standalone {
981+
write!(f, ", STANDALONE {standalone}")?;
982+
}
983+
write!(f, ")")
984+
}
985+
}
986+
987+
/// Optional indentation behavior in XMLSERIALIZE.
988+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
989+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
990+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
991+
pub enum XmlIndentOption {
992+
/// `INDENT`
993+
Indent,
994+
/// `NO INDENT`
995+
NoIndent,
996+
}
997+
998+
impl fmt::Display for XmlIndentOption {
999+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1000+
match self {
1001+
XmlIndentOption::Indent => write!(f, "INDENT"),
1002+
XmlIndentOption::NoIndent => write!(f, "NO INDENT"),
1003+
}
1004+
}
1005+
}
1006+
1007+
/// `XMLSERIALIZE(<mode> <expr> AS <type> [indent-option])`.
1008+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1009+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1010+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
1011+
pub struct XmlSerializeExpr {
1012+
/// Input XML mode.
1013+
pub mode: XmlParseMode,
1014+
/// Expression to serialize.
1015+
pub expr: Box<Expr>,
1016+
/// Output SQL data type.
1017+
pub data_type: DataType,
1018+
/// Optional indentation behavior.
1019+
pub indent: Option<XmlIndentOption>,
1020+
}
1021+
1022+
impl fmt::Display for XmlSerializeExpr {
1023+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1024+
write!(
1025+
f,
1026+
"XMLSERIALIZE({} {} AS {}",
1027+
self.mode, self.expr, self.data_type
1028+
)?;
1029+
if let Some(indent) = self.indent {
1030+
write!(f, " {indent}")?;
1031+
}
1032+
write!(f, ")")
1033+
}
1034+
}
1035+
7951036
/// An SQL expression of any type.
7961037
///
7971038
/// # Semantics / Type Checking
@@ -1181,6 +1422,20 @@ pub enum Expr {
11811422
/// This can represent ANSI SQL `DATE`, `TIME`, and `TIMESTAMP` literals (such as `DATE '2020-01-01'`),
11821423
/// as well as constants of other types (a non-standard PostgreSQL extension).
11831424
TypedString(TypedString),
1425+
/// XML concatenation expression: `XMLCONCAT(expr [, ...])`.
1426+
XmlConcat(Vec<Expr>),
1427+
/// XML element constructor: `XMLELEMENT(NAME ... [, XMLATTRIBUTES(...)] [, content ...])`.
1428+
XmlElement(XmlElementExpr),
1429+
/// XML forest constructor: `XMLFOREST(expr [AS name] [, ...])`.
1430+
XmlForest(Vec<XmlNamedExpr>),
1431+
/// XML parse expression: `XMLPARSE(CONTENT|DOCUMENT expr)`.
1432+
XmlParse(XmlParseExpr),
1433+
/// XML processing instruction constructor: `XMLPI(NAME target [, content])`.
1434+
XmlPi(XmlPiExpr),
1435+
/// XML root mutator: `XMLROOT(expr, VERSION ... [, STANDALONE ...])`.
1436+
XmlRoot(XmlRootExpr),
1437+
/// XML serialization expression: `XMLSERIALIZE(CONTENT|DOCUMENT expr AS type [INDENT|NO INDENT])`.
1438+
XmlSerialize(XmlSerializeExpr),
11841439
/// Scalar function call e.g. `LEFT(foo, 5)`
11851440
Function(Function),
11861441
/// `CASE [<operand>] WHEN <condition> THEN <result> ... [ELSE <result>] END`
@@ -1970,6 +2225,15 @@ impl fmt::Display for Expr {
19702225
Expr::Value(v) => write!(f, "{v}"),
19712226
Expr::Prefixed { prefix, value } => write!(f, "{prefix} {value}"),
19722227
Expr::TypedString(ts) => ts.fmt(f),
2228+
Expr::XmlConcat(exprs) => write!(f, "XMLCONCAT({})", display_comma_separated(exprs)),
2229+
Expr::XmlElement(xml_element) => write!(f, "{xml_element}"),
2230+
Expr::XmlForest(items) => {
2231+
write!(f, "XMLFOREST({})", display_comma_separated(items))
2232+
}
2233+
Expr::XmlParse(xml_parse) => write!(f, "{xml_parse}"),
2234+
Expr::XmlPi(xml_pi) => write!(f, "{xml_pi}"),
2235+
Expr::XmlRoot(xml_root) => write!(f, "{xml_root}"),
2236+
Expr::XmlSerialize(xml_serialize) => write!(f, "{xml_serialize}"),
19732237
Expr::Function(fun) => fun.fmt(f),
19742238
Expr::Case {
19752239
case_token: _,

src/ast/spans.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,34 @@ impl Spanned for Expr {
15171517
Expr::Nested(expr) => expr.span(),
15181518
Expr::Value(value) => value.span(),
15191519
Expr::TypedString(TypedString { value, .. }) => value.span(),
1520+
Expr::XmlConcat(exprs) => union_spans(exprs.iter().map(|expr| expr.span())),
1521+
Expr::XmlElement(xml_element) => union_spans(
1522+
iter::once(xml_element.name.span)
1523+
.chain(
1524+
xml_element
1525+
.attributes
1526+
.as_ref()
1527+
.into_iter()
1528+
.flatten()
1529+
.flat_map(|attr| {
1530+
iter::once(attr.expr.span())
1531+
.chain(attr.alias.as_ref().map(|ident| ident.span))
1532+
}),
1533+
)
1534+
.chain(xml_element.content.iter().map(|expr| expr.span())),
1535+
),
1536+
Expr::XmlForest(items) => union_spans(items.iter().flat_map(|item| {
1537+
iter::once(item.expr.span()).chain(item.alias.as_ref().map(|ident| ident.span))
1538+
})),
1539+
Expr::XmlParse(xml_parse) => xml_parse.expr.span(),
1540+
Expr::XmlPi(xml_pi) => union_spans(
1541+
iter::once(xml_pi.name.span).chain(xml_pi.content.as_ref().map(|expr| expr.span())),
1542+
),
1543+
Expr::XmlRoot(xml_root) => xml_root.expr.span().union_opt(&match &xml_root.version {
1544+
crate::ast::XmlRootVersion::NoValue => None,
1545+
crate::ast::XmlRootVersion::Value(expr) => Some(expr.span()),
1546+
}),
1547+
Expr::XmlSerialize(xml_serialize) => xml_serialize.expr.span(),
15201548
Expr::Function(function) => function.span(),
15211549
Expr::GroupingSets(vec) => {
15221550
union_spans(vec.iter().flat_map(|i| i.iter().map(|k| k.span())))

0 commit comments

Comments
 (0)