mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-10 16:06:22 +00:00
Allow parsing of mysql empty row inserts (#783)
This commit is contained in:
parent
524b8a7e7b
commit
98403c07b1
2 changed files with 81 additions and 32 deletions
|
@ -1433,7 +1433,7 @@ impl<'a> Parser<'a> {
|
||||||
///
|
///
|
||||||
/// [(1)]: Expr::MatchAgainst
|
/// [(1)]: Expr::MatchAgainst
|
||||||
pub fn parse_match_against(&mut self) -> Result<Expr, ParserError> {
|
pub fn parse_match_against(&mut self) -> Result<Expr, ParserError> {
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
|
|
||||||
self.expect_keyword(Keyword::AGAINST)?;
|
self.expect_keyword(Keyword::AGAINST)?;
|
||||||
|
|
||||||
|
@ -2419,7 +2419,7 @@ impl<'a> Parser<'a> {
|
||||||
// general that the arguments can be made to appear as column
|
// general that the arguments can be made to appear as column
|
||||||
// definitions in a traditional CREATE TABLE statement", but
|
// definitions in a traditional CREATE TABLE statement", but
|
||||||
// we don't implement that.
|
// we don't implement that.
|
||||||
let module_args = self.parse_parenthesized_column_list(Optional)?;
|
let module_args = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
Ok(Statement::CreateVirtualTable {
|
Ok(Statement::CreateVirtualTable {
|
||||||
name: table_name,
|
name: table_name,
|
||||||
if_not_exists,
|
if_not_exists,
|
||||||
|
@ -2698,12 +2698,12 @@ impl<'a> Parser<'a> {
|
||||||
// Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet).
|
// Many dialects support `OR ALTER` right after `CREATE`, but we don't (yet).
|
||||||
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
|
// ANSI SQL and Postgres support RECURSIVE here, but we don't support it either.
|
||||||
let name = self.parse_object_name()?;
|
let name = self.parse_object_name()?;
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
let with_options = self.parse_options(Keyword::WITH)?;
|
let with_options = self.parse_options(Keyword::WITH)?;
|
||||||
|
|
||||||
let cluster_by = if self.parse_keyword(Keyword::CLUSTER) {
|
let cluster_by = if self.parse_keyword(Keyword::CLUSTER) {
|
||||||
self.expect_keyword(Keyword::BY)?;
|
self.expect_keyword(Keyword::BY)?;
|
||||||
self.parse_parenthesized_column_list(Optional)?
|
self.parse_parenthesized_column_list(Optional, false)?
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
@ -3441,7 +3441,7 @@ impl<'a> Parser<'a> {
|
||||||
let foreign_table = self.parse_object_name()?;
|
let foreign_table = self.parse_object_name()?;
|
||||||
// PostgreSQL allows omitting the column list and
|
// PostgreSQL allows omitting the column list and
|
||||||
// uses the primary key column of the foreign table by default
|
// uses the primary key column of the foreign table by default
|
||||||
let referred_columns = self.parse_parenthesized_column_list(Optional)?;
|
let referred_columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
let mut on_delete = None;
|
let mut on_delete = None;
|
||||||
let mut on_update = None;
|
let mut on_update = None;
|
||||||
loop {
|
loop {
|
||||||
|
@ -3525,7 +3525,7 @@ impl<'a> Parser<'a> {
|
||||||
if is_primary {
|
if is_primary {
|
||||||
self.expect_keyword(Keyword::KEY)?;
|
self.expect_keyword(Keyword::KEY)?;
|
||||||
}
|
}
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
Ok(Some(TableConstraint::Unique {
|
Ok(Some(TableConstraint::Unique {
|
||||||
name,
|
name,
|
||||||
columns,
|
columns,
|
||||||
|
@ -3534,10 +3534,10 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
Token::Word(w) if w.keyword == Keyword::FOREIGN => {
|
Token::Word(w) if w.keyword == Keyword::FOREIGN => {
|
||||||
self.expect_keyword(Keyword::KEY)?;
|
self.expect_keyword(Keyword::KEY)?;
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
self.expect_keyword(Keyword::REFERENCES)?;
|
self.expect_keyword(Keyword::REFERENCES)?;
|
||||||
let foreign_table = self.parse_object_name()?;
|
let foreign_table = self.parse_object_name()?;
|
||||||
let referred_columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let referred_columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
let mut on_delete = None;
|
let mut on_delete = None;
|
||||||
let mut on_update = None;
|
let mut on_update = None;
|
||||||
loop {
|
loop {
|
||||||
|
@ -3582,7 +3582,7 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
|
|
||||||
Ok(Some(TableConstraint::Index {
|
Ok(Some(TableConstraint::Index {
|
||||||
display_as_key,
|
display_as_key,
|
||||||
|
@ -3617,7 +3617,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
let opt_index_name = self.maybe_parse(|parser| parser.parse_identifier());
|
let opt_index_name = self.maybe_parse(|parser| parser.parse_identifier());
|
||||||
|
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
|
|
||||||
Ok(Some(TableConstraint::FulltextOrSpatial {
|
Ok(Some(TableConstraint::FulltextOrSpatial {
|
||||||
fulltext,
|
fulltext,
|
||||||
|
@ -3864,7 +3864,7 @@ impl<'a> Parser<'a> {
|
||||||
/// Parse a copy statement
|
/// Parse a copy statement
|
||||||
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
|
||||||
let table_name = self.parse_object_name()?;
|
let table_name = self.parse_object_name()?;
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
let to = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::TO]) {
|
let to = match self.parse_one_of_keywords(&[Keyword::FROM, Keyword::TO]) {
|
||||||
Some(Keyword::FROM) => false,
|
Some(Keyword::FROM) => false,
|
||||||
Some(Keyword::TO) => true,
|
Some(Keyword::TO) => true,
|
||||||
|
@ -3950,13 +3950,13 @@ impl<'a> Parser<'a> {
|
||||||
Some(Keyword::QUOTE) => CopyOption::Quote(self.parse_literal_char()?),
|
Some(Keyword::QUOTE) => CopyOption::Quote(self.parse_literal_char()?),
|
||||||
Some(Keyword::ESCAPE) => CopyOption::Escape(self.parse_literal_char()?),
|
Some(Keyword::ESCAPE) => CopyOption::Escape(self.parse_literal_char()?),
|
||||||
Some(Keyword::FORCE_QUOTE) => {
|
Some(Keyword::FORCE_QUOTE) => {
|
||||||
CopyOption::ForceQuote(self.parse_parenthesized_column_list(Mandatory)?)
|
CopyOption::ForceQuote(self.parse_parenthesized_column_list(Mandatory, false)?)
|
||||||
}
|
}
|
||||||
Some(Keyword::FORCE_NOT_NULL) => {
|
Some(Keyword::FORCE_NOT_NULL) => {
|
||||||
CopyOption::ForceNotNull(self.parse_parenthesized_column_list(Mandatory)?)
|
CopyOption::ForceNotNull(self.parse_parenthesized_column_list(Mandatory, false)?)
|
||||||
}
|
}
|
||||||
Some(Keyword::FORCE_NULL) => {
|
Some(Keyword::FORCE_NULL) => {
|
||||||
CopyOption::ForceNull(self.parse_parenthesized_column_list(Mandatory)?)
|
CopyOption::ForceNull(self.parse_parenthesized_column_list(Mandatory, false)?)
|
||||||
}
|
}
|
||||||
Some(Keyword::ENCODING) => CopyOption::Encoding(self.parse_literal_string()?),
|
Some(Keyword::ENCODING) => CopyOption::Encoding(self.parse_literal_string()?),
|
||||||
_ => self.expected("option", self.peek_token())?,
|
_ => self.expected("option", self.peek_token())?,
|
||||||
|
@ -4454,7 +4454,7 @@ impl<'a> Parser<'a> {
|
||||||
) -> Result<Option<TableAlias>, ParserError> {
|
) -> Result<Option<TableAlias>, ParserError> {
|
||||||
match self.parse_optional_alias(reserved_kwds)? {
|
match self.parse_optional_alias(reserved_kwds)? {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
Ok(Some(TableAlias { name, columns }))
|
Ok(Some(TableAlias { name, columns }))
|
||||||
}
|
}
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
@ -4505,11 +4505,17 @@ impl<'a> Parser<'a> {
|
||||||
pub fn parse_parenthesized_column_list(
|
pub fn parse_parenthesized_column_list(
|
||||||
&mut self,
|
&mut self,
|
||||||
optional: IsOptional,
|
optional: IsOptional,
|
||||||
|
allow_empty: bool,
|
||||||
) -> Result<Vec<Ident>, ParserError> {
|
) -> Result<Vec<Ident>, ParserError> {
|
||||||
if self.consume_token(&Token::LParen) {
|
if self.consume_token(&Token::LParen) {
|
||||||
|
if allow_empty && self.peek_token().token == Token::RParen {
|
||||||
|
self.next_token();
|
||||||
|
Ok(vec![])
|
||||||
|
} else {
|
||||||
let cols = self.parse_comma_separated(Parser::parse_identifier)?;
|
let cols = self.parse_comma_separated(Parser::parse_identifier)?;
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
Ok(cols)
|
Ok(cols)
|
||||||
|
}
|
||||||
} else if optional == Optional {
|
} else if optional == Optional {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
} else {
|
} else {
|
||||||
|
@ -4811,7 +4817,7 @@ impl<'a> Parser<'a> {
|
||||||
from: None,
|
from: None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
self.expect_keyword(Keyword::AS)?;
|
self.expect_keyword(Keyword::AS)?;
|
||||||
self.expect_token(&Token::LParen)?;
|
self.expect_token(&Token::LParen)?;
|
||||||
let query = Box::new(self.parse_query()?);
|
let query = Box::new(self.parse_query()?);
|
||||||
|
@ -4848,7 +4854,8 @@ impl<'a> Parser<'a> {
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
SetExpr::Query(Box::new(subquery))
|
SetExpr::Query(Box::new(subquery))
|
||||||
} else if self.parse_keyword(Keyword::VALUES) {
|
} else if self.parse_keyword(Keyword::VALUES) {
|
||||||
SetExpr::Values(self.parse_values()?)
|
let is_mysql = dialect_of!(self is MySqlDialect);
|
||||||
|
SetExpr::Values(self.parse_values(is_mysql)?)
|
||||||
} else if self.parse_keyword(Keyword::TABLE) {
|
} else if self.parse_keyword(Keyword::TABLE) {
|
||||||
SetExpr::Table(Box::new(self.parse_as_table()?))
|
SetExpr::Table(Box::new(self.parse_as_table()?))
|
||||||
} else {
|
} else {
|
||||||
|
@ -5645,7 +5652,7 @@ impl<'a> Parser<'a> {
|
||||||
let constraint = self.parse_expr()?;
|
let constraint = self.parse_expr()?;
|
||||||
Ok(JoinConstraint::On(constraint))
|
Ok(JoinConstraint::On(constraint))
|
||||||
} else if self.parse_keyword(Keyword::USING) {
|
} else if self.parse_keyword(Keyword::USING) {
|
||||||
let columns = self.parse_parenthesized_column_list(Mandatory)?;
|
let columns = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
Ok(JoinConstraint::Using(columns))
|
Ok(JoinConstraint::Using(columns))
|
||||||
} else {
|
} else {
|
||||||
Ok(JoinConstraint::None)
|
Ok(JoinConstraint::None)
|
||||||
|
@ -5770,7 +5777,7 @@ impl<'a> Parser<'a> {
|
||||||
]) {
|
]) {
|
||||||
let columns = match kw {
|
let columns = match kw {
|
||||||
Keyword::INSERT | Keyword::REFERENCES | Keyword::SELECT | Keyword::UPDATE => {
|
Keyword::INSERT | Keyword::REFERENCES | Keyword::SELECT | Keyword::UPDATE => {
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
if columns.is_empty() {
|
if columns.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -5856,7 +5863,8 @@ impl<'a> Parser<'a> {
|
||||||
// Hive lets you put table here regardless
|
// Hive lets you put table here regardless
|
||||||
let table = self.parse_keyword(Keyword::TABLE);
|
let table = self.parse_keyword(Keyword::TABLE);
|
||||||
let table_name = self.parse_object_name()?;
|
let table_name = self.parse_object_name()?;
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let is_mysql = dialect_of!(self is MySqlDialect);
|
||||||
|
let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?;
|
||||||
|
|
||||||
let partitioned = if self.parse_keyword(Keyword::PARTITION) {
|
let partitioned = if self.parse_keyword(Keyword::PARTITION) {
|
||||||
self.expect_token(&Token::LParen)?;
|
self.expect_token(&Token::LParen)?;
|
||||||
|
@ -5868,7 +5876,7 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hive allows you to specify columns after partitions as well if you want.
|
// Hive allows you to specify columns after partitions as well if you want.
|
||||||
let after_columns = self.parse_parenthesized_column_list(Optional)?;
|
let after_columns = self.parse_parenthesized_column_list(Optional, false)?;
|
||||||
|
|
||||||
let source = Box::new(self.parse_query()?);
|
let source = Box::new(self.parse_query()?);
|
||||||
let on = if self.parse_keyword(Keyword::ON) {
|
let on = if self.parse_keyword(Keyword::ON) {
|
||||||
|
@ -5878,7 +5886,7 @@ impl<'a> Parser<'a> {
|
||||||
Some(ConflictTarget::OnConstraint(self.parse_object_name()?))
|
Some(ConflictTarget::OnConstraint(self.parse_object_name()?))
|
||||||
} else if self.peek_token() == Token::LParen {
|
} else if self.peek_token() == Token::LParen {
|
||||||
Some(ConflictTarget::Columns(
|
Some(ConflictTarget::Columns(
|
||||||
self.parse_parenthesized_column_list(IsOptional::Mandatory)?,
|
self.parse_parenthesized_column_list(IsOptional::Mandatory, false)?,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -6091,7 +6099,7 @@ impl<'a> Parser<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> Result<Option<ExceptSelectItem>, ParserError> {
|
) -> Result<Option<ExceptSelectItem>, ParserError> {
|
||||||
let opt_except = if self.parse_keyword(Keyword::EXCEPT) {
|
let opt_except = if self.parse_keyword(Keyword::EXCEPT) {
|
||||||
let idents = self.parse_parenthesized_column_list(Mandatory)?;
|
let idents = self.parse_parenthesized_column_list(Mandatory, false)?;
|
||||||
match &idents[..] {
|
match &idents[..] {
|
||||||
[] => {
|
[] => {
|
||||||
return self.expected(
|
return self.expected(
|
||||||
|
@ -6236,7 +6244,7 @@ impl<'a> Parser<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_values(&mut self) -> Result<Values, ParserError> {
|
pub fn parse_values(&mut self, allow_empty: bool) -> Result<Values, ParserError> {
|
||||||
let mut explicit_row = false;
|
let mut explicit_row = false;
|
||||||
|
|
||||||
let rows = self.parse_comma_separated(|parser| {
|
let rows = self.parse_comma_separated(|parser| {
|
||||||
|
@ -6245,9 +6253,14 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
parser.expect_token(&Token::LParen)?;
|
parser.expect_token(&Token::LParen)?;
|
||||||
|
if allow_empty && parser.peek_token().token == Token::RParen {
|
||||||
|
parser.next_token();
|
||||||
|
Ok(vec![])
|
||||||
|
} else {
|
||||||
let exprs = parser.parse_comma_separated(Parser::parse_expr)?;
|
let exprs = parser.parse_comma_separated(Parser::parse_expr)?;
|
||||||
parser.expect_token(&Token::RParen)?;
|
parser.expect_token(&Token::RParen)?;
|
||||||
Ok(exprs)
|
Ok(exprs)
|
||||||
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok(Values { explicit_row, rows })
|
Ok(Values { explicit_row, rows })
|
||||||
}
|
}
|
||||||
|
@ -6413,9 +6426,10 @@ impl<'a> Parser<'a> {
|
||||||
"INSERT in MATCHED merge clause".to_string(),
|
"INSERT in MATCHED merge clause".to_string(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let columns = self.parse_parenthesized_column_list(Optional)?;
|
let is_mysql = dialect_of!(self is MySqlDialect);
|
||||||
|
let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?;
|
||||||
self.expect_keyword(Keyword::VALUES)?;
|
self.expect_keyword(Keyword::VALUES)?;
|
||||||
let values = self.parse_values()?;
|
let values = self.parse_values(is_mysql)?;
|
||||||
MergeClause::NotMatched {
|
MergeClause::NotMatched {
|
||||||
predicate,
|
predicate,
|
||||||
columns,
|
columns,
|
||||||
|
|
|
@ -692,6 +692,41 @@ fn parse_simple_insert() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_empty_row_insert() {
|
||||||
|
let sql = "INSERT INTO tb () VALUES (), ()";
|
||||||
|
|
||||||
|
match mysql().one_statement_parses_to(sql, "INSERT INTO tb VALUES (), ()") {
|
||||||
|
Statement::Insert {
|
||||||
|
table_name,
|
||||||
|
columns,
|
||||||
|
source,
|
||||||
|
on,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
assert_eq!(ObjectName(vec![Ident::new("tb")]), table_name);
|
||||||
|
assert!(columns.is_empty());
|
||||||
|
assert!(on.is_none());
|
||||||
|
assert_eq!(
|
||||||
|
Box::new(Query {
|
||||||
|
with: None,
|
||||||
|
body: Box::new(SetExpr::Values(Values {
|
||||||
|
explicit_row: false,
|
||||||
|
rows: vec![vec![], vec![]]
|
||||||
|
})),
|
||||||
|
order_by: vec![],
|
||||||
|
limit: None,
|
||||||
|
offset: None,
|
||||||
|
fetch: None,
|
||||||
|
locks: vec![],
|
||||||
|
}),
|
||||||
|
source
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_insert_with_on_duplicate_update() {
|
fn parse_insert_with_on_duplicate_update() {
|
||||||
let sql = "INSERT INTO permission_groups (name, description, perm_create, perm_read, perm_update, perm_delete) VALUES ('accounting_manager', 'Some description about the group', true, true, true, true) ON DUPLICATE KEY UPDATE description = VALUES(description), perm_create = VALUES(perm_create), perm_read = VALUES(perm_read), perm_update = VALUES(perm_update), perm_delete = VALUES(perm_delete)";
|
let sql = "INSERT INTO permission_groups (name, description, perm_create, perm_read, perm_update, perm_delete) VALUES ('accounting_manager', 'Some description about the group', true, true, true, true) ON DUPLICATE KEY UPDATE description = VALUES(description), perm_create = VALUES(perm_create), perm_read = VALUES(perm_read), perm_update = VALUES(perm_update), perm_delete = VALUES(perm_delete)";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue