From e93e50ad67cce6f138e47e6090e36e3c893f2ca7 Mon Sep 17 00:00:00 2001 From: TcMits Date: Wed, 6 Aug 2025 18:29:14 +0700 Subject: [PATCH] more tests --- parser/src/error.rs | 77 ++- parser/src/lexer.rs | 60 +-- parser/src/parser.rs | 1097 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 1127 insertions(+), 107 deletions(-) diff --git a/parser/src/error.rs b/parser/src/error.rs index 8d582b6c1..f3556369b 100644 --- a/parser/src/error.rs +++ b/parser/src/error.rs @@ -9,86 +9,79 @@ use crate::token::TokenType; #[diagnostic()] pub enum Error { /// Lexer error - UnrecognizedToken(usize, #[label("here")] Option), + UnrecognizedToken(#[label("here")] miette::SourceSpan), /// Missing quote or double-quote or backtick - UnterminatedLiteral(usize, #[label("here")] Option), + UnterminatedLiteral(#[label("here")] miette::SourceSpan), /// Missing `]` - UnterminatedBracket(usize, #[label("here")] Option), + UnterminatedBracket(#[label("here")] miette::SourceSpan), /// Missing `*/` - UnterminatedBlockComment(usize, #[label("here")] Option), + UnterminatedBlockComment(#[label("here")] miette::SourceSpan), /// Invalid parameter name - BadVariableName(usize, #[label("here")] Option), + BadVariableName(#[label("here")] miette::SourceSpan), /// Invalid number format - #[diagnostic(help("Invalid digit at `{0}`"))] - BadNumber( - usize, - #[label("here")] Option, - String, // Holds the offending number as a string - ), - #[diagnostic(help("Invalid digit at `{0}`"))] - BadFractionalPart( - usize, - #[label("here")] Option, - String, // Holds the offending number as a string - ), - #[diagnostic(help("Invalid digit at `{0}`"))] - BadExponentPart( - usize, - #[label("here")] Option, - String, // Holds the offending number as a string - ), + BadNumber(#[label("here")] miette::SourceSpan), + // Bad fractional part of a number + BadFractionalPart(#[label("here")] miette::SourceSpan), + // Bad exponent part of a number + BadExponentPart(#[label("here")] miette::SourceSpan), /// Invalid or missing sign after `!` - ExpectedEqualsSign(usize, #[label("here")] Option), + ExpectedEqualsSign(#[label("here")] miette::SourceSpan), /// Hexadecimal integer literals follow the C-language notation of "0x" or "0X" followed by hexadecimal digits. - MalformedHexInteger( - usize, - #[label("here")] Option, - #[help] Option<&'static str>, - ), + MalformedHexInteger(#[label("here")] miette::SourceSpan), // parse errors + // Unexpected end of file ParseUnexpectedEOF, + // Unexpected token ParseUnexpectedToken { + #[label("parsed to here")] + parsed_offset: miette::SourceSpan, + got: TokenType, expected: &'static [TokenType], }, + // Custom error message Custom(String), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - Self::UnrecognizedToken(pos, _) => { + Self::UnrecognizedToken(pos) => { write!(f, "unrecognized token at {:?}", pos) } - Self::UnterminatedLiteral(pos, _) => { + Self::UnterminatedLiteral(pos) => { write!(f, "non-terminated literal at {:?}", pos) } - Self::UnterminatedBracket(pos, _) => { + Self::UnterminatedBracket(pos) => { write!(f, "non-terminated bracket at {:?}", pos) } - Self::UnterminatedBlockComment(pos, _) => { + Self::UnterminatedBlockComment(pos) => { write!(f, "non-terminated block comment at {:?}", pos) } - Self::BadVariableName(pos, _) => write!(f, "bad variable name at {:?}", pos), - Self::BadNumber(pos, _, _) => write!(f, "bad number at {:?}", pos), - Self::BadFractionalPart(pos, _, _) => { + Self::BadVariableName(pos) => write!(f, "bad variable name at {:?}", pos), + Self::BadNumber(pos) => write!(f, "bad number at {:?}", pos), + Self::BadFractionalPart(pos) => { write!(f, "bad fractional part at {:?}", pos) } - Self::BadExponentPart(pos, _, _) => { + Self::BadExponentPart(pos) => { write!(f, "bad exponent part at {:?}", pos) } - Self::ExpectedEqualsSign(pos, _) => write!(f, "expected = sign at {:?}", pos), - Self::MalformedHexInteger(pos, _, _) => { + Self::ExpectedEqualsSign(pos) => write!(f, "expected = sign at {:?}", pos), + Self::MalformedHexInteger(pos) => { write!(f, "malformed hex integer at {:?}", pos) } Self::ParseUnexpectedEOF => { write!(f, "unexpected end of file") } - Self::ParseUnexpectedToken { got, expected } => { + Self::ParseUnexpectedToken { + parsed_offset, + got, + expected, + } => { write!( f, - "got unexpected token: expected {:?}, found {}", - expected, got + "got unexpected token after parsing to offset {:?}: expected {:?}, found {}", + parsed_offset, expected, got ) } Self::Custom(ref s) => { diff --git a/parser/src/lexer.rs b/parser/src/lexer.rs index 746873fbe..1d83fc129 100644 --- a/parser/src/lexer.rs +++ b/parser/src/lexer.rs @@ -142,9 +142,7 @@ impl<'a> Lexer<'a> { Some(b'_') => { if start == self.offset { // before the underscore, there was no digit - return Err(Error::BadNumber(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset].to_vec()) - })); + return Err(Error::BadNumber((self.offset, 1).into())); } self.eat_and_assert(|b| b == b'_'); @@ -152,9 +150,7 @@ impl<'a> Lexer<'a> { Some(b) if b.is_ascii_digit() => continue, // Continue if next is a digit _ => { // after the underscore, there is no digit - return Err(Error::BadNumber(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset].to_vec()) - })); + return Err(Error::BadNumber((self.offset, 1).into())); } } } @@ -171,9 +167,7 @@ impl<'a> Lexer<'a> { Some(b'_') => { if start == self.offset { // before the underscore, there was no digit - return Err(Error::BadNumber(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset].to_vec()) - })); + return Err(Error::BadNumber((self.offset, 1).into())); } self.eat_and_assert(|b| b == b'_'); @@ -181,9 +175,7 @@ impl<'a> Lexer<'a> { Some(b) if b.is_ascii_hexdigit() => continue, // Continue if next is a digit _ => { // after the underscore, there is no digit - return Err(Error::BadNumber(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset].to_vec()) - })); + return Err(Error::BadNumber((self.offset, 1).into())); } } } @@ -266,12 +258,12 @@ impl<'a> Lexer<'a> { break; // End of block comment } None => { - return Err(Error::UnterminatedBlockComment(self.offset, None)) + return Err(Error::UnterminatedBlockComment((self.offset, 1).into())) } _ => {} } } - None => return Err(Error::UnterminatedBlockComment(self.offset, None)), + None => return Err(Error::UnterminatedBlockComment((self.offset, 1).into())), _ => unreachable!(), // We should not reach here } } @@ -365,7 +357,7 @@ impl<'a> Lexer<'a> { Some(b'=') => { self.eat_and_assert(|b| b == b'='); } - _ => return Err(Error::ExpectedEqualsSign(self.offset, None)), + _ => return Err(Error::ExpectedEqualsSign((self.offset, 1).into())), } Ok(Token { @@ -414,7 +406,7 @@ impl<'a> Lexer<'a> { _ => break, } } - None => return Err(Error::UnterminatedLiteral(self.offset, None)), + None => return Err(Error::UnterminatedLiteral((self.offset, 1).into())), _ => unreachable!(), }; } @@ -441,9 +433,7 @@ impl<'a> Lexer<'a> { }) } Some(b) if is_identifier_start(b) => { - Err(Error::BadFractionalPart(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset + 1].to_vec()) - })) + Err(Error::BadFractionalPart((self.offset, 1).into())) } _ => Ok(Token { value: &self.input[start..self.offset], @@ -471,15 +461,11 @@ impl<'a> Lexer<'a> { let start_num = self.offset; self.eat_while_number_digit()?; if start_num == self.offset { - return Err(Error::BadExponentPart(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset].to_vec()) - })); + return Err(Error::BadExponentPart((self.offset, 1).into())); } if self.peek().is_some() && is_identifier_start(self.peek().unwrap()) { - return Err(Error::BadExponentPart(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset + 1].to_vec()) - })); + return Err(Error::BadExponentPart((self.offset, 1).into())); } Ok(Token { @@ -502,17 +488,11 @@ impl<'a> Lexer<'a> { self.eat_while_number_hexdigit()?; if start_hex == self.offset { - return Err(Error::MalformedHexInteger( - self.offset, - None, - Some("Did you forget to add digits after '0x' or '0X'?"), // Help Message - )); + return Err(Error::MalformedHexInteger((self.offset, 1).into())); } if self.peek().is_some() && is_identifier_start(self.peek().unwrap()) { - return Err(Error::BadNumber(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset + 1].to_vec()) - })); + return Err(Error::BadNumber((self.offset, 1).into())); } return Ok(Token { @@ -540,9 +520,7 @@ impl<'a> Lexer<'a> { token_type: Some(TokenType::TK_FLOAT), }) } - Some(b) if is_identifier_start(b) => Err(Error::BadNumber(self.offset, None, unsafe { - String::from_utf8_unchecked(self.input[start..self.offset + 1].to_vec()) - })), + Some(b) if is_identifier_start(b) => Err(Error::BadNumber((self.offset, 1).into())), _ => Ok(Token { value: &self.input[start..self.offset], token_type: Some(TokenType::TK_INTEGER), @@ -562,7 +540,7 @@ impl<'a> Lexer<'a> { token_type: Some(TokenType::TK_ID), }) } - None => Err(Error::UnterminatedBracket(self.offset, None)), + None => Err(Error::UnterminatedBracket((self.offset, 1).into())), _ => unreachable!(), // We should not reach here } } @@ -579,7 +557,7 @@ impl<'a> Lexer<'a> { // empty variable name if start_digit == self.offset { - return Err(Error::BadVariableName(self.offset, None)); + return Err(Error::BadVariableName((self.offset, 1).into())); } Ok(Token { @@ -593,7 +571,7 @@ impl<'a> Lexer<'a> { // empty variable name if start_id == self.offset { - return Err(Error::BadVariableName(self.offset, None)); + return Err(Error::BadVariableName((self.offset, 1).into())); } Ok(Token { @@ -622,7 +600,7 @@ impl<'a> Lexer<'a> { self.eat_and_assert(|b| b == b'\''); if (end_hex - start_hex) % 2 != 0 { - return Err(Error::UnrecognizedToken(self.offset, None)); + return Err(Error::UnrecognizedToken((self.offset, 1).into())); } Ok(Token { @@ -630,7 +608,7 @@ impl<'a> Lexer<'a> { token_type: Some(TokenType::TK_BLOB), }) } - _ => Err(Error::UnterminatedLiteral(self.offset, None)), + _ => Err(Error::UnterminatedLiteral((self.offset, 1).into())), } } _ => { diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 4048cb178..591a59f8a 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -153,6 +153,7 @@ impl<'a> Parser<'a> { Some(token) => { if !found_semi { return Err(Error::ParseUnexpectedToken { + parsed_offset: (self.lexer.offset, 1).into(), expected: &[TokenType::TK_SEMI], got: token.token_type.unwrap(), }); @@ -427,6 +428,7 @@ impl<'a> Parser<'a> { } Err(Error::ParseUnexpectedToken { + parsed_offset: (self.lexer.offset, 1).into(), expected: expected, got: token.token_type.unwrap(), // no whitespace or comment tokens here }) @@ -985,7 +987,6 @@ impl<'a> Parser<'a> { }; let partition_by = match self.peek()? { - None => vec![], Some(tok) if tok.token_type == Some(TokenType::TK_PARTITION) => { self.eat_assert(&[TokenType::TK_PARTITION]); self.eat_expect(&[TokenType::TK_BY])?; @@ -1026,7 +1027,7 @@ impl<'a> Parser<'a> { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let window = self.parse_window()?; - self.eat_expect(&[TokenType::TK_LP])?; + self.eat_expect(&[TokenType::TK_RP])?; Ok(Some(Over::Window(window))) } _ => { @@ -1234,6 +1235,7 @@ impl<'a> Parser<'a> { None }; + self.eat_expect(&[TokenType::TK_RP])?; Ok(Box::new(Expr::Raise(resolve, expr))) } _ => { @@ -1249,6 +1251,7 @@ impl<'a> Parser<'a> { } else if tok.token_type == Some(TokenType::TK_LP) { if can_be_lit_str { return Err(Error::ParseUnexpectedToken { + parsed_offset: (self.lexer.offset, 1).into(), got: TokenType::TK_STRING, expected: &[ TokenType::TK_ID, @@ -1293,6 +1296,7 @@ impl<'a> Parser<'a> { let third_name = if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_DOT) { + debug_assert!(second_name.is_some()); self.eat_assert(&[TokenType::TK_DOT]); self.peek_nm()?; Some(self.parse_nm()) @@ -3212,28 +3216,1073 @@ mod tests { limit: None, }))], ), - // ( - // b"SELECT col_1".as_slice(), - // vec![Cmd::Stmt(Stmt::Select(Select { - // with: None, - // body: SelectBody { - // select: OneSelect::Select { - // distinctness: None, - // columns: vec![ResultColumn::Expr( - // Box::new(Expr::Column("col_1".to_owned())), - // None, - // )], - // from: None, - // where_clause: None, - // group_by: None, - // window_clause: vec![], - // }, - // compounds: vec![], - // }, - // order_by: vec![], - // limit: None, - // }))], - // ), + ( + b"SELECT (SELECT 1)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Subquery(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Literal(Literal::Numeric( + "1".to_owned(), + ))), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + })), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT RAISE (Ignore)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Raise(ResolveType::Ignore, None)), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT RAISE (FAIL, 'error')".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Raise( + ResolveType::Fail, + Some(Box::new(Expr::Literal(Literal::String( + "'error'".to_owned(), + )))), + )), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT RAISE (ROLLBACK, 'error')".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Raise( + ResolveType::Rollback, + Some(Box::new(Expr::Literal(Literal::String( + "'error'".to_owned(), + )))), + )), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT RAISE (ABORT, 'error')".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Raise( + ResolveType::Abort, + Some(Box::new(Expr::Literal(Literal::String( + "'error'".to_owned(), + )))), + )), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT RAISE (ABORT, 'error')".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Raise( + ResolveType::Abort, + Some(Box::new(Expr::Literal(Literal::String( + "'error'".to_owned(), + )))), + )), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT col_1".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Id(Name::Ident("col_1".to_owned()))), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT 'col_1'".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Literal(Literal::String("'col_1'".to_owned()))), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT tbl_name.col_1".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::Qualified( + Name::Ident("tbl_name".to_owned()), + Name::Ident("col_1".to_owned()), + )), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT schema_name.tbl_name.col_1".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::DoublyQualified( + Name::Ident("schema_name".to_owned()), + Name::Ident("tbl_name".to_owned()), + Name::Ident("col_1".to_owned()), + )), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name()".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: None, + args: vec![], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: None, + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) FILTER (WHERE x) OVER window_name".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: Some(Box::new(Expr::Id(Name::Ident( + "x".to_owned(), + )))), + over_clause: Some(Over::Name(Name::Ident( + "window_name".to_owned(), + ))), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (PARTITION BY product)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: None, + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: None, + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: None, + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product ORDER BY test ASC NULLS LAST)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![ + SortedColumn { + expr: Box::new(Expr::Id(Name::Ident("test".to_owned()))), + order: Some(SortOrder::Asc), + nulls: Some(NullsOrder::Last), + } + ], + frame_clause: None, + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Rows, + start: FrameBound::Preceding(Box::new(Expr::Literal(Literal::Numeric("2".to_owned())))), + end: Some(FrameBound::CurrentRow), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product RANGE BETWEEN 2 PRECEDING AND CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Range, + start: FrameBound::Preceding(Box::new(Expr::Literal(Literal::Numeric("2".to_owned())))), + end: Some(FrameBound::CurrentRow), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::Preceding(Box::new(Expr::Literal(Literal::Numeric("2".to_owned())))), + end: Some(FrameBound::CurrentRow), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN 2 FOLLOWING AND CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::Following(Box::new(Expr::Literal(Literal::Numeric("2".to_owned())))), + end: Some(FrameBound::CurrentRow), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::UnboundedPreceding, + end: Some(FrameBound::CurrentRow), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN CURRENT ROW AND CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: Some(FrameBound::CurrentRow), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: Some(FrameBound::UnboundedFollowing), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN CURRENT ROW AND 1 PRECEDING)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: Some(FrameBound::Preceding( + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))) + )), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: Some(FrameBound::Following( + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))) + )), + exclude: None + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS CURRENT ROW EXCLUDE NO OTHERS)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: None, + exclude: Some(FrameExclude::NoOthers) + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS CURRENT ROW EXCLUDE CURRENT ROW)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: None, + exclude: Some(FrameExclude::CurrentRow) + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS CURRENT ROW EXCLUDE GROUP)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: None, + exclude: Some(FrameExclude::Group) + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), + ( + b"SELECT func_name(DISTINCT 1, 2) OVER (test PARTITION BY product GROUPS CURRENT ROW EXCLUDE TIES)".as_slice(), + vec![Cmd::Stmt(Stmt::Select(Select { + with: None, + body: SelectBody { + select: OneSelect::Select { + distinctness: None, + columns: vec![ResultColumn::Expr( + Box::new(Expr::FunctionCall { + name: Name::Ident("func_name".to_owned()), + distinctness: Some(Distinctness::Distinct), + args: vec![ + Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), + Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), + ], + order_by: vec![], + filter_over: FunctionTail { + filter_clause: None, + over_clause: Some(Over::Window(Window { + base: Some(Name::Ident("test".to_owned())), + partition_by: vec![Box::new(Expr::Id(Name::Ident( + "product".to_owned(), + )))], + order_by: vec![], + frame_clause: Some(FrameClause{ + mode: FrameMode::Groups, + start: FrameBound::CurrentRow, + end: None, + exclude: Some(FrameExclude::Ties) + }), + })), + }, + }), + None, + )], + from: None, + where_clause: None, + group_by: None, + window_clause: vec![], + }, + compounds: vec![], + }, + order_by: vec![], + limit: None, + }))], + ), ]; for (input, expected) in test_cases {