Skip to content

Commit c62ecb1

Browse files
authored
Support Mysql REPLACE statement and PRIORITY clause of INSERT (apache#1072)
1 parent 7ea47c7 commit c62ecb1

File tree

5 files changed

+211
-2
lines changed

5 files changed

+211
-2
lines changed

src/ast/mod.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,10 @@ pub enum Statement {
14431443
on: Option<OnInsert>,
14441444
/// RETURNING
14451445
returning: Option<Vec<SelectItem>>,
1446+
/// Only for mysql
1447+
replace_into: bool,
1448+
/// Only for mysql
1449+
priority: Option<MysqlInsertPriority>,
14461450
},
14471451
// TODO: Support ROW FORMAT
14481452
Directory {
@@ -2404,18 +2408,29 @@ impl fmt::Display for Statement {
24042408
table,
24052409
on,
24062410
returning,
2411+
replace_into,
2412+
priority,
24072413
} => {
24082414
if let Some(action) = or {
24092415
write!(f, "INSERT OR {action} INTO {table_name} ")?;
24102416
} else {
24112417
write!(
24122418
f,
2413-
"INSERT{ignore}{over}{int}{tbl} {table_name} ",
2419+
"{start}",
2420+
start = if *replace_into { "REPLACE" } else { "INSERT" },
2421+
)?;
2422+
if let Some(priority) = priority {
2423+
write!(f, " {priority}",)?;
2424+
}
2425+
2426+
write!(
2427+
f,
2428+
"{ignore}{over}{int}{tbl} {table_name} ",
24142429
table_name = table_name,
24152430
ignore = if *ignore { " IGNORE" } else { "" },
24162431
over = if *overwrite { " OVERWRITE" } else { "" },
24172432
int = if *into { " INTO" } else { "" },
2418-
tbl = if *table { " TABLE" } else { "" }
2433+
tbl = if *table { " TABLE" } else { "" },
24192434
)?;
24202435
}
24212436
if !columns.is_empty() {
@@ -4522,6 +4537,31 @@ impl fmt::Display for SqliteOnConflict {
45224537
}
45234538
}
45244539

4540+
/// Mysql specific syntax
4541+
///
4542+
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/replace.html)
4543+
/// See [Mysql documentation](https://dev.mysql.com/doc/refman/8.0/en/insert.html)
4544+
/// for more details.
4545+
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
4546+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4547+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
4548+
pub enum MysqlInsertPriority {
4549+
LowPriority,
4550+
Delayed,
4551+
HighPriority,
4552+
}
4553+
4554+
impl fmt::Display for crate::ast::MysqlInsertPriority {
4555+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
4556+
use MysqlInsertPriority::*;
4557+
match self {
4558+
LowPriority => write!(f, "LOW_PRIORITY"),
4559+
Delayed => write!(f, "DELAYED"),
4560+
HighPriority => write!(f, "HIGH_PRIORITY"),
4561+
}
4562+
}
4563+
}
4564+
45254565
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
45264566
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
45274567
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/keywords.rs

+2
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ define_keywords!(
210210
DECLARE,
211211
DEFAULT,
212212
DEFERRED,
213+
DELAYED,
213214
DELETE,
214215
DELIMITED,
215216
DELIMITER,
@@ -315,6 +316,7 @@ define_keywords!(
315316
HASH,
316317
HAVING,
317318
HEADER,
319+
HIGH_PRIORITY,
318320
HISTORY,
319321
HIVEVAR,
320322
HOLD,

src/parser/mod.rs

+31
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ impl<'a> Parser<'a> {
490490
Keyword::FETCH => Ok(self.parse_fetch_statement()?),
491491
Keyword::DELETE => Ok(self.parse_delete()?),
492492
Keyword::INSERT => Ok(self.parse_insert()?),
493+
Keyword::REPLACE => Ok(self.parse_replace()?),
493494
Keyword::UNCACHE => Ok(self.parse_uncache_table()?),
494495
Keyword::UPDATE => Ok(self.parse_update()?),
495496
Keyword::ALTER => Ok(self.parse_alter()?),
@@ -7379,6 +7380,20 @@ impl<'a> Parser<'a> {
73797380
})
73807381
}
73817382

7383+
/// Parse an REPLACE statement
7384+
pub fn parse_replace(&mut self) -> Result<Statement, ParserError> {
7385+
if !dialect_of!(self is MySqlDialect | GenericDialect) {
7386+
return parser_err!("Unsupported statement REPLACE", self.peek_token().location);
7387+
}
7388+
7389+
let insert = &mut self.parse_insert().unwrap();
7390+
if let Statement::Insert { replace_into, .. } = insert {
7391+
*replace_into = true;
7392+
}
7393+
7394+
Ok(insert.clone())
7395+
}
7396+
73827397
/// Parse an INSERT statement
73837398
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
73847399
let or = if !dialect_of!(self is SQLiteDialect) {
@@ -7399,9 +7414,23 @@ impl<'a> Parser<'a> {
73997414
None
74007415
};
74017416

7417+
let priority = if !dialect_of!(self is MySqlDialect | GenericDialect) {
7418+
None
7419+
} else if self.parse_keyword(Keyword::LOW_PRIORITY) {
7420+
Some(MysqlInsertPriority::LowPriority)
7421+
} else if self.parse_keyword(Keyword::DELAYED) {
7422+
Some(MysqlInsertPriority::Delayed)
7423+
} else if self.parse_keyword(Keyword::HIGH_PRIORITY) {
7424+
Some(MysqlInsertPriority::HighPriority)
7425+
} else {
7426+
None
7427+
};
7428+
74027429
let ignore = dialect_of!(self is MySqlDialect | GenericDialect)
74037430
&& self.parse_keyword(Keyword::IGNORE);
74047431

7432+
let replace_into = false;
7433+
74057434
let action = self.parse_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE]);
74067435
let into = action == Some(Keyword::INTO);
74077436
let overwrite = action == Some(Keyword::OVERWRITE);
@@ -7511,6 +7540,8 @@ impl<'a> Parser<'a> {
75117540
table,
75127541
on,
75137542
returning,
7543+
replace_into,
7544+
priority,
75147545
})
75157546
}
75167547
}

