From f86ed2f2256c6690d305a327d99f7f9e4a855b78 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Sat, 3 Sep 2022 17:53:55 +0800 Subject: [PATCH 01/11] Fix on update current_timestamp for mysql --- src/parser.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 877c47303..95259a8f6 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2526,9 +2526,15 @@ impl<'a> Parser<'a> { } else if self.parse_keywords(&[Keyword::ON, Keyword::UPDATE]) && dialect_of!(self is MySqlDialect) { - Ok(Some(ColumnOption::DialectSpecific(vec![ - Token::make_keyword("ON UPDATE"), - ]))) + if self.parse_keyword(Keyword::CURRENT_TIMESTAMP) { + Ok(Some(ColumnOption::DialectSpecific(vec![ + Token::make_keyword("ON UPDATE CURRENT_TIMESTAMP"), + ]))) + } else { + Ok(Some(ColumnOption::DialectSpecific(vec![ + Token::make_keyword("ON UPDATE"), + ]))) + } } else { Ok(None) } From cb1d2abaeb1d11dadfc9c8fb58dee9e35a8b5c82 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Sat, 3 Sep 2022 17:55:40 +0800 Subject: [PATCH 02/11] Add test for mysql on update current_timestamp --- tests/sqlparser_mysql.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index f46d5d23e..23e258d95 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1016,6 +1016,28 @@ fn parse_table_colum_option_on_update() { } _ => unreachable!(), } + + let sql2 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE CURRENT_TIMESTAMP)"; + match mysql().verified_stmt(sql2) { + Statement::CreateTable { name, columns, .. } => { + assert_eq!(name.to_string(), "foo"); + assert_eq!( + vec![ColumnDef { + name: Ident::with_quote('`', "modification_time"), + data_type: DataType::Datetime, + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::DialectSpecific(vec![Token::make_keyword( + "ON UPDATE CURRENT_TIMESTAMP" + )]), + },], + }], + columns + ); + } + _ => unreachable!(), + } } #[test] From 311a771b56c7e413d4b5815353aac50c277a1855 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 12:07:10 +0800 Subject: [PATCH 03/11] Add index for mysql create statement --- src/parser.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 95259a8f6..aac90e611 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2568,6 +2568,15 @@ impl<'a> Parser<'a> { None }; match self.next_token() { + Token::Word(w) if w.keyword == Keyword::KEY || w.keyword == Keyword::INDEX => { + let name = Some(self.parse_identifier()?); + let columns = self.parse_parenthesized_column_list(Mandatory)?; + Ok(Some(TableConstraint::Key { + name, + columns, + keyword: w.keyword, + })) + } Token::Word(w) if w.keyword == Keyword::PRIMARY || w.keyword == Keyword::UNIQUE => { let is_primary = w.keyword == Keyword::PRIMARY; if is_primary { From bca276e736b19fd8042ffc22eb22aa09a01d1ffb Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 12:08:23 +0800 Subject: [PATCH 04/11] Add index for mysql create statement --- src/ast/ddl.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 1847f2518..7c1827cf5 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -22,6 +22,7 @@ use serde::{Deserialize, Serialize}; use crate::ast::value::escape_single_quote_string; use crate::ast::{display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName}; +use crate::keywords::Keyword; use crate::tokenizer::Token; /// An `ALTER TABLE` (`Statement::AlterTable`) operation @@ -247,6 +248,11 @@ pub enum TableConstraint { name: Option, expr: Box, }, + Key { + name: Option, + columns: Vec, + keyword: Keyword, + }, } impl fmt::Display for TableConstraint { @@ -263,6 +269,16 @@ impl fmt::Display for TableConstraint { if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, display_comma_separated(columns) ), + TableConstraint::Key { + name, + columns, + keyword, + } => write!( + f, + "{}({})", + display_key_name(name, *keyword), + display_comma_separated(columns) + ), TableConstraint::ForeignKey { name, columns, @@ -428,6 +444,27 @@ fn display_constraint_name(name: &'_ Option) -> impl fmt::Display + '_ { ConstraintName(name) } +fn display_key_name(name: &'_ Option, keyword: Keyword) -> impl fmt::Display + '_ { + struct KeyName<'a>(&'a Option, Keyword); + impl<'a> fmt::Display for KeyName<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(name) = self.0 { + match self.1 { + Keyword::KEY => { + write!(f, "KEY {} ", name)?; + } + Keyword::INDEX => { + write!(f, "INDEX {} ", name)?; + } + _ => unreachable!(), + } + } + Ok(()) + } + } + KeyName(name, keyword) +} + /// ` = /// { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }` /// From 3aaaf24e83090432b615fa57ba974429d68f8fa8 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 12:09:22 +0800 Subject: [PATCH 05/11] Add index test for mysql create statement --- tests/sqlparser_mysql.rs | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 23e258d95..17ba41290 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -17,6 +17,7 @@ #[macro_use] mod test_utils; +use sqlparser::keywords::Keyword; use test_utils::*; use sqlparser::ast::Expr; @@ -1040,6 +1041,78 @@ fn parse_table_colum_option_on_update() { } } +#[test] +fn parse_table_colum_option_key() { + let sql1 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE, KEY `k_m_t` (modification_time))"; + match mysql().verified_stmt(sql1) { + Statement::CreateTable { + name, + columns, + constraints, + .. + } => { + assert_eq!(name.to_string(), "foo"); + assert_eq!( + vec![ColumnDef { + name: Ident::with_quote('`', "modification_time"), + data_type: DataType::Datetime, + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::DialectSpecific(vec![Token::make_keyword( + "ON UPDATE" + )]), + },], + }], + columns + ); + assert_eq!( + vec![TableConstraint::Key { + name: Some(Ident::with_quote('`', "k_m_t")), + columns: vec![Ident::new("modification_time")], + keyword: Keyword::KEY + }], + constraints + ); + } + _ => unreachable!(), + } + let sql2 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE, INDEX `k_m_t` (modification_time))"; + match mysql().verified_stmt(sql2) { + Statement::CreateTable { + name, + columns, + constraints, + .. + } => { + assert_eq!(name.to_string(), "foo"); + assert_eq!( + vec![ColumnDef { + name: Ident::with_quote('`', "modification_time"), + data_type: DataType::Datetime, + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::DialectSpecific(vec![Token::make_keyword( + "ON UPDATE" + )]), + },], + }], + columns + ); + assert_eq!( + vec![TableConstraint::Key { + name: Some(Ident::with_quote('`', "k_m_t")), + columns: vec![Ident::new("modification_time")], + keyword: Keyword::INDEX + }], + constraints + ); + } + _ => unreachable!(), + } +} + #[test] fn parse_set_names() { let stmt = mysql_and_generic().verified_stmt("SET NAMES utf8mb4"); From 36be43ab50684524c6029f96b691f5d55e067347 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 12:43:25 +0800 Subject: [PATCH 06/11] Fix create statement by using mysql specific dialect --- src/parser.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index aac90e611..5c3ffdc41 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2568,7 +2568,10 @@ impl<'a> Parser<'a> { None }; match self.next_token() { - Token::Word(w) if w.keyword == Keyword::KEY || w.keyword == Keyword::INDEX => { + Token::Word(w) + if (w.keyword == Keyword::KEY || w.keyword == Keyword::INDEX) + && dialect_of!(self is MySqlDialect) => + { let name = Some(self.parse_identifier()?); let columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(Some(TableConstraint::Key { From 7521106eabfc2dbdf148c92dd15db13f3f645b17 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 14:50:02 +0800 Subject: [PATCH 07/11] Fix unique key for mysql --- src/parser.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 5c3ffdc41..86eefcdfe 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2562,7 +2562,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_table_constraint( &mut self, ) -> Result, ParserError> { - let name = if self.parse_keyword(Keyword::CONSTRAINT) { + let mut name = if self.parse_keyword(Keyword::CONSTRAINT) { Some(self.parse_identifier()?) } else { None @@ -2582,14 +2582,20 @@ impl<'a> Parser<'a> { } Token::Word(w) if w.keyword == Keyword::PRIMARY || w.keyword == Keyword::UNIQUE => { let is_primary = w.keyword == Keyword::PRIMARY; + let mut is_mysql_unique_key = false; if is_primary { self.expect_keyword(Keyword::KEY)?; + } else if dialect_of!(self is MySqlDialect) { + self.expect_keyword(Keyword::KEY)?; + name = Some(self.parse_identifier()?); + is_mysql_unique_key = true; } let columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(Some(TableConstraint::Unique { name, columns, is_primary, + is_mysql_unique_key, })) } Token::Word(w) if w.keyword == Keyword::FOREIGN => { From 033d1831e5537b173ce067c327baa525825a2df5 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 14:50:44 +0800 Subject: [PATCH 08/11] Fix unique key for mysql --- src/ast/ddl.rs | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 7c1827cf5..e810ec200 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -229,6 +229,7 @@ pub enum TableConstraint { columns: Vec, /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint is_primary: bool, + is_mysql_unique_key: bool, }, /// A referential integrity constraint (`[ CONSTRAINT ] FOREIGN KEY () /// REFERENCES () @@ -262,13 +263,30 @@ impl fmt::Display for TableConstraint { name, columns, is_primary, - } => write!( - f, - "{}{} ({})", - display_constraint_name(name), - if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, - display_comma_separated(columns) - ), + is_mysql_unique_key, + } => { + if *is_mysql_unique_key { + write!( + f, + "{}{}({})", + if *is_primary { + "PRIMARY KEY" + } else { + "UNIQUE " + }, + display_key_name(name, Keyword::KEY), + display_comma_separated(columns) + ) + } else { + write!( + f, + "{}{} ({})", + display_constraint_name(name), + if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, + display_comma_separated(columns) + ) + } + } TableConstraint::Key { name, columns, From b0d115931745f74ad668aa81179952f32abc6271 Mon Sep 17 00:00:00 2001 From: Hao <1358137595@qq.com> Date: Mon, 5 Sep 2022 14:51:23 +0800 Subject: [PATCH 09/11] Add test for mysql unique key --- tests/sqlparser_mysql.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 17ba41290..666b895c5 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1111,6 +1111,41 @@ fn parse_table_colum_option_key() { } _ => unreachable!(), } + let sql2 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE, UNIQUE KEY `u_m_t` (modification_time))"; + match mysql().verified_stmt(sql2) { + Statement::CreateTable { + name, + columns, + constraints, + .. + } => { + assert_eq!(name.to_string(), "foo"); + assert_eq!( + vec![ColumnDef { + name: Ident::with_quote('`', "modification_time"), + data_type: DataType::Datetime, + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::DialectSpecific(vec![Token::make_keyword( + "ON UPDATE" + )]), + },], + }], + columns + ); + assert_eq!( + vec![TableConstraint::Unique { + name: Some(Ident::with_quote('`', "u_m_t")), + columns: vec![Ident::new("modification_time")], + is_primary: false, + is_mysql_unique_key: true, + }], + constraints + ); + } + _ => unreachable!(), + } } #[test] From 7afaedf3f41b6557aeb288350e9609920f0fb779 Mon Sep 17 00:00:00 2001 From: RainJoe Date: Sun, 2 Oct 2022 14:19:01 +0800 Subject: [PATCH 10/11] Add mysql key/index suuport in create table statement --- src/ast/ddl.rs | 92 ++++++++++++----------- src/ast/mod.rs | 2 +- src/parser.rs | 32 +++++--- tests/sqlparser_mysql.rs | 153 ++++++++++++++++----------------------- 4 files changed, 134 insertions(+), 145 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index e810ec200..012f7db2b 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -22,7 +22,6 @@ use serde::{Deserialize, Serialize}; use crate::ast::value::escape_single_quote_string; use crate::ast::{display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName}; -use crate::keywords::Keyword; use crate::tokenizer::Token; /// An `ALTER TABLE` (`Statement::AlterTable`) operation @@ -218,6 +217,14 @@ impl fmt::Display for AlterColumnOperation { } } +/// For mysql index, here may be there format for index, there patterns are similar。 +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum KeyFormat { + Unique, + Key, + Index, +} /// A table-level constraint, specified in a `CREATE TABLE` or an /// `ALTER TABLE ADD ` statement. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -229,7 +236,6 @@ pub enum TableConstraint { columns: Vec, /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint is_primary: bool, - is_mysql_unique_key: bool, }, /// A referential integrity constraint (`[ CONSTRAINT ] FOREIGN KEY () /// REFERENCES () @@ -249,10 +255,18 @@ pub enum TableConstraint { name: Option, expr: Box, }, + /// Index in constraint, for mysql specific dialect. + /// Deal with this pattern: + /// 1. {INDEX | KEY} [index_name] [index_type] (key_part,...) + /// 2. [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY] + /// [index_name] [index_type] (key_part,...) + /// [index_option] + /// Key { name: Option, columns: Vec, - keyword: Keyword, + /// key format, eg: unique/key/index + format: KeyFormat, }, } @@ -263,40 +277,38 @@ impl fmt::Display for TableConstraint { name, columns, is_primary, - is_mysql_unique_key, } => { - if *is_mysql_unique_key { - write!( - f, - "{}{}({})", - if *is_primary { - "PRIMARY KEY" - } else { - "UNIQUE " - }, - display_key_name(name, Keyword::KEY), - display_comma_separated(columns) - ) - } else { - write!( - f, - "{}{} ({})", - display_constraint_name(name), - if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, - display_comma_separated(columns) - ) - } + write!( + f, + "{}{} ({})", + display_constraint_name(name), + if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, + display_comma_separated(columns) + ) } TableConstraint::Key { name, columns, - keyword, - } => write!( - f, - "{}({})", - display_key_name(name, *keyword), - display_comma_separated(columns) - ), + format, + } => { + match *format { + KeyFormat::Unique => { + write!(f, "UNIQUE ")?; + } + KeyFormat::Key => { + write!(f, "KEY ")?; + } + KeyFormat::Index => { + write!(f, "INDEX ")?; + } + } + write!( + f, + "{} ({})", + display_key_name(name), + display_comma_separated(columns) + ) + } TableConstraint::ForeignKey { name, columns, @@ -462,25 +474,17 @@ fn display_constraint_name(name: &'_ Option) -> impl fmt::Display + '_ { ConstraintName(name) } -fn display_key_name(name: &'_ Option, keyword: Keyword) -> impl fmt::Display + '_ { - struct KeyName<'a>(&'a Option, Keyword); +fn display_key_name(name: &'_ Option) -> impl fmt::Display + '_ { + struct KeyName<'a>(&'a Option); impl<'a> fmt::Display for KeyName<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(name) = self.0 { - match self.1 { - Keyword::KEY => { - write!(f, "KEY {} ", name)?; - } - Keyword::INDEX => { - write!(f, "INDEX {} ", name)?; - } - _ => unreachable!(), - } + write!(f, "{}", name)?; } Ok(()) } } - KeyName(name, keyword) + KeyName(name) } /// ` = diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4f5fdb2eb..5a25a61bb 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -30,7 +30,7 @@ use serde::{Deserialize, Serialize}; pub use self::data_type::DataType; pub use self::ddl::{ - AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, + AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, KeyFormat, ReferentialAction, TableConstraint, }; pub use self::operator::{BinaryOperator, UnaryOperator}; diff --git a/src/parser.rs b/src/parser.rs index 86eefcdfe..98400631b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2562,7 +2562,7 @@ impl<'a> Parser<'a> { pub fn parse_optional_table_constraint( &mut self, ) -> Result, ParserError> { - let mut name = if self.parse_keyword(Keyword::CONSTRAINT) { + let name = if self.parse_keyword(Keyword::CONSTRAINT) { Some(self.parse_identifier()?) } else { None @@ -2574,28 +2574,38 @@ impl<'a> Parser<'a> { { let name = Some(self.parse_identifier()?); let columns = self.parse_parenthesized_column_list(Mandatory)?; - Ok(Some(TableConstraint::Key { - name, - columns, - keyword: w.keyword, - })) + match w.keyword { + Keyword::KEY => Ok(Some(TableConstraint::Key { + name, + columns, + format: KeyFormat::Key, + })), + Keyword::INDEX => Ok(Some(TableConstraint::Key { + name, + columns, + format: KeyFormat::Index, + })), + _ => unreachable!(), + } } Token::Word(w) if w.keyword == Keyword::PRIMARY || w.keyword == Keyword::UNIQUE => { let is_primary = w.keyword == Keyword::PRIMARY; - let mut is_mysql_unique_key = false; if is_primary { self.expect_keyword(Keyword::KEY)?; } else if dialect_of!(self is MySqlDialect) { - self.expect_keyword(Keyword::KEY)?; - name = Some(self.parse_identifier()?); - is_mysql_unique_key = true; + let name = Some(self.parse_identifier()?); + let columns = self.parse_parenthesized_column_list(Mandatory)?; + return Ok(Some(TableConstraint::Key { + name, + columns, + format: KeyFormat::Unique, + })); } let columns = self.parse_parenthesized_column_list(Mandatory)?; Ok(Some(TableConstraint::Unique { name, columns, is_primary, - is_mysql_unique_key, })) } Token::Word(w) if w.keyword == Keyword::FOREIGN => { diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 666b895c5..53f640517 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -17,7 +17,6 @@ #[macro_use] mod test_utils; -use sqlparser::keywords::Keyword; use test_utils::*; use sqlparser::ast::Expr; @@ -1042,105 +1041,81 @@ fn parse_table_colum_option_on_update() { } #[test] -fn parse_table_colum_option_key() { - let sql1 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE, KEY `k_m_t` (modification_time))"; - match mysql().verified_stmt(sql1) { - Statement::CreateTable { - name, - columns, - constraints, - .. - } => { - assert_eq!(name.to_string(), "foo"); - assert_eq!( - vec![ColumnDef { - name: Ident::with_quote('`', "modification_time"), - data_type: DataType::Datetime, - collation: None, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::DialectSpecific(vec![Token::make_keyword( - "ON UPDATE" - )]), - },], - }], - columns - ); - assert_eq!( - vec![TableConstraint::Key { - name: Some(Ident::with_quote('`', "k_m_t")), - columns: vec![Ident::new("modification_time")], - keyword: Keyword::KEY - }], - constraints - ); - } - _ => unreachable!(), - } - let sql2 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE, INDEX `k_m_t` (modification_time))"; - match mysql().verified_stmt(sql2) { +fn parse_table_create_constraint_option() { + let sql = "CREATE TABLE `foo` (`id` INT(11) NOT NULL, `node_id` INT(11) NOT NULL, `weight` INT(11) NOT NULL, `score` INT(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE `node_id` (`node_id`), KEY `weight` (`weight`), INDEX `score` (`score`)) ENGINE=InnoDB"; + + match mysql().verified_stmt(sql) { Statement::CreateTable { name, columns, constraints, .. } => { - assert_eq!(name.to_string(), "foo"); + assert_eq!(name.to_string(), "`foo`"); assert_eq!( - vec![ColumnDef { - name: Ident::with_quote('`', "modification_time"), - data_type: DataType::Datetime, - collation: None, - options: vec![ColumnOptionDef { - name: None, - option: ColumnOption::DialectSpecific(vec![Token::make_keyword( - "ON UPDATE" - )]), - },], - }], + vec![ + ColumnDef { + name: Ident::with_quote('`', "id"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + },] + }, + ColumnDef { + name: Ident::with_quote('`', "node_id"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + }] + }, + ColumnDef { + name: Ident::with_quote('`', "weight"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + }] + }, + ColumnDef { + name: Ident::with_quote('`', "score"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + }] + } + ], columns ); assert_eq!( - vec![TableConstraint::Key { - name: Some(Ident::with_quote('`', "k_m_t")), - columns: vec![Ident::new("modification_time")], - keyword: Keyword::INDEX - }], - constraints - ); - } - _ => unreachable!(), - } - let sql2 = "CREATE TABLE foo (`modification_time` DATETIME ON UPDATE, UNIQUE KEY `u_m_t` (modification_time))"; - match mysql().verified_stmt(sql2) { - Statement::CreateTable { - name, - columns, - constraints, - .. - } => { - assert_eq!(name.to_string(), "foo"); - assert_eq!( - vec![ColumnDef { - name: Ident::with_quote('`', "modification_time"), - data_type: DataType::Datetime, - collation: None, - options: vec![ColumnOptionDef { + vec![ + TableConstraint::Unique { name: None, - option: ColumnOption::DialectSpecific(vec![Token::make_keyword( - "ON UPDATE" - )]), - },], - }], - columns - ); - assert_eq!( - vec![TableConstraint::Unique { - name: Some(Ident::with_quote('`', "u_m_t")), - columns: vec![Ident::new("modification_time")], - is_primary: false, - is_mysql_unique_key: true, - }], + columns: vec![Ident::with_quote('`', "id")], + is_primary: true + }, + TableConstraint::Key { + name: Some(Ident::with_quote('`', "node_id")), + columns: vec![Ident::with_quote('`', "node_id")], + format: KeyFormat::Unique + }, + TableConstraint::Key { + name: Some(Ident::with_quote('`', "weight")), + columns: vec![Ident::with_quote('`', "weight")], + format: KeyFormat::Key + }, + TableConstraint::Key { + name: Some(Ident::with_quote('`', "score")), + columns: vec![Ident::with_quote('`', "score")], + format: KeyFormat::Index + }, + ], constraints ); } From 72703ce2b9ccecc03bea6f0d1f53a22cf51ec2f9 Mon Sep 17 00:00:00 2001 From: RainJoe Date: Mon, 3 Oct 2022 00:14:38 +0800 Subject: [PATCH 11/11] Fix table constraint unique case for mysql create statement --- src/parser.rs | 3 +- tests/sqlparser_mysql.rs | 79 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 98400631b..60119de5b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2592,7 +2592,8 @@ impl<'a> Parser<'a> { let is_primary = w.keyword == Keyword::PRIMARY; if is_primary { self.expect_keyword(Keyword::KEY)?; - } else if dialect_of!(self is MySqlDialect) { + } else if dialect_of!(self is MySqlDialect) && name == None { + //For mysql case: UNIQUE KEY (column_name), there is a little difference from CONSTRAINT constraint_name UNIQUE (column_name) let name = Some(self.parse_identifier()?); let columns = self.parse_parenthesized_column_list(Mandatory)?; return Ok(Some(TableConstraint::Key { diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 53f640517..d8dbecd36 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1121,6 +1121,85 @@ fn parse_table_create_constraint_option() { } _ => unreachable!(), } + + let sql2 = "CREATE TABLE `foo` (`id` INT(11) NOT NULL, `node_id` INT(11) NOT NULL, `weight` INT(11) NOT NULL, `score` INT(11) NOT NULL, PRIMARY KEY (`id`), CONSTRAINT `node_id` UNIQUE (`node_id`), KEY `weight` (`weight`), INDEX `score` (`score`)) ENGINE=InnoDB"; + match mysql().verified_stmt(sql2) { + Statement::CreateTable { + name, + columns, + constraints, + .. + } => { + assert_eq!(name.to_string(), "`foo`"); + assert_eq!( + vec![ + ColumnDef { + name: Ident::with_quote('`', "id"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + },] + }, + ColumnDef { + name: Ident::with_quote('`', "node_id"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + }] + }, + ColumnDef { + name: Ident::with_quote('`', "weight"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + }] + }, + ColumnDef { + name: Ident::with_quote('`', "score"), + data_type: DataType::Int(Some(11)), + collation: None, + options: vec![ColumnOptionDef { + name: None, + option: ColumnOption::NotNull + }] + } + ], + columns + ); + assert_eq!( + vec![ + TableConstraint::Unique { + name: None, + columns: vec![Ident::with_quote('`', "id")], + is_primary: true + }, + TableConstraint::Unique { + name: Some(Ident::with_quote('`', "node_id")), + columns: vec![Ident::with_quote('`', "node_id")], + is_primary: false, + }, + TableConstraint::Key { + name: Some(Ident::with_quote('`', "weight")), + columns: vec![Ident::with_quote('`', "weight")], + format: KeyFormat::Key + }, + TableConstraint::Key { + name: Some(Ident::with_quote('`', "score")), + columns: vec![Ident::with_quote('`', "score")], + format: KeyFormat::Index + }, + ], + constraints + ); + } + _ => unreachable!(), + } } #[test]