From 9651ee598c88881bcdcaa222a751fbb2ba8ddce8 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Wed, 23 Sep 2026 07:06:03 +0200 Subject: [PATCH 1/2] PostgreSQL: Display the index operator class before the sort options --- src/ast/ddl.rs | 4 ++-- src/ast/query.rs | 9 ++++++++- tests/sqlparser_common.rs | 24 ++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 5d5b964d17..3ff6de1629 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -85,11 +85,11 @@ impl<'a> From<&'a str> for IndexColumn { impl fmt::Display for IndexColumn { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.column)?; + write!(f, "{}", self.column.expr)?; if let Some(operator_class) = &self.operator_class { write!(f, " {operator_class}")?; } - Ok(()) + self.column.fmt_sort_modifiers(f) } } diff --git a/src/ast/query.rs b/src/ast/query.rs index 4aa82c7c3b..40bf671b68 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -2981,8 +2981,15 @@ impl From for OrderByExpr { impl fmt::Display for OrderByExpr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.expr)?; + self.fmt_sort_modifiers(f) + } +} + +impl OrderByExpr { + /// Writes the modifiers that follow the sort expression (`ASC`/`DESC`, `NULLS`, `WITH FILL`). + pub(crate) fn fmt_sort_modifiers(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.options)?; - if let Some(ref with_fill) = self.with_fill { + if let Some(with_fill) = &self.with_fill { write!(f, " {with_fill}")? } Ok(()) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 87fb61dc17..5381e36902 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -21022,3 +21022,27 @@ fn parse_window_spec_keyword_base_window_name() { "SELECT count(*) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM t", ); } + +#[test] +fn parse_create_index_operator_class_before_sort_options() { + let dialects = all_dialects(); + dialects.verified_stmt("CREATE INDEX ii ON t(c)"); + dialects.verified_stmt("CREATE INDEX ii ON t(c ops)"); + dialects.verified_stmt("CREATE INDEX ii ON t(c DESC)"); + dialects.verified_stmt("CREATE INDEX ii ON t(c ops DESC)"); + dialects.verified_stmt("CREATE INDEX ii ON t(c ops DESC NULLS FIRST)"); + + let statements = dialects + .parse_sql_statements("CREATE INDEX ii ON t(c ops DESC)") + .unwrap(); + let Statement::CreateIndex(create_index) = &statements[0] else { + panic!("expected CREATE INDEX") + }; + let column = &create_index.columns[0]; + assert_eq!(column.column.expr, Expr::Identifier(Ident::new("c"))); + assert_eq!( + column.operator_class, + Some(ObjectName::from(vec![Ident::new("ops")])) + ); + assert_eq!(column.column.options.sort, Some(OrderBySort::Desc)); +} From d2539348c7d4998f80246583fa6635eebe532fb2 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Tue, 20 Oct 2026 12:51:15 +0200 Subject: [PATCH 2/2] Cover index-column formatter error propagation --- tests/sqlparser_common.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5381e36902..76839bee73 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -21046,3 +21046,30 @@ fn parse_create_index_operator_class_before_sort_options() { ); assert_eq!(column.column.options.sort, Some(OrderBySort::Desc)); } + +#[test] +fn display_index_column_propagates_expression_errors() { + use core::fmt::{self, Write}; + + struct RejectingWriter(char); + + impl Write for RejectingWriter { + fn write_str(&mut self, value: &str) -> fmt::Result { + if value.contains(self.0) { + Err(fmt::Error) + } else { + Ok(()) + } + } + } + + let statements = all_dialects() + .parse_sql_statements("CREATE INDEX ii ON t(c ops DESC)") + .unwrap(); + let Statement::CreateIndex(create_index) = &statements[0] else { + panic!("expected CREATE INDEX") + }; + let column = &create_index.columns[0]; + let mut writer = RejectingWriter('c'); + assert_eq!(write!(writer, "{column}"), Err(fmt::Error)); +}