diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index a0e69ad8a..c0b56ef88 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1939,7 +1939,7 @@ pub enum ColumnOption { /// [] /// `). ForeignKey(ForeignKeyConstraint), - /// `CHECK ()` + /// `CHECK () [NO INHERIT] [[NOT] ENFORCED]` Check(CheckConstraint), /// Dialect-specific options, such as: /// - MySQL's `AUTO_INCREMENT` or SQLite's `AUTOINCREMENT` diff --git a/src/ast/table_constraints.rs b/src/ast/table_constraints.rs index 0db71bb69..799843f3a 100644 --- a/src/ast/table_constraints.rs +++ b/src/ast/table_constraints.rs @@ -78,7 +78,7 @@ pub enum TableConstraint { /// [ON UPDATE ] [ON DELETE ] /// }`). ForeignKey(ForeignKeyConstraint), - /// `[ CONSTRAINT ] CHECK () [[NOT] ENFORCED]` + /// `[ CONSTRAINT ] CHECK () [NO INHERIT] [[NOT] ENFORCED]` Check(CheckConstraint), /// MySQLs [index definition][1] for index creation. Not present on ANSI so, for now, the usage /// is restricted to MySQL, as no other dialects that support this syntax were found. @@ -186,12 +186,15 @@ impl fmt::Display for TableConstraint { #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -/// A `CHECK` constraint (`[ CONSTRAINT ] CHECK () [[NOT] ENFORCED]`). +/// A `CHECK` constraint (`[ CONSTRAINT ] CHECK () [NO INHERIT] [[NOT] ENFORCED]`). pub struct CheckConstraint { /// Optional constraint name. pub name: Option, /// The boolean expression the CHECK constraint enforces. pub expr: Box, + /// PostgreSQL-specific `NO INHERIT` flag: child tables do not inherit the constraint. + /// + pub no_inherit: bool, /// MySQL-specific `ENFORCED` / `NOT ENFORCED` flag. /// pub enforced: Option, @@ -206,11 +209,13 @@ impl fmt::Display for CheckConstraint { display_constraint_name(&self.name), self.expr )?; + if self.no_inherit { + write!(f, " NO INHERIT")?; + } if let Some(b) = self.enforced { - write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" }) - } else { - Ok(()) + write!(f, " {}", if b { "ENFORCED" } else { "NOT ENFORCED" })?; } + Ok(()) } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b2b3f42bb..6fbf04165 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9672,6 +9672,7 @@ impl<'a> Parser<'a> { // since `CHECK` requires parentheses, we can parse the inner expression in ParserState::Normal let expr: Expr = self.with_state(ParserState::Normal, |p| p.parse_expr())?; self.expect_token(&Token::RParen)?; + let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]); let enforced = if self.parse_keyword(Keyword::ENFORCED) { Some(true) @@ -9685,6 +9686,7 @@ impl<'a> Parser<'a> { CheckConstraint { name: None, // Column-level check constraints don't have names expr: Box::new(expr), + no_inherit, enforced, } .into(), @@ -10148,6 +10150,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let expr = Box::new(self.parse_expr()?); self.expect_token(&Token::RParen)?; + let no_inherit = self.parse_keywords(&[Keyword::NO, Keyword::INHERIT]); let enforced = if self.parse_keyword(Keyword::ENFORCED) { Some(true) @@ -10161,6 +10164,7 @@ impl<'a> Parser<'a> { CheckConstraint { name, expr, + no_inherit, enforced, } .into(), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 0800bc41f..6338fec32 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -3983,6 +3983,7 @@ fn parse_create_table() { option: ColumnOption::Check(CheckConstraint { name: None, expr: Box::new(verified_expr("constrained > 0")), + no_inherit: false, enforced: None, }), }, @@ -17579,6 +17580,19 @@ fn column_check_enforced() { ); } +#[test] +fn table_check_no_inherit() { + all_dialects().verified_stmt("CREATE TABLE t (a INT, CONSTRAINT c CHECK (a > 0) NO INHERIT)"); + all_dialects().verified_stmt("CREATE TABLE t (a INT, CHECK (a > 0) NO INHERIT)"); + all_dialects().verified_stmt("CREATE TABLE t (a INT, CHECK (a > 0) NO INHERIT NOT ENFORCED)"); +} + +#[test] +fn column_check_no_inherit() { + all_dialects().verified_stmt("CREATE TABLE t (x INT CHECK (x > 1) NO INHERIT)"); + all_dialects().verified_stmt("CREATE TABLE t (x INT CHECK (x > 1) NO INHERIT NOT ENFORCED)"); +} + #[test] fn join_precedence() { all_dialects().verified_query_with_canonical( diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index a7128eafd..b51fba883 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -6531,6 +6531,7 @@ fn parse_create_domain() { op: BinaryOperator::Gt, right: Box::new(Expr::Value(test_utils::number("0").into())), }), + no_inherit: false, enforced: None, } .into()], @@ -6551,6 +6552,7 @@ fn parse_create_domain() { op: BinaryOperator::Gt, right: Box::new(Expr::Value(test_utils::number("0").into())), }), + no_inherit: false, enforced: None, } .into()], @@ -6571,6 +6573,7 @@ fn parse_create_domain() { op: BinaryOperator::Gt, right: Box::new(Expr::Value(test_utils::number("0").into())), }), + no_inherit: false, enforced: None, } .into()], @@ -6591,6 +6594,7 @@ fn parse_create_domain() { op: BinaryOperator::Gt, right: Box::new(Expr::Value(test_utils::number("0").into())), }), + no_inherit: false, enforced: None, } .into()], @@ -6611,6 +6615,7 @@ fn parse_create_domain() { op: BinaryOperator::Gt, right: Box::new(Expr::Value(test_utils::number("0").into())), }), + no_inherit: false, enforced: None, } .into()], @@ -9663,3 +9668,32 @@ fn parse_right_deep_join_chain() { // NATURAL JOIN followed by a constrained join must stay left-associative. pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true"); } + +#[test] +fn parse_alter_table_constraint_check_no_inherit() { + match pg_and_generic() + .verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT NOT VALID") + { + Statement::AlterTable(AlterTable { operations, .. }) => { + assert_eq!( + operations, + vec![AlterTableOperation::AddConstraint { + constraint: CheckConstraint { + name: Some("c".into()), + expr: Box::new(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("id"))), + op: BinaryOperator::Gt, + right: Box::new(Expr::Value(test_utils::number("0").into())), + }), + no_inherit: true, + enforced: None, + } + .into(), + not_valid: true, + }] + ); + } + _ => unreachable!(), + } + pg_and_generic().verified_stmt("ALTER TABLE docs ADD CONSTRAINT c CHECK (id > 0) NO INHERIT"); +}