Skip to content

Commit d266014

Browse files
committed
feat(ast): add ExclusionConstraint and ExclusionElement types
1 parent 9f04ebe commit d266014

3 files changed

Lines changed: 100 additions & 2 deletions

File tree

src/ast/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,9 @@ mod dml;
138138
pub mod helpers;
139139
pub mod table_constraints;
140140
pub use table_constraints::{
141-
CheckConstraint, ConstraintUsingIndex, ForeignKeyConstraint, FullTextOrSpatialConstraint,
142-
IndexConstraint, PrimaryKeyConstraint, TableConstraint, UniqueConstraint,
141+
CheckConstraint, ConstraintUsingIndex, ExclusionConstraint, ExclusionElement,
142+
ForeignKeyConstraint, FullTextOrSpatialConstraint, IndexConstraint, PrimaryKeyConstraint,
143+
TableConstraint, UniqueConstraint,
143144
};
144145
mod operator;
145146
mod query;

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,7 @@ impl Spanned for TableConstraint {
643643
TableConstraint::FulltextOrSpatial(constraint) => constraint.span(),
644644
TableConstraint::PrimaryKeyUsingIndex(constraint)
645645
| TableConstraint::UniqueUsingIndex(constraint) => constraint.span(),
646+
TableConstraint::Exclusion(constraint) => constraint.span(),
646647
}
647648
}
648649
}

src/ast/table_constraints.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ pub enum TableConstraint {
117117
///
118118
/// [1]: https://www.postgresql.org/docs/current/sql-altertable.html
119119
UniqueUsingIndex(ConstraintUsingIndex),
120+
/// PostgreSQL `EXCLUDE` constraint.
121+
///
122+
/// `[ CONSTRAINT <name> ] EXCLUDE [ USING <index_method> ] ( <element> WITH <operator> [, ...] ) [ INCLUDE (<cols>) ] [ WHERE (<predicate>) ]`
123+
///
124+
/// See <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE>
125+
Exclusion(ExclusionConstraint),
120126
}
121127

122128
impl From<UniqueConstraint> for TableConstraint {
@@ -155,6 +161,12 @@ impl From<FullTextOrSpatialConstraint> for TableConstraint {
155161
}
156162
}
157163

164+
impl From<ExclusionConstraint> for TableConstraint {
165+
fn from(constraint: ExclusionConstraint) -> Self {
166+
TableConstraint::Exclusion(constraint)
167+
}
168+
}
169+
158170
impl fmt::Display for TableConstraint {
159171
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160172
match self {
@@ -166,6 +178,7 @@ impl fmt::Display for TableConstraint {
166178
TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
167179
TableConstraint::PrimaryKeyUsingIndex(c) => c.fmt_with_keyword(f, "PRIMARY KEY"),
168180
TableConstraint::UniqueUsingIndex(c) => c.fmt_with_keyword(f, "UNIQUE"),
181+
TableConstraint::Exclusion(constraint) => constraint.fmt(f),
169182
}
170183
}
171184
}
@@ -603,3 +616,86 @@ impl crate::ast::Spanned for ConstraintUsingIndex {
603616
start.union(&end)
604617
}
605618
}
619+
620+
/// One element in an `EXCLUDE` constraint's element list.
621+
///
622+
/// `<expr> WITH <operator>`
623+
///
624+
/// See <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE>
625+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
626+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
627+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
628+
pub struct ExclusionElement {
629+
/// The index expression or column name.
630+
pub expr: Expr,
631+
/// The exclusion operator (e.g. `&&`, `<->`, `=`).
632+
pub operator: String,
633+
}
634+
635+
impl fmt::Display for ExclusionElement {
636+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
637+
write!(f, "{} WITH {}", self.expr, self.operator)
638+
}
639+
}
640+
641+
/// A PostgreSQL `EXCLUDE` constraint.
642+
///
643+
/// `[ CONSTRAINT <name> ] EXCLUDE [ USING <index_method> ] ( <element> WITH <operator> [, ...] ) [ INCLUDE (<cols>) ] [ WHERE (<predicate>) ]`
644+
///
645+
/// See <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE>
646+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
647+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
648+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
649+
pub struct ExclusionConstraint {
650+
/// Optional constraint name.
651+
pub name: Option<Ident>,
652+
/// Optional index method (e.g. `gist`, `spgist`).
653+
pub index_method: Option<Ident>,
654+
/// The list of index expressions with their exclusion operators.
655+
pub elements: Vec<ExclusionElement>,
656+
/// Optional list of additional columns to include in the index.
657+
pub include: Vec<Ident>,
658+
/// Optional `WHERE` predicate to restrict the constraint to a subset of rows.
659+
pub where_clause: Option<Box<Expr>>,
660+
/// Optional constraint characteristics like `DEFERRABLE`.
661+
pub characteristics: Option<ConstraintCharacteristics>,
662+
}
663+
664+
impl fmt::Display for ExclusionConstraint {
665+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
666+
use crate::ast::ddl::display_constraint_name;
667+
write!(f, "{}EXCLUDE", display_constraint_name(&self.name))?;
668+
if let Some(method) = &self.index_method {
669+
write!(f, " USING {method}")?;
670+
}
671+
write!(f, " ({})", display_comma_separated(&self.elements))?;
672+
if !self.include.is_empty() {
673+
write!(f, " INCLUDE ({})", display_comma_separated(&self.include))?;
674+
}
675+
if let Some(predicate) = &self.where_clause {
676+
write!(f, " WHERE ({predicate})")?;
677+
}
678+
if let Some(characteristics) = &self.characteristics {
679+
write!(f, " {characteristics}")?;
680+
}
681+
Ok(())
682+
}
683+
}
684+
685+
impl crate::ast::Spanned for ExclusionConstraint {
686+
fn span(&self) -> Span {
687+
fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
688+
Span::union_iter(iter)
689+
}
690+
691+
union_spans(
692+
self.name
693+
.iter()
694+
.map(|i| i.span)
695+
.chain(self.index_method.iter().map(|i| i.span))
696+
.chain(self.include.iter().map(|i| i.span))
697+
.chain(self.where_clause.iter().map(|e| e.span()))
698+
.chain(self.characteristics.iter().map(|c| c.span())),
699+
)
700+
}
701+
}

0 commit comments

Comments
 (0)