tests/sqlparser_common.rs

+11
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,17 @@ fn parse_insert_values() {
107107
verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)");
108108
}
109109

110+
#[test]
111+
fn parse_replace_into() {
112+
let dialect = PostgreSqlDialect {};
113+
let sql = "REPLACE INTO public.customer (id, name, active) VALUES (1, 2, 3)";
114+
115+
assert_eq!(
116+
ParserError::ParserError("Unsupported statement REPLACE at Line: 1, Column 9".to_string()),
117+
Parser::parse_sql(&dialect, sql,).unwrap_err(),
118+
)
119+
}
120+
110121
#[test]
111122
fn parse_insert_default_values() {
112123
let insert_with_default_values = verified_stmt("INSERT INTO test_table DEFAULT VALUES");

tests/sqlparser_mysql.rs

+125
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
1717
use matches::assert_matches;
1818
use sqlparser::ast::Expr;
19+
use sqlparser::ast::MysqlInsertPriority::{Delayed, HighPriority, LowPriority};
1920
use sqlparser::ast::Value;
2021
use sqlparser::ast::*;
2122
use sqlparser::dialect::{GenericDialect, MySqlDialect};
@@ -1035,6 +1036,130 @@ fn parse_ignore_insert() {
10351036
}
10361037
}
10371038

