From de8858305a1ba77de495091d0cb313b8133c519a Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Tue, 17 Dec 2024 16:15:38 -0800 Subject: [PATCH 1/2] Allow foreign table constraint without columns Postgres makes skipping the `(referenced-columns)` part in `ALTER TABLE ... FOREIGN KEY .. REFERENCES (referenced-columns)` per https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES. Since the generic SQL grammar requires referenced columns (https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#referential-constraint-definition), this change applies only to Postgres. --- src/parser/mod.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 94d63cf80..d0744585c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6830,7 +6830,15 @@ impl<'a> Parser<'a> { let columns = self.parse_parenthesized_column_list(Mandatory, false)?; self.expect_keyword(Keyword::REFERENCES)?; let foreign_table = self.parse_object_name(false)?; - let referred_columns = self.parse_parenthesized_column_list(Mandatory, false)?; + // PostgreSQL allows foreign key columns to be optional + // (https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES) + let parenthesized_column_list_optional = if dialect_of!(self is PostgreSqlDialect) { + Optional + } else { + Mandatory + }; + let referred_columns = self + .parse_parenthesized_column_list(parenthesized_column_list_optional, false)?; let mut on_delete = None; let mut on_update = None; loop { From 100f02c23dd9ea6a138050bb50c52151fb893cb9 Mon Sep 17 00:00:00 2001 From: Ramnivas Laddad Date: Wed, 18 Dec 2024 15:28:33 -0800 Subject: [PATCH 2/2] Apply optionality to all dialects Also, fixes DDL generation when reference columns aren't specified and adds a test. --- src/ast/ddl.rs | 6 ++++-- src/parser/mod.rs | 10 +--------- tests/sqlparser_common.rs | 1 + 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 849b583ed..c87960679 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -885,12 +885,14 @@ impl fmt::Display for TableConstraint { } => { write!( f, - "{}FOREIGN KEY ({}) REFERENCES {}({})", + "{}FOREIGN KEY ({}) REFERENCES {}", display_constraint_name(name), display_comma_separated(columns), foreign_table, - display_comma_separated(referred_columns), )?; + if !referred_columns.is_empty() { + write!(f, "({})", display_comma_separated(referred_columns))?; + } if let Some(action) = on_delete { write!(f, " ON DELETE {action}")?; } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d0744585c..07e76cdfa 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -6830,15 +6830,7 @@ impl<'a> Parser<'a> { let columns = self.parse_parenthesized_column_list(Mandatory, false)?; self.expect_keyword(Keyword::REFERENCES)?; let foreign_table = self.parse_object_name(false)?; - // PostgreSQL allows foreign key columns to be optional - // (https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-PARMS-REFERENCES) - let parenthesized_column_list_optional = if dialect_of!(self is PostgreSqlDialect) { - Optional - } else { - Mandatory - }; - let referred_columns = self - .parse_parenthesized_column_list(parenthesized_column_list_optional, false)?; + let referred_columns = self.parse_parenthesized_column_list(Optional, false)?; let mut on_delete = None; let mut on_update = None; loop { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 1bf9383af..d04066c70 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4167,6 +4167,7 @@ fn parse_alter_table_constraints() { check_one("UNIQUE (id)"); check_one("FOREIGN KEY (foo, bar) REFERENCES AnotherTable(foo, bar)"); check_one("CHECK (end_date > start_date OR end_date IS NULL)"); + check_one("CONSTRAINT fk FOREIGN KEY (lng) REFERENCES othertable4"); fn check_one(constraint_text: &str) { match alter_table_op(verified_stmt(&format!(