From 6ce4328d1f759c4f8909e8c8ba535fd9b16c5971 Mon Sep 17 00:00:00 2001 From: vaggelisd Date: Tue, 16 Jul 2024 18:16:35 +0300 Subject: [PATCH] fix(postgres): Decouple UNIQUE from DEFAULT constraints --- sqlglot/dialects/postgres.py | 3 +++ sqlglot/expressions.py | 2 +- sqlglot/generator.py | 3 ++- sqlglot/parser.py | 6 +++++- tests/dialects/test_postgres.py | 4 ++++ 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/sqlglot/dialects/postgres.py b/sqlglot/dialects/postgres.py index 332b2959a1..c25f9f8906 100644 --- a/sqlglot/dialects/postgres.py +++ b/sqlglot/dialects/postgres.py @@ -447,6 +447,9 @@ def _parse_date_part(self) -> exp.Expression: return self.expression(exp.Extract, this=part, expression=value) + def _parse_unique_key(self) -> t.Optional[exp.Expression]: + return None + class Generator(generator.Generator): SINGLE_STRING_INTERVAL = True RENAME_TABLE_WITH_DB = False diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py index 2d86d2daf5..8906c97c13 100644 --- a/sqlglot/expressions.py +++ b/sqlglot/expressions.py @@ -1869,7 +1869,7 @@ class TitleColumnConstraint(ColumnConstraintKind): class UniqueColumnConstraint(ColumnConstraintKind): - arg_types = {"this": False, "index_type": False, "on_conflict": False} + arg_types = {"this": False, "index_type": False, "on_conflict": False, "nulls": False} class UppercaseColumnConstraint(ColumnConstraintKind): diff --git a/sqlglot/generator.py b/sqlglot/generator.py index 7e9bde7694..cf101bbfab 100644 --- a/sqlglot/generator.py +++ b/sqlglot/generator.py @@ -961,7 +961,8 @@ def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> index_type = f" USING {index_type}" if index_type else "" on_conflict = self.sql(expression, "on_conflict") on_conflict = f" {on_conflict}" if on_conflict else "" - return f"UNIQUE{this}{index_type}{on_conflict}" + nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else "" + return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}" def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: return self.sql(expression, "this") diff --git a/sqlglot/parser.py b/sqlglot/parser.py index 44894d324c..691b92df4e 100644 --- a/sqlglot/parser.py +++ b/sqlglot/parser.py @@ -5179,11 +5179,15 @@ def _parse_unnamed_constraint( return self.CONSTRAINT_PARSERS[constraint](self) + def _parse_unique_key(self) -> t.Optional[exp.Expression]: + return self._parse_id_var(any_token=False) + def _parse_unique(self) -> exp.UniqueColumnConstraint: self._match_text_seq("KEY") return self.expression( exp.UniqueColumnConstraint, - this=self._parse_schema(self._parse_id_var(any_token=False)), + nulls=self._match_text_seq("NULLS", "NOT", "DISTINCT"), + this=self._parse_schema(self._parse_unique_key()), index_type=self._match(TokenType.USING) and self._advance_any() and self._prev.text, on_conflict=self._parse_on_conflict(), ) diff --git a/tests/dialects/test_postgres.py b/tests/dialects/test_postgres.py index 339e84b80c..16191c55e5 100644 --- a/tests/dialects/test_postgres.py +++ b/tests/dialects/test_postgres.py @@ -977,6 +977,10 @@ def test_ddl(self): }, ) + self.validate_identity("CREATE TABLE tbl (col INT UNIQUE NULLS NOT DISTINCT DEFAULT 9.99)") + self.validate_identity("CREATE TABLE tbl (col UUID UNIQUE DEFAULT GEN_RANDOM_UUID())") + self.validate_identity("CREATE TABLE tbl (col UUID, UNIQUE NULLS NOT DISTINCT (col))") + with self.assertRaises(ParseError): transpile("CREATE TABLE products (price DECIMAL CHECK price > 0)", read="postgres") with self.assertRaises(ParseError):