Skip to content

Commit c62ca7a

Browse files
Support NO INHERIT modifier on CHECK constraints
1 parent 30d0836 commit c62ca7a

5 files changed

Lines changed: 63 additions & 6 deletions

File tree

src/ast/ddl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1939,7 +1939,7 @@ pub enum ColumnOption {
19391939
/// [<constraint_characteristics>]
19401940
/// `).
19411941
ForeignKey(ForeignKeyConstraint),
1942-
/// `CHECK (<expr>)`
1942+
/// `CHECK (<expr>) [NO INHERIT] [[NOT] ENFORCED]`
19431943
Check(CheckConstraint),
19441944
/// Dialect-specific options, such as:
19451945
/// - MySQL's `AUTO_INCREMENT` or SQLite's `AUTOINCREMENT`

src/ast/table_constraints.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub enum TableConstraint {
7878
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
7979
/// }`).
8080
ForeignKey(ForeignKeyConstraint),
81-
/// `[ CONSTRAINT <name> ] CHECK (<expr>) [[NOT] ENFORCED]`
81+
/// `[ CONSTRAINT <name> ] CHECK (<expr>) [NO INHERIT] [[NOT] ENFORCED]`
8282
Check(CheckConstraint),
8383
/// MySQLs [index definition][1] for index creation. Not present on ANSI so, for now, the usage
8484
/// is restricted to MySQL, as no other dialects that support this syntax were found.
@@ -186,12 +186,15 @@ impl fmt::Display for TableConstraint {
186186
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
187187
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188188
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
189-
/// A `CHECK` constraint (`[ CONSTRAINT <name> ] CHECK (<expr>) [[NOT] ENFORCED]`).
189+
/// A `CHECK` constraint (`[ CONSTRAINT <name> ] CHECK (<expr>) [NO INHERIT] [[NOT] ENFORCED]`).
190190
pub struct CheckConstraint {
191191
/// Optional constraint name.
192192
pub name: Option<Ident>,
193193
/// The boolean expression the CHECK constraint enforces.
194194
pub expr: Box<Expr>,
195+
/// PostgreSQL-specific `NO INHERIT` flag: child tables do not inherit the constraint.
196+
/// <https://www.postgresql.org/docs/current/sql-createtable.html>
197+
pub no_inherit: bool,
195198
/// MySQL-specific `ENFORCED` / `NOT ENFORCED` flag.
196199
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
197200
pub enforced: Option<bool>,
@@ -206,11 +209,13 @@ impl fmt::Display for CheckConstraint {
206209
display_constraint_name(&self.name),
207210
self.expr
208211
)?;
212+
if self.no_inherit {
213+
write!(f, " NO INHERIT")?;
214+
}
209215
if let Some(b) = self.enforced {
210-
write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })
211-
} else {
212-
Ok(())
216+
write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })?;
213217
}
218+
Ok(())
214219
}
215220
}
216221

src/parser/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9672,6 +9672,7 @@ impl<'a> Parser<'a> {
96729672
// since `CHECK` requires parentheses, we can parse the inner expression in ParserState::Normal
96739673
let expr: Expr = self.with_state(ParserState::Normal, |p| p.parse_expr())?;
96749674
self.expect_token(&Token::RParen)?;
9675+
let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]);
96759676

