mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-26 23:49:10 +00:00
feat: support sqlite insert or statement (#281)
This commit is contained in:
parent
07342d5853
commit
add8991144
4 changed files with 105 additions and 9 deletions
|
@ -508,6 +508,8 @@ pub enum Statement {
|
||||||
Query(Box<Query>),
|
Query(Box<Query>),
|
||||||
/// INSERT
|
/// INSERT
|
||||||
Insert {
|
Insert {
|
||||||
|
/// Only for Sqlite
|
||||||
|
or: Option<SqliteOnConflict>,
|
||||||
/// TABLE
|
/// TABLE
|
||||||
table_name: ObjectName,
|
table_name: ObjectName,
|
||||||
/// COLUMNS
|
/// COLUMNS
|
||||||
|
@ -804,6 +806,7 @@ impl fmt::Display for Statement {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Statement::Insert {
|
Statement::Insert {
|
||||||
|
or,
|
||||||
table_name,
|
table_name,
|
||||||
overwrite,
|
overwrite,
|
||||||
partitioned,
|
partitioned,
|
||||||
|
@ -812,13 +815,17 @@ impl fmt::Display for Statement {
|
||||||
source,
|
source,
|
||||||
table,
|
table,
|
||||||
} => {
|
} => {
|
||||||
write!(
|
if let Some(action) = or {
|
||||||
f,
|
write!(f, "INSERT OR {} INTO {} ", action, table_name)?;
|
||||||
"INSERT {act}{tbl} {table_name} ",
|
} else {
|
||||||
table_name = table_name,
|
write!(
|
||||||
act = if *overwrite { "OVERWRITE" } else { "INTO" },
|
f,
|
||||||
tbl = if *table { " TABLE" } else { "" }
|
"INSERT {act}{tbl} {table_name} ",
|
||||||
)?;
|
table_name = table_name,
|
||||||
|
act = if *overwrite { "OVERWRITE" } else { "INTO" },
|
||||||
|
tbl = if *table { " TABLE" } else { "" }
|
||||||
|
)?;
|
||||||
|
}
|
||||||
if !columns.is_empty() {
|
if !columns.is_empty() {
|
||||||
write!(f, "({}) ", display_comma_separated(columns))?;
|
write!(f, "({}) ", display_comma_separated(columns))?;
|
||||||
}
|
}
|
||||||
|
@ -832,6 +839,7 @@ impl fmt::Display for Statement {
|
||||||
}
|
}
|
||||||
write!(f, "{}", source)
|
write!(f, "{}", source)
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::Copy {
|
Statement::Copy {
|
||||||
table_name,
|
table_name,
|
||||||
columns,
|
columns,
|
||||||
|
@ -1560,3 +1568,29 @@ impl fmt::Display for SetVariableValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sqlite specific syntax
|
||||||
|
///
|
||||||
|
/// https://sqlite.org/lang_conflict.html
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum SqliteOnConflict {
|
||||||
|
Rollback,
|
||||||
|
Abort,
|
||||||
|
Fail,
|
||||||
|
Ignore,
|
||||||
|
Replace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SqliteOnConflict {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use SqliteOnConflict::*;
|
||||||
|
match self {
|
||||||
|
Rollback => write!(f, "ROLLBACK"),
|
||||||
|
Abort => write!(f, "ABORT"),
|
||||||
|
Fail => write!(f, "FAIL"),
|
||||||
|
Ignore => write!(f, "IGNORE"),
|
||||||
|
Replace => write!(f, "REPLACE"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ macro_rules! define_keywords {
|
||||||
|
|
||||||
// The following keywords should be sorted to be able to match using binary search
|
// The following keywords should be sorted to be able to match using binary search
|
||||||
define_keywords!(
|
define_keywords!(
|
||||||
|
ABORT,
|
||||||
ABS,
|
ABS,
|
||||||
ACTION,
|
ACTION,
|
||||||
ADD,
|
ADD,
|
||||||
|
@ -202,6 +203,7 @@ define_keywords!(
|
||||||
EXTENDED,
|
EXTENDED,
|
||||||
EXTERNAL,
|
EXTERNAL,
|
||||||
EXTRACT,
|
EXTRACT,
|
||||||
|
FAIL,
|
||||||
FALSE,
|
FALSE,
|
||||||
FETCH,
|
FETCH,
|
||||||
FIELDS,
|
FIELDS,
|
||||||
|
@ -233,6 +235,7 @@ define_keywords!(
|
||||||
HOUR,
|
HOUR,
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
IF,
|
IF,
|
||||||
|
IGNORE,
|
||||||
IN,
|
IN,
|
||||||
INDEX,
|
INDEX,
|
||||||
INDICATOR,
|
INDICATOR,
|
||||||
|
|
|
@ -163,6 +163,10 @@ impl<'a> Parser<'a> {
|
||||||
Keyword::DEALLOCATE => Ok(self.parse_deallocate()?),
|
Keyword::DEALLOCATE => Ok(self.parse_deallocate()?),
|
||||||
Keyword::EXECUTE => Ok(self.parse_execute()?),
|
Keyword::EXECUTE => Ok(self.parse_execute()?),
|
||||||
Keyword::PREPARE => Ok(self.parse_prepare()?),
|
Keyword::PREPARE => Ok(self.parse_prepare()?),
|
||||||
|
Keyword::REPLACE if dialect_of!(self is SQLiteDialect ) => {
|
||||||
|
self.prev_token();
|
||||||
|
Ok(self.parse_insert()?)
|
||||||
|
}
|
||||||
_ => self.expected("an SQL statement", Token::Word(w)),
|
_ => self.expected("an SQL statement", Token::Word(w)),
|
||||||
},
|
},
|
||||||
Token::LParen => {
|
Token::LParen => {
|
||||||
|
@ -2719,6 +2723,23 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
/// Parse an INSERT statement
|
/// Parse an INSERT statement
|
||||||
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
|
pub fn parse_insert(&mut self) -> Result<Statement, ParserError> {
|
||||||
|
let or = if !dialect_of!(self is SQLiteDialect) {
|
||||||
|
None
|
||||||
|
} else if self.parse_keywords(&[Keyword::OR, Keyword::REPLACE]) {
|
||||||
|
Some(SqliteOnConflict::Replace)
|
||||||
|
} else if self.parse_keywords(&[Keyword::OR, Keyword::ROLLBACK]) {
|
||||||
|
Some(SqliteOnConflict::Rollback)
|
||||||
|
} else if self.parse_keywords(&[Keyword::OR, Keyword::ABORT]) {
|
||||||
|
Some(SqliteOnConflict::Abort)
|
||||||
|
} else if self.parse_keywords(&[Keyword::OR, Keyword::FAIL]) {
|
||||||
|
Some(SqliteOnConflict::Fail)
|
||||||
|
} else if self.parse_keywords(&[Keyword::OR, Keyword::IGNORE]) {
|
||||||
|
Some(SqliteOnConflict::Ignore)
|
||||||
|
} else if self.parse_keyword(Keyword::REPLACE) {
|
||||||
|
Some(SqliteOnConflict::Replace)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
let action = self.expect_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE])?;
|
let action = self.expect_one_of_keywords(&[Keyword::INTO, Keyword::OVERWRITE])?;
|
||||||
let overwrite = action == Keyword::OVERWRITE;
|
let overwrite = action == Keyword::OVERWRITE;
|
||||||
let local = self.parse_keyword(Keyword::LOCAL);
|
let local = self.parse_keyword(Keyword::LOCAL);
|
||||||
|
@ -2758,6 +2779,7 @@ impl<'a> Parser<'a> {
|
||||||
|
|
||||||
let source = Box::new(self.parse_query()?);
|
let source = Box::new(self.parse_query()?);
|
||||||
Ok(Statement::Insert {
|
Ok(Statement::Insert {
|
||||||
|
or,
|
||||||
table_name,
|
table_name,
|
||||||
overwrite,
|
overwrite,
|
||||||
partitioned,
|
partitioned,
|
||||||
|
|
|
@ -24,8 +24,8 @@ use test_utils::{all_dialects, expr_from_projection, join, number, only, table,
|
||||||
|
|
||||||
use matches::assert_matches;
|
use matches::assert_matches;
|
||||||
use sqlparser::ast::*;
|
use sqlparser::ast::*;
|
||||||
use sqlparser::dialect::keywords::ALL_KEYWORDS;
|
use sqlparser::dialect::{keywords::ALL_KEYWORDS, SQLiteDialect};
|
||||||
use sqlparser::parser::ParserError;
|
use sqlparser::parser::{Parser, ParserError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_insert_values() {
|
fn parse_insert_values() {
|
||||||
|
@ -97,6 +97,43 @@ fn parse_insert_invalid() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_insert_sqlite() {
|
||||||
|
let dialect = SQLiteDialect {};
|
||||||
|
|
||||||
|
let check = |sql: &str, expected_action: Option<SqliteOnConflict>| match Parser::parse_sql(
|
||||||
|
&dialect, &sql,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.pop()
|
||||||
|
.unwrap()
|
||||||
|
{
|
||||||
|
Statement::Insert { or, .. } => assert_eq!(or, expected_action),
|
||||||
|
_ => panic!(sql.to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let sql = "INSERT INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, None);
|
||||||
|
|
||||||
|
let sql = "REPLACE INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, Some(SqliteOnConflict::Replace));
|
||||||
|
|
||||||
|
let sql = "INSERT OR REPLACE INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, Some(SqliteOnConflict::Replace));
|
||||||
|
|
||||||
|
let sql = "INSERT OR ROLLBACK INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, Some(SqliteOnConflict::Rollback));
|
||||||
|
|
||||||
|
let sql = "INSERT OR ABORT INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, Some(SqliteOnConflict::Abort));
|
||||||
|
|
||||||
|
let sql = "INSERT OR FAIL INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, Some(SqliteOnConflict::Fail));
|
||||||
|
|
||||||
|
let sql = "INSERT OR IGNORE INTO test_table(id) VALUES(1)";
|
||||||
|
check(sql, Some(SqliteOnConflict::Ignore));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_update() {
|
fn parse_update() {
|
||||||
let sql = "UPDATE t SET a = 1, b = 2, c = 3 WHERE d";
|
let sql = "UPDATE t SET a = 1, b = 2, c = 3 WHERE d";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue