use crate::ast::{ AlterTableBody, As, Cmd, ColumnConstraint, ColumnDefinition, CommonTableExpr, CompoundOperator, CompoundSelect, CreateTableBody, DeferSubclause, Distinctness, Expr, ForeignKeyClause, FrameBound, FrameClause, FrameExclude, FrameMode, FromClause, FunctionTail, GroupBy, Indexed, IndexedColumn, InitDeferredPred, JoinConstraint, JoinOperator, JoinType, JoinedSelectTable, LikeOperator, Limit, Literal, Materialized, Name, NamedColumnConstraint, NamedTableConstraint, NullsOrder, OneSelect, Operator, Over, PragmaBody, PragmaValue, QualifiedName, RefAct, RefArg, ResolveType, ResultColumn, Select, SelectBody, SelectTable, Set, SortOrder, SortedColumn, Stmt, TableConstraint, TableOptions, TransactionType, TriggerCmd, TriggerEvent, TriggerTime, Type, TypeSize, UnaryOperator, Upsert, UpsertDo, UpsertIndex, Window, WindowDef, With, }; use crate::error::Error; use crate::lexer::{Lexer, Token}; use crate::token::TokenType; #[inline(always)] fn from_bytes_as_str(bytes: &[u8]) -> &str { unsafe { str::from_utf8_unchecked(bytes) } } #[inline(always)] fn from_bytes(bytes: &[u8]) -> String { unsafe { str::from_utf8_unchecked(bytes).to_owned() } } #[inline(always)] fn join_type_from_bytes(s: &[u8]) -> Result { if b"CROSS".eq_ignore_ascii_case(s) { Ok(JoinType::INNER | JoinType::CROSS) } else if b"FULL".eq_ignore_ascii_case(s) { Ok(JoinType::LEFT | JoinType::RIGHT | JoinType::OUTER) } else if b"INNER".eq_ignore_ascii_case(s) { Ok(JoinType::INNER) } else if b"LEFT".eq_ignore_ascii_case(s) { Ok(JoinType::LEFT | JoinType::OUTER) } else if b"NATURAL".eq_ignore_ascii_case(s) { Ok(JoinType::NATURAL) } else if b"RIGHT".eq_ignore_ascii_case(s) { Ok(JoinType::RIGHT | JoinType::OUTER) } else if b"OUTER".eq_ignore_ascii_case(s) { Ok(JoinType::OUTER) } else { Err(Error::Custom(format!( "unsupported JOIN type: {:?}", str::from_utf8(s) ))) } } #[inline(always)] fn new_join_type(n0: &[u8], n1: Option<&[u8]>, n2: Option<&[u8]>) -> Result { let mut jt = join_type_from_bytes(n0)?; if let Some(n1) = n1 { jt |= join_type_from_bytes(n1)?; } if let Some(n2) = n2 { jt |= join_type_from_bytes(n2)?; } if (jt & (JoinType::INNER | JoinType::OUTER)) == (JoinType::INNER | JoinType::OUTER) || (jt & (JoinType::OUTER | JoinType::LEFT | JoinType::RIGHT)) == JoinType::OUTER { return Err(Error::Custom(format!( "unsupported JOIN type: {:?} {:?} {:?}", from_bytes_as_str(n0), from_bytes_as_str(n1.unwrap_or(&[])), from_bytes_as_str(n2.unwrap_or(&[])), ))); } Ok(jt) } pub struct Parser<'a> { lexer: Lexer<'a>, /// The current token being processed current_token: Token<'a>, peekable: bool, } impl<'a> Iterator for Parser<'a> { type Item = Result; #[inline(always)] fn next(&mut self) -> Option { match self.mark(|p| p.next_cmd()) { Ok(None) => None, // EOF Ok(Some(cmd)) => Some(Ok(cmd)), Err(err) => Some(Err(err)), } } } impl<'a> Parser<'a> { #[inline(always)] pub fn new(input: &'a [u8]) -> Self { Self { lexer: Lexer::new(input), peekable: false, current_token: Token { value: b"", token_type: None, }, } } // entrypoint of parsing fn next_cmd(&mut self) -> Result, Error> { // consumes prefix SEMI while let Some(token) = self.peek()? { if token.token_type == Some(TokenType::TK_SEMI) { self.eat_assert(&[TokenType::TK_SEMI]); } else { break; } } let result = match self.peek()? { None => None, // EOF Some(token) => match token.token_type.unwrap() { TokenType::TK_EXPLAIN => { self.eat_assert(&[TokenType::TK_EXPLAIN]); let mut is_query_plan = false; if self.peek_no_eof()?.token_type == Some(TokenType::TK_QUERY) { self.eat_assert(&[TokenType::TK_QUERY]); self.eat_expect(&[TokenType::TK_PLAN])?; is_query_plan = true; } let stmt = self.parse_stmt()?; if is_query_plan { Some(Cmd::ExplainQueryPlan(stmt)) } else { Some(Cmd::Explain(stmt)) } } _ => { let stmt = self.parse_stmt()?; Some(Cmd::Stmt(stmt)) } }, }; let mut found_semi = false; loop { match self.peek()? { None => break, Some(token) if token.token_type == Some(TokenType::TK_SEMI) => { found_semi = true; self.eat_assert(&[TokenType::TK_SEMI]); } 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(), }); } break; } } } Ok(result) } #[inline(always)] fn consume_lexer_without_whitespaces_or_comments( &mut self, ) -> Option, Error>> { debug_assert!(!self.peekable); loop { let tok = self.lexer.next(); if let Some(Ok(ref token)) = tok { if token.token_type.is_none() { continue; // white space or comment } } return tok; } } fn next_token(&mut self) -> Result>, Error> { debug_assert!(!self.peekable); let mut next = self.consume_lexer_without_whitespaces_or_comments(); fn get_token(tt: TokenType) -> TokenType { match tt { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_JOIN_KW | TokenType::TK_UNION | TokenType::TK_EXCEPT | TokenType::TK_INTERSECT | TokenType::TK_GENERATED | TokenType::TK_WITHOUT | TokenType::TK_COLUMNKW | TokenType::TK_WINDOW | TokenType::TK_FILTER | TokenType::TK_OVER => TokenType::TK_ID, _ => tt.fallback_id_if_ok(), } } if let Some(Ok(ref mut tok)) = next { /* ** The following three functions are called immediately after the tokenizer ** reads the keywords WINDOW, OVER and FILTER, respectively, to determine ** whether the token should be treated as a keyword or an SQL identifier. ** This cannot be handled by the usual lemon %fallback method, due to ** the ambiguity in some constructions. e.g. ** ** SELECT sum(x) OVER ... ** ** In the above, "OVER" might be a keyword, or it might be an alias for the ** sum(x) expression. If a "%fallback ID OVER" directive were added to ** grammar, then SQLite would always treat "OVER" as an alias, making it ** impossible to call a window-function without a FILTER clause. ** ** WINDOW is treated as a keyword if: ** ** * the following token is an identifier, or a keyword that can fallback ** to being an identifier, and ** * the token after than one is TK_AS. ** ** OVER is a keyword if: ** ** * the previous token was TK_RP, and ** * the next token is either TK_LP or an identifier. ** ** FILTER is a keyword if: ** ** * the previous token was TK_RP, and ** * the next token is TK_LP. ** ** UNION is a keyword if: ** ** * the next token is TK_ALL|TK_SELECT|TK_VALUES. ** ** EXCEPT is a keyword if: ** ** * the next token is TK_SELECT|TK_VALUES. ** ** INTERSECT is a keyword if: ** ** * the next token is TK_SELECT|TK_VALUES. ** ** COLUMNKW is a keyword if: ** ** * the previous token is TK_ADD|TK_RENAME|TK_DROP. ** ** GENERATED is a keyword if: ** ** * the next token is TK_ALWAYS. ** * the token after than one is TK_AS. ** ** WITHOUT is a keyword if: ** ** * the previous token is TK_RP|TK_COMMA. ** * the next token is TK_ID. */ match tok.token_type.unwrap() { TokenType::TK_WINDOW => { let can_be_window = self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match get_token(tok?.token_type.unwrap()) { TokenType::TK_ID => {} _ => return Ok(false), }, } match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match tok?.token_type.unwrap() { TokenType::TK_AS => Ok(true), _ => Ok(false), }, } })?; if !can_be_window { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_OVER => { let prev_tt = self.current_token.token_type.unwrap_or(TokenType::TK_EOF); let can_be_over = { if prev_tt == TokenType::TK_RP { self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match get_token(tok?.token_type.unwrap()) { TokenType::TK_LP | TokenType::TK_ID => Ok(true), _ => Ok(false), }, } })? } else { false } }; if !can_be_over { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_FILTER => { let prev_tt = self.current_token.token_type.unwrap_or(TokenType::TK_EOF); let can_be_filter = { if prev_tt == TokenType::TK_RP { self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match tok?.token_type.unwrap() { TokenType::TK_LP => Ok(true), _ => Ok(false), }, } })? } else { false } }; if !can_be_filter { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_UNION => { let can_be_union = self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match tok?.token_type.unwrap() { TokenType::TK_ALL | TokenType::TK_SELECT | TokenType::TK_VALUES => { Ok(true) } _ => Ok(false), }, } })?; if !can_be_union { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_EXCEPT | TokenType::TK_INTERSECT => { let can_be_except = self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match tok?.token_type.unwrap() { TokenType::TK_SELECT | TokenType::TK_VALUES => Ok(true), _ => Ok(false), }, } })?; if !can_be_except { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_COLUMNKW => { let prev_tt = self.current_token.token_type.unwrap_or(TokenType::TK_EOF); let can_be_columnkw = match prev_tt { TokenType::TK_ADD | TokenType::TK_RENAME | TokenType::TK_DROP => true, _ => false, }; if !can_be_columnkw { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_GENERATED => { let can_be_generated = self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match tok?.token_type.unwrap() { TokenType::TK_ALWAYS => {} _ => return Ok(false), }, } match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match tok?.token_type.unwrap() { TokenType::TK_AS => Ok(true), _ => Ok(false), }, } })?; if !can_be_generated { tok.token_type = Some(TokenType::TK_ID); } } TokenType::TK_WITHOUT => { let prev_tt = self.current_token.token_type.unwrap_or(TokenType::TK_EOF); let can_be_without = match prev_tt { TokenType::TK_RP | TokenType::TK_COMMA => self.try_parse(|p| { match p.consume_lexer_without_whitespaces_or_comments() { None => return Ok(false), Some(tok) => match get_token(tok?.token_type.unwrap()) { TokenType::TK_ID => Ok(true), _ => Ok(false), }, } })?, _ => false, }; if !can_be_without { tok.token_type = Some(TokenType::TK_ID); } } _ => {} } } match next { None => Ok(None), // EOF Some(Ok(tok)) => { self.current_token = tok.clone(); self.peekable = true; Ok(Some(tok)) } Some(Err(err)) => Err(err), } } #[inline(always)] fn mark(&mut self, exc: F) -> Result where F: FnOnce(&mut Self) -> Result, { let old_peekable = self.peekable; let old_current_token = self.current_token.clone(); let start_offset = self.lexer.offset; let result = exc(self); if result.is_err() { self.peekable = old_peekable; self.current_token = old_current_token; self.lexer.offset = start_offset; } result } #[inline(always)] fn try_parse(&mut self, exc: F) -> R where F: FnOnce(&mut Self) -> R, { debug_assert!(!self.peekable); let start_offset = self.lexer.offset; let result = exc(self); self.peekable = false; self.lexer.offset = start_offset; result } /// Get the next token from the lexer #[inline(always)] fn eat(&mut self) -> Result>, Error> { let result = self.peek()?; self.peekable = false; // Clear the peek mark after consuming Ok(result) } /// Peek at the next token without consuming it #[inline(always)] fn peek(&mut self) -> Result>, Error> { if self.peekable { return Ok(Some(self.current_token.clone())); } self.next_token() } #[inline(always)] fn eat_no_eof(&mut self) -> Result, Error> { match self.eat()? { None => Err(Error::ParseUnexpectedEOF), Some(token) => Ok(token), } } #[inline(always)] fn eat_expect(&mut self, expected: &'static [TokenType]) -> Result, Error> { self.peek_expect(expected)?; Ok(self.eat_assert(expected)) } // this function expected to be called after checking the token type (peek) #[inline(always)] fn eat_assert(&mut self, _expected: &'static [TokenType]) -> Token<'a> { let token = self.eat_no_eof().unwrap(); #[cfg(debug_assertions)] { for expected in _expected { if token.token_type == Some(*expected) { return token; } if *expected == TokenType::TK_ID && token.token_type.unwrap().fallback_id_if_ok() == TokenType::TK_ID { return token; } } panic!( "Expected token {:?}, got {:?}", _expected, token.token_type.unwrap() ); } #[cfg(not(debug_assertions))] token // in release mode, we assume the caller has checked the token type } #[inline(always)] fn peek_no_eof(&mut self) -> Result, Error> { match self.peek()? { None => Err(Error::ParseUnexpectedEOF), Some(token) => Ok(token), } } #[inline(always)] fn peek_expect(&mut self, expected: &'static [TokenType]) -> Result, Error> { let token = self.peek_no_eof()?; for expected in expected { if token.token_type == Some(*expected) { return Ok(token); } if *expected == TokenType::TK_ID && token.token_type.unwrap().fallback_id_if_ok() == TokenType::TK_ID { return Ok(token); } } Err(Error::ParseUnexpectedToken { parsed_offset: (self.lexer.offset, 1).into(), expected: expected, got: token.token_type.unwrap(), // no whitespace or comment tokens here }) } #[inline(always)] fn parse_stmt(&mut self) -> Result { let tok = self.peek_expect(&[ TokenType::TK_BEGIN, TokenType::TK_COMMIT, TokenType::TK_END, TokenType::TK_ROLLBACK, TokenType::TK_SAVEPOINT, TokenType::TK_RELEASE, TokenType::TK_CREATE, TokenType::TK_SELECT, TokenType::TK_VALUES, TokenType::TK_WITH, TokenType::TK_ANALYZE, TokenType::TK_ATTACH, TokenType::TK_DETACH, TokenType::TK_PRAGMA, TokenType::TK_VACUUM, TokenType::TK_ALTER, TokenType::TK_DELETE, // add more ])?; match tok.token_type.unwrap() { TokenType::TK_BEGIN => self.parse_begin(), TokenType::TK_COMMIT | TokenType::TK_END => self.parse_commit(), TokenType::TK_ROLLBACK => self.parse_rollback(), TokenType::TK_SAVEPOINT => self.parse_savepoint(), TokenType::TK_RELEASE => self.parse_release(), TokenType::TK_CREATE => self.parse_create_stmt(), TokenType::TK_SELECT | TokenType::TK_VALUES => Ok(Stmt::Select(self.parse_select()?)), TokenType::TK_WITH => self.parse_with_stmt(), TokenType::TK_ANALYZE => self.parse_analyze(), TokenType::TK_ATTACH => self.parse_attach(), TokenType::TK_DETACH => self.parse_detach(), TokenType::TK_PRAGMA => self.parse_pragma(), TokenType::TK_VACUUM => self.parse_vacuum(), TokenType::TK_ALTER => self.parse_alter(), TokenType::TK_DELETE => self.parse_delete(), _ => unreachable!(), } } fn parse_nm(&mut self) -> Result { let tok = self.eat_expect(&[ TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, ])?; let first_char = tok.value[0]; // no need to check empty match first_char { b'[' | b'\'' | b'`' | b'"' => Ok(Name::Quoted(from_bytes(tok.value))), _ => Ok(Name::Ident(from_bytes(tok.value))), } } fn parse_transopt(&mut self) -> Result, Error> { match self.peek()? { None => Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_TRANSACTION => { self.eat_assert(&[TokenType::TK_TRANSACTION]); match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => Ok(Some(self.parse_nm()?)), _ => Ok(None), }, _ => Ok(None), } } _ => Ok(None), }, } } fn parse_begin(&mut self) -> Result { self.eat_assert(&[TokenType::TK_BEGIN]); let transtype = match self.peek()? { None => None, Some(tok) => match tok.token_type.unwrap() { TokenType::TK_DEFERRED => { self.eat_assert(&[TokenType::TK_DEFERRED]); Some(TransactionType::Deferred) } TokenType::TK_IMMEDIATE => { self.eat_assert(&[TokenType::TK_IMMEDIATE]); Some(TransactionType::Immediate) } TokenType::TK_EXCLUSIVE => { self.eat_assert(&[TokenType::TK_EXCLUSIVE]); Some(TransactionType::Exclusive) } _ => None, }, }; Ok(Stmt::Begin { typ: transtype, name: self.parse_transopt()?, }) } fn parse_commit(&mut self) -> Result { self.eat_assert(&[TokenType::TK_COMMIT, TokenType::TK_END]); Ok(Stmt::Commit { name: self.parse_transopt()?, }) } fn parse_rollback(&mut self) -> Result { self.eat_assert(&[TokenType::TK_ROLLBACK]); let tx_name = self.parse_transopt()?; let savepoint_name = match self.peek()? { None => None, Some(tok) => { if tok.token_type == Some(TokenType::TK_TO) { self.eat_assert(&[TokenType::TK_TO]); if self.peek_no_eof()?.token_type == Some(TokenType::TK_SAVEPOINT) { self.eat_assert(&[TokenType::TK_SAVEPOINT]); } Some(self.parse_nm()?) } else { None } } }; Ok(Stmt::Rollback { tx_name, savepoint_name, }) } fn parse_savepoint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_SAVEPOINT]); Ok(Stmt::Savepoint { name: self.parse_nm()?, }) } fn parse_release(&mut self) -> Result { self.eat_assert(&[TokenType::TK_RELEASE]); if self.peek_no_eof()?.token_type == Some(TokenType::TK_SAVEPOINT) { self.eat_assert(&[TokenType::TK_SAVEPOINT]); } Ok(Stmt::Release { name: self.parse_nm()?, }) } fn parse_create_view(&mut self, temporary: bool) -> Result { self.eat_assert(&[TokenType::TK_VIEW]); let if_not_exists = self.parse_if_not_exists()?; let view_name = self.parse_fullname(false)?; let columns = self.parse_eid_list()?; self.eat_expect(&[TokenType::TK_AS])?; let select = self.parse_select()?; Ok(Stmt::CreateView { temporary, if_not_exists, view_name, columns, select, }) } fn parse_vtab_arg(&mut self) -> Result { let tok = self.peek_no_eof()?; // minus len() because lexer already consumed the token let start_idx = self.lexer.offset - tok.value.len(); loop { match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_LP => { let mut lp_count: usize = 0; loop { let tok = self.eat_no_eof()?; match tok.token_type.unwrap() { TokenType::TK_LP => { lp_count += 1; } TokenType::TK_RP => { lp_count -= 1; // FIXME: no need to check underflow if lp_count == 0 { break; } } _ => {} } } } TokenType::TK_COMMA | TokenType::TK_RP => break, _ => { self.eat_no_eof()?; } } } // minus 1 because lexer already consumed TK_COMMA or TK_RP let end_idx = self.lexer.offset - 1; if start_idx == end_idx { return Err(Error::Custom("unexpected COMMA in vtab args".to_owned())); } Ok(from_bytes(&self.lexer.input[start_idx..end_idx])) } fn parse_create_virtual(&mut self) -> Result { self.eat_assert(&[TokenType::TK_VIRTUAL]); self.eat_expect(&[TokenType::TK_TABLE])?; let if_not_exists = self.parse_if_not_exists()?; let tbl_name = self.parse_fullname(false)?; self.eat_expect(&[TokenType::TK_USING])?; let module_name = self.parse_nm()?; let args = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let mut result = vec![]; loop { match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_RP => break, // handle empty args case _ => {} } result.push(self.parse_vtab_arg()?); let tok = self.peek_expect(&[TokenType::TK_COMMA, TokenType::TK_RP])?; match tok.token_type.unwrap() { TokenType::TK_COMMA => { self.eat_assert(&[TokenType::TK_COMMA]); } TokenType::TK_RP => break, _ => unreachable!(), } } self.eat_assert(&[TokenType::TK_RP]); result } _ => vec![], }, _ => vec![], }; Ok(Stmt::CreateVirtualTable { if_not_exists, tbl_name, module_name, args, }) } fn parse_create_stmt(&mut self) -> Result { self.eat_assert(&[TokenType::TK_CREATE]); let mut first_tok = self.peek_expect(&[ TokenType::TK_TEMP, TokenType::TK_TABLE, TokenType::TK_VIRTUAL, TokenType::TK_VIEW, TokenType::TK_INDEX, TokenType::TK_UNIQUE, TokenType::TK_TRIGGER, ])?; let mut temp = false; if first_tok.token_type == Some(TokenType::TK_TEMP) { self.eat_assert(&[TokenType::TK_TEMP]); temp = true; first_tok = self.peek_expect(&[ TokenType::TK_TABLE, TokenType::TK_VIEW, TokenType::TK_TRIGGER, ])?; } match first_tok.token_type.unwrap() { TokenType::TK_TABLE => self.parse_create_table(temp), TokenType::TK_VIEW => self.parse_create_view(temp), TokenType::TK_TRIGGER => self.parse_create_trigger(temp), TokenType::TK_VIRTUAL => self.parse_create_virtual(), TokenType::TK_INDEX | TokenType::TK_UNIQUE => self.parse_create_index(), _ => unreachable!(), } } fn parse_with_stmt(&mut self) -> Result { let with = self.parse_with()?; debug_assert!(with.is_some()); let first_tok = self.peek_expect(&[ TokenType::TK_SELECT, TokenType::TK_VALUES, TokenType::TK_UPDATE, TokenType::TK_DELETE, TokenType::TK_INSERT, TokenType::TK_REPLACE, ])?; match first_tok.token_type.unwrap() { TokenType::TK_SELECT | TokenType::TK_VALUES => { Ok(Stmt::Select(self.parse_select_without_cte(with)?)) } TokenType::TK_UPDATE => todo!(), TokenType::TK_DELETE => self.parse_delete_without_cte(with), TokenType::TK_INSERT | TokenType::TK_REPLACE => todo!(), _ => unreachable!(), } } fn parse_if_not_exists(&mut self) -> Result { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_IF) { self.eat_assert(&[TokenType::TK_IF]); } else { return Ok(false); } } else { return Ok(false); } self.eat_expect(&[TokenType::TK_NOT])?; self.eat_expect(&[TokenType::TK_EXISTS])?; Ok(true) } fn parse_fullname(&mut self, allow_alias: bool) -> Result { let first_name = self.parse_nm()?; let secone_name = if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_DOT) { self.eat_assert(&[TokenType::TK_DOT]); Some(self.parse_nm()?) } else { None } } else { None }; let alias_name = if allow_alias { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_AS) { self.eat_assert(&[TokenType::TK_AS]); Some(self.parse_nm()?) } else { None } } else { None } } else { None }; if secone_name.is_some() { Ok(QualifiedName { db_name: Some(first_name), name: secone_name.unwrap(), alias: alias_name, }) } else { Ok(QualifiedName { db_name: None, name: first_name, alias: alias_name, }) } } fn parse_signed(&mut self) -> Result, Error> { self.peek_expect(&[ TokenType::TK_FLOAT, TokenType::TK_INTEGER, TokenType::TK_PLUS, TokenType::TK_MINUS, ])?; let expr = self.parse_expr_operand()?; match expr.as_ref() { Expr::Unary(_, inner) => match inner.as_ref() { Expr::Literal(Literal::Numeric(_)) => Ok(expr), _ => Err(Error::Custom( "Expected a numeric literal after unary operator".to_string(), )), }, _ => Ok(expr), } } fn parse_type(&mut self) -> Result, Error> { let mut type_name = if let Some(tok) = self.peek()? { match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING => { self.eat_assert(&[TokenType::TK_ID, TokenType::TK_STRING]); from_bytes(tok.value) } _ => return Ok(None), } } else { return Ok(None); }; loop { if let Some(tok) = self.peek()? { match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING => { self.eat_assert(&[TokenType::TK_ID, TokenType::TK_STRING]); type_name.push_str(" "); type_name.push_str(from_bytes_as_str(tok.value)); } _ => break, } } else { break; } } let size = if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_LP) { self.eat_assert(&[TokenType::TK_LP]); let first_size = self.parse_signed()?; let tok = self.eat_expect(&[TokenType::TK_RP, TokenType::TK_COMMA])?; match tok.token_type.unwrap() { TokenType::TK_RP => Some(TypeSize::MaxSize(first_size)), TokenType::TK_COMMA => { let second_size = self.parse_signed()?; self.eat_expect(&[TokenType::TK_RP])?; Some(TypeSize::TypeSize(first_size, second_size)) } _ => unreachable!(), } } else { None } } else { None }; Ok(Some(Type { name: type_name, size, })) } /// SQLite understands these operators, listed in precedence1 order /// (top to bottom / highest to lowest): /// /// Operators 2 /// 11 -> ~ [expr] + [expr] - [expr] /// 10 -> [expr] COLLATE (collation-name) 3 /// 9 -> || -> ->> /// 8 -> * / % /// 7 -> + - /// 6 -> &  | <<  >> /// 5 -> [expr] ESCAPE [escape-character-expr] 4 /// 4 -> < > <= >= /// 3 -> = == <> != IS IS NOT /// IS DISTINCT FROM IS NOT DISTINCT FROM /// [expr] BETWEEN5 [expr] AND [expr] /// IN5 MATCH5 LIKE5 REGEXP5 GLOB5 /// [expr] ISNULL [expr] NOTNULL [expr] NOT NULL /// 2 NOT [expr] /// 1 -> AND /// 0 -> OR /// /// this function detect precedence by peeking first token of operator /// after parsing a operand (binary operator) fn current_token_precedence(&mut self) -> Result, Error> { let tok = self.peek()?; if tok.is_none() { return Ok(None); } match tok.unwrap().token_type.unwrap() { TokenType::TK_OR => Ok(Some(0)), TokenType::TK_AND => Ok(Some(1)), TokenType::TK_NOT => Ok(Some(3)), // NOT is 3 because of binary operator TokenType::TK_EQ | TokenType::TK_NE | TokenType::TK_IS | TokenType::TK_BETWEEN | TokenType::TK_IN | TokenType::TK_MATCH | TokenType::TK_LIKE_KW | TokenType::TK_ISNULL | TokenType::TK_NOTNULL => Ok(Some(3)), TokenType::TK_LT | TokenType::TK_GT | TokenType::TK_LE | TokenType::TK_GE => { Ok(Some(4)) } TokenType::TK_ESCAPE => Ok(None), // ESCAPE will be consumed after parsing // MATCH|LIKE_KW TokenType::TK_BITAND | TokenType::TK_BITOR | TokenType::TK_LSHIFT | TokenType::TK_RSHIFT => Ok(Some(6)), TokenType::TK_PLUS | TokenType::TK_MINUS => Ok(Some(7)), TokenType::TK_STAR | TokenType::TK_SLASH | TokenType::TK_REM => Ok(Some(8)), TokenType::TK_CONCAT | TokenType::TK_PTR => Ok(Some(9)), TokenType::TK_COLLATE => Ok(Some(10)), // no need 11 because its for unary operators _ => Ok(None), } } fn parse_distinct(&mut self) -> Result, Error> { match self.peek()? { None => Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_DISTINCT => { self.eat_assert(&[TokenType::TK_DISTINCT]); Ok(Some(Distinctness::Distinct)) } TokenType::TK_ALL => { self.eat_assert(&[TokenType::TK_ALL]); Ok(Some(Distinctness::All)) } _ => Ok(None), }, } } fn parse_filter_clause(&mut self) -> Result>, Error> { match self.peek()? { None => return Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_FILTER => { self.eat_assert(&[TokenType::TK_FILTER]); } _ => return Ok(None), }, } self.eat_expect(&[TokenType::TK_LP])?; self.eat_expect(&[TokenType::TK_WHERE])?; let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Some(expr)) } fn parse_frame_opt(&mut self) -> Result, Error> { let range_or_rows = match self.peek()? { None => return Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_RANGE => { self.eat_assert(&[TokenType::TK_RANGE]); FrameMode::Range } TokenType::TK_ROWS => { self.eat_assert(&[TokenType::TK_ROWS]); FrameMode::Rows } TokenType::TK_GROUPS => { self.eat_assert(&[TokenType::TK_GROUPS]); FrameMode::Groups } _ => return Ok(None), }, }; let has_end = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_BETWEEN => { self.eat_assert(&[TokenType::TK_BETWEEN]); true } _ => false, }; let start = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_UNBOUNDED => { self.eat_assert(&[TokenType::TK_UNBOUNDED]); self.eat_expect(&[TokenType::TK_PRECEDING])?; FrameBound::UnboundedPreceding } TokenType::TK_CURRENT => { self.eat_assert(&[TokenType::TK_CURRENT]); self.eat_expect(&[TokenType::TK_ROW])?; FrameBound::CurrentRow } _ => { let expr = self.parse_expr(0)?; let tok = self.eat_expect(&[TokenType::TK_PRECEDING, TokenType::TK_FOLLOWING])?; match tok.token_type.unwrap() { TokenType::TK_PRECEDING => FrameBound::Preceding(expr), TokenType::TK_FOLLOWING => FrameBound::Following(expr), _ => unreachable!(), } } }; let end = if has_end { self.eat_expect(&[TokenType::TK_AND])?; Some(match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_UNBOUNDED => { self.eat_assert(&[TokenType::TK_UNBOUNDED]); self.eat_expect(&[TokenType::TK_FOLLOWING])?; FrameBound::UnboundedFollowing } TokenType::TK_CURRENT => { self.eat_assert(&[TokenType::TK_CURRENT]); self.eat_expect(&[TokenType::TK_ROW])?; FrameBound::CurrentRow } _ => { let expr = self.parse_expr(0)?; let tok = self.eat_expect(&[TokenType::TK_PRECEDING, TokenType::TK_FOLLOWING])?; match tok.token_type.unwrap() { TokenType::TK_PRECEDING => FrameBound::Preceding(expr), TokenType::TK_FOLLOWING => FrameBound::Following(expr), _ => unreachable!(), } } }) } else { None }; let exclude = match self.peek()? { None => None, Some(tok) => match tok.token_type.unwrap() { TokenType::TK_EXCLUDE => { self.eat_assert(&[TokenType::TK_EXCLUDE]); let tok = self.eat_expect(&[ TokenType::TK_NO, TokenType::TK_CURRENT, TokenType::TK_GROUP, TokenType::TK_TIES, ])?; match tok.token_type.unwrap() { TokenType::TK_NO => { self.eat_expect(&[TokenType::TK_OTHERS])?; Some(FrameExclude::NoOthers) } TokenType::TK_CURRENT => { self.eat_expect(&[TokenType::TK_ROW])?; Some(FrameExclude::CurrentRow) } TokenType::TK_GROUP => Some(FrameExclude::Group), TokenType::TK_TIES => Some(FrameExclude::Ties), _ => unreachable!(), } } _ => None, }, }; Ok(Some(FrameClause { mode: range_or_rows, start, end, exclude, })) } fn parse_window(&mut self) -> Result { let name = match self.peek()? { None => None, Some(tok) => match tok.token_type.unwrap() { TokenType::TK_PARTITION | TokenType::TK_ORDER | TokenType::TK_RANGE | TokenType::TK_ROWS | TokenType::TK_GROUPS => None, tt => match tt.fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => Some(self.parse_nm()?), _ => None, }, }, }; let partition_by = match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_PARTITION) => { self.eat_assert(&[TokenType::TK_PARTITION]); self.eat_expect(&[TokenType::TK_BY])?; self.parse_nexpr_list()? } _ => vec![], }; let order_by = self.parse_order_by()?; let frame_clause = self.parse_frame_opt()?; Ok(Window { base: name, partition_by, order_by, frame_clause, }) } fn parse_over_clause(&mut self) -> Result, Error> { match self.peek()? { None => return Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_OVER => { self.eat_assert(&[TokenType::TK_OVER]); } _ => return Ok(None), }, } let tok = self.peek_expect(&[ TokenType::TK_LP, TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, ])?; match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let window = self.parse_window()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Some(Over::Window(window))) } _ => Ok(Some(Over::Name(self.parse_nm()?))), } } fn parse_filter_over(&mut self) -> Result { let filter_clause = self.parse_filter_clause()?; let over_clause = self.parse_over_clause()?; Ok(FunctionTail { filter_clause, over_clause, }) } fn parse_raise_type(&mut self) -> Result { let tok = self.eat_expect(&[ TokenType::TK_ROLLBACK, TokenType::TK_ABORT, TokenType::TK_FAIL, ])?; match tok.token_type.unwrap() { TokenType::TK_ROLLBACK => Ok(ResolveType::Rollback), TokenType::TK_ABORT => Ok(ResolveType::Abort), TokenType::TK_FAIL => Ok(ResolveType::Fail), _ => unreachable!(), } } fn parse_expr_operand(&mut self) -> Result, Error> { let tok = self.peek_expect(&[ TokenType::TK_LP, TokenType::TK_CAST, TokenType::TK_CTIME_KW, TokenType::TK_RAISE, TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, TokenType::TK_NULL, TokenType::TK_BLOB, TokenType::TK_FLOAT, TokenType::TK_INTEGER, TokenType::TK_VARIABLE, TokenType::TK_NOT, TokenType::TK_BITNOT, TokenType::TK_PLUS, TokenType::TK_MINUS, TokenType::TK_EXISTS, TokenType::TK_CASE, ])?; match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_WITH | TokenType::TK_SELECT | TokenType::TK_VALUES => { let select = self.parse_select()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Box::new(Expr::Subquery(select))) } _ => { let exprs = self.parse_nexpr_list()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Box::new(Expr::Parenthesized(exprs))) } } } TokenType::TK_NULL => { self.eat_assert(&[TokenType::TK_NULL]); Ok(Box::new(Expr::Literal(Literal::Null))) } TokenType::TK_BLOB => { self.eat_assert(&[TokenType::TK_BLOB]); Ok(Box::new(Expr::Literal(Literal::Blob(from_bytes( tok.value, ))))) } TokenType::TK_FLOAT => { self.eat_assert(&[TokenType::TK_FLOAT]); Ok(Box::new(Expr::Literal(Literal::Numeric(from_bytes( tok.value, ))))) } TokenType::TK_INTEGER => { self.eat_assert(&[TokenType::TK_INTEGER]); Ok(Box::new(Expr::Literal(Literal::Numeric(from_bytes( tok.value, ))))) } TokenType::TK_VARIABLE => { self.eat_assert(&[TokenType::TK_VARIABLE]); Ok(Box::new(Expr::Variable(from_bytes(tok.value)))) } TokenType::TK_CAST => { self.eat_assert(&[TokenType::TK_CAST]); self.eat_expect(&[TokenType::TK_LP])?; let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_AS])?; let typ = self.parse_type()?; self.eat_expect(&[TokenType::TK_RP])?; return Ok(Box::new(Expr::Cast { expr, type_name: typ, })); } TokenType::TK_CTIME_KW => { let tok = self.eat_assert(&[TokenType::TK_CTIME_KW]); if b"CURRENT_DATE".eq_ignore_ascii_case(&tok.value) { Ok(Box::new(Expr::Literal(Literal::CurrentDate))) } else if b"CURRENT_TIME".eq_ignore_ascii_case(&tok.value) { Ok(Box::new(Expr::Literal(Literal::CurrentTime))) } else if b"CURRENT_TIMESTAMP".eq_ignore_ascii_case(&tok.value) { Ok(Box::new(Expr::Literal(Literal::CurrentTimestamp))) } else { unreachable!() } } TokenType::TK_NOT => { self.eat_assert(&[TokenType::TK_NOT]); let expr = self.parse_expr(2)?; // NOT precedence is 2 Ok(Box::new(Expr::Unary(UnaryOperator::Not, expr))) } TokenType::TK_BITNOT => { self.eat_assert(&[TokenType::TK_BITNOT]); let expr = self.parse_expr(11)?; // BITNOT precedence is 11 Ok(Box::new(Expr::Unary(UnaryOperator::BitwiseNot, expr))) } TokenType::TK_PLUS => { self.eat_assert(&[TokenType::TK_PLUS]); let expr = self.parse_expr(11)?; // PLUS precedence is 11 Ok(Box::new(Expr::Unary(UnaryOperator::Positive, expr))) } TokenType::TK_MINUS => { self.eat_assert(&[TokenType::TK_MINUS]); let expr = self.parse_expr(11)?; // MINUS precedence is 11 Ok(Box::new(Expr::Unary(UnaryOperator::Negative, expr))) } TokenType::TK_EXISTS => { self.eat_assert(&[TokenType::TK_EXISTS]); self.eat_expect(&[TokenType::TK_LP])?; let select = self.parse_select()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Box::new(Expr::Exists(select))) } TokenType::TK_CASE => { self.eat_assert(&[TokenType::TK_CASE]); let base = if self.peek_no_eof()?.token_type.unwrap() != TokenType::TK_WHEN { Some(self.parse_expr(0)?) } else { None }; self.eat_expect(&[TokenType::TK_WHEN])?; let first_when = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_THEN])?; let mut when_then_pairs = vec![(first_when, self.parse_expr(0)?)]; loop { if let Some(tok) = self.peek()? { if tok.token_type.unwrap() != TokenType::TK_WHEN { break; } } else { break; } self.eat_assert(&[TokenType::TK_WHEN]); let when = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_THEN])?; let then = self.parse_expr(0)?; when_then_pairs.push((when, then)); } let else_expr = if let Some(ok) = self.peek()? { if ok.token_type == Some(TokenType::TK_ELSE) { self.eat_assert(&[TokenType::TK_ELSE]); Some(self.parse_expr(0)?) } else { None } } else { None }; self.eat_expect(&[TokenType::TK_END])?; Ok(Box::new(Expr::Case { base, when_then_pairs, else_expr, })) } TokenType::TK_RAISE => { self.eat_assert(&[TokenType::TK_RAISE]); self.eat_expect(&[TokenType::TK_LP])?; let resolve = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_IGNORE => { self.eat_assert(&[TokenType::TK_IGNORE]); ResolveType::Ignore } _ => self.parse_raise_type()?, }; let expr = if resolve != ResolveType::Ignore { self.eat_expect(&[TokenType::TK_COMMA])?; Some(self.parse_expr(0)?) } else { None }; self.eat_expect(&[TokenType::TK_RP])?; Ok(Box::new(Expr::Raise(resolve, expr))) } _ => { let can_be_lit_str = tok.token_type == Some(TokenType::TK_STRING); let name = self.parse_nm()?; let second_name = if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_DOT) { self.eat_assert(&[TokenType::TK_DOT]); Some(self.parse_nm()?) } 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, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, ], }); } // can not be literal string in function name self.eat_assert(&[TokenType::TK_LP]); let tok = self.peek_no_eof()?; match tok.token_type.unwrap() { TokenType::TK_STAR => { self.eat_assert(&[TokenType::TK_STAR]); self.eat_expect(&[TokenType::TK_RP])?; return Ok(Box::new(Expr::FunctionCallStar { name: name, filter_over: self.parse_filter_over()?, })); } _ => { let distinct = self.parse_distinct()?; let exprs = self.parse_expr_list()?; self.eat_expect(&[TokenType::TK_RP])?; let order_by = self.parse_order_by()?; let filter_over = self.parse_filter_over()?; return Ok(Box::new(Expr::FunctionCall { name, distinctness: distinct, args: exprs, order_by, filter_over, })); } } } else { None } } else { None }; 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]); Some(self.parse_nm()?) } else { None } } else { None }; if second_name.is_some() && third_name.is_some() { Ok(Box::new(Expr::DoublyQualified( name, second_name.unwrap(), third_name.unwrap(), ))) } else if second_name.is_some() { Ok(Box::new(Expr::Qualified(name, second_name.unwrap()))) } else if can_be_lit_str { Ok(Box::new(Expr::Literal(match name { Name::Quoted(s) => Literal::String(s), Name::Ident(s) => Literal::String(s), }))) } else { Ok(Box::new(Expr::Id(name))) } } } } fn parse_expr_list(&mut self) -> Result>, Error> { let mut exprs = vec![]; loop { match self.peek()? { Some(tok) => match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_LP | TokenType::TK_CAST | TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW | TokenType::TK_NULL | TokenType::TK_BLOB | TokenType::TK_FLOAT | TokenType::TK_INTEGER | TokenType::TK_VARIABLE | TokenType::TK_CTIME_KW | TokenType::TK_NOT | TokenType::TK_BITNOT | TokenType::TK_PLUS | TokenType::TK_MINUS | TokenType::TK_EXISTS | TokenType::TK_CASE => {} _ => break, }, None => break, } exprs.push(self.parse_expr(0)?); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_COMMA => { self.eat_assert(&[TokenType::TK_COMMA]); } _ => break, } } Ok(exprs) } fn parse_expr(&mut self, precedence: u8) -> Result, Error> { let mut result = self.parse_expr_operand()?; loop { let pre = match self.current_token_precedence()? { Some(pre) if pre < precedence => break, None => break, // no more ops Some(pre) => pre, }; let mut tok = self.peek_no_eof()?; let mut not = false; if tok.token_type.unwrap() == TokenType::TK_NOT { self.eat_assert(&[TokenType::TK_NOT]); tok = self.peek_expect(&[ TokenType::TK_BETWEEN, TokenType::TK_IN, TokenType::TK_MATCH, TokenType::TK_LIKE_KW, ])?; not = true; } result = match tok.token_type.unwrap() { TokenType::TK_OR => { self.eat_assert(&[TokenType::TK_OR]); Box::new(Expr::Binary(result, Operator::Or, self.parse_expr(pre)?)) } TokenType::TK_AND => { self.eat_assert(&[TokenType::TK_AND]); Box::new(Expr::Binary(result, Operator::And, self.parse_expr(pre)?)) } TokenType::TK_EQ => { self.eat_assert(&[TokenType::TK_EQ]); Box::new(Expr::Binary( result, Operator::Equals, self.parse_expr(pre)?, )) } TokenType::TK_NE => { self.eat_assert(&[TokenType::TK_NE]); Box::new(Expr::Binary( result, Operator::NotEquals, self.parse_expr(pre)?, )) } TokenType::TK_BETWEEN => { self.eat_assert(&[TokenType::TK_BETWEEN]); let start = self.parse_expr(pre)?; self.eat_expect(&[TokenType::TK_AND])?; let end = self.parse_expr(pre)?; Box::new(Expr::Between { lhs: result, not, start, end, }) } TokenType::TK_IN => { self.eat_assert(&[TokenType::TK_IN]); let tok = self.peek_no_eof()?; match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let tok = self.peek_no_eof()?; match tok.token_type.unwrap() { TokenType::TK_SELECT | TokenType::TK_WITH | TokenType::TK_VALUES => { let select = self.parse_select()?; self.eat_expect(&[TokenType::TK_RP])?; Box::new(Expr::InSelect { lhs: result, not, rhs: select, }) } _ => { let exprs = self.parse_expr_list()?; self.eat_expect(&[TokenType::TK_RP])?; Box::new(Expr::InList { lhs: result, not, rhs: exprs, }) } } } _ => { let name = self.parse_fullname(false)?; let mut exprs = vec![]; if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_LP) { self.eat_assert(&[TokenType::TK_LP]); exprs = self.parse_expr_list()?; self.eat_expect(&[TokenType::TK_RP])?; } } Box::new(Expr::InTable { lhs: result, not, rhs: name, args: exprs, }) } } } TokenType::TK_MATCH | TokenType::TK_LIKE_KW => { let tok = self.eat_assert(&[TokenType::TK_MATCH, TokenType::TK_LIKE_KW]); let op = match tok.token_type.unwrap() { TokenType::TK_MATCH => LikeOperator::Match, TokenType::TK_LIKE_KW => { if b"LIKE".eq_ignore_ascii_case(tok.value) { LikeOperator::Like } else if b"GLOB".eq_ignore_ascii_case(tok.value) { LikeOperator::Glob } else if b"REGEXP".eq_ignore_ascii_case(tok.value) { LikeOperator::Regexp } else { unreachable!() } } _ => unreachable!(), }; let expr = self.parse_expr(pre)?; let escape = if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_ESCAPE) { self.eat_assert(&[TokenType::TK_ESCAPE]); Some(self.parse_expr(pre)?) } else { None } } else { None }; Box::new(Expr::Like { lhs: result, not, op, rhs: expr, escape, }) } TokenType::TK_ISNULL => { self.eat_assert(&[TokenType::TK_ISNULL]); Box::new(Expr::IsNull(result)) } TokenType::TK_NOTNULL => { self.eat_assert(&[TokenType::TK_NOTNULL]); Box::new(Expr::NotNull(result)) } TokenType::TK_LT => { self.eat_assert(&[TokenType::TK_LT]); Box::new(Expr::Binary(result, Operator::Less, self.parse_expr(pre)?)) } TokenType::TK_GT => { self.eat_assert(&[TokenType::TK_GT]); Box::new(Expr::Binary( result, Operator::Greater, self.parse_expr(pre)?, )) } TokenType::TK_LE => { self.eat_assert(&[TokenType::TK_LE]); Box::new(Expr::Binary( result, Operator::LessEquals, self.parse_expr(pre)?, )) } TokenType::TK_GE => { self.eat_assert(&[TokenType::TK_GE]); Box::new(Expr::Binary( result, Operator::GreaterEquals, self.parse_expr(pre)?, )) } TokenType::TK_ESCAPE => unreachable!(), TokenType::TK_BITAND => { self.eat_assert(&[TokenType::TK_BITAND]); Box::new(Expr::Binary( result, Operator::BitwiseAnd, self.parse_expr(pre)?, )) } TokenType::TK_BITOR => { self.eat_assert(&[TokenType::TK_BITOR]); Box::new(Expr::Binary( result, Operator::BitwiseOr, self.parse_expr(pre)?, )) } TokenType::TK_LSHIFT => { self.eat_assert(&[TokenType::TK_LSHIFT]); Box::new(Expr::Binary( result, Operator::LeftShift, self.parse_expr(pre)?, )) } TokenType::TK_RSHIFT => { self.eat_assert(&[TokenType::TK_RSHIFT]); Box::new(Expr::Binary( result, Operator::RightShift, self.parse_expr(pre)?, )) } TokenType::TK_PLUS => { self.eat_assert(&[TokenType::TK_PLUS]); Box::new(Expr::Binary(result, Operator::Add, self.parse_expr(pre)?)) } TokenType::TK_MINUS => { self.eat_assert(&[TokenType::TK_MINUS]); Box::new(Expr::Binary( result, Operator::Subtract, self.parse_expr(pre)?, )) } TokenType::TK_STAR => { self.eat_assert(&[TokenType::TK_STAR]); Box::new(Expr::Binary( result, Operator::Multiply, self.parse_expr(pre)?, )) } TokenType::TK_SLASH => { self.eat_assert(&[TokenType::TK_SLASH]); Box::new(Expr::Binary( result, Operator::Divide, self.parse_expr(pre)?, )) } TokenType::TK_REM => { self.eat_assert(&[TokenType::TK_REM]); Box::new(Expr::Binary( result, Operator::Modulus, self.parse_expr(pre)?, )) } TokenType::TK_CONCAT => { self.eat_assert(&[TokenType::TK_CONCAT]); Box::new(Expr::Binary( result, Operator::Concat, self.parse_expr(pre)?, )) } TokenType::TK_PTR => { let tok = self.eat_assert(&[TokenType::TK_PTR]); let op = if tok.value.len() == 2 { Operator::ArrowRight } else { Operator::ArrowRightShift }; Box::new(Expr::Binary(result, op, self.parse_expr(pre)?)) } TokenType::TK_COLLATE => { Box::new(Expr::Collate(result, self.parse_collate()?.unwrap())) } _ => unreachable!(), } } Ok(result) } fn parse_collate(&mut self) -> Result, Error> { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_COLLATE) { self.eat_assert(&[TokenType::TK_COLLATE]); } else { return Ok(None); } } else { return Ok(None); } let tok = self.eat_expect(&[TokenType::TK_ID, TokenType::TK_STRING])?; let first_char = tok.value[0]; // no need to check empty match first_char { b'[' | b'\'' | b'`' | b'"' => Ok(Some(Name::Quoted(from_bytes(tok.value)))), _ => Ok(Some(Name::Ident(from_bytes(tok.value)))), } } fn parse_sort_order(&mut self) -> Result, Error> { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_ASC) => { self.eat_assert(&[TokenType::TK_ASC]); Ok(Some(SortOrder::Asc)) } Some(tok) if tok.token_type == Some(TokenType::TK_DESC) => { self.eat_assert(&[TokenType::TK_DESC]); Ok(Some(SortOrder::Desc)) } _ => Ok(None), } } fn parse_eid(&mut self) -> Result { let nm = self.parse_nm()?; let collate = self.parse_collate()?; let sort_order = self.parse_sort_order()?; Ok(IndexedColumn { col_name: nm, collation_name: collate, order: sort_order, }) } fn parse_eid_list(&mut self) -> Result, Error> { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_LP) { self.eat_assert(&[TokenType::TK_LP]); } else { return Ok(vec![]); } } else { return Ok(vec![]); } let mut columns = vec![self.parse_eid()?]; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); columns.push(self.parse_eid()?); } _ => break, } } self.eat_expect(&[TokenType::TK_RP])?; Ok(columns) } fn parse_common_table_expr(&mut self) -> Result { let nm = self.parse_nm()?; let eid_list = self.parse_eid_list()?; self.eat_expect(&[TokenType::TK_AS])?; let wqas = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_MATERIALIZED => { self.eat_assert(&[TokenType::TK_MATERIALIZED]); Materialized::Yes } TokenType::TK_NOT => { self.eat_assert(&[TokenType::TK_NOT]); self.eat_expect(&[TokenType::TK_MATERIALIZED])?; Materialized::No } _ => Materialized::Any, }; self.eat_expect(&[TokenType::TK_LP])?; let select = self.parse_select()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(CommonTableExpr { tbl_name: nm, columns: eid_list, materialized: wqas, select, }) } fn parse_with(&mut self) -> Result, Error> { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_WITH) { self.eat_assert(&[TokenType::TK_WITH]); } else { return Ok(None); } } else { return Ok(None); } let recursive = if self.peek_no_eof()?.token_type == Some(TokenType::TK_RECURSIVE) { self.eat_assert(&[TokenType::TK_RECURSIVE]); true } else { false }; let mut ctes = vec![self.parse_common_table_expr()?]; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); ctes.push(self.parse_common_table_expr()?); } _ => break, } } Ok(Some(With { recursive, ctes })) } fn parse_as(&mut self) -> Result, Error> { match self.peek()? { None => Ok(None), Some(tok) => match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_AS => { self.eat_assert(&[TokenType::TK_AS]); Ok(Some(As::As(self.parse_nm()?))) } TokenType::TK_STRING | TokenType::TK_ID => Ok(Some(As::Elided(self.parse_nm()?))), _ => Ok(None), }, } } fn parse_window_defn(&mut self) -> Result { let name = self.parse_nm()?; self.eat_expect(&[TokenType::TK_AS])?; self.eat_expect(&[TokenType::TK_LP])?; let window = self.parse_window()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(WindowDef { name, window }) } fn parse_window_clause(&mut self) -> Result, Error> { match self.peek()? { None => return Ok(vec![]), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_WINDOW => { self.eat_assert(&[TokenType::TK_WINDOW]); } _ => return Ok(vec![]), }, } let mut result = vec![self.parse_window_defn()?]; loop { match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_COMMA => { self.eat_assert(&[TokenType::TK_COMMA]); result.push(self.parse_window_defn()?); } _ => break, }, _ => break, } } Ok(result) } fn parse_group_by(&mut self) -> Result, Error> { match self.peek()? { None => return Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_GROUP => { self.eat_assert(&[TokenType::TK_GROUP]); self.eat_expect(&[TokenType::TK_BY])?; } _ => return Ok(None), }, } let exprs = self.parse_nexpr_list()?; let having = match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_HAVING) => { self.eat_assert(&[TokenType::TK_HAVING]); Some(self.parse_expr(0)?) } _ => None, }; Ok(Some(GroupBy { exprs, having })) } fn parse_where(&mut self) -> Result>, Error> { match self.peek()? { None => Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_WHERE => { self.eat_assert(&[TokenType::TK_WHERE]); let expr = self.parse_expr(0)?; Ok(Some(expr)) } _ => Ok(None), }, } } fn parse_indexed(&mut self) -> Result, Error> { match self.peek()? { None => Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_INDEXED => { self.eat_assert(&[TokenType::TK_INDEXED]); self.eat_expect(&[TokenType::TK_BY])?; Ok(Some(Indexed::IndexedBy(self.parse_nm()?))) } TokenType::TK_NOT => { self.eat_assert(&[TokenType::TK_NOT]); self.eat_expect(&[TokenType::TK_INDEXED])?; Ok(Some(Indexed::NotIndexed)) } _ => Ok(None), }, } } fn parse_nm_list(&mut self) -> Result, Error> { let mut names = vec![self.parse_nm()?]; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); names.push(self.parse_nm()?); } _ => break, } } Ok(names) } fn parse_nm_list_opt(&mut self) -> Result, Error> { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_LP) => { self.eat_assert(&[TokenType::TK_LP]); } _ => return Ok(vec![]), } let result = self.parse_nm_list()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(result) } fn parse_on_using(&mut self) -> Result, Error> { match self.peek()? { None => Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_ON => { self.eat_assert(&[TokenType::TK_ON]); let expr = self.parse_expr(0)?; Ok(Some(JoinConstraint::On(expr))) } TokenType::TK_USING => { self.eat_assert(&[TokenType::TK_USING]); self.eat_expect(&[TokenType::TK_LP])?; let names = self.parse_nm_list()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Some(JoinConstraint::Using(names))) } _ => Ok(None), }, } } fn parse_joined_tables(&mut self) -> Result, Error> { let mut result = vec![]; loop { let op = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_COMMA => { self.eat_assert(&[TokenType::TK_COMMA]); JoinOperator::Comma } TokenType::TK_JOIN => { self.eat_assert(&[TokenType::TK_JOIN]); JoinOperator::TypedJoin(None) } TokenType::TK_JOIN_KW => { let jkw = self.eat_assert(&[TokenType::TK_JOIN_KW]); let tok = self.eat_expect(&[ TokenType::TK_JOIN, TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, ])?; match tok.token_type.unwrap() { TokenType::TK_JOIN => { JoinOperator::TypedJoin(Some(new_join_type(jkw.value, None, None)?)) } _ => { let name_1 = tok.value; let tok = self.eat_expect(&[ TokenType::TK_JOIN, TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, ])?; match tok.token_type.unwrap() { TokenType::TK_JOIN => JoinOperator::TypedJoin(Some( new_join_type(jkw.value, Some(name_1), None)?, )), _ => { let name_2 = tok.value; self.eat_expect(&[TokenType::TK_JOIN])?; JoinOperator::TypedJoin(Some(new_join_type( jkw.value, Some(name_1), Some(name_2), )?)) } } } } } _ => break, }, None => break, }; let tok = self.peek_expect(&[ TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, TokenType::TK_LP, ])?; match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => { let name = self.parse_fullname(false)?; match self.peek()? { None => { result.push(JoinedSelectTable { operator: op, table: Box::new(SelectTable::Table(name, None, None)), constraint: None, }); } Some(tok) => match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let exprs = self.parse_expr_list()?; self.eat_assert(&[TokenType::TK_RP]); let alias = self.parse_as()?; let on_using = self.parse_on_using()?; result.push(JoinedSelectTable { operator: op, table: Box::new(SelectTable::TableCall(name, exprs, alias)), constraint: on_using, }); } _ => { let alias = self.parse_as()?; let indexed = self.parse_indexed()?; let on_using = self.parse_on_using()?; result.push(JoinedSelectTable { operator: op, table: Box::new(SelectTable::Table(name, alias, indexed)), constraint: on_using, }); } }, } } TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_SELECT | TokenType::TK_WITH | TokenType::TK_VALUES => { let select = self.parse_select()?; self.eat_expect(&[TokenType::TK_RP])?; let alias = self.parse_as()?; let on_using = self.parse_on_using()?; result.push(JoinedSelectTable { operator: op, table: Box::new(SelectTable::Select(select, alias)), constraint: on_using, }); } _ => { let fr = self.parse_from_clause()?; self.eat_expect(&[TokenType::TK_RP])?; let alias = self.parse_as()?; let on_using = self.parse_on_using()?; result.push(JoinedSelectTable { operator: op, table: Box::new(SelectTable::Sub(fr, alias)), constraint: on_using, }); } } } _ => unreachable!(), } } Ok(result) } fn parse_from_clause(&mut self) -> Result { let tok = self.peek_expect(&[ TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_INDEXED, TokenType::TK_JOIN_KW, TokenType::TK_LP, ])?; match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => { let name = self.parse_fullname(false)?; match self.peek()? { None => Ok(FromClause { select: Box::new(SelectTable::Table(name, None, None)), joins: vec![], }), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let exprs = self.parse_expr_list()?; self.eat_assert(&[TokenType::TK_RP]); let alias = self.parse_as()?; Ok(FromClause { select: Box::new(SelectTable::TableCall(name, exprs, alias)), joins: self.parse_joined_tables()?, }) } _ => { let alias = self.parse_as()?; let indexed = self.parse_indexed()?; Ok(FromClause { select: Box::new(SelectTable::Table(name, alias, indexed)), joins: self.parse_joined_tables()?, }) } }, } } TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_SELECT | TokenType::TK_WITH | TokenType::TK_VALUES => { let select = self.parse_select()?; self.eat_expect(&[TokenType::TK_RP])?; let alias = self.parse_as()?; Ok(FromClause { select: Box::new(SelectTable::Select(select, alias)), joins: self.parse_joined_tables()?, }) } _ => { let fr = self.parse_from_clause()?; self.eat_expect(&[TokenType::TK_RP])?; let alias = self.parse_as()?; Ok(FromClause { select: Box::new(SelectTable::Sub(fr, alias)), joins: self.parse_joined_tables()?, }) } } } _ => unreachable!(), } } fn parse_from_clause_opt(&mut self) -> Result, Error> { match self.peek()? { None => return Ok(None), Some(tok) if tok.token_type == Some(TokenType::TK_FROM) => { self.eat_assert(&[TokenType::TK_FROM]); } _ => return Ok(None), } Ok(Some(self.parse_from_clause()?)) } fn parse_select_column(&mut self) -> Result { match self.peek_no_eof()?.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_STAR => { self.eat_assert(&[TokenType::TK_STAR]); Ok(ResultColumn::Star) } tt => { // dot STAR case if tt == TokenType::TK_ID || tt == TokenType::TK_STRING || tt == TokenType::TK_INDEXED || tt == TokenType::TK_JOIN_KW { if let Ok(res) = self.mark(|p| -> Result { let name = p.parse_nm()?; p.eat_expect(&[TokenType::TK_DOT])?; p.eat_expect(&[TokenType::TK_STAR])?; Ok(ResultColumn::TableStar(name)) }) { return Ok(res); } } let expr = self.parse_expr(0)?; let alias = self.parse_as()?; Ok(ResultColumn::Expr(expr, alias)) } } } fn parse_select_columns(&mut self) -> Result, Error> { let mut result = vec![self.parse_select_column()?]; loop { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_COMMA) { self.eat_assert(&[TokenType::TK_COMMA]); } else { break; } } else { break; } result.push(self.parse_select_column()?); } Ok(result) } fn parse_nexpr_list(&mut self) -> Result>, Error> { let mut result = vec![self.parse_expr(0)?]; loop { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_COMMA) { self.eat_assert(&[TokenType::TK_COMMA]); } else { break; } } else { break; } result.push(self.parse_expr(0)?); } Ok(result) } fn parse_one_select(&mut self) -> Result { let tok = self.eat_expect(&[TokenType::TK_SELECT, TokenType::TK_VALUES])?; match tok.token_type.unwrap() { TokenType::TK_SELECT => { let distinct = self.parse_distinct()?; let collist = self.parse_select_columns()?; let from = self.parse_from_clause_opt()?; let where_clause = self.parse_where()?; let group_by = self.parse_group_by()?; let window_clause = self.parse_window_clause()?; Ok(OneSelect::Select { distinctness: distinct, columns: collist, from, where_clause, group_by, window_clause, }) } TokenType::TK_VALUES => { self.eat_expect(&[TokenType::TK_LP])?; let mut values = vec![self.parse_nexpr_list()?]; self.eat_expect(&[TokenType::TK_RP])?; loop { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_COMMA) { self.eat_assert(&[TokenType::TK_COMMA]); } else { break; } } else { break; } self.eat_expect(&[TokenType::TK_LP])?; values.push(self.parse_nexpr_list()?); self.eat_expect(&[TokenType::TK_RP])?; } Ok(OneSelect::Values(values)) } _ => unreachable!(), } } fn parse_select_body(&mut self) -> Result { let select = self.parse_one_select()?; let mut compounds = vec![]; loop { let op = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_UNION => { self.eat_assert(&[TokenType::TK_UNION]); if self.peek_no_eof()?.token_type == Some(TokenType::TK_ALL) { self.eat_assert(&[TokenType::TK_ALL]); CompoundOperator::UnionAll } else { CompoundOperator::Union } } TokenType::TK_EXCEPT => { self.eat_assert(&[TokenType::TK_EXCEPT]); CompoundOperator::Except } TokenType::TK_INTERSECT => { self.eat_assert(&[TokenType::TK_INTERSECT]); CompoundOperator::Intersect } _ => break, }, None => break, }; compounds.push(CompoundSelect { operator: op, select: self.parse_one_select()?, }); } Ok(SelectBody { select, compounds }) } fn parse_sorted_column(&mut self) -> Result { let expr = self.parse_expr(0)?; let sort_order = self.parse_sort_order()?; let nulls = match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_NULLS) => { self.eat_assert(&[TokenType::TK_NULLS]); let tok = self.eat_expect(&[TokenType::TK_FIRST, TokenType::TK_LAST])?; match tok.token_type.unwrap() { TokenType::TK_FIRST => Some(NullsOrder::First), TokenType::TK_LAST => Some(NullsOrder::Last), _ => unreachable!(), } } _ => None, }; Ok(SortedColumn { expr, order: sort_order, nulls, }) } fn parse_sort_list(&mut self) -> Result, Error> { let mut columns = vec![self.parse_sorted_column()?]; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); columns.push(self.parse_sorted_column()?); } _ => break, } } Ok(columns) } fn parse_order_by(&mut self) -> Result, Error> { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_ORDER) { self.eat_assert(&[TokenType::TK_ORDER]); } else { return Ok(vec![]); } } else { return Ok(vec![]); } self.eat_expect(&[TokenType::TK_BY])?; Ok(self.parse_sort_list()?) } fn parse_limit(&mut self) -> Result, Error> { if let Some(tok) = self.peek()? { if tok.token_type == Some(TokenType::TK_LIMIT) { self.eat_assert(&[TokenType::TK_LIMIT]); } else { return Ok(None); } } else { return Ok(None); } let limit = self.parse_expr(0)?; let offset = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_OFFSET | TokenType::TK_COMMA => { self.eat_assert(&[TokenType::TK_OFFSET, TokenType::TK_COMMA]); Some(self.parse_expr(0)?) } _ => None, }, _ => None, }; Ok(Some(Limit { expr: limit, offset, })) } fn parse_select_without_cte(&mut self, with: Option) -> Result { let body = self.parse_select_body()?; let order_by = self.parse_order_by()?; let limit = self.parse_limit()?; Ok(Select { with, body, order_by, limit, }) } fn parse_select(&mut self) -> Result { let with = self.parse_with()?; self.parse_select_without_cte(with) } fn parse_primary_table_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_PRIMARY]); self.eat_expect(&[TokenType::TK_KEY])?; self.eat_expect(&[TokenType::TK_LP])?; let columns = self.parse_sort_list()?; let auto_increment = self.parse_auto_increment()?; self.eat_expect(&[TokenType::TK_RP])?; let conflict_clause = self.parse_on_conflict()?; Ok(TableConstraint::PrimaryKey { columns, auto_increment, conflict_clause, }) } fn parse_unique_table_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_UNIQUE]); self.eat_expect(&[TokenType::TK_LP])?; let columns = self.parse_sort_list()?; self.eat_expect(&[TokenType::TK_RP])?; let conflict_clause = self.parse_on_conflict()?; Ok(TableConstraint::Unique { columns, conflict_clause, }) } fn parse_check_table_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_CHECK]); self.eat_expect(&[TokenType::TK_LP])?; let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_RP])?; Ok(TableConstraint::Check(expr)) } fn parse_foreign_key_table_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_FOREIGN]); self.eat_expect(&[TokenType::TK_KEY])?; self.peek_expect(&[TokenType::TK_LP])?; // make sure we have columns let columns = self.parse_eid_list()?; self.peek_expect(&[TokenType::TK_REFERENCES])?; let clause = self.parse_foreign_key_clause()?; let deref_clause = self.parse_defer_subclause()?; Ok(TableConstraint::ForeignKey { columns, clause, deref_clause, }) } fn parse_named_table_constraints(&mut self) -> Result, Error> { let mut result = vec![]; loop { match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_COMMA => { self.eat_assert(&[TokenType::TK_COMMA]); } TokenType::TK_CONSTRAINT | TokenType::TK_PRIMARY | TokenType::TK_UNIQUE | TokenType::TK_CHECK | TokenType::TK_FOREIGN => {} _ => break, }, _ => break, } let name = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_CONSTRAINT => { self.eat_assert(&[TokenType::TK_CONSTRAINT]); Some(self.parse_nm()?) } _ => None, }; let tok = self.peek_expect(&[ TokenType::TK_PRIMARY, TokenType::TK_UNIQUE, TokenType::TK_CHECK, TokenType::TK_FOREIGN, ])?; match tok.token_type.unwrap() { TokenType::TK_PRIMARY => { result.push(NamedTableConstraint { name, constraint: self.parse_primary_table_constraint()?, }); } TokenType::TK_UNIQUE => { result.push(NamedTableConstraint { name, constraint: self.parse_unique_table_constraint()?, }); } TokenType::TK_CHECK => { result.push(NamedTableConstraint { name, constraint: self.parse_check_table_constraint()?, }); } TokenType::TK_FOREIGN => { result.push(NamedTableConstraint { name, constraint: self.parse_foreign_key_table_constraint()?, }); } _ => unreachable!(), } } Ok(result) } fn parse_table_option(&mut self) -> Result { match self.peek()? { Some(tok) => match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_WITHOUT => { self.eat_assert(&[TokenType::TK_WITHOUT]); let tok = self.eat_expect(&[TokenType::TK_ID])?; if b"ROWID".eq_ignore_ascii_case(tok.value) { Ok(TableOptions::WITHOUT_ROWID) } else { Err(Error::Custom(format!( "unknown table option: {}", from_bytes(tok.value) ))) } } TokenType::TK_ID => { let tok = self.eat_assert(&[TokenType::TK_ID]); if b"STRICT".eq_ignore_ascii_case(tok.value) { Ok(TableOptions::STRICT) } else { Err(Error::Custom(format!( "unknown table option: {}", from_bytes(tok.value) ))) } } _ => Ok(TableOptions::NONE), }, _ => Ok(TableOptions::NONE), } } fn parse_table_options(&mut self) -> Result { let mut result = self.parse_table_option()?; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); result = result | self.parse_table_option()?; } _ => break, } } Ok(result) } fn parse_create_table_args(&mut self) -> Result { let tok = self.eat_expect(&[TokenType::TK_LP, TokenType::TK_AS])?; match tok.token_type.unwrap() { TokenType::TK_AS => Ok(CreateTableBody::AsSelect(self.parse_select()?)), TokenType::TK_LP => { let mut columns = vec![self.parse_column_definition()?]; let mut constraints = vec![]; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_CONSTRAINT | TokenType::TK_PRIMARY | TokenType::TK_UNIQUE | TokenType::TK_CHECK | TokenType::TK_FOREIGN => { constraints = self.parse_named_table_constraints()?; break; } _ => { columns.push(self.parse_column_definition()?); } } } _ => break, } } self.eat_expect(&[TokenType::TK_RP])?; let options = self.parse_table_options()?; Ok(CreateTableBody::ColumnsAndConstraints { columns, constraints, options, }) } _ => unreachable!(), } } fn parse_create_table(&mut self, temporary: bool) -> Result { self.eat_assert(&[TokenType::TK_TABLE]); let if_not_exists = self.parse_if_not_exists()?; let tbl_name = self.parse_fullname(false)?; let body = self.parse_create_table_args()?; Ok(Stmt::CreateTable { temporary, if_not_exists, tbl_name, body, }) } fn parse_analyze(&mut self) -> Result { self.eat_assert(&[TokenType::TK_ANALYZE]); let name = match self.peek()? { Some(tok) => match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => Some(self.parse_fullname(false)?), _ => None, }, _ => None, }; Ok(Stmt::Analyze { name: name }) } fn parse_attach(&mut self) -> Result { self.eat_assert(&[TokenType::TK_ATTACH]); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_DATABASE => { self.eat_assert(&[TokenType::TK_DATABASE]); } _ => {} } let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_AS])?; let db_name = self.parse_expr(0)?; let key = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_KEY => { self.eat_assert(&[TokenType::TK_KEY]); Some(self.parse_expr(0)?) } _ => None, }, _ => None, }; Ok(Stmt::Attach { expr, db_name, key }) } fn parse_detach(&mut self) -> Result { self.eat_assert(&[TokenType::TK_DETACH]); match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_DATABASE => { self.eat_assert(&[TokenType::TK_DATABASE]); } _ => {} } Ok(Stmt::Detach { name: self.parse_expr(0)?, }) } fn parse_pragma_value(&mut self) -> Result { match self.peek_no_eof()?.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ON | TokenType::TK_DELETE | TokenType::TK_DEFAULT => { let tok = self.eat_assert(&[ TokenType::TK_ON, TokenType::TK_DELETE, TokenType::TK_DEFAULT, ]); Ok(Box::new(Expr::Literal(Literal::Keyword(from_bytes( tok.value, ))))) } TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => Ok(Box::new(Expr::Name(self.parse_nm()?))), _ => self.parse_signed(), } } fn parse_pragma(&mut self) -> Result { self.eat_assert(&[TokenType::TK_PRAGMA]); let name = self.parse_fullname(false)?; match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_EQ => { self.eat_assert(&[TokenType::TK_EQ]); Ok(Stmt::Pragma { name: name, body: Some(PragmaBody::Equals(self.parse_pragma_value()?)), }) } TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let value = self.parse_pragma_value()?; self.eat_expect(&[TokenType::TK_RP])?; Ok(Stmt::Pragma { name: name, body: Some(PragmaBody::Call(value)), }) } _ => Ok(Stmt::Pragma { name: name, body: None, }), }, _ => Ok(Stmt::Pragma { name: name, body: None, }), } } fn parse_vacuum(&mut self) -> Result { self.eat_assert(&[TokenType::TK_VACUUM]); let name = match self.peek()? { Some(tok) => match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => Some(self.parse_nm()?), _ => None, }, _ => None, }; let into = match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_INTO) => { self.eat_assert(&[TokenType::TK_INTO]); Some(self.parse_expr(0)?) } _ => None, }; Ok(Stmt::Vacuum { name, into }) } fn parse_term(&mut self) -> Result, Error> { self.peek_expect(&[ TokenType::TK_NULL, TokenType::TK_BLOB, TokenType::TK_STRING, TokenType::TK_FLOAT, TokenType::TK_INTEGER, TokenType::TK_CTIME_KW, ])?; self.parse_expr_operand() } fn parse_default_column_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_DEFAULT]); match self.peek_no_eof()?.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_RP])?; Ok(ColumnConstraint::Default(Box::new(Expr::Parenthesized( vec![expr], )))) } TokenType::TK_PLUS => { self.eat_assert(&[TokenType::TK_PLUS]); Ok(ColumnConstraint::Default(Box::new(Expr::Unary( UnaryOperator::Positive, self.parse_term()?, )))) } TokenType::TK_MINUS => { self.eat_assert(&[TokenType::TK_MINUS]); Ok(ColumnConstraint::Default(Box::new(Expr::Unary( UnaryOperator::Negative, self.parse_term()?, )))) } TokenType::TK_ID | TokenType::TK_INDEXED => Ok(ColumnConstraint::Default(Box::new( Expr::Id(self.parse_nm()?), ))), _ => Ok(ColumnConstraint::Default(self.parse_term()?)), } } fn parse_resolve_type(&mut self) -> Result { match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_IGNORE => { self.eat_assert(&[TokenType::TK_IGNORE]); Ok(ResolveType::Ignore) } TokenType::TK_REPLACE => { self.eat_assert(&[TokenType::TK_REPLACE]); Ok(ResolveType::Replace) } _ => Ok(self.parse_raise_type()?), } } fn parse_on_conflict(&mut self) -> Result, Error> { match self.peek()? { None => return Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_ON => { self.eat_assert(&[TokenType::TK_ON]); self.eat_expect(&[TokenType::TK_CONFLICT])?; } _ => return Ok(None), }, } Ok(Some(self.parse_resolve_type()?)) } fn parse_or_conflict(&mut self) -> Result, Error> { match self.peek()? { None => return Ok(None), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_OR => { self.eat_assert(&[TokenType::TK_OR]); } _ => return Ok(None), }, } Ok(Some(self.parse_resolve_type()?)) } fn parse_auto_increment(&mut self) -> Result { match self.peek()? { None => Ok(false), Some(tok) => match tok.token_type.unwrap() { TokenType::TK_AUTOINCR => { self.eat_assert(&[TokenType::TK_AUTOINCR]); Ok(true) } _ => Ok(false), }, } } fn parse_not_null_column_constraint(&mut self) -> Result { let has_not = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_NOT => { self.eat_assert(&[TokenType::TK_NOT]); true } _ => false, }; self.eat_expect(&[TokenType::TK_NULL])?; Ok(ColumnConstraint::NotNull { nullable: !has_not, conflict_clause: self.parse_on_conflict()?, }) } fn parse_primary_column_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_PRIMARY]); self.eat_expect(&[TokenType::TK_KEY])?; let sort_order = self.parse_sort_order()?; let conflict_clause = self.parse_on_conflict()?; let autoincr = self.parse_auto_increment()?; Ok(ColumnConstraint::PrimaryKey { order: sort_order, conflict_clause, auto_increment: autoincr, }) } fn parse_unique_column_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_UNIQUE]); Ok(ColumnConstraint::Unique(self.parse_on_conflict()?)) } fn parse_check_column_constraint(&mut self) -> Result { self.eat_assert(&[TokenType::TK_CHECK]); self.eat_expect(&[TokenType::TK_LP])?; let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_RP])?; Ok(ColumnConstraint::Check(expr)) } fn parse_ref_act(&mut self) -> Result { let tok = self.eat_expect(&[ TokenType::TK_SET, TokenType::TK_CASCADE, TokenType::TK_RESTRICT, TokenType::TK_NO, ])?; match tok.token_type.unwrap() { TokenType::TK_SET => { let tok = self.eat_expect(&[TokenType::TK_NULL, TokenType::TK_DEFAULT])?; match tok.token_type.unwrap() { TokenType::TK_NULL => Ok(RefAct::SetNull), TokenType::TK_DEFAULT => Ok(RefAct::SetDefault), _ => unreachable!(), } } TokenType::TK_CASCADE => Ok(RefAct::Cascade), TokenType::TK_RESTRICT => Ok(RefAct::Restrict), TokenType::TK_NO => { self.eat_expect(&[TokenType::TK_ACTION])?; Ok(RefAct::NoAction) } _ => unreachable!(), } } fn parse_ref_args(&mut self) -> Result, Error> { let mut result = vec![]; loop { match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_MATCH => { self.eat_assert(&[TokenType::TK_MATCH]); result.push(RefArg::Match(self.parse_nm()?)); } TokenType::TK_ON => { self.eat_assert(&[TokenType::TK_ON]); let tok = self.eat_expect(&[ TokenType::TK_INSERT, TokenType::TK_DELETE, TokenType::TK_UPDATE, ])?; match tok.token_type.unwrap() { TokenType::TK_INSERT => { result.push(RefArg::OnInsert(self.parse_ref_act()?)) } TokenType::TK_DELETE => { result.push(RefArg::OnDelete(self.parse_ref_act()?)) } TokenType::TK_UPDATE => { result.push(RefArg::OnUpdate(self.parse_ref_act()?)) } _ => unreachable!(), } } _ => break, }, _ => break, } } Ok(result) } fn parse_foreign_key_clause(&mut self) -> Result { self.eat_assert(&[TokenType::TK_REFERENCES]); let name = self.parse_nm()?; let eid_list = self.parse_eid_list()?; let ref_args = self.parse_ref_args()?; Ok(ForeignKeyClause { tbl_name: name, columns: eid_list, args: ref_args, }) } fn parse_defer_subclause(&mut self) -> Result, Error> { let has_not = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_DEFERRABLE => false, TokenType::TK_NOT => { self.eat_assert(&[TokenType::TK_NOT]); true } _ => return Ok(None), }, _ => return Ok(None), }; self.eat_expect(&[TokenType::TK_DEFERRABLE])?; let init = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_INITIALLY => { self.eat_assert(&[TokenType::TK_INITIALLY]); let tok = self.eat_expect(&[TokenType::TK_DEFERRED, TokenType::TK_IMMEDIATE])?; match tok.token_type.unwrap() { TokenType::TK_DEFERRED => Some(InitDeferredPred::InitiallyDeferred), TokenType::TK_IMMEDIATE => Some(InitDeferredPred::InitiallyImmediate), _ => unreachable!(), } } _ => None, }, _ => None, }; Ok(Some(DeferSubclause { deferrable: !has_not, init_deferred: init, })) } fn parse_reference_column_constraint(&mut self) -> Result { let clause = self.parse_foreign_key_clause()?; let deref_clause = self.parse_defer_subclause()?; Ok(ColumnConstraint::ForeignKey { clause, deref_clause, }) } fn parse_generated_column_constraint(&mut self) -> Result { let tok = self.eat_assert(&[TokenType::TK_GENERATED, TokenType::TK_AS]); match tok.token_type.unwrap() { TokenType::TK_GENERATED => { self.eat_expect(&[TokenType::TK_ALWAYS])?; self.eat_expect(&[TokenType::TK_AS])?; } TokenType::TK_AS => {} _ => unreachable!(), } self.eat_expect(&[TokenType::TK_LP])?; let expr = self.parse_expr(0)?; self.eat_expect(&[TokenType::TK_RP])?; let typ = match self.peek()? { Some(tok) => match tok.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_ID => { let tok = self.eat_assert(&[TokenType::TK_ID]); Some(Name::Ident(from_bytes(tok.value))) } _ => None, }, _ => None, }; Ok(ColumnConstraint::Generated { expr, typ }) } fn parse_named_column_constraints(&mut self) -> Result, Error> { let mut result = vec![]; loop { let name = match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_CONSTRAINT => { self.eat_assert(&[TokenType::TK_CONSTRAINT]); Some(self.parse_nm()?) } _ => None, }, _ => None, }; if name.is_some() { self.peek_expect(&[ TokenType::TK_DEFAULT, TokenType::TK_NOT, TokenType::TK_NULL, TokenType::TK_PRIMARY, TokenType::TK_UNIQUE, TokenType::TK_CHECK, TokenType::TK_REFERENCES, TokenType::TK_COLLATE, TokenType::TK_GENERATED, TokenType::TK_AS, ])?; } match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_DEFAULT => { result.push(NamedColumnConstraint { name, constraint: self.parse_default_column_constraint()?, }); } TokenType::TK_NOT | TokenType::TK_NULL => { result.push(NamedColumnConstraint { name, constraint: self.parse_not_null_column_constraint()?, }); } TokenType::TK_PRIMARY => { result.push(NamedColumnConstraint { name, constraint: self.parse_primary_column_constraint()?, }); } TokenType::TK_UNIQUE => { result.push(NamedColumnConstraint { name, constraint: self.parse_unique_column_constraint()?, }); } TokenType::TK_CHECK => { result.push(NamedColumnConstraint { name, constraint: self.parse_check_column_constraint()?, }); } TokenType::TK_REFERENCES => { result.push(NamedColumnConstraint { name, constraint: self.parse_reference_column_constraint()?, }); } TokenType::TK_COLLATE => { result.push(NamedColumnConstraint { name, constraint: ColumnConstraint::Collate { collation_name: self.parse_collate()?.unwrap(), }, }); } TokenType::TK_GENERATED | TokenType::TK_AS => { result.push(NamedColumnConstraint { name, constraint: self.parse_generated_column_constraint()?, }); } _ => break, }, _ => break, } } Ok(result) } fn parse_column_definition(&mut self) -> Result { let col_name = self.parse_nm()?; let col_type = self.parse_type()?; let constraints = self.parse_named_column_constraints()?; Ok(ColumnDefinition { col_name, col_type, constraints, }) } fn parse_alter(&mut self) -> Result { self.eat_assert(&[TokenType::TK_ALTER]); self.eat_expect(&[TokenType::TK_TABLE])?; let tbl_name = self.parse_fullname(false)?; let tok = self.eat_expect(&[TokenType::TK_ADD, TokenType::TK_DROP, TokenType::TK_RENAME])?; match tok.token_type.unwrap() { TokenType::TK_ADD => { match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_COLUMNKW => { self.eat_assert(&[TokenType::TK_COLUMNKW]); } _ => {} } Ok(Stmt::AlterTable { name: tbl_name, body: AlterTableBody::AddColumn(self.parse_column_definition()?), }) } TokenType::TK_DROP => { match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_COLUMNKW => { self.eat_assert(&[TokenType::TK_COLUMNKW]); } _ => {} } Ok(Stmt::AlterTable { name: tbl_name, body: AlterTableBody::DropColumn(self.parse_nm()?), }) } TokenType::TK_RENAME => { let col_name = match self.peek_no_eof()?.token_type.unwrap().fallback_id_if_ok() { TokenType::TK_COLUMNKW => { self.eat_assert(&[TokenType::TK_COLUMNKW]); Some(self.parse_nm()?) } TokenType::TK_ID | TokenType::TK_STRING | TokenType::TK_INDEXED | TokenType::TK_JOIN_KW => Some(self.parse_nm()?), _ => None, }; self.eat_expect(&[TokenType::TK_TO])?; let to_name = self.parse_nm()?; if col_name.is_some() { Ok(Stmt::AlterTable { name: tbl_name, body: AlterTableBody::RenameColumn { old: col_name.unwrap(), new: to_name, }, }) } else { Ok(Stmt::AlterTable { name: tbl_name, body: AlterTableBody::RenameTo(to_name), }) } } _ => unreachable!(), } } fn parse_create_index(&mut self) -> Result { let tok = self.eat_assert(&[TokenType::TK_INDEX, TokenType::TK_UNIQUE]); let has_unique = tok.token_type == Some(TokenType::TK_UNIQUE); if has_unique { self.eat_expect(&[TokenType::TK_INDEX])?; } let if_not_exists = self.parse_if_not_exists()?; let idx_name = self.parse_fullname(false)?; self.eat_expect(&[TokenType::TK_ON])?; let tbl_name = self.parse_nm()?; self.eat_expect(&[TokenType::TK_LP])?; let columns = self.parse_sort_list()?; self.eat_expect(&[TokenType::TK_RP])?; let where_clause = self.parse_where()?; Ok(Stmt::CreateIndex { if_not_exists, idx_name, tbl_name, columns, where_clause, unique: has_unique, }) } fn parse_set(&mut self) -> Result { let tok = self.peek_expect(&[ TokenType::TK_LP, TokenType::TK_ID, TokenType::TK_STRING, TokenType::TK_JOIN_KW, TokenType::TK_INDEXED, ])?; match tok.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let names = self.parse_nm_list()?; self.eat_expect(&[TokenType::TK_RP])?; self.eat_expect(&[TokenType::TK_EQ])?; Ok(Set { col_names: names, expr: self.parse_expr(0)?, }) } _ => { let name = self.parse_nm()?; self.eat_expect(&[TokenType::TK_EQ])?; Ok(Set { col_names: vec![name], expr: self.parse_expr(0)?, }) } } } fn parse_set_list(&mut self) -> Result, Error> { let mut results = vec![self.parse_set()?]; loop { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_COMMA) => { self.eat_assert(&[TokenType::TK_COMMA]); results.push(self.parse_set()?); } _ => break, } } Ok(results) } fn parse_returning(&mut self) -> Result, Error> { match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_RETURNING) => { self.eat_assert(&[TokenType::TK_RETURNING]); } _ => return Ok(vec![]), } self.parse_select_columns() } fn parse_upsert(&mut self) -> Result<(Option>, Vec), Error> { match self.peek()? { Some(tok) => match tok.token_type.unwrap() { TokenType::TK_ON => { self.eat_assert(&[TokenType::TK_ON]); } TokenType::TK_RETURNING => { return Ok((None, self.parse_returning()?)); } _ => return Ok((None, vec![])), }, _ => return Ok((None, vec![])), } self.eat_expect(&[TokenType::TK_CONFLICT])?; let targets = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_LP => { self.eat_assert(&[TokenType::TK_LP]); let result = self.parse_sort_list()?; self.eat_expect(&[TokenType::TK_RP])?; result } _ => vec![], }; let where_clause = if !targets.is_empty() { self.parse_where()? } else { None }; self.eat_expect(&[TokenType::TK_DO])?; let tok = self.eat_expect(&[TokenType::TK_NOTHING, TokenType::TK_UPDATE])?; let do_clause = match tok.token_type.unwrap() { TokenType::TK_NOTHING => UpsertDo::Nothing, TokenType::TK_UPDATE => { self.eat_expect(&[TokenType::TK_SET])?; let set_list = self.parse_set_list()?; let update_where_clause = self.parse_where()?; UpsertDo::Set { sets: set_list, where_clause: update_where_clause, } } _ => unreachable!(), }; if !targets.is_empty() { let (next, returning) = self.parse_upsert()?; Ok(( Some(Box::new(Upsert { index: Some(UpsertIndex { targets, where_clause, }), do_clause, next, })), returning, )) } else { Ok(( Some(Box::new(Upsert { index: None, do_clause, next: None, })), self.parse_returning()?, )) } } fn parse_trigger_insert_cmd(&mut self) -> Result { let tok = self.eat_assert(&[TokenType::TK_INSERT, TokenType::TK_REPLACE]); let resolve_type = match tok.token_type.unwrap() { TokenType::TK_INSERT => self.parse_or_conflict()?, TokenType::TK_REPLACE => Some(ResolveType::Replace), _ => unreachable!(), }; self.eat_expect(&[TokenType::TK_INTO])?; let tbl_name = self.parse_nm()?; let col_names = self.parse_nm_list_opt()?; let select = self.parse_select()?; let (upsert, returning) = self.parse_upsert()?; Ok(TriggerCmd::Insert { or_conflict: resolve_type, tbl_name, col_names, select, upsert, returning, }) } fn parse_trigger_update_cmd(&mut self) -> Result { self.eat_assert(&[TokenType::TK_UPDATE]); let or_conflict = self.parse_or_conflict()?; let tbl_name = self.parse_nm()?; self.eat_expect(&[TokenType::TK_SET])?; let sets = self.parse_set_list()?; let from = self.parse_from_clause_opt()?; let where_clause = self.parse_where()?; Ok(TriggerCmd::Update { or_conflict, tbl_name, sets, from, where_clause, }) } fn parse_trigger_delete_cmd(&mut self) -> Result { self.eat_assert(&[TokenType::TK_DELETE]); self.eat_expect(&[TokenType::TK_FROM])?; let tbl_name = self.parse_nm()?; let where_clause = self.parse_where()?; Ok(TriggerCmd::Delete { tbl_name, where_clause, }) } fn parse_trigger_cmd(&mut self) -> Result { let tok = self.peek_expect(&[ TokenType::TK_INSERT, TokenType::TK_REPLACE, TokenType::TK_UPDATE, TokenType::TK_DELETE, TokenType::TK_WITH, TokenType::TK_SELECT, TokenType::TK_VALUES, ])?; let result = match tok.token_type.unwrap() { TokenType::TK_WITH | TokenType::TK_SELECT | TokenType::TK_VALUES => { TriggerCmd::Select(self.parse_select()?) } TokenType::TK_INSERT | TokenType::TK_REPLACE => self.parse_trigger_insert_cmd()?, TokenType::TK_UPDATE => self.parse_trigger_update_cmd()?, TokenType::TK_DELETE => self.parse_trigger_delete_cmd()?, _ => unreachable!(), }; self.eat_expect(&[TokenType::TK_SEMI])?; Ok(result) } fn parse_create_trigger(&mut self, temporary: bool) -> Result { self.eat_assert(&[TokenType::TK_TRIGGER]); let if_not_exists = self.parse_if_not_exists()?; let trigger_name = self.parse_fullname(false)?; let trigger_time = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_BEFORE => { self.eat_assert(&[TokenType::TK_BEFORE]); Some(TriggerTime::Before) } TokenType::TK_AFTER => { self.eat_assert(&[TokenType::TK_AFTER]); Some(TriggerTime::After) } TokenType::TK_INSTEAD => { self.eat_assert(&[TokenType::TK_INSTEAD]); self.eat_expect(&[TokenType::TK_OF])?; Some(TriggerTime::InsteadOf) } _ => None, }; let tok = self.eat_expect(&[ TokenType::TK_INSERT, TokenType::TK_UPDATE, TokenType::TK_DELETE, ])?; let trigger_event = match tok.token_type.unwrap() { TokenType::TK_INSERT => TriggerEvent::Insert, TokenType::TK_DELETE => TriggerEvent::Delete, TokenType::TK_UPDATE => match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_OF => { self.eat_assert(&[TokenType::TK_OF]); TriggerEvent::UpdateOf(self.parse_nm_list()?) } _ => TriggerEvent::Update, }, _ => unreachable!(), }; self.eat_expect(&[TokenType::TK_ON])?; let tbl_name = self.parse_fullname(false)?; let foreach_clause = match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_FOR => { self.eat_assert(&[TokenType::TK_FOR]); self.eat_expect(&[TokenType::TK_EACH])?; self.eat_expect(&[TokenType::TK_ROW])?; true } _ => false, }; let when_clause = match self.peek()? { Some(tok) if tok.token_type == Some(TokenType::TK_WHEN) => { self.eat_assert(&[TokenType::TK_WHEN]); Some(self.parse_expr(0)?) } _ => None, }; self.eat_expect(&[TokenType::TK_BEGIN])?; let mut cmds = vec![self.parse_trigger_cmd()?]; loop { match self.peek_no_eof()?.token_type.unwrap() { TokenType::TK_UPDATE | TokenType::TK_REPLACE | TokenType::TK_INSERT | TokenType::TK_DELETE | TokenType::TK_WITH | TokenType::TK_SELECT | TokenType::TK_VALUES => {} _ => break, } cmds.push(self.parse_trigger_cmd()?); } self.eat_expect(&[TokenType::TK_END])?; Ok(Stmt::CreateTrigger { temporary, if_not_exists, trigger_name, time: trigger_time, event: trigger_event, tbl_name, for_each_row: foreach_clause, when_clause, commands: cmds, }) } fn parse_delete_without_cte(&mut self, with: Option) -> Result { self.eat_assert(&[TokenType::TK_DELETE]); self.eat_expect(&[TokenType::TK_FROM])?; let tbl_name = self.parse_fullname(true)?; let indexed = self.parse_indexed()?; let where_clause = self.parse_where()?; let returning = self.parse_returning()?; let order_by = self.parse_order_by()?; let limit = self.parse_limit()?; Ok(Stmt::Delete { with, tbl_name, indexed, where_clause, returning, order_by, limit, }) } fn parse_delete(&mut self) -> Result { let with = self.parse_with()?; self.parse_delete_without_cte(with) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parser() { let test_cases = vec![ // begin ( b"BEGIN".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: None, name: None, })], ), ( b"EXPLAIN BEGIN".as_slice(), vec![Cmd::Explain(Stmt::Begin { typ: None, name: None, })], ), ( b"EXPLAIN QUERY PLAN BEGIN".as_slice(), vec![Cmd::ExplainQueryPlan(Stmt::Begin { typ: None, name: None, })], ), ( b"BEGIN TRANSACTION".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: None, name: None, })], ), ( b"BEGIN DEFERRED TRANSACTION".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Deferred), name: None, })], ), ( b"BEGIN IMMEDIATE TRANSACTION".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Immediate), name: None, })], ), ( b"BEGIN EXCLUSIVE TRANSACTION".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Exclusive), name: None, })], ), ( b"BEGIN DEFERRED TRANSACTION my_transaction".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Deferred), name: Some(Name::Ident("my_transaction".to_string())), })], ), ( b"BEGIN IMMEDIATE TRANSACTION my_transaction".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Immediate), name: Some(Name::Ident("my_transaction".to_string())), })], ), ( b"BEGIN EXCLUSIVE TRANSACTION my_transaction".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Exclusive), name: Some(Name::Ident("my_transaction".to_string())), })], ), ( b"BEGIN EXCLUSIVE TRANSACTION 'my_transaction'".as_slice(), vec![Cmd::Stmt(Stmt::Begin { typ: Some(TransactionType::Exclusive), name: Some(Name::Quoted("'my_transaction'".to_string())), })], ), ( ";;;BEGIN;BEGIN;;;;;;BEGIN".as_bytes(), vec![ Cmd::Stmt(Stmt::Begin { typ: None, name: None, }), Cmd::Stmt(Stmt::Begin { typ: None, name: None, }), Cmd::Stmt(Stmt::Begin { typ: None, name: None, }), ], ), // commit ( b"COMMIT".as_slice(), vec![Cmd::Stmt(Stmt::Commit { name: None })], ), ( b"END".as_slice(), vec![Cmd::Stmt(Stmt::Commit { name: None })], ), ( b"COMMIT TRANSACTION".as_slice(), vec![Cmd::Stmt(Stmt::Commit { name: None })], ), ( b"END TRANSACTION".as_slice(), vec![Cmd::Stmt(Stmt::Commit { name: None })], ), ( b"COMMIT TRANSACTION my_transaction".as_slice(), vec![Cmd::Stmt(Stmt::Commit { name: Some(Name::Ident("my_transaction".to_string())), })], ), ( b"END TRANSACTION my_transaction".as_slice(), vec![Cmd::Stmt(Stmt::Commit { name: Some(Name::Ident("my_transaction".to_string())), })], ), // Rollback ( b"ROLLBACK".as_slice(), vec![Cmd::Stmt(Stmt::Rollback { tx_name: None, savepoint_name: None, })], ), ( b"ROLLBACK TO SAVEPOINT my_savepoint".as_slice(), vec![Cmd::Stmt(Stmt::Rollback { tx_name: None, savepoint_name: Some(Name::Ident("my_savepoint".to_string())), })], ), ( b"ROLLBACK TO my_savepoint".as_slice(), vec![Cmd::Stmt(Stmt::Rollback { tx_name: None, savepoint_name: Some(Name::Ident("my_savepoint".to_string())), })], ), ( b"ROLLBACK TRANSACTION my_transaction".as_slice(), vec![Cmd::Stmt(Stmt::Rollback { tx_name: Some(Name::Ident("my_transaction".to_string())), savepoint_name: None, })], ), ( b"ROLLBACK TRANSACTION my_transaction TO my_savepoint".as_slice(), vec![Cmd::Stmt(Stmt::Rollback { tx_name: Some(Name::Ident("my_transaction".to_string())), savepoint_name: Some(Name::Ident("my_savepoint".to_string())), })], ), // savepoint ( b"SAVEPOINT my_savepoint".as_slice(), vec![Cmd::Stmt(Stmt::Savepoint { name: Name::Ident("my_savepoint".to_string()), })], ), ( b"SAVEPOINT 'my_savepoint'".as_slice(), vec![Cmd::Stmt(Stmt::Savepoint { name: Name::Quoted("'my_savepoint'".to_string()), })], ), // release ( b"RELEASE my_savepoint".as_slice(), vec![Cmd::Stmt(Stmt::Release { name: Name::Ident("my_savepoint".to_string()), })], ), ( b"RELEASE SAVEPOINT my_savepoint".as_slice(), vec![Cmd::Stmt(Stmt::Release { name: Name::Ident("my_savepoint".to_string()), })], ), ( b"RELEASE SAVEPOINT 'my_savepoint'".as_slice(), vec![Cmd::Stmt(Stmt::Release { name: Name::Quoted("'my_savepoint'".to_string()), })], ), ( b"RELEASE SAVEPOINT ABORT".as_slice(), vec![Cmd::Stmt(Stmt::Release { name: Name::Ident("ABORT".to_string()), })], ), // test expr operand ( b"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::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, }))], ), ( b"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::Parenthesized(vec![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, }))], ), ( b"SELECT NULL".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::Null)), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT X'ab'".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::Blob("ab".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 3.333".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::Numeric("3.333".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"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::Variable("1".to_owned())), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CAST(1 AS INTEGER)".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Cast { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), type_name: Some(Type { name: "INTEGER".to_owned(), size: None, }), }), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CAST(1 AS VARCHAR(255))".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Cast { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), type_name: Some(Type { name: "VARCHAR".to_owned(), size: Some(TypeSize::MaxSize(Box::new(Expr::Literal( Literal::Numeric("255".to_owned()), )))), }), }), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CAST(1 AS DECIMAL(10, 5))".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Cast { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), type_name: Some(Type { name: "DECIMAL".to_owned(), size: Some(TypeSize::TypeSize( Box::new(Expr::Literal(Literal::Numeric( "10".to_owned(), ))), Box::new(Expr::Literal(Literal::Numeric( "5".to_owned(), ))), )), }), }), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CURRENT_DATE".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::CurrentDate)), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CURRENT_TIME".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::CurrentTime)), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CURRENT_TIMESTAMP".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::CurrentTimestamp)), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT NOT 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::Unary( UnaryOperator::Not, 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, }))], ), ( b"SELECT NOT 1 + 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::Unary( UnaryOperator::Not, Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Add, 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, }))], ), ( b"SELECT ~1 + 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::Binary( Box::new(Expr::Unary( UnaryOperator::BitwiseNot, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), Operator::Add, 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, }))], ), ( b"SELECT +1 + 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::Binary( Box::new(Expr::Unary( UnaryOperator::Positive, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), Operator::Add, 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, }))], ), ( b"SELECT -1 + 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::Binary( Box::new(Expr::Unary( UnaryOperator::Negative, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), Operator::Add, 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, }))], ), ( b"SELECT EXISTS (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::Exists(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 CASE WHEN 1 THEN 2 ELSE 3 END".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Case { base: None, when_then_pairs: vec![( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )], else_expr: Some(Box::new(Expr::Literal(Literal::Numeric( "3".to_owned(), )))), }), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CASE 4 WHEN 1 THEN 2 ELSE 3 END".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Case { base: Some(Box::new(Expr::Literal(Literal::Numeric( "4".to_owned(), )))), when_then_pairs: vec![( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )], else_expr: Some(Box::new(Expr::Literal(Literal::Numeric( "3".to_owned(), )))), }), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT CASE 4 WHEN 1 THEN 2 END".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Case { base: Some(Box::new(Expr::Literal(Literal::Numeric( "4".to_owned(), )))), when_then_pairs: vec![( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )], else_expr: None, }), 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, }))], ), // parse expr ( b"SELECT 1 + 2 * 3".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Add, Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), Operator::Multiply, Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))) )) )), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 AND 2 OR 3".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::And, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))) )), Operator::Or, Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), )), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 = 0 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("0".to_owned()))) )), Operator::And, 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, }))], ), ( b"SELECT 1 != 0 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::NotEquals, Box::new(Expr::Literal(Literal::Numeric("0".to_owned()))) )), Operator::And, 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, }))], ), ( b"SELECT 1 BETWEEN 2 AND 3 AND 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::Binary ( Box::new(Expr::Between { lhs: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), not: false, start: Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), end: Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), }), Operator::And, 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, }))], ), ( b"SELECT 1 NOT BETWEEN 2 AND 3 AND 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::Binary ( Box::new(Expr::Between { lhs: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), not: true, start: Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), end: Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), }), Operator::And, 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, }))], ), ( b"SELECT 1 IN (SELECT 1) AND 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::Binary ( Box::new(Expr::InSelect { lhs: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), not: false, rhs: 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 }, }), Operator::And, 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, }))], ), ( b"SELECT 1 NOT IN (SELECT 1) AND 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::Binary ( Box::new(Expr::InSelect { lhs: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), not: true, rhs: 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 }, }), Operator::And, 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, }))], ), ( b"SELECT 1 IN (1, 2, 3) AND 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::Binary ( Box::new(Expr::InList { lhs: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), not: false, rhs: vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), ], }), Operator::And, 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, }))], ), ( b"SELECT 1 IN test(1, 2, 3) AND 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::Binary ( Box::new(Expr::InTable { lhs: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), not: false, rhs: QualifiedName { db_name: None, name: Name::Ident("test".to_owned()), alias: None, }, args: vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), ], }), Operator::And, 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, }))], ), ( b"SELECT 'test' MATCH 'foo' AND 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::Binary ( Box::new(Expr::Like { lhs: Box::new(Expr::Literal(Literal::String("'test'".to_owned()))), not: false, op: LikeOperator::Match, rhs: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), escape: None, }), Operator::And, 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, }))], ), ( b"SELECT 'test' NOT MATCH 'foo' AND 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::Binary ( Box::new(Expr::Like { lhs: Box::new(Expr::Literal(Literal::String("'test'".to_owned()))), not: true, op: LikeOperator::Match, rhs: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), escape: None, }), Operator::And, 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, }))], ), ( b"SELECT 'test' NOT MATCH 'foo' ESCAPE 'bar' AND 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::Binary ( Box::new(Expr::Like { lhs: Box::new(Expr::Literal(Literal::String("'test'".to_owned()))), not: true, op: LikeOperator::Match, rhs: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), escape: Some(Box::new(Expr::Literal(Literal::String("'bar'".to_owned())))), }), Operator::And, 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, }))], ), ( b"SELECT 'test' NOT LIKE 'foo' ESCAPE 'bar' AND 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::Binary ( Box::new(Expr::Like { lhs: Box::new(Expr::Literal(Literal::String("'test'".to_owned()))), not: true, op: LikeOperator::Like, rhs: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), escape: Some(Box::new(Expr::Literal(Literal::String("'bar'".to_owned())))), }), Operator::And, 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, }))], ), ( b"SELECT 'test' NOT GLOB 'foo' ESCAPE 'bar' AND 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::Binary ( Box::new(Expr::Like { lhs: Box::new(Expr::Literal(Literal::String("'test'".to_owned()))), not: true, op: LikeOperator::Glob, rhs: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), escape: Some(Box::new(Expr::Literal(Literal::String("'bar'".to_owned())))), }), Operator::And, 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, }))], ), ( b"SELECT 'test' NOT REGEXP 'foo' ESCAPE 'bar' AND 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::Binary ( Box::new(Expr::Like { lhs: Box::new(Expr::Literal(Literal::String("'test'".to_owned()))), not: true, op: LikeOperator::Regexp, rhs: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), escape: Some(Box::new(Expr::Literal(Literal::String("'bar'".to_owned())))), }), Operator::And, 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, }))], ), ( b"SELECT 1 ISNULL AND 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::Binary ( Box::new(Expr::IsNull ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 NOTNULL AND 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::Binary ( Box::new(Expr::NotNull( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 < 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Less, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 > 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Greater, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 <= 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::LessEquals, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 >= 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::GreaterEquals, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 & 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::BitwiseAnd, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 | 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::BitwiseOr, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 << 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::LeftShift, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 >> 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::RightShift, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 / 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Divide, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 % 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Modulus, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 || 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Concat, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 -> 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::ArrowRight, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 1 ->> 2 AND 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::Binary ( Box::new(Expr::Binary ( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::ArrowRightShift, Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), )), Operator::And, 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, }))], ), ( b"SELECT 'foo' COLLATE bar AND 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::Binary ( Box::new(Expr::Collate ( Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), Name::Ident("bar".to_owned()), )), Operator::And, 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, }))], ), // test select ( b"VALUES (1, 2), (3, 4), (5, 6)".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], vec![ Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("4".to_owned()))), ], vec![ Box::new(Expr::Literal(Literal::Numeric("5".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("6".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT *".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Star], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT tbl_name.*".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::TableStar( Name::Ident("tbl_name".to_owned()), )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT col_1 OVER".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()))), Some(As::Elided(Name::Ident("OVER".to_owned()))), )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT col_1 AS OVER".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()))), Some(As::As(Name::Ident("OVER".to_owned()))), )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"WITH test AS (SELECT 1) SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: Some(With { recursive: false, ctes: vec![ CommonTableExpr { tbl_name: Name::Ident("test".to_owned()), columns: vec![], materialized: Materialized::Any, select: 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, } }, ] }), 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, }))], ), ( b"WITH test(col_1) AS MATERIALIZED (SELECT 1 AS col_1) SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: Some(With { recursive: false, ctes: vec![ CommonTableExpr { tbl_name: Name::Ident("test".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("col_1".to_owned()), collation_name: None, order: None, }, ], materialized: Materialized::Yes, select: Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Some(As::As(Name::Ident("col_1".to_owned()))), )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: 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, }))], ), ( b"WITH test(col_1) AS NOT MATERIALIZED (SELECT 1 AS col_1) SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: Some(With { recursive: false, ctes: vec![ CommonTableExpr { tbl_name: Name::Ident("test".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("col_1".to_owned()), collation_name: None, order: None, }, ], materialized: Materialized::No, select: Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Some(As::As(Name::Ident("col_1".to_owned()))), )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: 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, }))], ), ( b"WITH test AS (SELECT 1), test_2 AS (SELECT 1) SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: Some(With { recursive: false, ctes: vec![ CommonTableExpr { tbl_name: Name::Ident("test".to_owned()), columns: vec![], materialized: Materialized::Any, select: 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, } }, CommonTableExpr { tbl_name: Name::Ident("test_2".to_owned()), columns: vec![], materialized: Materialized::Any, select: 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, } }, ] }), 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, }))], ), ( b"SELECT 1 ORDER BY 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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![ SortedColumn { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), order: None, nulls: None, }, ], limit: None, }))], ), ( b"SELECT 1 ORDER BY 1 DESC NULLS FIRST".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![ SortedColumn { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), order: Some(SortOrder::Desc), nulls: Some(NullsOrder::First), }, ], limit: None, }))], ), ( b"SELECT 1 ORDER BY 1 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::Literal(Literal::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![ SortedColumn { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), order: Some(SortOrder::Asc), nulls: Some(NullsOrder::Last), }, ], limit: None, }))], ), ( b"SELECT 1 LIMIT 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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: Some(Limit { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), offset: None, }), }))], ), ( b"SELECT 1 LIMIT 1,2".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: Some(Limit { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), offset: Some(Box::new(Expr::Literal(Literal::Numeric("2".to_owned())))), }), }))], ), ( b"SELECT 1 LIMIT 1 OFFSET 2".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: Some(Limit { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), offset: Some(Box::new(Expr::Literal(Literal::Numeric("2".to_owned())))), }), }))], ), ( b"SELECT 1 UNION SELECT 2".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![ CompoundSelect { operator: CompoundOperator::Union, select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], } } ], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 UNION ALL SELECT 2".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![ CompoundSelect { operator: CompoundOperator::UnionAll, select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], } } ], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 EXCEPT SELECT 2".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![ CompoundSelect { operator: CompoundOperator::Except, select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], } } ], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 INTERSECT SELECT 2".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::Numeric("1".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![ CompoundSelect { operator: CompoundOperator::Intersect, select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Expr( Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), None, )], from: None, where_clause: None, group_by: None, window_clause: vec![], } } ], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo(1, 2)".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::TableCall( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], None, )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM (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::Literal(Literal::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Select( 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, )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM (tbl_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::Literal(Literal::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Sub( FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("tbl_name".to_owned()), alias: None }, None, None )), joins: vec![] }, None, )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo INDEXED BY bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, Some(Indexed::IndexedBy(Name::Ident("bar".to_owned()))), )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo NOT INDEXED".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, Some(Indexed::NotIndexed), )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo, bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::Comma, table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo, bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::Comma, table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo NATURAL JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::NATURAL)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo CROSS JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::INNER|JoinType::CROSS)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo LEFT JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::LEFT|JoinType::OUTER)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo RIGHT JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::RIGHT|JoinType::OUTER)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo FULL JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::LEFT | JoinType::RIGHT | JoinType::OUTER)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo INNER JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::INNER)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo NATURAL INNER JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::NATURAL | JoinType::INNER)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo NATURAL LEFT OUTER JOIN bar".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(Some(JoinType::NATURAL | JoinType::LEFT | JoinType::OUTER)), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN bar ON 1 = 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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: Some(JoinConstraint::On(Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )))), } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN bar USING (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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), constraint: Some(JoinConstraint::Using(vec![ Name::Ident("col_1".to_owned()), ])), } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN bar bar_alias USING (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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, Some(As::Elided(Name::Ident("bar_alias".to_owned()))), None, )), constraint: Some(JoinConstraint::Using(vec![ Name::Ident("col_1".to_owned()), ])), } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN bar(1, 2)".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::TableCall( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN (VALUES (1,2), (3, 4))".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Select( Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))) ], vec![ Box::new(Expr::Literal(Literal::Numeric("3".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("4".to_owned()))) ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo JOIN (bar)".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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Sub( FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None }, None, None, )), joins: vec![] }, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo WHERE 1 = 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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: Some(Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), ))), group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo GROUP BY 1 = 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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: None, group_by: Some(GroupBy { exprs: vec![ Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), ], having: None, }), window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo GROUP BY 1 = 1 HAVING 1 = 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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: None, group_by: Some(GroupBy { exprs: vec![ Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), ], having: Some(Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), ))), }), window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT 1 FROM foo GROUP BY 1 = 1 HAVING 1 = 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::Numeric("1".to_owned()))), None, )], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: None, group_by: Some(GroupBy { exprs: vec![ Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )), ], having: Some(Box::new(Expr::Binary( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Operator::Equals, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), ))), }), window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT * FROM t0 WINDOW JOIN t0;".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Star], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("t0".to_owned()), alias: None }, Some(As::Elided(Name::Ident("WINDOW".to_owned()))), None, )), joins: vec![ JoinedSelectTable { operator: JoinOperator::TypedJoin(None), table: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("t0".to_owned()), alias: None }, None, None, )), constraint: None, } ] }), where_clause: None, group_by: None, window_clause: vec![], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT * FROM t0 WINDOW window_1 AS (PARTITION BY product)".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Star], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("t0".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![ WindowDef { name: Name::Ident("window_1".to_owned()), window: Window { base: None, partition_by: vec![ Box::new(Expr::Id(Name::Ident("product".to_owned()))), ], order_by: vec![], frame_clause: None, }, } ], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), ( b"SELECT * FROM t0 WINDOW window_1 AS (PARTITION BY product), window_2 AS (PARTITION BY product_2)".as_slice(), vec![Cmd::Stmt(Stmt::Select(Select { with: None, body: SelectBody { select: OneSelect::Select { distinctness: None, columns: vec![ResultColumn::Star], from: Some(FromClause { select: Box::new(SelectTable::Table( QualifiedName { db_name: None, name: Name::Ident("t0".to_owned()), alias: None }, None, None, )), joins: vec![] }), where_clause: None, group_by: None, window_clause: vec![ WindowDef { name: Name::Ident("window_1".to_owned()), window: Window { base: None, partition_by: vec![ Box::new(Expr::Id(Name::Ident("product".to_owned()))), ], order_by: vec![], frame_clause: None, }, }, WindowDef { name: Name::Ident("window_2".to_owned()), window: Window { base: None, partition_by: vec![ Box::new(Expr::Id(Name::Ident("product_2".to_owned()))), ], order_by: vec![], frame_clause: None, }, } ], }, compounds: vec![], }, order_by: vec![], limit: None, }))], ), // parse Analyze ( b"ANALYZE".as_slice(), vec![Cmd::Stmt(Stmt::Analyze { name: None, })], ), ( b"ANALYZE foo".as_slice(), vec![Cmd::Stmt(Stmt::Analyze { name: Some(QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }), })], ), // parse attach ( b"ATTACH DATABASE 'foo' AS bar".as_slice(), vec![Cmd::Stmt(Stmt::Attach { expr: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), db_name: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), key: None, })], ), ( b"ATTACH 'foo' AS bar".as_slice(), vec![Cmd::Stmt(Stmt::Attach { expr: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), db_name: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), key: None, })], ), ( b"ATTACH 'foo' AS bar key baz".as_slice(), vec![Cmd::Stmt(Stmt::Attach { expr: Box::new(Expr::Literal(Literal::String("'foo'".to_owned()))), db_name: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), key: Some(Box::new(Expr::Id(Name::Ident("baz".to_owned())))), })], ), // parse detach ( b"DETACH DATABASE bar".as_slice(), vec![Cmd::Stmt(Stmt::Detach { name: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), })], ), ( b"DETACH bar".as_slice(), vec![Cmd::Stmt(Stmt::Detach { name: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), })], ), // parse pragma ( b"PRAGMA foreign_keys = ON".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: Some(PragmaBody::Equals(Box::new(Expr::Literal(Literal::Keyword("ON".to_owned()))))), })], ), ( b"PRAGMA foreign_keys = DELETE".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: Some(PragmaBody::Equals(Box::new(Expr::Literal(Literal::Keyword("DELETE".to_owned()))))), })], ), ( b"PRAGMA foreign_keys = DEFAULT".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: Some(PragmaBody::Equals(Box::new(Expr::Literal(Literal::Keyword("DEFAULT".to_owned()))))), })], ), ( b"PRAGMA foreign_keys".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: None, })], ), ( b"PRAGMA foreign_keys = 1".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: Some(PragmaBody::Equals(Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))))), })], ), ( b"PRAGMA foreign_keys = test".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: Some(PragmaBody::Equals(Box::new(Expr::Name(Name::Ident("test".to_owned()))))), })], ), ( b"PRAGMA foreign_keys".as_slice(), vec![Cmd::Stmt(Stmt::Pragma { name: QualifiedName { db_name: None, name: Name::Ident("foreign_keys".to_owned()), alias: None }, body: None, })], ), // parse vacuum ( b"VACUUM".as_slice(), vec![Cmd::Stmt(Stmt::Vacuum { name: None, into: None, })], ), ( b"VACUUM INTO 'foo'".as_slice(), vec![Cmd::Stmt(Stmt::Vacuum { name: None, into: Some(Box::new(Expr::Literal(Literal::String("'foo'".to_owned())))), })], ), ( b"VACUUM INTO foo".as_slice(), vec![Cmd::Stmt(Stmt::Vacuum { name: None, into: Some(Box::new(Expr::Id(Name::Ident("foo".to_owned())))), })], ), ( b"VACUUM foo".as_slice(), vec![Cmd::Stmt(Stmt::Vacuum { name: Some(Name::Ident("foo".to_owned())), into: None, })], ), ( b"VACUUM foo INTO 'bar'".as_slice(), vec![Cmd::Stmt(Stmt::Vacuum { name: Some(Name::Ident("foo".to_owned())), into: Some(Box::new(Expr::Literal(Literal::String("'bar'".to_owned())))), })], ), // parse alter ( b"ALTER TABLE foo RENAME TO bar".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::RenameTo(Name::Ident("bar".to_owned())), })], ), ( b"ALTER TABLE foo RENAME baz TO bar".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::RenameColumn { old: Name::Ident("baz".to_owned()), new: Name::Ident("bar".to_owned()) }, })], ), ( b"ALTER TABLE foo RENAME COLUMN baz TO bar".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::RenameColumn { old: Name::Ident("baz".to_owned()), new: Name::Ident("bar".to_owned()) }, })], ), ( b"ALTER TABLE foo DROP baz".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::DropColumn(Name::Ident("baz".to_owned())), })], ), ( b"ALTER TABLE foo DROP COLUMN baz".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::DropColumn(Name::Ident("baz".to_owned())), })], ), ( b"ALTER TABLE foo ADD baz".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: None, constraints: vec![], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT 1".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Default( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))) ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT (1)".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Default( Box::new(Expr::Parenthesized(vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), ])) ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT +1".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Default( Box::new(Expr::Unary( UnaryOperator::Positive, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )) ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT -1".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Default( Box::new(Expr::Unary( UnaryOperator::Negative, Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), )) ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER DEFAULT hello".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Default( Box::new(Expr::Id(Name::Ident("hello".to_owned()))) ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NULL".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::NotNull { nullable: true, conflict_clause: None, }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT IGNORE".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::NotNull { nullable: false, conflict_clause: Some(ResolveType::Ignore), }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT REPLACE".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::NotNull { nullable: false, conflict_clause: Some(ResolveType::Replace), }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT ROLLBACK".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::NotNull { nullable: false, conflict_clause: Some(ResolveType::Rollback), }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER NOT NULL ON CONFLICT ROLLBACK".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::NotNull { nullable: false, conflict_clause: Some(ResolveType::Rollback), }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER PRIMARY KEY".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::PrimaryKey { order: None, conflict_clause: None, auto_increment: false, } }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::PrimaryKey { order: Some(SortOrder::Asc), conflict_clause: Some(ResolveType::Rollback), auto_increment: true, } }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER UNIQUE".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Unique(None), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER UNIQUE ON CONFLICT ROLLBACK".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Unique(Some(ResolveType::Rollback)), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER CHECK (1)".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Check( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER CHECK (1)".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Check( Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), ), }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![], args: vec![] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON INSERT SET NULL".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnInsert(RefAct::SetNull), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON UPDATE SET NULL".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnUpdate(RefAct::SetNull), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE SET NULL".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnDelete(RefAct::SetNull), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE SET DEFAULT".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnDelete(RefAct::SetDefault), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE CASCADE".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnDelete(RefAct::Cascade), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE RESTRICT".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnDelete(RefAct::Restrict), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar(test, test_2) MATCH test_3 ON DELETE NO ACTION".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("test".to_owned()), collation_name: None, order: None, }, IndexedColumn { col_name: Name::Ident("test_2".to_owned()), collation_name: None, order: None, }, ], args: vec![ RefArg::Match(Name::Ident("test_3".to_owned())), RefArg::OnDelete(RefAct::NoAction), ] }, deref_clause: None }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar DEFERRABLE".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![], args: vec![] }, deref_clause: Some(DeferSubclause { deferrable: true, init_deferred: None, }) }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar NOT DEFERRABLE INITIALLY IMMEDIATE".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![], args: vec![] }, deref_clause: Some(DeferSubclause { deferrable: false, init_deferred: Some(InitDeferredPred::InitiallyImmediate), }) }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar NOT DEFERRABLE INITIALLY DEFERRED".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![], args: vec![] }, deref_clause: Some(DeferSubclause { deferrable: false, init_deferred: Some(InitDeferredPred::InitiallyDeferred), }) }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER REFERENCES bar NOT DEFERRABLE INITIALLY DEFERRED".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::ForeignKey { clause: ForeignKeyClause { tbl_name: Name::Ident("bar".to_owned()), columns: vec![], args: vec![] }, deref_clause: Some(DeferSubclause { deferrable: false, init_deferred: Some(InitDeferredPred::InitiallyDeferred), }) }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER COLLATE bar".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Collate { collation_name: Name::Ident("bar".to_owned()), }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER GENERATED ALWAYS AS (1)".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Generated { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), typ: None, }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER AS (1)".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Generated { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), typ: None, }, }, ], }), })], ), ( b"ALTER TABLE foo ADD COLUMN baz INTEGER AS (1) STORED".as_slice(), vec![Cmd::Stmt(Stmt::AlterTable { name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, body: AlterTableBody::AddColumn(ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![ NamedColumnConstraint { name: None, constraint: ColumnConstraint::Generated { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), typ: Some(Name::Ident("STORED".to_owned())), }, }, ], }), })], ), // parse create index ( b"CREATE INDEX idx_foo ON foo (bar)".as_slice(), vec![Cmd::Stmt(Stmt::CreateIndex { unique: false, if_not_exists: false, idx_name: QualifiedName { db_name: None, name: Name::Ident("idx_foo".to_owned()), alias: None, }, tbl_name: Name::Ident("foo".to_owned()), columns: vec![SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None, }], where_clause: None, })], ), ( b"CREATE UNIQUE INDEX IF NOT EXISTS idx_foo ON foo (bar) WHERE 1".as_slice(), vec![Cmd::Stmt(Stmt::CreateIndex { unique: true, if_not_exists: true, idx_name: QualifiedName { db_name: None, name: Name::Ident("idx_foo".to_owned()), alias: None, }, tbl_name: Name::Ident("foo".to_owned()), columns: vec![SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None, }], where_clause: Some(Box::new( Expr::Literal(Literal::Numeric("1".to_owned())) )), })], ), // parse create table ( b"CREATE TABLE foo (column)".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: false, if_not_exists: false, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, body: CreateTableBody::ColumnsAndConstraints { columns: vec![ ColumnDefinition { col_name: Name::Ident("column".to_owned()), col_type: None, constraints: vec![], }, ], constraints: vec![], options: TableOptions::NONE, }, })], ), ( b"CREATE TABLE foo AS SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: false, if_not_exists: false, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, body: CreateTableBody::AsSelect(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, }), })], ), ( b"CREATE TEMP TABLE IF NOT EXISTS foo (bar, baz INTEGER, CONSTRAINT tbl_cons PRIMARY KEY (bar AUTOINCREMENT) ON CONFLICT ROLLBACK) STRICT".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: true, if_not_exists: true, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, body: CreateTableBody::ColumnsAndConstraints { columns: vec![ ColumnDefinition { col_name: Name::Ident("bar".to_owned()), col_type: None, constraints: vec![], }, ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![], }, ], constraints: vec![ NamedTableConstraint { name: Some(Name::Ident("tbl_cons".to_owned())), constraint: TableConstraint::PrimaryKey { columns: vec![ SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None, }, ], auto_increment: true, conflict_clause: Some(ResolveType::Rollback) } }, ], options: TableOptions::STRICT, }, })], ), ( b"CREATE TEMP TABLE IF NOT EXISTS foo (bar, baz INTEGER, UNIQUE (bar) ON CONFLICT ROLLBACK) WITHOUT ROWID".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: true, if_not_exists: true, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, body: CreateTableBody::ColumnsAndConstraints { columns: vec![ ColumnDefinition { col_name: Name::Ident("bar".to_owned()), col_type: None, constraints: vec![], }, ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![], }, ], constraints: vec![ NamedTableConstraint { name: None, constraint: TableConstraint::Unique { columns: vec![ SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None, }, ], conflict_clause: Some(ResolveType::Rollback) } }, ], options: TableOptions::WITHOUT_ROWID, }, })], ), ( b"CREATE TEMP TABLE IF NOT EXISTS foo (bar, baz INTEGER, CHECK (1))".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: true, if_not_exists: true, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, body: CreateTableBody::ColumnsAndConstraints { columns: vec![ ColumnDefinition { col_name: Name::Ident("bar".to_owned()), col_type: None, constraints: vec![], }, ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![], }, ], constraints: vec![ NamedTableConstraint { name: None, constraint: TableConstraint::Check(Box::new( Expr::Literal(Literal::Numeric("1".to_owned())) )), }, ], options: TableOptions::NONE, }, })], ), ( b"CREATE TEMP TABLE IF NOT EXISTS foo (bar, baz INTEGER, FOREIGN KEY (bar) REFERENCES foo_2(bar_2), CHECK (1))".as_slice(), vec![Cmd::Stmt(Stmt::CreateTable { temporary: true, if_not_exists: true, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, body: CreateTableBody::ColumnsAndConstraints { columns: vec![ ColumnDefinition { col_name: Name::Ident("bar".to_owned()), col_type: None, constraints: vec![], }, ColumnDefinition { col_name: Name::Ident("baz".to_owned()), col_type: Some(Type { name: "INTEGER".to_owned(), size: None, }), constraints: vec![], }, ], constraints: vec![ NamedTableConstraint { name: None, constraint: TableConstraint::ForeignKey { columns: vec![ IndexedColumn { col_name: Name::Ident("bar".to_owned()), collation_name: None, order: None, }, ], clause: ForeignKeyClause { tbl_name: Name::Ident("foo_2".to_owned()), columns: vec![ IndexedColumn { col_name: Name::Ident("bar_2".to_owned()), collation_name: None, order: None, }, ], args: vec![], }, deref_clause: None, }, }, NamedTableConstraint { name: None, constraint: TableConstraint::Check(Box::new( Expr::Literal(Literal::Numeric("1".to_owned())) )), }, ], options: TableOptions::NONE, }, })], ), // parse create trigger ( b"CREATE TRIGGER foo INSERT ON bar BEGIN SELECT 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Select(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, }) ], })], ), ( b"CREATE TEMP TRIGGER IF NOT EXISTS foo AFTER UPDATE ON bar FOR EACH ROW WHEN 1 BEGIN SELECT 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: true, if_not_exists: true, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: Some(TriggerTime::After), event: TriggerEvent::Update, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: true, when_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), commands: vec![ TriggerCmd::Select(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, }) ], })], ), ( b"CREATE TEMP TRIGGER IF NOT EXISTS foo BEFORE DELETE ON bar FOR EACH ROW WHEN 1 BEGIN SELECT 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: true, if_not_exists: true, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: Some(TriggerTime::Before), event: TriggerEvent::Delete, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: true, when_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), commands: vec![ TriggerCmd::Select(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, }) ], })], ), ( b"CREATE TEMP TRIGGER IF NOT EXISTS foo INSTEAD OF UPDATE OF baz, bar ON bar FOR EACH ROW WHEN 1 BEGIN SELECT 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: true, if_not_exists: true, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: Some(TriggerTime::InsteadOf), event: TriggerEvent::UpdateOf(vec![ Name::Ident("baz".to_owned()), Name::Ident("bar".to_owned()), ]), tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: true, when_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), commands: vec![ TriggerCmd::Select(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, }) ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN INSERT INTO foo VALUES (1, 2); END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Insert { or_conflict: None, tbl_name: Name::Ident("foo".to_owned()), col_names: vec![], select: Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, upsert: None, returning: vec![], }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN INSERT OR ROLLBACK INTO foo(bar, baz) VALUES (1, 2); END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Insert { or_conflict: Some(ResolveType::Rollback), tbl_name: Name::Ident("foo".to_owned()), col_names: vec![ Name::Ident("bar".to_owned()), Name::Ident("baz".to_owned()), ], select: Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, upsert: None, returning: vec![], }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN INSERT INTO foo VALUES (1, 2) RETURNING bar, baz; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Insert { or_conflict: None, tbl_name: Name::Ident("foo".to_owned()), col_names: vec![], select: Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, upsert: None, returning: vec![ ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("bar".to_owned()))), None, ), ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("baz".to_owned()))), None, ), ], }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN INSERT INTO foo VALUES (1, 2) ON CONFLICT (bar, baz) WHERE 1 DO NOTHING RETURNING bar, baz; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Insert { or_conflict: None, tbl_name: Name::Ident("foo".to_owned()), col_names: vec![], select: Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, upsert: Some(Box::new(Upsert { index: Some(UpsertIndex { targets: vec![ SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None }, SortedColumn { expr: Box::new(Expr::Id(Name::Ident("baz".to_owned()))), order: None, nulls: None }, ], where_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), }), do_clause: UpsertDo::Nothing, next: None })), returning: vec![ ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("bar".to_owned()))), None, ), ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("baz".to_owned()))), None, ), ], }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN INSERT INTO foo VALUES (1, 2) ON CONFLICT DO UPDATE SET (bar, baz) = 1 WHERE 1 RETURNING bar, baz; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Insert { or_conflict: None, tbl_name: Name::Ident("foo".to_owned()), col_names: vec![], select: Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, upsert: Some(Box::new(Upsert { index: None, do_clause: UpsertDo::Set { sets: vec![ Set { col_names: vec![ Name::Ident("bar".to_owned()), Name::Ident("baz".to_owned()), ], expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), } ], where_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), }, next: None })), returning: vec![ ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("bar".to_owned()))), None, ), ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("baz".to_owned()))), None, ), ], }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN INSERT INTO foo VALUES (1, 2) ON CONFLICT (bar, baz) DO NOTHING ON CONFLICT DO NOTHING; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Insert { or_conflict: None, tbl_name: Name::Ident("foo".to_owned()), col_names: vec![], select: Select { with: None, body: SelectBody { select: OneSelect::Values(vec![ vec![ Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), Box::new(Expr::Literal(Literal::Numeric("2".to_owned()))), ], ]), compounds: vec![], }, order_by: vec![], limit: None, }, upsert: Some(Box::new(Upsert { index: Some(UpsertIndex { targets: vec![ SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None }, SortedColumn { expr: Box::new(Expr::Id(Name::Ident("baz".to_owned()))), order: None, nulls: None }, ], where_clause: None, }), do_clause: UpsertDo::Nothing, next: Some(Box::new(Upsert { index: None, do_clause: UpsertDo::Nothing, next: None, })), })), returning: vec![], }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN UPDATE foo SET bar = 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Update { or_conflict: None, tbl_name: Name::Ident("foo".to_owned()), sets: vec![ Set { col_names: vec![Name::Ident("bar".to_owned())], expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), }, ], from: None, where_clause: None, }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN DELETE FROM foo; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Delete { tbl_name: Name::Ident("foo".to_owned()), where_clause: None, }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN DELETE FROM foo WHERE 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Delete { tbl_name: Name::Ident("foo".to_owned()), where_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), }, ], })], ), ( b"CREATE TRIGGER foo INSERT ON bar BEGIN SELECT 1; SELECT 1; END".as_slice(), vec![Cmd::Stmt(Stmt::CreateTrigger { temporary: false, if_not_exists: false, trigger_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, time: None, event: TriggerEvent::Insert, tbl_name: QualifiedName { db_name: None, name: Name::Ident("bar".to_owned()), alias: None, }, for_each_row: false, when_clause: None, commands: vec![ TriggerCmd::Select(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, }), TriggerCmd::Select(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, }) ], })], ), // parse create view ( b"CREATE VIEW foo AS SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::CreateView { temporary: false, if_not_exists: false, view_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, columns: vec![], select: 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, }, })], ), ( b"CREATE TEMP VIEW IF NOT EXISTS foo(bar) AS SELECT 1".as_slice(), vec![Cmd::Stmt(Stmt::CreateView { temporary: true, if_not_exists: true, view_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, columns: vec![ IndexedColumn { col_name: Name::Ident("bar".to_owned()), collation_name: None, order: None, } ], select: 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, }, })], ), // parse CREATE VIRTUAL TABLE ( b"CREATE VIRTUAL TABLE foo USING bar".as_slice(), vec![Cmd::Stmt(Stmt::CreateVirtualTable { if_not_exists: false, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, module_name: Name::Ident("bar".to_owned()), args: vec![], })], ), ( b"CREATE VIRTUAL TABLE foo USING bar()".as_slice(), vec![Cmd::Stmt(Stmt::CreateVirtualTable { if_not_exists: false, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, module_name: Name::Ident("bar".to_owned()), args: vec![], })], ), ( b"CREATE VIRTUAL TABLE IF NOT EXISTS foo USING bar(1, 2, ('hello', (3.333), 'world', (1, 2)))".as_slice(), vec![Cmd::Stmt(Stmt::CreateVirtualTable { if_not_exists: true, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None, }, module_name: Name::Ident("bar".to_owned()), args: vec![ "1".to_owned(), "2".to_owned(), "('hello', (3.333), 'world', (1, 2))".to_owned(), ], })], ), ( b"CREATE VIRTUAL TABLE ft USING fts5(x, tokenize = '''porter'' ''ascii''')".as_slice(), vec![Cmd::Stmt(Stmt::CreateVirtualTable { if_not_exists: false, tbl_name: QualifiedName { db_name: None, name: Name::Ident("ft".to_owned()), alias: None, }, module_name: Name::Ident("fts5".to_owned()), args: vec![ "x".to_owned(), "tokenize = '''porter'' ''ascii'''".to_owned(), ], })], ), // parse delete ( b"DELETE FROM foo".as_slice(), vec![Cmd::Stmt(Stmt::Delete { with: None, tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, indexed: None, where_clause: None, returning: vec![], order_by: vec![], limit: None, })], ), ( b"WITH test AS (SELECT 1) DELETE FROM foo NOT INDEXED WHERE 1 RETURNING bar ORDER BY bar LIMIT 1".as_slice(), vec![Cmd::Stmt(Stmt::Delete { with: Some(With { recursive: false, ctes: vec![ CommonTableExpr { tbl_name: Name::Ident("test".to_owned()), columns: vec![], materialized: Materialized::Any, select: 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, }, } ], }), tbl_name: QualifiedName { db_name: None, name: Name::Ident("foo".to_owned()), alias: None }, indexed: Some(Indexed::NotIndexed), where_clause: Some(Box::new(Expr::Literal(Literal::Numeric("1".to_owned())))), returning: vec![ ResultColumn::Expr( Box::new(Expr::Id(Name::Ident("bar".to_owned()))), None, ), ], order_by: vec![ SortedColumn { expr: Box::new(Expr::Id(Name::Ident("bar".to_owned()))), order: None, nulls: None, } ], limit: Some(Limit { expr: Box::new(Expr::Literal(Literal::Numeric("1".to_owned()))), offset: None, }), })], ), ]; for (input, expected) in test_cases { println!("Testing input: {:?}", from_bytes(input)); let mut parser = Parser::new(input); let mut results = Vec::new(); while let Some(cmd) = parser.next() { match cmd { Ok(cmd) => results.push(cmd), Err(err) => panic!("Parse error: {}", err), } } assert_eq!(results, expected, "Input: {:?}", input); } } }