From 88094e65dd7f45c38fed69744891fa4c8bdbc818 Mon Sep 17 00:00:00 2001 From: Ben Herzberg Date: Thu, 3 Sep 2026 13:31:05 +0300 Subject: [PATCH 1/3] Adding support for APPROXIMATE PERCENTILE_DISC for Redshift --- src/dialect/redshift.rs | 25 +++++++++++++++++++++++++ tests/sqlparser_redshift.rs | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs index aa403618f..7ef7f71e4 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -15,8 +15,11 @@ // specific language governing permissions and limitations // under the License. +use crate::ast::Expr; use crate::dialect::Dialect; use crate::keywords::Keyword; +use crate::parser::{Parser, ParserError}; +use crate::tokenizer::Token; use core::iter::Peekable; use core::str::Chars; @@ -35,6 +38,28 @@ pub struct RedshiftSqlDialect {} // in the Postgres dialect, the query will be parsed as an array, while in the Redshift dialect it will // be a json path impl Dialect for RedshiftSqlDialect { + fn parse_prefix(&self, parser: &mut Parser) -> Option> { + if matches!(&parser.peek_token_ref().token, Token::Word(word) if word.value.eq_ignore_ascii_case("approximate")) + && matches!(&parser.peek_nth_token_ref(1).token, Token::Word(word) if word.value.eq_ignore_ascii_case("percentile_disc")) + { + parser.next_token(); + let function_name = match parser.parse_object_name(false) { + Ok(name) => name, + Err(error) => return Some(Err(error)), + }; + return Some( + parser + .parse_function(function_name) + .map(|function| Expr::Prefixed { + prefix: "APPROXIMATE".into(), + value: Box::new(function), + }), + ); + } + + None + } + /// Determine if a character starts a potential nested quoted identifier. /// Example: RedShift supports the following quote styles to all mean the same thing: /// ```sql diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index 31cc4fe58..be4bfdbae 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -557,3 +557,17 @@ fn parse_unpivot_expression() { fn test_interval_as_column_name() { redshift().verified_stmt("SELECT * FROM table_name WHERE interval = 78"); } + +#[test] +fn parse_approximate_percentile_disc() { + redshift().one_statement_parses_to( + r#"SELECT TOP 10 date.caldate, +COUNT(totalprice), SUM(totalprice), +APPROXIMATE PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY totalprice) +FROM listing +JOIN date ON listing.dateid = date.dateid +GROUP BY date.caldate +ORDER BY 3 DESC"#, + "SELECT TOP 10 date.caldate, COUNT(totalprice), SUM(totalprice), APPROXIMATE PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY totalprice) FROM listing JOIN date ON listing.dateid = date.dateid GROUP BY date.caldate ORDER BY 3 DESC", + ); +} From 369887924db1df11c8e2eedd38f344a061a907fe Mon Sep 17 00:00:00 2001 From: Ben Herzberg Date: Thu, 3 Sep 2026 14:13:23 +0300 Subject: [PATCH 2/3] Fix no_std Redshift build --- src/dialect/redshift.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs index 7ef7f71e4..3041fb199 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -15,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +#[cfg(not(feature = "std"))] +use alloc::boxed::Box; + use crate::ast::Expr; use crate::dialect::Dialect; use crate::keywords::Keyword; From 47afc93571c08d5c91157725c332d5f6816ce695 Mon Sep 17 00:00:00 2001 From: Ben Herzberg Date: Mon, 7 Sep 2026 17:46:18 +0300 Subject: [PATCH 3/3] Refactored parsing to parser and simplified test --- src/dialect/mod.rs | 5 +++++ src/dialect/redshift.rs | 32 ++++---------------------------- src/parser/mod.rs | 14 ++++++++++++++ tests/sqlparser_redshift.rs | 11 ++--------- 4 files changed, 25 insertions(+), 37 deletions(-) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index ff83a4da6..e91dc1264 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -598,6 +598,11 @@ pub trait Dialect: Debug + Any { None } + /// Does the dialect support the `APPROXIMATE PERCENTILE_DISC` function syntax? + fn supports_approximate_percentile_disc(&self) -> bool { + false + } + /// Does the dialect support trailing commas around the query? fn supports_trailing_commas(&self) -> bool { false diff --git a/src/dialect/redshift.rs b/src/dialect/redshift.rs index 3041fb199..02b3f6973 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -15,14 +15,8 @@ // specific language governing permissions and limitations // under the License. -#[cfg(not(feature = "std"))] -use alloc::boxed::Box; - -use crate::ast::Expr; use crate::dialect::Dialect; use crate::keywords::Keyword; -use crate::parser::{Parser, ParserError}; -use crate::tokenizer::Token; use core::iter::Peekable; use core::str::Chars; @@ -41,28 +35,6 @@ pub struct RedshiftSqlDialect {} // in the Postgres dialect, the query will be parsed as an array, while in the Redshift dialect it will // be a json path impl Dialect for RedshiftSqlDialect { - fn parse_prefix(&self, parser: &mut Parser) -> Option> { - if matches!(&parser.peek_token_ref().token, Token::Word(word) if word.value.eq_ignore_ascii_case("approximate")) - && matches!(&parser.peek_nth_token_ref(1).token, Token::Word(word) if word.value.eq_ignore_ascii_case("percentile_disc")) - { - parser.next_token(); - let function_name = match parser.parse_object_name(false) { - Ok(name) => name, - Err(error) => return Some(Err(error)), - }; - return Some( - parser - .parse_function(function_name) - .map(|function| Expr::Prefixed { - prefix: "APPROXIMATE".into(), - value: Box::new(function), - }), - ); - } - - None - } - /// Determine if a character starts a potential nested quoted identifier. /// Example: RedShift supports the following quote styles to all mean the same thing: /// ```sql @@ -156,6 +128,10 @@ impl Dialect for RedshiftSqlDialect { true } + fn supports_approximate_percentile_disc(&self) -> bool { + true + } + fn supports_geometric_types(&self) -> bool { true } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5edc43714..568460d44 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1836,6 +1836,20 @@ impl<'a> Parser<'a> { let dialect = self.dialect; + if dialect.supports_approximate_percentile_disc() + && matches!(&self.peek_token_ref().token, Token::Word(word) if word.value.eq_ignore_ascii_case("approximate")) + && matches!(&self.peek_nth_token_ref(1).token, Token::Word(word) if word.value.eq_ignore_ascii_case("percentile_disc")) + { + self.next_token(); + let function_name = self.parse_object_name(false)?; + return self + .parse_function(function_name) + .map(|function| Expr::Prefixed { + prefix: "APPROXIMATE".into(), + value: Box::new(function), + }); + } + self.advance_token(); let next_token_index = self.get_current_index(); let next_token = self.get_current_token(); diff --git a/tests/sqlparser_redshift.rs b/tests/sqlparser_redshift.rs index be4bfdbae..be7b0b1b8 100644 --- a/tests/sqlparser_redshift.rs +++ b/tests/sqlparser_redshift.rs @@ -560,14 +560,7 @@ fn test_interval_as_column_name() { #[test] fn parse_approximate_percentile_disc() { - redshift().one_statement_parses_to( - r#"SELECT TOP 10 date.caldate, -COUNT(totalprice), SUM(totalprice), -APPROXIMATE PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY totalprice) -FROM listing -JOIN date ON listing.dateid = date.dateid -GROUP BY date.caldate -ORDER BY 3 DESC"#, - "SELECT TOP 10 date.caldate, COUNT(totalprice), SUM(totalprice), APPROXIMATE PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY totalprice) FROM listing JOIN date ON listing.dateid = date.dateid GROUP BY date.caldate ORDER BY 3 DESC", + redshift().verified_stmt( + "SELECT APPROXIMATE PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY totalprice)", ); }