mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-09 23:46:20 +00:00
Support dialect-specific auto-increment column options for MySQL and SQLite (#234)
In MySQL it's AUTO_INCREMENT (see https://dev.mysql.com/doc/refman/8.0/en/create-table.html) and in SQLite it's AUTOINCREMENT. We use `ColumnOption::DialectSpecific(Vec<Token>)` to avoid adding a new variant for each vendor-specific column option.
This commit is contained in:
parent
8020b2e5f0
commit
09ca14fe8e
7 changed files with 95 additions and 5 deletions
|
@ -11,6 +11,9 @@ Check https://github.com/ballista-compute/sqlparser-rs/commits/main for undocume
|
|||
### Changed
|
||||
|
||||
### Added
|
||||
- Support `CREATE OR REPLACE VIEW`/`TABLE` (#239) - thanks @Dandandan!
|
||||
- Support PostgreSQL `PREPARE`, `EXECUTE`, and `DEALLOCATE` (#243) - thanks @silathdiir!
|
||||
- Support SQLite `AUTOINCREMENT` and MySQL `AUTO_INCREMENT` column option in `CREATE TABLE` - thanks @mashuai!
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
//! AST types specific to CREATE/ALTER variants of [Statement]
|
||||
//! (commonly referred to as Data Definition Language, or DDL)
|
||||
use super::{display_comma_separated, DataType, Expr, Ident, ObjectName};
|
||||
use crate::ast::display_separated;
|
||||
use crate::tokenizer::Token;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
@ -212,8 +214,12 @@ pub enum ColumnOption {
|
|||
on_delete: Option<ReferentialAction>,
|
||||
on_update: Option<ReferentialAction>,
|
||||
},
|
||||
// `CHECK (<expr>)`
|
||||
/// `CHECK (<expr>)`
|
||||
Check(Expr),
|
||||
/// Dialect-specific options, such as:
|
||||
/// - MySQL's `AUTO_INCREMENT` or SQLite's `AUTOINCREMENT`
|
||||
/// - ...
|
||||
DialectSpecific(Vec<Token>),
|
||||
}
|
||||
|
||||
impl fmt::Display for ColumnOption {
|
||||
|
@ -245,6 +251,7 @@ impl fmt::Display for ColumnOption {
|
|||
Ok(())
|
||||
}
|
||||
Check(expr) => write!(f, "CHECK ({})", expr),
|
||||
DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
/// and could be removed.
|
||||
/// 3) a `RESERVED_FOR_TABLE_ALIAS` array with keywords reserved in a
|
||||
/// "table alias" context.
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Defines a string constant for a single keyword: `kw_def!(SELECT);`
|
||||
/// expands to `pub const SELECT = "SELECT";`
|
||||
|
@ -41,7 +43,8 @@ macro_rules! define_keywords {
|
|||
($(
|
||||
$ident:ident $(= $string_keyword:expr)?
|
||||
),*) => {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum Keyword {
|
||||
NoKeyword,
|
||||
|
@ -84,6 +87,8 @@ define_keywords!(
|
|||
AT,
|
||||
ATOMIC,
|
||||
AUTHORIZATION,
|
||||
AUTOINCREMENT,
|
||||
AUTO_INCREMENT,
|
||||
AVG,
|
||||
AVRO,
|
||||
BEGIN,
|
||||
|
|
|
@ -1283,6 +1283,12 @@ impl Parser {
|
|||
let expr = self.parse_expr()?;
|
||||
self.expect_token(&Token::RParen)?;
|
||||
ColumnOption::Check(expr)
|
||||
} else if self.parse_keyword(Keyword::AUTO_INCREMENT) {
|
||||
// Support AUTO_INCREMENT for MySQL
|
||||
ColumnOption::DialectSpecific(vec![Token::make_keyword("AUTO_INCREMENT")])
|
||||
} else if self.parse_keyword(Keyword::AUTOINCREMENT) {
|
||||
// Support AUTOINCREMENT for SQLite
|
||||
ColumnOption::DialectSpecific(vec![Token::make_keyword("AUTOINCREMENT")])
|
||||
} else {
|
||||
return self.expected("column option", self.peek_token());
|
||||
};
|
||||
|
|
|
@ -21,10 +21,13 @@ use std::str::Chars;
|
|||
|
||||
use super::dialect::keywords::{Keyword, ALL_KEYWORDS, ALL_KEYWORDS_INDEX};
|
||||
use super::dialect::Dialect;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
/// SQL Token enumeration
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Token {
|
||||
/// An end-of-file marker, not a real token
|
||||
EOF,
|
||||
|
@ -160,7 +163,8 @@ impl Token {
|
|||
}
|
||||
|
||||
/// A keyword (like SELECT) or an optionally quoted SQL identifier
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct Word {
|
||||
/// The value of the token, without the enclosing quotes, and with the
|
||||
/// escape sequences (if any) processed (TODO: escapes are not handled)
|
||||
|
@ -196,7 +200,8 @@ impl Word {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Whitespace {
|
||||
Space,
|
||||
Newline,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
use sqlparser::ast::*;
|
||||
use sqlparser::dialect::{GenericDialect, MySqlDialect};
|
||||
use sqlparser::test_utils::*;
|
||||
use sqlparser::tokenizer::Token;
|
||||
|
||||
#[test]
|
||||
fn parse_identifiers() {
|
||||
|
@ -97,6 +98,37 @@ fn parse_show_columns() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_auto_increment() {
|
||||
let sql = "CREATE TABLE foo (bar INT PRIMARY KEY AUTO_INCREMENT)";
|
||||
match mysql().verified_stmt(sql) {
|
||||
Statement::CreateTable { name, columns, .. } => {
|
||||
assert_eq!(name.to_string(), "foo");
|
||||
assert_eq!(
|
||||
vec![ColumnDef {
|
||||
name: "bar".into(),
|
||||
data_type: DataType::Int,
|
||||
collation: None,
|
||||
options: vec![
|
||||
ColumnOptionDef {
|
||||
name: None,
|
||||
option: ColumnOption::Unique { is_primary: true }
|
||||
},
|
||||
ColumnOptionDef {
|
||||
name: None,
|
||||
option: ColumnOption::DialectSpecific(vec![Token::make_keyword(
|
||||
"AUTO_INCREMENT"
|
||||
)])
|
||||
}
|
||||
],
|
||||
}],
|
||||
columns
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn mysql() -> TestedDialects {
|
||||
TestedDialects {
|
||||
dialects: vec![Box::new(MySqlDialect {})],
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
use sqlparser::ast::*;
|
||||
use sqlparser::dialect::GenericDialect;
|
||||
use sqlparser::test_utils::*;
|
||||
use sqlparser::tokenizer::Token;
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_without_rowid() {
|
||||
|
@ -55,6 +56,37 @@ fn parse_create_virtual_table() {
|
|||
sqlite_and_generic().verified_stmt(sql);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_auto_increment() {
|
||||
let sql = "CREATE TABLE foo (bar INT PRIMARY KEY AUTOINCREMENT)";
|
||||
match sqlite_and_generic().verified_stmt(sql) {
|
||||
Statement::CreateTable { name, columns, .. } => {
|
||||
assert_eq!(name.to_string(), "foo");
|
||||
assert_eq!(
|
||||
vec![ColumnDef {
|
||||
name: "bar".into(),
|
||||
data_type: DataType::Int,
|
||||
collation: None,
|
||||
options: vec![
|
||||
ColumnOptionDef {
|
||||
name: None,
|
||||
option: ColumnOption::Unique { is_primary: true }
|
||||
},
|
||||
ColumnOptionDef {
|
||||
name: None,
|
||||
option: ColumnOption::DialectSpecific(vec![Token::make_keyword(
|
||||
"AUTOINCREMENT"
|
||||
)])
|
||||
}
|
||||
],
|
||||
}],
|
||||
columns
|
||||
);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn sqlite_and_generic() -> TestedDialects {
|
||||
TestedDialects {
|
||||
// we don't have a separate SQLite dialect, so test only the generic dialect for now
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue