mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Support Mysql REPLACE
statement and PRIORITY
clause of INSERT
(#1072)
This commit is contained in:
parent
7ea47c71fb
commit
c62ecb1100
5 changed files with 211 additions and 2 deletions
|
@ -1443,6 +1443,10 @@ pub enum Statement {
|
|||
on: Option<OnInsert>,
|
||||
/// RETURNING
|
||||
returning: Option<Vec<SelectItem>>,
|
||||
/// Only for mysql
|
||||
replace_into: bool,
|
||||
/// Only for mysql
|
||||
priority: Option<MysqlInsertPriority>,
|
||||
},
|
||||
// TODO: Support ROW FORMAT
|
||||
Directory {
|
||||
|
@ -2404,18 +2408,29 @@ impl fmt::Display for Statement {
|
|||
table,
|
||||
on,
|
||||
returning,
|
||||
replace_into,
|
||||
priority,
|
||||
} => {
|
||||
if let Some(action) = or {
|
||||
write!(f, "INSERT OR {action} INTO {table_name} ")?;
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"INSERT{ignore}{over}{int}{tbl} {table_name} ",
|
||||
"{start}",
|
||||
start = if *replace_into { "REPLACE" } else { "INSERT" },
|
||||
)?;
|
||||
if let Some(priority) = priority {
|
||||
write!(f, " {priority}",)?;
|
||||
}
|
||||
|
||||
write!(
|
||||
f,
|
||||
"{ignore}{over}{int}{tbl} {table_name} ",
|
||||
table_name = table_name,
|
||||
ignore = if *ignore { " IGNORE" } else { "" },
|
||||
over = if *overwrite { " OVERWRITE" } else { "" },
|
||||
int = if *into { " INTO" } else { "" },
|
||||
tbl = if *table { " TABLE" } else { "" }
|
||||
tbl = if *table { " TABLE" } else { "" },
|
||||
)?;
|
||||
}
|
||||
if !columns.is_empty() {
|
||||
|
@ -4522,6 +4537,31 @@ impl fmt::Display for SqliteOnConflict {
|
|||
}
|
||||
}
|
||||
|
||||
/// Mysql specific syntax
|
||||
///
|
||||
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/replace.html)
|
||||
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/insert.html)
|
||||
/// for more details.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
pub enum MysqlInsertPriority {
|
||||
LowPriority,
|
||||
Delayed,
|
||||
HighPriority,
|
||||
}
|
||||
|
||||
impl fmt::Display for crate::ast::MysqlInsertPriority {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use MysqlInsertPriority::*;
|
||||
match self {
|
||||
LowPriority => write!(f, "LOW_PRIORITY"),
|
||||
Delayed => write!(f, "DELAYED"),
|
||||
HighPriority => write!(f, "HIGH_PRIORITY"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
|
||||
|
|
|
@ -210,6 +210,7 @@ define_keywords!(
|
|||
DECLARE,
|
||||
DEFAULT,
|
||||
DEFERRED,
|
||||
DELAYED,
|
||||
DELETE,
|
||||
DELIMITED,
|
||||
DELIMITER,
|
||||
|
@ -315,6 +316,7 @@ define_keywords!(
|
|||
HASH,
|
||||
HAVING,
|
||||
HEADER,
|
||||
HIGH_PRIORITY,
|
||||
HISTORY,
|
||||
HIVEVAR,
|
||||
HOLD,
|
||||
|
|
|
@ -490,6 +490,7 @@ impl<'a> Parser<'a> {
|
|||
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
|
||||
Keyword::DELETE => Ok(self.parse_delete()?),
|
||||
Keyword::INSERT => Ok(self.parse_insert()?),
|
||||
Keyword::REPLACE => Ok(self.parse_replace()?),
|
||||
Keyword::UNCACHE => Ok(self.parse_uncache_table()?),
|
||||
Keyword::UPDATE => Ok(self.parse_update()?),
|
||||
Keyword::ALTER => Ok(self.parse_alter()?),
|
||||
|
@ -7379,6 +7380,20 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parse an REPLACE statement
|
||||
pub fn parse_replace(&mut self) -> Result<Statement, ParserError> {
|
||||
if !dialect_of!(self is MySqlDialect | GenericDialect) {
|
||||
return parser_err!("Unsupported statement REPLACE", self.peek_token().location);
|
||||
}
|
||||
|
||||
let insert = &mut self.parse_insert().unwrap();
|
||||
if let Statement::Insert { replace_into, .. } = insert {
|
||||
*replace_into = true;
|
||||
}
|
||||
|
||||
Ok(insert.clone())
|
||||
}
|
||||
|
||||
/// Parse an INSERT statement
|
||||
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
|
||||
let or = if !dialect_of!(self is SQLiteDialect) {
|
||||
|
@ -7399,9 +7414,23 @@ impl<'a> Parser<'a> {
|
|||
None
|
||||
};
|
||||
|
||||
let priority = if !dialect_of!(self is MySqlDialect | GenericDialect) {
|
||||
None
|
||||
} else if self.parse_keyword(Keyword::LOW_PRIORITY) {
|
||||
Some(MysqlInsertPriority::LowPriority)
|
||||
} else if self.parse_keyword(Keyword::DELAYED) {
|
||||
Some(MysqlInsertPriority::Delayed)
|
||||
} else if self.parse_keyword(Keyword::HIGH_PRIORITY) {
|
||||
Some(MysqlInsertPriority::HighPriority)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ignore = dialect_of!(self is MySqlDialect | GenericDialect)
|
||||
&& self.parse_keyword(Keyword::IGNORE);
|
||||
|
||||
let replace_into = false;
|
||||
|
||||
let action = self.parse_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE]);
|
||||
let into = action == Some(Keyword::INTO);
|
||||
let overwrite = action == Some(Keyword::OVERWRITE);
|
||||
|
@ -7511,6 +7540,8 @@ impl<'a> Parser<'a> {
|
|||
table,
|
||||
on,
|
||||
returning,
|
||||
replace_into,
|
||||
priority,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,17 @@ fn parse_insert_values() {
|
|||
verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_replace_into() {
|
||||
let dialect = PostgreSqlDialect {};
|
||||
let sql = "REPLACE INTO public.customer (id, name, active) VALUES (1, 2, 3)";
|
||||
|
||||
assert_eq!(
|
||||
ParserError::ParserError("Unsupported statement REPLACE at Line: 1, Column 9".to_string()),
|
||||
Parser::parse_sql(&dialect, sql,).unwrap_err(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_insert_default_values() {
|
||||
let insert_with_default_values = verified_stmt("INSERT INTO test_table DEFAULT VALUES");
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
use matches::assert_matches;
|
||||
use sqlparser::ast::Expr;
|
||||
use sqlparser::ast::MysqlInsertPriority::{Delayed, HighPriority, LowPriority};
|
||||
use sqlparser::ast::Value;
|
||||
use sqlparser::ast::*;
|
||||
use sqlparser::dialect::{GenericDialect, MySqlDialect};
|
||||
|
@ -1035,6 +1036,130 @@ fn parse_ignore_insert() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_priority_insert() {
|
||||
let sql = r"INSERT HIGH_PRIORITY INTO tasks (title, priority) VALUES ('Test Some Inserts', 1)";
|
||||
|
||||
match mysql_and_generic().verified_stmt(sql) {
|
||||
Statement::Insert {
|
||||
table_name,
|
||||
columns,
|
||||
source,
|
||||
on,
|
||||
priority,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(ObjectName(vec![Ident::new("tasks")]), table_name);
|
||||
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
|
||||
assert!(on.is_none());
|
||||
assert_eq!(priority, Some(HighPriority));
|
||||
assert_eq!(
|
||||
Some(Box::new(Query {
|
||||
with: None,
|
||||
body: Box::new(SetExpr::Values(Values {
|
||||
explicit_row: false,
|
||||
rows: vec![vec![
|
||||
Expr::Value(Value::SingleQuotedString("Test Some Inserts".to_string())),
|
||||
Expr::Value(number("1"))
|
||||
]]
|
||||
})),
|
||||
order_by: vec![],
|
||||
limit: None,
|
||||
limit_by: vec![],
|
||||
offset: None,
|
||||
fetch: None,
|
||||
locks: vec![],
|
||||
for_clause: None,
|
||||
})),
|
||||
source
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let sql2 = r"INSERT LOW_PRIORITY INTO tasks (title, priority) VALUES ('Test Some Inserts', 1)";
|
||||
|
||||
match mysql().verified_stmt(sql2) {
|
||||
Statement::Insert {
|
||||
table_name,
|
||||
columns,
|
||||
source,
|
||||
on,
|
||||
priority,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(ObjectName(vec![Ident::new("tasks")]), table_name);
|
||||
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
|
||||
assert!(on.is_none());
|
||||
assert_eq!(priority, Some(LowPriority));
|
||||
assert_eq!(
|
||||
Some(Box::new(Query {
|
||||
with: None,
|
||||
body: Box::new(SetExpr::Values(Values {
|
||||
explicit_row: false,
|
||||
rows: vec![vec![
|
||||
Expr::Value(Value::SingleQuotedString("Test Some Inserts".to_string())),
|
||||
Expr::Value(number("1"))
|
||||
]]
|
||||
})),
|
||||
order_by: vec![],
|
||||
limit: None,
|
||||
limit_by: vec![],
|
||||
offset: None,
|
||||
fetch: None,
|
||||
locks: vec![],
|
||||
for_clause: None,
|
||||
})),
|
||||
source
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_replace_insert() {
|
||||
let sql = r"REPLACE DELAYED INTO tasks (title, priority) VALUES ('Test Some Inserts', 1)";
|
||||
match mysql().verified_stmt(sql) {
|
||||
Statement::Insert {
|
||||
table_name,
|
||||
columns,
|
||||
source,
|
||||
on,
|
||||
replace_into,
|
||||
priority,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(ObjectName(vec![Ident::new("tasks")]), table_name);
|
||||
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
|
||||
assert!(on.is_none());
|
||||
assert!(replace_into);
|
||||
assert_eq!(priority, Some(Delayed));
|
||||
assert_eq!(
|
||||
Some(Box::new(Query {
|
||||
with: None,
|
||||
body: Box::new(SetExpr::Values(Values {
|
||||
explicit_row: false,
|
||||
rows: vec![vec![
|
||||
Expr::Value(Value::SingleQuotedString("Test Some Inserts".to_string())),
|
||||
Expr::Value(number("1"))
|
||||
]]
|
||||
})),
|
||||
order_by: vec![],
|
||||
limit: None,
|
||||
limit_by: vec![],
|
||||
offset: None,
|
||||
fetch: None,
|
||||
locks: vec![],
|
||||
for_clause: None,
|
||||
})),
|
||||
source
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_row_insert() {
|
||||
let sql = "INSERT INTO tb () VALUES (), ()";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue