diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 7eb3d8b32..56bedd1e6 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -2,12 +2,13 @@ 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, + IndexedColumn, InitDeferredPred, InsertBody, 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}; @@ -577,6 +578,8 @@ impl<'a> Parser<'a> { TokenType::TK_ALTER, TokenType::TK_DELETE, TokenType::TK_DROP, + TokenType::TK_INSERT, + TokenType::TK_REPLACE, // add more ])?; @@ -597,6 +600,7 @@ impl<'a> Parser<'a> { TokenType::TK_ALTER => self.parse_alter(), TokenType::TK_DELETE => self.parse_delete(), TokenType::TK_DROP => self.parse_drop_stmt(), + TokenType::TK_INSERT | TokenType::TK_REPLACE => self.parse_insert(), _ => unreachable!(), } } @@ -873,7 +877,7 @@ impl<'a> Parser<'a> { } TokenType::TK_UPDATE => todo!(), TokenType::TK_DELETE => self.parse_delete_without_cte(with), - TokenType::TK_INSERT | TokenType::TK_REPLACE => todo!(), + TokenType::TK_INSERT | TokenType::TK_REPLACE => self.parse_insert_without_cte(with), _ => unreachable!(), } } @@ -3901,6 +3905,45 @@ impl<'a> Parser<'a> { _ => unreachable!(), } } + + fn parse_insert_without_cte(&mut self, with: Option) -> 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_fullname(true)?; + let columns = self.parse_nm_list_opt()?; + let (body, returning) = match self.peek_no_eof()?.token_type.unwrap() { + TokenType::TK_DEFAULT => { + self.eat_assert(&[TokenType::TK_DEFAULT]); + self.eat_expect(&[TokenType::TK_VALUES])?; + (InsertBody::DefaultValues, self.parse_returning()?) + } + _ => { + let select = self.parse_select()?; + let (upsert, returning) = self.parse_upsert()?; + (InsertBody::Select(select, upsert), returning) + } + }; + + Ok(Stmt::Insert { + with, + or_conflict: resolve_type, + tbl_name, + columns, + body, + returning, + }) + } + + fn parse_insert(&mut self) -> Result { + let with = self.parse_with()?; + self.parse_insert_without_cte(with) + } } #[cfg(test)] @@ -10910,6 +10953,165 @@ mod tests { }, })], ), + // parse insert + ( + b"INSERT INTO foo VALUES (1, 2)".as_slice(), + vec![Cmd::Stmt(Stmt::Insert { + with: None, + or_conflict: None, + tbl_name: QualifiedName { + db_name: None, + name: Name::Ident("foo".to_owned()), + alias: None, + }, + columns: vec![], + body: InsertBody::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, + }, None), + returning: vec![], + })], + ), + ( + b"REPLACE INTO foo VALUES (1, 2)".as_slice(), + vec![Cmd::Stmt(Stmt::Insert { + with: None, + or_conflict: Some(ResolveType::Replace), + tbl_name: QualifiedName { + db_name: None, + name: Name::Ident("foo".to_owned()), + alias: None, + }, + columns: vec![], + body: InsertBody::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, + }, None), + returning: vec![], + })], + ), + ( + b"WITH test AS (SELECT 1) INSERT INTO foo(bar, baz) DEFAULT VALUES RETURNING bar".as_slice(), + vec![Cmd::Stmt(Stmt::Insert { + 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, + }, + } + ], + }), + or_conflict: None, + tbl_name: QualifiedName { + db_name: None, + name: Name::Ident("foo".to_owned()), + alias: None, + }, + columns: vec![ + Name::Ident("bar".to_owned()), + Name::Ident("baz".to_owned()), + ], + body: InsertBody::DefaultValues, + returning: vec![ + ResultColumn::Expr( + Box::new(Expr::Id(Name::Ident("bar".to_owned()))), + None, + ), + ], + })], + ), + ( + b"WITH test AS (SELECT 1) REPLACE INTO foo(bar, baz) DEFAULT VALUES RETURNING bar".as_slice(), + vec![Cmd::Stmt(Stmt::Insert { + 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, + }, + } + ], + }), + or_conflict: Some(ResolveType::Replace), + tbl_name: QualifiedName { + db_name: None, + name: Name::Ident("foo".to_owned()), + alias: None, + }, + columns: vec![ + Name::Ident("bar".to_owned()), + Name::Ident("baz".to_owned()), + ], + body: InsertBody::DefaultValues, + returning: vec![ + ResultColumn::Expr( + Box::new(Expr::Id(Name::Ident("bar".to_owned()))), + None, + ), + ], + })], + ), ]; for (input, expected) in test_cases {