Skip to content

Commit a0cacb2

Browse files
committed
fix: tighten exclusion constraint parsing per upstream review
1 parent 17cbba9 commit a0cacb2

3 files changed

Lines changed: 59 additions & 1 deletion

File tree

src/ast/table_constraints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ impl fmt::Display for ExclusionElement {
640640

641641
impl crate::ast::Spanned for ExclusionElement {
642642
fn span(&self) -> Span {
643+
// Operator is stored as a plain String with no source span; only expr contributes.
643644
self.expr.span()
644645
}
645646
}

src/parser/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9974,6 +9974,15 @@ impl<'a> Parser<'a> {
99749974
let expr = self.parse_expr()?;
99759975
self.expect_keyword_is(Keyword::WITH)?;
99769976
let operator_token = self.next_token();
9977+
match &operator_token.token {
9978+
Token::EOF
9979+
| Token::RParen
9980+
| Token::Comma
9981+
| Token::SemiColon => {
9982+
return self.expected("exclusion operator", operator_token);
9983+
}
9984+
_ => {}
9985+
}
99779986
let operator = operator_token.token.to_string();
99789987
Ok(ExclusionElement { expr, operator })
99799988
}

tests/sqlparser_postgres.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9170,7 +9170,15 @@ fn parse_exclude_constraint_multi_element() {
91709170
assert_eq!(c.index_method, Some(Ident::new("gist")));
91719171
assert_eq!(c.elements.len(), 2);
91729172
assert_eq!(c.elements[0].operator, "=");
9173+
assert_eq!(
9174+
c.elements[0].expr,
9175+
Expr::Identifier(Ident::new("room"))
9176+
);
91739177
assert_eq!(c.elements[1].operator, "&&");
9178+
assert_eq!(
9179+
c.elements[1].expr,
9180+
Expr::Identifier(Ident::new("during"))
9181+
);
91749182
}
91759183
other => panic!("Expected Exclusion, got {other:?}"),
91769184
}
@@ -9249,6 +9257,23 @@ fn parse_exclude_constraint_with_where() {
92499257
match &create_table.constraints[0] {
92509258
TableConstraint::Exclusion(c) => {
92519259
assert!(c.where_clause.is_some());
9260+
match c.where_clause.as_ref().unwrap().as_ref() {
9261+
Expr::BinaryOp { left, op, right } => {
9262+
assert_eq!(
9263+
**left,
9264+
Expr::Identifier(Ident::new("col"))
9265+
);
9266+
assert_eq!(*op, BinaryOperator::Gt);
9267+
assert_eq!(
9268+
**right,
9269+
Expr::Value(
9270+
(Value::Number("0".to_string(), false))
9271+
.with_empty_span()
9272+
)
9273+
);
9274+
}
9275+
other => panic!("Expected BinaryOp, got {other:?}"),
9276+
}
92529277
}
92539278
other => panic!("Expected Exclusion, got {other:?}"),
92549279
}
@@ -9319,11 +9344,34 @@ fn parse_exclude_constraint_deferrable() {
93199344
fn parse_exclude_constraint_in_alter_table() {
93209345
let sql =
93219346
"ALTER TABLE t ADD CONSTRAINT no_overlap EXCLUDE USING gist (room WITH =)";
9322-
pg().verified_stmt(sql);
9347+
match pg().verified_stmt(sql) {
9348+
Statement::AlterTable { operations, .. } => {
9349+
match &operations[0] {
9350+
AlterTableOperation::AddConstraint(TableConstraint::Exclusion(c)) => {
9351+
assert_eq!(c.name, Some(Ident::new("no_overlap")));
9352+
assert_eq!(c.elements[0].operator, "=");
9353+
}
9354+
other => panic!("Expected AddConstraint(Exclusion), got {other:?}"),
9355+
}
9356+
}
9357+
_ => panic!("Expected AlterTable"),
9358+
}
93239359
}
93249360

93259361
#[test]
93269362
fn roundtrip_exclude_constraint() {
93279363
let sql = "CREATE TABLE t (CONSTRAINT no_overlap EXCLUDE USING gist (room WITH =, during WITH &&) INCLUDE (id) WHERE (active = true))";
93289364
pg().verified_stmt(sql);
93299365
}
9366+
9367+
#[test]
9368+
fn exclude_missing_with_keyword_errors() {
9369+
let sql = "CREATE TABLE t (CONSTRAINT c EXCLUDE USING gist (col))";
9370+
assert!(pg().parse_sql_statements(sql).is_err());
9371+
}
9372+
9373+
#[test]
9374+
fn exclude_empty_element_list_errors() {
9375+
let sql = "CREATE TABLE t (CONSTRAINT c EXCLUDE USING gist ())";
9376+
assert!(pg().parse_sql_statements(sql).is_err());
9377+
}

0 commit comments

Comments
 (0)