1039+
#[test]
1040+
fn parse_priority_insert() {
1041+
let sql = r"INSERT HIGH_PRIORITY INTO tasks (title, priority) VALUES ('Test Some Inserts', 1)";
1042+
1043+
match mysql_and_generic().verified_stmt(sql) {
1044+
Statement::Insert {
1045+
table_name,
1046+
columns,
1047+
source,
1048+
on,
1049+
priority,
1050+
..
1051+
} => {
1052+
assert_eq!(ObjectName(vec![Ident::new("tasks")]), table_name);
1053+
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
1054+
assert!(on.is_none());
1055+
assert_eq!(priority, Some(HighPriority));
1056+
assert_eq!(
1057+
Some(Box::new(Query {
1058+
with: None,
1059+
body: Box::new(SetExpr::Values(Values {
1060+
explicit_row: false,
1061+
rows: vec![vec![
1062+
Expr::Value(Value::SingleQuotedString("Test Some Inserts".to_string())),
1063+
Expr::Value(number("1"))
1064+
]]
1065+
})),
1066+
order_by: vec![],
1067+
limit: None,
1068+
limit_by: vec![],
1069+
offset: None,
1070+
fetch: None,
1071+
locks: vec![],
1072+
for_clause: None,
1073+
})),
1074+
source
1075+
);
1076+
}
1077+
_ => unreachable!(),
1078+
}
1079+
1080+
let sql2 = r"INSERT LOW_PRIORITY INTO tasks (title, priority) VALUES ('Test Some Inserts', 1)";
1081+
1082+
match mysql().verified_stmt(sql2) {
1083+
Statement::Insert {
1084+
table_name,
1085+
columns,
1086+
source,
1087+
on,
1088+
priority,
1089+
..
1090+
} => {
1091+
assert_eq!(ObjectName(vec![Ident::new("tasks")]), table_name);
1092+
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
1093+
assert!(on.is_none());
1094+
assert_eq!(priority, Some(LowPriority));
1095+
assert_eq!(
1096+
Some(Box::new(Query {
1097+
with: None,
1098+
body: Box::new(SetExpr::Values(Values {
1099+
explicit_row: false,
1100+
rows: vec![vec![
1101+
Expr::Value(Value::SingleQuotedString("Test Some Inserts".to_string())),
1102+
Expr::Value(number("1"))
1103+
]]
1104+
})),
1105+
order_by: vec![],
1106+
limit: None,
1107+
limit_by: vec![],
1108+
offset: None,
1109+
fetch: None,
1110+
locks: vec![],
1111+
for_clause: None,
1112+
})),
1113+
source
1114+
);
1115+
}
1116+
_ => unreachable!(),
1117+
}
1118+
}
1119+
1120+
#[test]
1121+
fn parse_replace_insert() {
1122+
let sql = r"REPLACE DELAYED INTO tasks (title, priority) VALUES ('Test Some Inserts', 1)";
1123+
match mysql().verified_stmt(sql) {
1124+
Statement::Insert {
1125+
table_name,
1126+
columns,
1127+
source,
1128+
on,
1129+
replace_into,
1130+
priority,
1131+
..
1132+
} => {
1133+
assert_eq!(ObjectName(vec![Ident::new("tasks")]), table_name);
1134+
assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns);
1135+
assert!(on.is_none());
1136+
assert!(replace_into);
1137+
assert_eq!(priority, Some(Delayed));
1138+
assert_eq!(
1139+
Some(Box::new(Query {
1140+
with: None,
1141+
body: Box::new(SetExpr::Values(Values {
1142+
explicit_row: false,
1143+
rows: vec![vec![
1144+
Expr::Value(Value::SingleQuotedString("Test Some Inserts".to_string())),
1145+
Expr::Value(number("1"))
1146+
]]
1147+
})),
1148+
order_by: vec![],
1149+
limit: None,
1150+
limit_by: vec![],
1151+
offset: None,
1152+
fetch: None,
1153+
locks: vec![],
1154+
for_clause: None,
1155+
})),
1156+
source
1157+
);
1158+
}
1159+
_ => unreachable!(),
1160+
}
1161+
}
1162+
10381163
#[test]
10391164
fn parse_empty_row_insert() {
10401165
let sql = "INSERT INTO tb () VALUES (), ()";

0 commit comments

Comments
 (0)