From 8d4569ad32a0f6bdacc866f6e3acb5d6d3a1e17e Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Tue, 27 Oct 2026 10:55:05 +0100 Subject: [PATCH 1/2] PostgreSQL: Parse NULLS [NOT] DISTINCT on column UNIQUE constraints --- src/ast/ddl.rs | 8 ++++++-- src/parser/mod.rs | 3 ++- tests/sqlparser_postgres.rs | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index c1780e2a43..61a4d4002a 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1978,7 +1978,7 @@ pub enum ColumnOption { /// `PRIMARY KEY []` PrimaryKey(PrimaryKeyConstraint), - /// `UNIQUE []` + /// `UNIQUE [NULLS [NOT] DISTINCT] []` Unique(UniqueConstraint), /// A referential integrity constraint (`REFERENCES () /// [ MATCH { FULL | PARTIAL | SIMPLE } ] @@ -2113,7 +2113,11 @@ impl fmt::Display for ColumnOption { Ok(()) } Unique(constraint) => { - write!(f, "UNIQUE{:>}", constraint.index_type_display)?; + write!( + f, + "UNIQUE{:>}{}", + constraint.index_type_display, constraint.nulls_distinct + )?; if let Some(characteristics) = &constraint.characteristics { write!(f, " {characteristics}")?; } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index c5a6f4e854..53f54a048b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -10129,6 +10129,7 @@ impl<'a> Parser<'a> { } else { KeyOrIndexDisplay::None }; + let nulls_distinct = self.parse_optional_nulls_distinct()?; let characteristics = self.parse_constraint_characteristics()?; Ok(Some( UniqueConstraint { @@ -10140,7 +10141,7 @@ impl<'a> Parser<'a> { include: vec![], index_options: vec![], characteristics, - nulls_distinct: NullsDistinctOption::None, + nulls_distinct, } .into(), )) diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 11a4703f55..df88c26b7b 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -11057,3 +11057,41 @@ fn parse_role_signed_connection_limit() { "sql parser error: Expected: option, found: CONNECTION" ); } + +#[test] +fn parse_create_table_column_unique_nulls_distinct() { + let Statement::CreateTable(CreateTable { columns, .. }) = pg_and_generic().verified_stmt( + "CREATE TABLE t (u INT UNIQUE NULLS NOT DISTINCT, v INT UNIQUE NULLS DISTINCT, w INT UNIQUE)", + ) else { + panic!("Expected CREATE TABLE"); + }; + let nulls_distinct: Vec<_> = columns + .iter() + .map(|column| match &column.options[..] { + [ColumnOptionDef { + option: ColumnOption::Unique(constraint), + .. + }] => &constraint.nulls_distinct, + options => panic!("Expected one UNIQUE option, got {options:?}"), + }) + .collect(); + assert_eq!( + nulls_distinct, + vec![ + &NullsDistinctOption::NotDistinct, + &NullsDistinctOption::Distinct, + &NullsDistinctOption::None, + ] + ); + + pg_and_generic().verified_stmt( + "CREATE TABLE t (u INT CONSTRAINT k UNIQUE NULLS NOT DISTINCT DEFERRABLE NOT NULL)", + ); + + assert_eq!( + pg_and_generic() + .parse_sql_statements("CREATE TABLE t (u INT UNIQUE NULLS NOT)") + .unwrap_err(), + ParserError::ParserError("Expected: DISTINCT, found: )".to_string()) + ); +} From 9e652602a969f5e1794482842372414f2d800b59 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Wed, 28 Oct 2026 13:35:47 +0100 Subject: [PATCH 2/2] Test formatter error propagation for column UNIQUE NULLS DISTINCT --- tests/sqlparser_postgres.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index df88c26b7b..a21435b00f 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -11095,3 +11095,23 @@ fn parse_create_table_column_unique_nulls_distinct() { ParserError::ParserError("Expected: DISTINCT, found: )".to_string()) ); } + +#[test] +fn display_column_unique_nulls_distinct_propagates_formatter_errors() { + use core::fmt::{self, Write}; + + struct RejectingWriter; + + impl Write for RejectingWriter { + fn write_str(&mut self, value: &str) -> fmt::Result { + if value.contains("NULLS") { + Err(fmt::Error) + } else { + Ok(()) + } + } + } + + let stmt = pg_and_generic().verified_stmt("CREATE TABLE t (u INT UNIQUE NULLS NOT DISTINCT)"); + assert_eq!(write!(RejectingWriter, "{stmt}"), Err(fmt::Error)); +}