mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-21 05:09:46 +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
|
### Changed
|
||||||
|
|
||||||
### Added
|
### 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
|
### Fixed
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
//! AST types specific to CREATE/ALTER variants of [Statement]
|
//! AST types specific to CREATE/ALTER variants of [Statement]
|
||||||
//! (commonly referred to as Data Definition Language, or DDL)
|
//! (commonly referred to as Data Definition Language, or DDL)
|
||||||
use super::{display_comma_separated, DataType, Expr, Ident, ObjectName};
|
use super::{display_comma_separated, DataType, Expr, Ident, ObjectName};
|
||||||
|
use crate::ast::display_separated;
|
||||||
|
use crate::tokenizer::Token;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -212,8 +214,12 @@ pub enum ColumnOption {
|
||||||
on_delete: Option<ReferentialAction>,
|
on_delete: Option<ReferentialAction>,
|
||||||
on_update: Option<ReferentialAction>,
|
on_update: Option<ReferentialAction>,
|
||||||
},
|
},
|
||||||
// `CHECK (<expr>)`
|
/// `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 {
|
impl fmt::Display for ColumnOption {
|
||||||
|
@ -245,6 +251,7 @@ impl fmt::Display for ColumnOption {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Check(expr) => write!(f, "CHECK ({})", expr),
|
Check(expr) => write!(f, "CHECK ({})", expr),
|
||||||
|
DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
/// and could be removed.
|
/// and could be removed.
|
||||||
/// 3) a `RESERVED_FOR_TABLE_ALIAS` array with keywords reserved in a
|
/// 3) a `RESERVED_FOR_TABLE_ALIAS` array with keywords reserved in a
|
||||||
/// "table alias" context.
|
/// "table alias" context.
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Defines a string constant for a single keyword: `kw_def!(SELECT);`
|
/// Defines a string constant for a single keyword: `kw_def!(SELECT);`
|
||||||
/// expands to `pub const SELECT = "SELECT";`
|
/// expands to `pub const SELECT = "SELECT";`
|
||||||
|
@ -41,7 +43,8 @@ macro_rules! define_keywords {
|
||||||
($(
|
($(
|
||||||
$ident:ident $(= $string_keyword:expr)?
|
$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)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum Keyword {
|
pub enum Keyword {
|
||||||
NoKeyword,
|
NoKeyword,
|
||||||
|
@ -84,6 +87,8 @@ define_keywords!(
|
||||||
AT,
|
AT,
|
||||||
ATOMIC,
|
ATOMIC,
|
||||||
AUTHORIZATION,
|
AUTHORIZATION,
|
||||||
|
AUTOINCREMENT,
|
||||||
|
AUTO_INCREMENT,
|
||||||
AVG,
|
AVG,
|
||||||
AVRO,
|
AVRO,
|
||||||
BEGIN,
|
BEGIN,
|
||||||
|
|
|
@ -1283,6 +1283,12 @@ impl Parser {
|
||||||
let expr = self.parse_expr()?;
|
let expr = self.parse_expr()?;
|
||||||
self.expect_token(&Token::RParen)?;
|
self.expect_token(&Token::RParen)?;
|
||||||
ColumnOption::Check(expr)
|
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 {
|
} else {
|
||||||
return self.expected("column option", self.peek_token());
|
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::keywords::{Keyword, ALL_KEYWORDS, ALL_KEYWORDS_INDEX};
|
||||||
use super::dialect::Dialect;
|
use super::dialect::Dialect;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// SQL Token enumeration
|
/// SQL Token enumeration
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
/// An end-of-file marker, not a real token
|
/// An end-of-file marker, not a real token
|
||||||
EOF,
|
EOF,
|
||||||
|
@ -160,7 +163,8 @@ impl Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A keyword (like SELECT) or an optionally quoted SQL identifier
|
/// 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 {
|
pub struct Word {
|
||||||
/// The value of the token, without the enclosing quotes, and with the
|
/// The value of the token, without the enclosing quotes, and with the
|
||||||
/// escape sequences (if any) processed (TODO: escapes are not handled)
|
/// 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 {
|
pub enum Whitespace {
|
||||||
Space,
|
Space,
|
||||||
Newline,
|
Newline,
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
use sqlparser::ast::*;
|
use sqlparser::ast::*;
|
||||||
use sqlparser::dialect::{GenericDialect, MySqlDialect};
|
use sqlparser::dialect::{GenericDialect, MySqlDialect};
|
||||||
use sqlparser::test_utils::*;
|
use sqlparser::test_utils::*;
|
||||||
|
use sqlparser::tokenizer::Token;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_identifiers() {
|
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 {
|
fn mysql() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
dialects: vec![Box::new(MySqlDialect {})],
|
dialects: vec![Box::new(MySqlDialect {})],
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
use sqlparser::ast::*;
|
use sqlparser::ast::*;
|
||||||
use sqlparser::dialect::GenericDialect;
|
use sqlparser::dialect::GenericDialect;
|
||||||
use sqlparser::test_utils::*;
|
use sqlparser::test_utils::*;
|
||||||
|
use sqlparser::tokenizer::Token;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_create_table_without_rowid() {
|
fn parse_create_table_without_rowid() {
|
||||||
|
@ -55,6 +56,37 @@ fn parse_create_virtual_table() {
|
||||||
sqlite_and_generic().verified_stmt(sql);
|
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 {
|
fn sqlite_and_generic() -> TestedDialects {
|
||||||
TestedDialects {
|
TestedDialects {
|
||||||
// we don't have a separate SQLite dialect, so test only the generic dialect for now
|
// 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