Skip to content

Support AUTO_INCREMENT for MySQL and AUTOINCREMENT for SQLite #234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jul 28, 2020
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 8 additions & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -245,6 +251,7 @@ impl fmt::Display for ColumnOption {
Ok(())
}
Check(expr) => write!(f, "CHECK ({})", expr),
DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion src/dialect/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";`
Expand All @@ -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,
Expand Down Expand Up @@ -84,6 +87,8 @@ define_keywords!(
AT,
ATOMIC,
AUTHORIZATION,
AUTOINCREMENT,
AUTO_INCREMENT,
AVG,
AVRO,
BEGIN,
Expand Down
6 changes: 6 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,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());
};
Expand Down
11 changes: 8 additions & 3 deletions src/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,
Expand Down
32 changes: 32 additions & 0 deletions tests/sqlparser_mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use sqlparser::ast::*;
use sqlparser::dialect::{GenericDialect, MySqlDialect};
use sqlparser::test_utils::*;
use sqlparser::tokenizer::Token;

#[test]
fn parse_identifiers() {
Expand Down Expand Up @@ -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 {})],
Expand Down
32 changes: 32 additions & 0 deletions tests/sqlparser_sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand Down