Skip to content

Commit add8991

Browse files
authored
feat: support sqlite insert or statement (apache#281)
1 parent 07342d5 commit add8991

File tree

4 files changed

+105
-9
lines changed

4 files changed

+105
-9
lines changed

src/ast/mod.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,8 @@ pub enum Statement {
508508
Query(Box<Query>),
509509
/// INSERT
510510
Insert {
511+
/// Only for Sqlite
512+
or: Option<SqliteOnConflict>,
511513
/// TABLE
512514
table_name: ObjectName,
513515
/// COLUMNS
@@ -804,6 +806,7 @@ impl fmt::Display for Statement {
804806
Ok(())
805807
}
806808
Statement::Insert {
809+
or,
807810
table_name,
808811
overwrite,
809812
partitioned,
@@ -812,13 +815,17 @@ impl fmt::Display for Statement {
812815
source,
813816
table,
814817
} => {
815-
write!(
816-
f,
817-
"INSERT {act}{tbl} {table_name} ",
818-
table_name = table_name,
819-
act = if *overwrite { "OVERWRITE" } else { "INTO" },
820-
tbl = if *table { " TABLE" } else { "" }
821-
)?;
818+
if let Some(action) = or {
819+
write!(f, "INSERT OR {} INTO {} ", action, table_name)?;
820+
} else {
821+
write!(
822+
f,
823+
"INSERT {act}{tbl} {table_name} ",
824+
table_name = table_name,
825+
act = if *overwrite { "OVERWRITE" } else { "INTO" },
826+
tbl = if *table { " TABLE" } else { "" }
827+
)?;
828+
}
822829
if !columns.is_empty() {
823830
write!(f, "({}) ", display_comma_separated(columns))?;
824831
}
@@ -832,6 +839,7 @@ impl fmt::Display for Statement {
832839
}
833840
write!(f, "{}", source)
834841
}
842+
835843
Statement::Copy {
836844
table_name,
837845
columns,
@@ -1560,3 +1568,29 @@ impl fmt::Display for SetVariableValue {
15601568
}
15611569
}
15621570
}
1571+
1572+
/// Sqlite specific syntax
1573+
///
1574+
/// https://sqlite.org/lang_conflict.html
1575+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1576+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1577+
pub enum SqliteOnConflict {
1578+
Rollback,
1579+
Abort,
1580+
Fail,
1581+
Ignore,
1582+
Replace,
1583+
}
1584+
1585+
impl fmt::Display for SqliteOnConflict {
1586+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1587+
use SqliteOnConflict::*;
1588+
match self {
1589+
Rollback => write!(f, "ROLLBACK"),
1590+
Abort => write!(f, "ABORT"),
1591+
Fail => write!(f, "FAIL"),
1592+
Ignore => write!(f, "IGNORE"),
1593+
Replace => write!(f, "REPLACE"),
1594+
}
1595+
}
1596+
}

src/dialect/keywords.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ macro_rules! define_keywords {
6666

6767
// The following keywords should be sorted to be able to match using binary search
6868
define_keywords!(
69+
ABORT,
6970
ABS,
7071
ACTION,
7172
ADD,
@@ -202,6 +203,7 @@ define_keywords!(
202203
EXTENDED,
203204
EXTERNAL,
204205
EXTRACT,
206+
FAIL,
205207
FALSE,
206208
FETCH,
207209
FIELDS,
@@ -233,6 +235,7 @@ define_keywords!(
233235
HOUR,
234236
IDENTITY,
235237
IF,
238+
IGNORE,
236239
IN,
237240
INDEX,
238241
INDICATOR,

src/parser.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,10 @@ impl<'a> Parser<'a> {
163163
Keyword::DEALLOCATE => Ok(self.parse_deallocate()?),
164164
Keyword::EXECUTE => Ok(self.parse_execute()?),
165165
Keyword::PREPARE => Ok(self.parse_prepare()?),
166+
Keyword::REPLACE if dialect_of!(self is SQLiteDialect ) => {
167+
self.prev_token();
168+
Ok(self.parse_insert()?)
169+
}
166170
_ => self.expected("an SQL statement", Token::Word(w)),
167171
},
168172
Token::LParen => {
@@ -2719,6 +2723,23 @@ impl<'a> Parser<'a> {
27192723

27202724
/// Parse an INSERT statement
27212725
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
2726+
let or = if !dialect_of!(self is SQLiteDialect) {
2727+
None
2728+
} else if self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]) {
2729+
Some(SqliteOnConflict::Replace)
2730+
} else if self.parse_keywords(&[Keyword::OR, Keyword::ROLLBACK]) {
2731+
Some(SqliteOnConflict::Rollback)
2732+
} else if self.parse_keywords(&[Keyword::OR, Keyword::ABORT]) {
2733+
Some(SqliteOnConflict::Abort)
2734+
} else if self.parse_keywords(&[Keyword::OR, Keyword::FAIL]) {
2735+
Some(SqliteOnConflict::Fail)
2736+
} else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) {
2737+
Some(SqliteOnConflict::Ignore)
2738+
} else if self.parse_keyword(Keyword::REPLACE) {
2739+
Some(SqliteOnConflict::Replace)
2740+
} else {
2741+
None
2742+
};
27222743
let action = self.expect_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE])?;
27232744
let overwrite = action == Keyword::OVERWRITE;
27242745
let local = self.parse_keyword(Keyword::LOCAL);
@@ -2758,6 +2779,7 @@ impl<'a> Parser<'a> {
27582779

27592780
let source = Box::new(self.parse_query()?);
27602781
Ok(Statement::Insert {
2782+
or,
27612783
table_name,
27622784
overwrite,
27632785
partitioned,

tests/sqlparser_common.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use test_utils::{all_dialects, expr_from_projection, join, number, only, table,
2424

2525
use matches::assert_matches;
2626
use sqlparser::ast::*;
27-
use sqlparser::dialect::keywords::ALL_KEYWORDS;
28-
use sqlparser::parser::ParserError;
27+
use sqlparser::dialect::{keywords::ALL_KEYWORDS, SQLiteDialect};
28+
use sqlparser::parser::{Parser, ParserError};
2929

3030
#[test]
3131
fn parse_insert_values() {
@@ -97,6 +97,43 @@ fn parse_insert_invalid() {
9797
);
9898
}
9999

100+
#[test]
101+
fn parse_insert_sqlite() {
102+
let dialect = SQLiteDialect {};
103+
104+
let check = |sql: &str, expected_action: Option<SqliteOnConflict>| match Parser::parse_sql(
105+
&dialect, &sql,
106+
)
107+
.unwrap()
108+
.pop()
109+
.unwrap()
110+
{
111+
Statement::Insert { or, .. } => assert_eq!(or, expected_action),
112+
_ => panic!(sql.to_string()),
113+
};
114+
115+
let sql = "INSERT INTO test_table(id) VALUES(1)";
116+
check(sql, None);
117+
118+
let sql = "REPLACE INTO test_table(id) VALUES(1)";
119+
check(sql, Some(SqliteOnConflict::Replace));
120+
121+
let sql = "INSERT OR REPLACE INTO test_table(id) VALUES(1)";
122+
check(sql, Some(SqliteOnConflict::Replace));
123+
124+
let sql = "INSERT OR ROLLBACK INTO test_table(id) VALUES(1)";
125+
check(sql, Some(SqliteOnConflict::Rollback));
126+
127+
let sql = "INSERT OR ABORT INTO test_table(id) VALUES(1)";
128+
check(sql, Some(SqliteOnConflict::Abort));
129+
130+
let sql = "INSERT OR FAIL INTO test_table(id) VALUES(1)";
131+
check(sql, Some(SqliteOnConflict::Fail));
132+
133+
let sql = "INSERT OR IGNORE INTO test_table(id) VALUES(1)";
134+
check(sql, Some(SqliteOnConflict::Ignore));
135+
}
136+
100137
#[test]
101138
fn parse_update() {
102139
let sql = "UPDATE t SET a = 1, b = 2, c = 3 WHERE d";

0 commit comments

Comments
 (0)