Skip to content

Commit d8e35d9

Browse files
committed
Merge remote-tracking branch 'origin/feat/exclude-constraint-upstream' into feat/exclude-constraint-upstream
2 parents 442e196 + 90803e0 commit d8e35d9

3 files changed

Lines changed: 266 additions & 66 deletions

File tree

src/ast/table_constraints.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
use crate::ast::{
2121
display_comma_separated, display_separated, ConstraintCharacteristics,
2222
ConstraintReferenceMatchKind, Expr, Ident, IndexColumn, IndexOption, IndexType,
23-
KeyOrIndexDisplay, NullsDistinctOption, ObjectName, ReferentialAction,
23+
KeyOrIndexDisplay, NullsDistinctOption, ObjectName, OrderByOptions, ReferentialAction,
2424
};
2525
use crate::tokenizer::Span;
2626
use core::fmt;
2727

2828
#[cfg(not(feature = "std"))]
29-
use alloc::{boxed::Box, vec::Vec};
29+
use alloc::{boxed::Box, string::String, vec::Vec};
3030

3131
#[cfg(feature = "serde")]
3232
use serde::{Deserialize, Serialize};
@@ -619,7 +619,7 @@ impl crate::ast::Spanned for ConstraintUsingIndex {
619619

620620
/// One element in an `EXCLUDE` constraint's element list.
621621
///
622-
/// `<expr> WITH <operator>`
622+
/// `{ column_name | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] WITH <operator>`
623623
///
624624
/// See <https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE>
625625
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
@@ -628,20 +628,32 @@ impl crate::ast::Spanned for ConstraintUsingIndex {
628628
pub struct ExclusionElement {
629629
/// The index expression or column name.
630630
pub expr: Expr,
631-
/// The exclusion operator (e.g. `&&`, `<->`, `=`).
631+
/// Optional operator class (e.g. `gist_geometry_ops_nd`).
632+
pub operator_class: Option<ObjectName>,
633+
/// Ordering options (ASC/DESC, NULLS FIRST/LAST).
634+
pub order: OrderByOptions,
635+
/// The exclusion operator. Either a simple token (`&&`, `=`, `<->`) or the
636+
/// Postgres schema-qualified form `OPERATOR(schema.op)`.
632637
pub operator: String,
633638
}
634639

635640
impl fmt::Display for ExclusionElement {
636641
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
637-
write!(f, "{} WITH {}", self.expr, self.operator)
642+
write!(f, "{}", self.expr)?;
643+
if let Some(opclass) = &self.operator_class {
644+
write!(f, " {opclass}")?;
645+
}
646+
write!(f, "{} WITH {}", self.order, self.operator)
638647
}
639648
}
640649

641650
impl crate::ast::Spanned for ExclusionElement {
642651
fn span(&self) -> Span {
643-
// Operator is stored as a plain String with no source span; only expr contributes.
644-
self.expr.span()
652+
let mut span = self.expr.span();
653+
if let Some(opclass) = &self.operator_class {
654+
span = span.union(&opclass.span());
655+
}
656+
span
645657
}
646658
}
647659

@@ -691,11 +703,7 @@ impl fmt::Display for ExclusionConstraint {
691703

692704
impl crate::ast::Spanned for ExclusionConstraint {
693705
fn span(&self) -> Span {
694-
fn union_spans<I: Iterator<Item = Span>>(iter: I) -> Span {
695-
Span::union_iter(iter)
696-
}
697-
698-
union_spans(
706+
Span::union_iter(
699707
self.name
700708
.iter()
701709
.map(|i| i.span)

src/parser/mod.rs

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9905,16 +9905,18 @@ impl<'a> Parser<'a> {
99059905
.into(),
99069906
))
99079907
}
9908-
Token::Word(w) if w.keyword == Keyword::EXCLUDE => {
9908+
Token::Word(w)
9909+
if w.keyword == Keyword::EXCLUDE
9910+
&& dialect_of!(self is PostgreSqlDialect | GenericDialect) =>
9911+
{
99099912
let index_method = if self.parse_keyword(Keyword::USING) {
99109913
Some(self.parse_identifier()?)
99119914
} else {
99129915
None
99139916
};
99149917

99159918
self.expect_token(&Token::LParen)?;
9916-
let elements =
9917-
self.parse_comma_separated(|p| p.parse_exclusion_element())?;
9919+
let elements = self.parse_comma_separated(|p| p.parse_exclusion_element())?;
99189920
self.expect_token(&Token::RParen)?;
99199921

99209922
let include = if self.parse_keyword(Keyword::INCLUDE) {
@@ -9961,20 +9963,54 @@ impl<'a> Parser<'a> {
99619963
}
99629964

99639965
fn parse_exclusion_element(&mut self) -> Result<ExclusionElement, ParserError> {
9964-
let expr = self.parse_expr()?;
9966+
// `index_elem` grammar: { col | (expr) } [ opclass ] [ ASC | DESC ] [ NULLS FIRST | LAST ].
9967+
// Shared with `CREATE INDEX` columns.
9968+
let (
9969+
OrderByExpr {
9970+
expr,
9971+
options: order,
9972+
..
9973+
},
9974+
operator_class,
9975+
) = self.parse_order_by_expr_inner(true)?;
9976+
99659977
self.expect_keyword_is(Keyword::WITH)?;
9978+
let operator = self.parse_exclusion_operator()?;
9979+
9980+
Ok(ExclusionElement {
9981+
expr,
9982+
operator_class,
9983+
order,
9984+
operator,
9985+
})
9986+
}
9987+
9988+
/// Parse the operator that follows `WITH` in an `EXCLUDE` element.
9989+
///
9990+
/// Accepts either a single operator token (e.g. `=`, `&&`, `<->`) or the
9991+
/// Postgres `OPERATOR(schema.op)` form for schema-qualified operators.
9992+
fn parse_exclusion_operator(&mut self) -> Result<String, ParserError> {
9993+
if self.parse_keyword(Keyword::OPERATOR) {
9994+
self.expect_token(&Token::LParen)?;
9995+
let mut parts = vec![];
9996+
loop {
9997+
self.advance_token();
9998+
parts.push(self.get_current_token().to_string());
9999+
if !self.consume_token(&Token::Period) {
10000+
break;
10001+
}
10002+
}
10003+
self.expect_token(&Token::RParen)?;
10004+
return Ok(format!("OPERATOR({})", parts.join(".")));
10005+
}
10006+
996610007
let operator_token = self.next_token();
996710008
match &operator_token.token {
9968-
Token::EOF
9969-
| Token::RParen
9970-
| Token::Comma
9971-
| Token::SemiColon => {
9972-
return self.expected("exclusion operator", operator_token);
10009+
Token::EOF | Token::RParen | Token::Comma | Token::SemiColon => {
10010+
self.expected("exclusion operator", operator_token)
997310011
}
9974-
_ => {}
10012+
_ => Ok(operator_token.token.to_string()),
997510013
}
9976-
let operator = operator_token.token.to_string();
9977-
Ok(ExclusionElement { expr, operator })
997810014
}
997910015

998010016
fn parse_optional_nulls_distinct(&mut self) -> Result<NullsDistinctOption, ParserError> {

0 commit comments

Comments
 (0)