Skip to content

Commit 8badcdc

Browse files
nuccccgit-hulkiffyio
authored
Add SQLite "ON CONFLICT" column option in CREATE TABLE statements (#1442)
Co-authored-by: hulk <[email protected]> Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent 84348d4 commit 8badcdc

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

Diff for: src/ast/ddl.rs

+8
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use crate::ast::{
3333
display_comma_separated, display_separated, DataType, Expr, Ident, MySQLColumnPosition,
3434
ObjectName, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Value,
3535
};
36+
use crate::keywords::Keyword;
3637
use crate::tokenizer::Token;
3738

3839
/// An `ALTER TABLE` (`Statement::AlterTable`) operation
@@ -1186,6 +1187,9 @@ pub enum ColumnOption {
11861187
/// ```
11871188
/// [MS SQL Server]: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql-identity-property
11881189
Identity(Option<IdentityProperty>),
1190+
/// SQLite specific: ON CONFLICT option on column definition
1191+
/// <https://www.sqlite.org/lang_conflict.html>
1192+
OnConflict(Keyword),
11891193
}
11901194

11911195
impl fmt::Display for ColumnOption {
@@ -1294,6 +1298,10 @@ impl fmt::Display for ColumnOption {
12941298
}
12951299
Ok(())
12961300
}
1301+
OnConflict(keyword) => {
1302+
write!(f, "ON CONFLICT {:?}", keyword)?;
1303+
Ok(())
1304+
}
12971305
}
12981306
}
12991307
}

Diff for: src/parser/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -6224,6 +6224,19 @@ impl<'a> Parser<'a> {
62246224
None
62256225
};
62266226
Ok(Some(ColumnOption::Identity(property)))
6227+
} else if dialect_of!(self is SQLiteDialect | GenericDialect)
6228+
&& self.parse_keywords(&[Keyword::ON, Keyword::CONFLICT])
6229+
{
6230+
// Support ON CONFLICT for SQLite
6231+
Ok(Some(ColumnOption::OnConflict(
6232+
self.expect_one_of_keywords(&[
6233+
Keyword::ROLLBACK,
6234+
Keyword::ABORT,
6235+
Keyword::FAIL,
6236+
Keyword::IGNORE,
6237+
Keyword::REPLACE,
6238+
])?,
6239+
)))
62276240
} else {
62286241
Ok(None)
62296242
}

Diff for: tests/sqlparser_sqlite.rs

+41
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#[macro_use]
2323
mod test_utils;
2424

25+
use sqlparser::keywords::Keyword;
2526
use test_utils::*;
2627

2728
use sqlparser::ast::SelectItem::UnnamedExpr;
@@ -281,6 +282,46 @@ fn parse_create_table_gencol() {
281282
sqlite_and_generic().verified_stmt("CREATE TABLE t1 (a INT, b INT AS (a * 2) STORED)");
282283
}
283284

285+
#[test]
286+
fn parse_create_table_on_conflict_col() {
287+
for keyword in [
288+
Keyword::ROLLBACK,
289+
Keyword::ABORT,
290+
Keyword::FAIL,
291+
Keyword::IGNORE,
292+
Keyword::REPLACE,
293+
] {
294+
let sql = format!("CREATE TABLE t1 (a INT, b INT ON CONFLICT {:?})", keyword);
295+
match sqlite_and_generic().verified_stmt(&sql) {
296+
Statement::CreateTable(CreateTable { columns, .. }) => {
297+
assert_eq!(
298+
vec![ColumnOptionDef {
299+
name: None,
300+
option: ColumnOption::OnConflict(keyword),
301+
}],
302+
columns[1].options
303+
);
304+
}
305+
_ => unreachable!(),
306+
}
307+
}
308+
}
309+
310+
#[test]
311+
fn test_parse_create_table_on_conflict_col_err() {
312+
let sql_err = "CREATE TABLE t1 (a INT, b INT ON CONFLICT BOH)";
313+
let err = sqlite_and_generic()
314+
.parse_sql_statements(sql_err)
315+
.unwrap_err();
316+
assert_eq!(
317+
err,
318+
ParserError::ParserError(
319+
"Expected: one of ROLLBACK or ABORT or FAIL or IGNORE or REPLACE, found: BOH"
320+
.to_string()
321+
)
322+
);
323+
}
324+
284325
#[test]
285326
fn parse_create_table_untyped() {
286327
sqlite().verified_stmt("CREATE TABLE t1 (a, b AS (a * 2), c NOT NULL)");

0 commit comments

Comments
 (0)