96769677
let enforced = if self.parse_keyword(Keyword::ENFORCED) {
96779678
Some(true)
@@ -9685,6 +9686,7 @@ impl<'a> Parser<'a> {
96859686
CheckConstraint {
96869687
name: None, // Column-level check constraints don't have names
96879688
expr: Box::new(expr),
9689+
no_inherit,
96889690
enforced,
96899691
}
96909692
.into(),
@@ -10148,6 +10150,7 @@ impl<'a> Parser<'a> {
1014810150
self.expect_token(&Token::LParen)?;
1014910151
let expr = Box::new(self.parse_expr()?);
1015010152
self.expect_token(&Token::RParen)?;
10153+
let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]);
1015110154

1015210155
let enforced = if self.parse_keyword(Keyword::ENFORCED) {
1015310156
Some(true)
@@ -10161,6 +10164,7 @@ impl<'a> Parser<'a> {
1016110164
CheckConstraint {
1016210165
name,
1016310166
expr,
10167+
no_inherit,
1016410168
enforced,
1016510169
}
1016610170
.into(),

tests/sqlparser_common.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3983,6 +3983,7 @@ fn parse_create_table() {
39833983
option: ColumnOption::Check(CheckConstraint {
39843984
name: None,
39853985
expr: Box::new(verified_expr("constrained > 0")),
3986+
no_inherit: false,
39863987
enforced: None,
39873988
}),
39883989
},
@@ -17579,6 +17580,19 @@ fn column_check_enforced() {
1757917580
);
1758017581
}
1758117582

17583+
#[test]
17584+
fn table_check_no_inherit() {
17585+
all_dialects().verified_stmt("CREATE TABLE t (a INT, CONSTRAINT c CHECK (a > 0) NO INHERIT)");
17586+
all_dialects().verified_stmt("CREATE TABLE t (a INT, CHECK (a > 0) NO INHERIT)");
17587+
all_dialects().verified_stmt("CREATE TABLE t (a INT, CHECK (a > 0) NO INHERIT NOT ENFORCED)");
17588+
}
17589+
17590+
#[test]
17591+
fn column_check_no_inherit() {
17592+
all_dialects().verified_stmt("CREATE TABLE t (x INT CHECK (x > 1) NO INHERIT)");
17593+
all_dialects().verified_stmt("CREATE TABLE t (x INT CHECK (x > 1) NO INHERIT NOT ENFORCED)");
17594+
}
17595+
1758217596
#[test]
1758317597
fn join_precedence() {
1758417598
all_dialects().verified_query_with_canonical(

tests/sqlparser_postgres.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6531,6 +6531,7 @@ fn parse_create_domain() {
65316531
op: BinaryOperator::Gt,
65326532
right: Box::new(Expr::Value(test_utils::number("0").into())),
65336533
}),
6534+
no_inherit: false,
65346535
enforced: None,
65356536
}
65366537
.into()],
@@ -6551,6 +6552,7 @@ fn parse_create_domain() {
65516552
op: BinaryOperator::Gt,
65526553
right: Box::new(Expr::Value(test_utils::number("0").into())),
65536554
}),
6555+
no_inherit: false,
65546556
enforced: None,
65556557
}
65566558
.into()],
@@ -6571,6 +6573,7 @@ fn parse_create_domain() {
65716573
op: BinaryOperator::Gt,
65726574
right: Box::new(Expr::Value(test_utils::number("0").into())),
65736575
}),
6576+
no_inherit: false,
65746577
enforced: None,
65756578
}
65766579
.into()],
@@ -6591,6 +6594,7 @@ fn parse_create_domain() {
65916594
op: BinaryOperator::Gt,
65926595
right: Box::new(Expr::Value(test_utils::number("0").into())),
65936596
}),
6597+
no_inherit: false,
65946598
enforced: None,
65956599
}
65966600
.into()],
@@ -6611,6 +6615,7 @@ fn parse_create_domain() {
66116615
op: BinaryOperator::Gt,
66126616
right: Box::new(Expr::Value(test_utils::number("0").into())),
66136617
}),
6618+
no_inherit: false,
66146619
enforced: None,
66156620
}
66166621
.into()],
@@ -9663,3 +9668,32 @@ fn parse_right_deep_join_chain() {
96639668
// NATURAL JOIN followed by a constrained join must stay left-associative.
96649669
pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true");
96659670
}
9671+
9672+
#[test]
9673+
fn parse_alter_table_constraint_check_no_inherit() {
9674+
match pg_and_generic()
9675+
.verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT NOT VALID")
9676+
{
9677+
Statement::AlterTable(AlterTable { operations, .. }) => {
9678+
assert_eq!(
9679+
operations,
9680+
vec![AlterTableOperation::AddConstraint {
9681+
constraint: CheckConstraint {
9682+
name: Some("c".into()),
9683+
expr: Box::new(Expr::BinaryOp {
9684+
left: Box::new(Expr::Identifier(Ident::new("id"))),
9685+
op: BinaryOperator::Gt,
9686+
right: Box::new(Expr::Value(test_utils::number("0").into())),
9687+
}),
9688+
no_inherit: true,
9689+
enforced: None,
9690+
}
9691+
.into(),
9692+
not_valid: true,
9693+
}]
9694+
);
9695+
}
9696+
_ => unreachable!(),
9697+
}
9698+
pg_and_generic().verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT");
9699+
}

0 commit comments

Comments
 (0)