diff --git a/fuzz/fuzz_seeds/postgres_2781_window_frame_untyped_offset.sql b/fuzz/fuzz_seeds/postgres_2781_window_frame_untyped_offset.sql new file mode 100644 index 0000000000..a9e9c7c32c --- /dev/null +++ b/fuzz/fuzz_seeds/postgres_2781_window_frame_untyped_offset.sql @@ -0,0 +1 @@ +SELECT sum(x) OVER (ORDER BY f RANGE BETWEEN 'inf' PRECEDING AND '1 day' FOLLOWING) FROM t; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 11accc3996..68ae456241 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2794,10 +2794,7 @@ impl<'a> Parser<'a> { let rows = if self.parse_keyword(Keyword::UNBOUNDED) { None } else { - Some(Box::new(match &self.peek_token_ref().token { - Token::SingleQuotedString(_) => self.parse_interval()?, - _ => self.parse_expr()?, - })) + Some(Box::new(self.parse_expr()?)) }; if self.parse_keyword(Keyword::PRECEDING) { Ok(WindowFrameBound::Preceding(rows)) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 7627f39185..97bf589da2 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -23323,3 +23323,51 @@ fn display_refresh_materialized_view_propagates_write_errors() { assert_eq!(write!(writer, "{statement}"), Err(fmt::Error), "{rejected}"); } } + +#[test] +fn parse_window_frame_untyped_string_offset() { + let dialects = all_dialects_except(|d| d.require_interval_qualifier()); + dialects.verified_stmt( + "SELECT sum(x) OVER (ORDER BY f RANGE BETWEEN 'inf' PRECEDING AND '1 day' FOLLOWING) FROM t", + ); + dialects.verified_stmt( + "SELECT sum(x) OVER (ORDER BY f ROWS BETWEEN 'hello' PRECEDING AND 10 FOLLOWING) FROM t", + ); + assert_eq!( + dialects + .parse_sql_statements( + "SELECT sum(x) OVER (ORDER BY f ROWS BETWEEN ) PRECEDING AND CURRENT ROW) FROM t" + ) + .unwrap_err(), + ParserError::ParserError("Expected: an expression, found: )".to_string()) + ); + + let select = dialects.verified_only_select( + "SELECT sum(x) OVER (ORDER BY f RANGE BETWEEN INTERVAL '1' DAY PRECEDING AND CURRENT ROW) FROM t", + ); + match expr_from_projection(&select.projection[0]) { + Expr::Function(Function { over, .. }) => match over.as_ref() { + Some(WindowType::WindowSpec(WindowSpec { + window_frame: Some(frame), + .. + })) => assert_eq!( + frame, + &WindowFrame { + units: WindowFrameUnits::Range, + start_bound: WindowFrameBound::Preceding(Some(Box::new(Expr::Interval( + Interval { + value: Box::new(Expr::value(Value::SingleQuotedString("1".to_owned()))), + leading_field: Some(DateTimeField::Day), + leading_precision: None, + last_field: None, + fractional_seconds_precision: None, + }, + )))), + end_bound: Some(WindowFrameBound::CurrentRow), + } + ), + _ => panic!("expected a window specification"), + }, + _ => panic!("expected a window function"), + } +}