diff --git a/.gitignore b/.gitignore index 48cc89d..24ee3e3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ HEADER /target coverage/ +statements/ dist/ diff --git a/Cargo.toml b/Cargo.toml index cba6ca7..077dbbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sqlparser-mysql" -version = "0.0.1" +version = "0.0.2" authors = ["xring "] documentation = "https://docs.rs/sqlparser-mysql" diff --git a/src/base/algorithm_type.rs b/src/base/algorithm_type.rs index a83d1ce..6dd3606 100644 --- a/src/base/algorithm_type.rs +++ b/src/base/algorithm_type.rs @@ -1,6 +1,6 @@ use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case}; -use nom::character::complete::multispace0; +use nom::character::complete::{multispace0, multispace1}; use nom::combinator::{map, opt}; use nom::sequence::tuple; use nom::IResult; @@ -21,21 +21,31 @@ impl AlgorithmType { /// algorithm_option: /// ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY} pub fn parse(i: &str) -> IResult<&str, AlgorithmType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("ALGORITHM"), - multispace0, - opt(tag("=")), - multispace0, - alt(( - map(tag_no_case("DEFAULT"), |_| AlgorithmType::Default), - map(tag_no_case("INSTANT"), |_| AlgorithmType::Instant), - map(tag_no_case("INPLACE"), |_| AlgorithmType::Inplace), - map(tag_no_case("COPY"), |_| AlgorithmType::Copy), + alt(( + map( + tuple((tag_no_case("ALGORITHM"), multispace1, Self::parse_algorithm)), + |(_, _, algorithm)| algorithm, + ), + map( + tuple(( + tag_no_case("ALGORITHM"), + multispace0, + tag("="), + multispace0, + Self::parse_algorithm, )), - )), - |x| x.4, - )(i) + |(_, _, _, _, algorithm)| algorithm, + ), + ))(i) + } + + fn parse_algorithm(i: &str) -> IResult<&str, AlgorithmType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("DEFAULT"), |_| AlgorithmType::Default), + map(tag_no_case("INSTANT"), |_| AlgorithmType::Instant), + map(tag_no_case("INPLACE"), |_| AlgorithmType::Inplace), + map(tag_no_case("COPY"), |_| AlgorithmType::Copy), + ))(i) } } @@ -80,5 +90,9 @@ mod tests { let res5 = AlgorithmType::parse(str5); assert!(res5.is_ok()); assert_eq!(res5.unwrap().1, AlgorithmType::Default); + + let str6 = "ALGORITHMDEFAULT"; + let res6 = AlgorithmType::parse(str6); + assert!(res6.is_err()); } } diff --git a/src/base/common_parser.rs b/src/base/common_parser.rs index b6d7df8..bd00ca5 100644 --- a/src/base/common_parser.rs +++ b/src/base/common_parser.rs @@ -10,7 +10,7 @@ use nom::sequence::{delimited, pair, preceded, terminated, tuple}; use nom::{IResult, InputLength, Parser}; use base::column::Column; -use base::{OrderType, ParseSQLError}; +use base::{DefaultOrZeroOrOne, OrderType, ParseSQLError}; /// collection of common used parsers pub struct CommonParser; @@ -360,6 +360,117 @@ impl CommonParser { multispace0, ))(i) } + + /// extract String quoted by `'` or `"` + pub fn parse_quoted_string(i: &str) -> IResult<&str, String, ParseSQLError<&str>> { + alt(( + map(delimited(tag("'"), take_until("'"), tag("'")), String::from), + map( + delimited(tag("\""), take_until("\""), tag("\"")), + String::from, + ), + ))(i) + } + + /// extract value from `key [=] 'value'` or `key [=] "value"` + pub fn parse_quoted_string_value_with_key( + i: &str, + key: String, + ) -> IResult<&str, String, ParseSQLError<&str>> { + alt(( + map( + tuple(( + tag_no_case(key.as_str()), + multispace1, + CommonParser::parse_quoted_string, + )), + |(_, _, value)| value, + ), + map( + tuple(( + tag_no_case(key.as_str()), + multispace0, + tag("="), + multispace0, + CommonParser::parse_quoted_string, + )), + |(_, _, _, _, value)| value, + ), + ))(i) + } + + /// extract value from `key [=] value` + pub fn parse_string_value_with_key( + i: &str, + key: String, + ) -> IResult<&str, String, ParseSQLError<&str>> { + alt(( + map( + tuple((tag_no_case(key.as_str()), multispace1, Self::sql_identifier)), + |(_, _, value)| String::from(value), + ), + map( + tuple(( + tag_no_case(key.as_str()), + multispace0, + tag("="), + multispace0, + Self::sql_identifier, + )), + |(_, _, _, _, value)| String::from(value), + ), + ))(i) + } + + /// extract value from `key [=] value` + pub fn parse_digit_value_with_key( + i: &str, + key: String, + ) -> IResult<&str, String, ParseSQLError<&str>> { + alt(( + map( + tuple((tag_no_case(key.as_str()), multispace1, Self::sql_identifier)), + |(_, _, value)| String::from(value), + ), + map( + tuple(( + tag_no_case(key.as_str()), + multispace0, + tag("="), + multispace0, + digit1, + )), + |(_, _, _, _, value)| String::from(value), + ), + ))(i) + } + + /// extract value from `key [=] {DEFAULT | 0 | 1}` + pub fn parse_default_value_with_key( + i: &str, + key: String, + ) -> IResult<&str, DefaultOrZeroOrOne, ParseSQLError<&str>> { + alt(( + map( + tuple(( + tag_no_case(key.as_str()), + multispace1, + DefaultOrZeroOrOne::parse, + )), + |(_, _, value)| value, + ), + map( + tuple(( + tag_no_case(key.as_str()), + multispace0, + tag("="), + multispace0, + DefaultOrZeroOrOne::parse, + )), + |(_, _, _, _, value)| value, + ), + ))(i) + } } #[cfg(test)] diff --git a/src/base/compression_type.rs b/src/base/compression_type.rs index 0fa56c2..d5c0cae 100644 --- a/src/base/compression_type.rs +++ b/src/base/compression_type.rs @@ -1,6 +1,6 @@ use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case}; -use nom::character::complete::multispace0; +use nom::character::complete::{multispace0, multispace1}; use nom::combinator::{map, opt}; use nom::sequence::{delimited, tuple}; use nom::IResult; @@ -28,28 +28,42 @@ impl Display for CompressionType { impl CompressionType { pub fn parse(i: &str) -> IResult<&str, CompressionType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("COMPRESSION"), - multispace0, - opt(tag("=")), - multispace0, - alt(( - map( - alt((tag_no_case("'ZLIB'"), tag_no_case("\"ZLIB\""))), - |_| CompressionType::ZLIB, - ), - map(alt((tag_no_case("'LZ4'"), tag_no_case("\"LZ4\""))), |_| { - CompressionType::LZ4 - }), - map( - alt((tag_no_case("'NONE'"), tag_no_case("\"NONE\""))), - |_| CompressionType::NONE, - ), + alt(( + map( + tuple(( + tag_no_case("COMPRESSION"), + multispace1, + Self::parse_compression, )), - )), - |(_, _, _, _, compression_type)| compression_type, - )(i) + |(_, _, compression_type)| compression_type, + ), + map( + tuple(( + tag_no_case("COMPRESSION"), + multispace0, + tag("="), + multispace0, + Self::parse_compression, + )), + |(_, _, _, _, compression_type)| compression_type, + ), + ))(i) + } + + fn parse_compression(i: &str) -> IResult<&str, CompressionType, ParseSQLError<&str>> { + alt(( + map( + alt((tag_no_case("'ZLIB'"), tag_no_case("\"ZLIB\""))), + |_| CompressionType::ZLIB, + ), + map(alt((tag_no_case("'LZ4'"), tag_no_case("\"LZ4\""))), |_| { + CompressionType::LZ4 + }), + map( + alt((tag_no_case("'NONE'"), tag_no_case("\"NONE\""))), + |_| CompressionType::NONE, + ), + ))(i) } } diff --git a/src/base/index_option.rs b/src/base/index_option.rs index fb26c32..93ec81a 100644 --- a/src/base/index_option.rs +++ b/src/base/index_option.rs @@ -140,18 +140,7 @@ impl IndexOption { /// SECONDARY_ENGINE_ATTRIBUTE [=] value fn secondary_engine_attribute(i: &str) -> IResult<&str, String, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("SECONDARY_ENGINE_ATTRIBUTE"), - multispace0, - opt(tag("=")), - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - multispace0, - )), - |(_, _, _, engine, _)| engine, - )(i) + CommonParser::parse_string_value_with_key(i, "SECONDARY_ENGINE_ATTRIBUTE".to_string()) } } diff --git a/src/base/insert_method_type.rs b/src/base/insert_method_type.rs index f046844..6f81342 100644 --- a/src/base/insert_method_type.rs +++ b/src/base/insert_method_type.rs @@ -1,9 +1,8 @@ use nom::branch::alt; -use nom::bytes::complete::tag_no_case; -use nom::character::complete::multispace0; +use nom::bytes::complete::{tag, tag_no_case}; +use nom::character::complete::{multispace0, multispace1}; use nom::combinator::{map, opt}; use nom::sequence::tuple; -use nom::streaming::tag; use nom::IResult; use std::fmt::{Display, Formatter}; @@ -29,20 +28,34 @@ impl Display for InsertMethodType { impl InsertMethodType { pub fn parse(i: &str) -> IResult<&str, InsertMethodType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("INSERT_METHOD"), - multispace0, - opt(tag_no_case("=")), - multispace0, - alt(( - map(tag_no_case("NO"), |_| InsertMethodType::No), - map(tag_no_case("FIRST"), |_| InsertMethodType::First), - map(tag_no_case("LAST"), |_| InsertMethodType::Last), + alt(( + map( + tuple(( + tag_no_case("INSERT_METHOD"), + multispace1, + Self::parse_method, )), - )), - |(_, _, _, _, inert_method_type)| inert_method_type, - )(i) + |(_, _, inert_method_type)| inert_method_type, + ), + map( + tuple(( + tag_no_case("INSERT_METHOD"), + multispace0, + tag("="), + multispace0, + Self::parse_method, + )), + |(_, _, _, _, inert_method_type)| inert_method_type, + ), + ))(i) + } + + fn parse_method(i: &str) -> IResult<&str, InsertMethodType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("NO"), |_| InsertMethodType::No), + map(tag_no_case("FIRST"), |_| InsertMethodType::First), + map(tag_no_case("LAST"), |_| InsertMethodType::Last), + ))(i) } } diff --git a/src/base/lock_type.rs b/src/base/lock_type.rs index ea6b819..42c5e61 100644 --- a/src/base/lock_type.rs +++ b/src/base/lock_type.rs @@ -1,6 +1,6 @@ use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case}; -use nom::character::complete::multispace0; +use nom::character::complete::{multispace0, multispace1}; use nom::combinator::{map, opt}; use nom::sequence::tuple; use nom::IResult; @@ -31,22 +31,31 @@ impl Display for LockType { impl LockType { pub fn parse(i: &str) -> IResult<&str, LockType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("LOCK"), - multispace0, - opt(tag("=")), - multispace0, - alt(( - map(tag_no_case("DEFAULT"), |_| LockType::Default), - map(tag_no_case("NONE"), |_| LockType::None), - map(tag_no_case("SHARED"), |_| LockType::Shared), - map(tag_no_case("EXCLUSIVE"), |_| LockType::Exclusive), + alt(( + map( + tuple((tag_no_case("LOCK"), multispace1, Self::parse_lock)), + |(_, _, lock)| lock, + ), + map( + tuple(( + tag_no_case("LOCK"), + multispace0, + tag("="), + multispace0, + Self::parse_lock, )), - multispace0, - )), - |x| x.4, - )(i) + |(_, _, _, _, lock)| lock, + ), + ))(i) + } + + fn parse_lock(i: &str) -> IResult<&str, LockType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("DEFAULT"), |_| LockType::Default), + map(tag_no_case("NONE"), |_| LockType::None), + map(tag_no_case("SHARED"), |_| LockType::Shared), + map(tag_no_case("EXCLUSIVE"), |_| LockType::Exclusive), + ))(i) } } @@ -75,5 +84,9 @@ mod tests { let res4 = LockType::parse(str4); assert!(res4.is_ok()); assert_eq!(res4.unwrap().1, LockType::Shared); + + let str5 = "lockSHARED"; + let res5 = LockType::parse(str5); + assert!(res5.is_err()); } } diff --git a/src/base/row_format_type.rs b/src/base/row_format_type.rs index 5378e80..470a2a0 100644 --- a/src/base/row_format_type.rs +++ b/src/base/row_format_type.rs @@ -1,6 +1,6 @@ use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case}; -use nom::character::complete::multispace0; +use nom::character::complete::{multispace0, multispace1}; use nom::combinator::{map, opt}; use nom::sequence::tuple; use nom::IResult; @@ -34,23 +34,37 @@ impl Display for RowFormatType { impl RowFormatType { pub fn parse(i: &str) -> IResult<&str, RowFormatType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("ROW_FORMAT"), - multispace0, - opt(tag("=")), - multispace0, - alt(( - map(tag_no_case("DEFAULT"), |_| RowFormatType::Default), - map(tag_no_case("DYNAMIC"), |_| RowFormatType::Dynamic), - map(tag_no_case("FIXED"), |_| RowFormatType::Fixed), - map(tag_no_case("COMPRESSED"), |_| RowFormatType::Compressed), - map(tag_no_case("REDUNDANT"), |_| RowFormatType::Redundant), - map(tag_no_case("COMPACT"), |_| RowFormatType::Compact), + alt(( + map( + tuple(( + tag_no_case("ROW_FORMAT"), + multispace1, + Self::parse_row_format, )), - )), - |(_, _, _, _, row_format_type)| row_format_type, - )(i) + |(_, _, row_format_type)| row_format_type, + ), + map( + tuple(( + tag_no_case("ROW_FORMAT"), + multispace0, + tag("="), + multispace0, + Self::parse_row_format, + )), + |(_, _, _, _, row_format_type)| row_format_type, + ), + ))(i) + } + + fn parse_row_format(i: &str) -> IResult<&str, RowFormatType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("DEFAULT"), |_| RowFormatType::Default), + map(tag_no_case("DYNAMIC"), |_| RowFormatType::Dynamic), + map(tag_no_case("FIXED"), |_| RowFormatType::Fixed), + map(tag_no_case("COMPRESSED"), |_| RowFormatType::Compressed), + map(tag_no_case("REDUNDANT"), |_| RowFormatType::Redundant), + map(tag_no_case("COMPACT"), |_| RowFormatType::Compact), + ))(i) } } diff --git a/src/base/table.rs b/src/base/table.rs index faeb7fe..184d8ec 100644 --- a/src/base/table.rs +++ b/src/base/table.rs @@ -124,3 +124,63 @@ impl<'a> From<(&'a str, &'a str)> for Table { } } } + +#[cfg(test)] +mod tests { + use base::Table; + + #[test] + fn parse_trigger() { + let str1 = "tbl_name"; + let res1 = Table::table_reference(str1); + let exp1 = Table { + name: "tbl_name".to_string(), + alias: None, + schema: None, + }; + assert!(res1.is_ok()); + assert_eq!(res1.unwrap().1, exp1); + + let str2 = "foo.tbl_name"; + let res2 = Table::schema_table_reference(str2); + let exp2 = Table { + name: "tbl_name".to_string(), + alias: None, + schema: Some("foo".to_string()), + }; + assert!(res2.is_ok()); + assert_eq!(res2.unwrap().1, exp2); + + let str3 = "foo.tbl_name as bar"; + let res3 = Table::schema_table_reference(str3); + let exp3 = Table { + name: "tbl_name".to_string(), + alias: Some("bar".to_string()), + schema: Some("foo".to_string()), + }; + assert!(res3.is_ok()); + assert_eq!(res3.unwrap().1, exp3); + } + + #[test] + fn from_str() { + let trigger1: Table = "tbl_name".into(); + let exp1 = Table { + name: "tbl_name".to_string(), + alias: None, + schema: None, + }; + assert_eq!(trigger1, exp1); + } + + #[test] + fn from_tuple_str() { + let table2: Table = ("foo", "tbl_name").into(); + let exp2 = Table { + name: "tbl_name".to_string(), + alias: None, + schema: Some("foo".to_string()), + }; + assert_eq!(table2, exp2); + } +} diff --git a/src/base/table_option.rs b/src/base/table_option.rs index aa02e8d..a870442 100644 --- a/src/base/table_option.rs +++ b/src/base/table_option.rs @@ -1,7 +1,7 @@ use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case, take_until}; use nom::character::complete::{digit1, multispace0, multispace1}; -use nom::combinator::{map, opt}; +use nom::combinator::{map, opt, value}; use nom::sequence::{delimited, tuple}; use nom::{IResult, Parser}; use std::fmt::{write, Display, Formatter}; @@ -133,7 +133,7 @@ impl TableOption { list.iter() .map(|x| x.to_string()) .collect::>() - .join(", ") + .join(" ") } fn table_option_part_1(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { @@ -178,120 +178,103 @@ impl TableOption { /// parse `AUTOEXTEND_SIZE [=] value` fn autoextend_size(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("AUTOEXTEND_SIZE "), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::AutoextendSize(value.parse::().unwrap()) - }, + |x| CommonParser::parse_string_value_with_key(x, "AUTOEXTEND_SIZE".to_string()), + |value| TableOption::AutoextendSize(value.parse::().unwrap()), )(i) } /// parse `AUTO_INCREMENT [=] value` fn auto_increment(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("AUTO_INCREMENT"), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::AutoIncrement(value.parse::().unwrap()) - }, + |x| CommonParser::parse_digit_value_with_key(x, "AUTO_INCREMENT".to_string()), + |value| TableOption::AutoIncrement(value.parse::().unwrap()), )(i) } /// parse `AVG_ROW_LENGTH [=] value` fn avg_row_length(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("AVG_ROW_LENGTH "), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::AvgRowLength(value.parse::().unwrap()) - }, + |x| CommonParser::parse_string_value_with_key(x, "AVG_ROW_LENGTH".to_string()), + |value| TableOption::AvgRowLength(value.parse::().unwrap()), )(i) } /// parse `[DEFAULT] CHARACTER SET [=] charset_name` fn default_character_set(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { - map( - tuple(( - opt(tag_no_case("DEFAULT ")), - multispace0, + alt(( + map( tuple(( - tag_no_case("CHARACTER"), - multispace1, - tag_no_case("SET"), + opt(tag_no_case("DEFAULT ")), multispace0, - opt(tag("=")), + tuple(( + tag_no_case("CHARACTER"), + multispace1, + tag_no_case("SET"), + multispace1, + )), + map(CommonParser::sql_identifier, String::from), + )), + |(_, _, _, charset_name)| TableOption::DefaultCharacterSet(charset_name), + ), + map( + tuple(( + opt(tag_no_case("DEFAULT ")), multispace0, + tuple(( + tag_no_case("CHARACTER"), + multispace1, + tag_no_case("SET"), + multispace0, + tag("="), + multispace0, + )), + map(CommonParser::sql_identifier, String::from), )), - map(CommonParser::sql_identifier, String::from), - )), - |(_, _, _, charset_name)| TableOption::DefaultCharacterSet(charset_name), - )(i) + |(_, _, _, charset_name)| TableOption::DefaultCharacterSet(charset_name), + ), + ))(i) } /// parse `[DEFAULT] CHARSET [=] charset_name` fn default_charset(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - opt(tag_no_case("DEFAULT")), - multispace1, - tuple(( - tag_no_case("CHARSET"), - multispace0, - opt(tag("=")), - multispace0, - )), - map(CommonParser::sql_identifier, String::from), - )), - |(_, _, _, charset_name)| TableOption::DefaultCharset(charset_name), + tuple((opt(tag_no_case("DEFAULT ")), multispace0, |x| { + CommonParser::parse_string_value_with_key(x, "CHARSET".to_string()) + })), + |(_, _, charset_name)| TableOption::DefaultCharset(charset_name), )(i) } /// parse `CHECKSUM [=] {0 | 1}` fn checksum(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("CHECKSUM "), - multispace0, - opt(tag("=")), - multispace0, - alt((map(tag("0"), |_| 0), map(tag("1"), |_| 1))), - )), - |(_, _, _, _, checksum)| TableOption::Checksum(checksum), - )(i) + alt(( + map( + tuple(( + tag_no_case("CHECKSUM "), + multispace1, + alt((map(tag("0"), |_| 0), map(tag("1"), |_| 1))), + )), + |(_, _, checksum)| TableOption::Checksum(checksum), + ), + map( + tuple(( + tag_no_case("CHECKSUM "), + multispace0, + tag("="), + multispace0, + alt((map(tag("0"), |_| 0), map(tag("1"), |_| 1))), + )), + |(_, _, _, _, checksum)| TableOption::Checksum(checksum), + ), + ))(i) } /// parse `[DEFAULT] COLLATE [=] collation_name` fn default_collate(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - opt(tag_no_case("DEFAULT ")), - multispace0, - map( - tuple(( - tag_no_case("COLLATE"), - multispace1, - opt(tag("=")), - multispace0, - CommonParser::sql_identifier, - )), - |(_, _, _, _, collation_name)| String::from(collation_name), - ), - )), + tuple((opt(tag_no_case("DEFAULT ")), multispace0, |x| { + CommonParser::parse_string_value_with_key(x, "COLLATE".to_string()) + })), |(_, _, collation_name)| TableOption::DefaultCollate(collation_name), )(i) } @@ -299,16 +282,8 @@ impl TableOption { /// parse COMMENT [=] 'string'` fn comment(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("COMMENT"), - multispace0, - opt(tag("=")), - multispace0, - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - )), - |(_, _, _, _, comment)| TableOption::Comment(comment), + |x| CommonParser::parse_quoted_string_value_with_key(x, "COMMENT".to_string()), + TableOption::Comment, )(i) } @@ -320,114 +295,92 @@ impl TableOption { /// parse `CONNECTION [=] 'connect_string'` fn connection(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("CONNECTION"), - multispace0, - opt(tag("=")), - multispace0, - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - )), - |(_, _, _, _, connect_string)| TableOption::Connection(connect_string), + |x| CommonParser::parse_string_value_with_key(x, "CONNECTION".to_string()), + TableOption::Connection, )(i) } /// parse `{DATA | INDEX} DIRECTORY [=] 'absolute path to directory'` fn data_directory(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("DATA"), - multispace1, - tag_no_case("DIRECTORY"), - multispace0, - opt(tag("=")), - multispace0, - map( - alt(( - delimited(tag("'"), take_until("'"), tag("'")), - delimited(tag("\""), take_until("\""), tag("\"")), - )), - String::from, - ), - )), - |(_, _, _, _, _, _, path)| TableOption::DataDirectory(path), + tuple((tag_no_case("DATA"), multispace1, |x| { + CommonParser::parse_quoted_string_value_with_key(x, "DIRECTORY".to_string()) + })), + |(_, _, path)| TableOption::DataDirectory(path), )(i) } /// parse `{DATA | INDEX} DIRECTORY [=] 'absolute path to directory'` fn index_directory(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("INDEX"), - multispace1, - tag_no_case("DIRECTORY"), - multispace0, - opt(tag("=")), - multispace0, - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - )), - |(_, _, _, _, _, _, path)| TableOption::DataDirectory(path), + tuple((tag_no_case("INDEX"), multispace1, |x| { + CommonParser::parse_quoted_string_value_with_key(x, "DIRECTORY".to_string()) + })), + |(_, _, path)| TableOption::DataDirectory(path), )(i) } /// parse `DELAY_KEY_WRITE [=] {0 | 1}` fn delay_key_write(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("DELAY_KEY_RITE"), - multispace0, - opt(tag("=")), - multispace0, - alt((map(tag("0"), |_| 0), map(tag("1"), |_| 1))), - )), - |(_, _, _, _, delay_key_rite)| TableOption::DelayKeyWrite(delay_key_rite), - )(i) + alt(( + map( + tuple(( + tag_no_case("DELAY_KEY_RITE"), + multispace1, + alt((map(tag("0"), |_| 0), map(tag("1"), |_| 1))), + )), + |(_, _, delay_key_rite)| TableOption::DelayKeyWrite(delay_key_rite), + ), + map( + tuple(( + tag_no_case("DELAY_KEY_RITE"), + multispace0, + tag("="), + multispace0, + alt((map(tag("0"), |_| 0), map(tag("1"), |_| 1))), + )), + |(_, _, _, _, delay_key_rite)| TableOption::DelayKeyWrite(delay_key_rite), + ), + ))(i) } /// parse `ENCRYPTION [=] {'Y' | 'N'}` fn encryption(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("ENCRYPTION"), - multispace0, - opt(tag("=")), - multispace0, - alt((map(tag("'Y'"), |_| true), map(tag("'N'"), |_| false))), - )), - |(_, _, _, _, encryption)| TableOption::Encryption(encryption), - )(i) + alt(( + map( + tuple(( + tag_no_case("ENCRYPTION"), + multispace1, + alt((map(tag("'Y'"), |_| true), map(tag("'N'"), |_| false))), + )), + |(_, _, encryption)| TableOption::Encryption(encryption), + ), + map( + tuple(( + tag_no_case("ENCRYPTION"), + multispace0, + tag("="), + multispace0, + alt((map(tag("'Y'"), |_| true), map(tag("'N'"), |_| false))), + )), + |(_, _, _, _, encryption)| TableOption::Encryption(encryption), + ), + ))(i) } /// parse `ENGINE [=] engine_name` fn engine(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("ENGINE"), - multispace0, - opt(tag("=")), - multispace0, - CommonParser::sql_identifier, - )), - |(_, _, _, _, engine)| TableOption::Engine(String::from(engine)), + |x| CommonParser::parse_string_value_with_key(x, "ENGINE".to_string()), + TableOption::Engine, )(i) } /// parse `ENGINE_ATTRIBUTE [=] 'string'` fn engine_attribute(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("ENGINE_ATTRIBUTE"), - multispace0, - opt(tag("=")), - multispace0, - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - )), - |(_, _, _, _, attribute)| TableOption::EngineAttribute(attribute), + |x| CommonParser::parse_quoted_string_value_with_key(x, "ENGINE_ATTRIBUTE".to_string()), + TableOption::EngineAttribute, )(i) } @@ -439,78 +392,40 @@ impl TableOption { /// parse `KEY_BLOCK_SIZE [=] value` fn key_block_size(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("KEY_BLOCK_SIZE"), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::KeyBlockSize(value.parse::().unwrap()) - }, + |x| CommonParser::parse_string_value_with_key(x, "KEY_BLOCK_SIZE".to_string()), + |value| TableOption::KeyBlockSize(value.parse::().unwrap()), )(i) } /// parse `MAX_ROWS [=] value` fn max_rows(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("MAX_ROWS"), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::MaxRows(value.parse::().unwrap()) - }, + |x| CommonParser::parse_string_value_with_key(x, "MAX_ROWS".to_string()), + |value| TableOption::MaxRows(value.parse::().unwrap()), )(i) } /// parse `MIN_ROWS [=] value` fn min_rows(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("MIN_ROWS"), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::MinRows(value.parse::().unwrap()) - }, + |x| CommonParser::parse_string_value_with_key(x, "MIN_ROWS".to_string()), + |value| TableOption::MinRows(value.parse::().unwrap()), )(i) } /// parse `PACK_KEYS [=] {0 | 1 | DEFAULT}` fn pack_keys(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("PACK_KEYS"), - multispace0, - opt(tag("=")), - multispace0, - DefaultOrZeroOrOne::parse, - )), - |(_, _, _, _, value)| TableOption::PackKeys(value), + |x| CommonParser::parse_default_value_with_key(x, "PACK_KEYS".to_string()), + TableOption::PackKeys, )(i) } /// parse `PASSWORD [=] 'string'` fn password(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("PASSWORD"), - multispace0, - opt(tag("=")), - multispace0, - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - )), - |(_, _, _, _, password)| TableOption::Password(password), + |x| CommonParser::parse_quoted_string_value_with_key(x, "PASSWORD".to_string()), + TableOption::Password, )(i) } @@ -535,60 +450,37 @@ impl TableOption { /// parse `SECONDARY_ENGINE_ATTRIBUTE [=] 'string'` fn secondary_engine_attribute(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("SECONDARY_ENGINE_ATTRIBUTE"), - multispace0, - opt(tag("=")), - multispace0, - map(delimited(tag("'"), take_until("'"), tag("'")), |x| { - String::from(x) - }), - )), - |(_, _, _, _, engine)| TableOption::SecondaryEngineAttribute(engine), + |x| { + CommonParser::parse_quoted_string_value_with_key( + x, + "SECONDARY_ENGINE_ATTRIBUTE".to_string(), + ) + }, + TableOption::SecondaryEngineAttribute, )(i) } /// parse `STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1}` fn stats_auto_recalc(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("STATS_AUTO_RECALC"), - multispace0, - opt(tag("=")), - multispace0, - DefaultOrZeroOrOne::parse, - )), - |(_, _, _, _, value)| TableOption::StatsAutoRecalc(value), + |x| CommonParser::parse_default_value_with_key(x, "STATS_AUTO_RECALC".to_string()), + TableOption::StatsAutoRecalc, )(i) } /// parse `STATS_PERSISTENT [=] {DEFAULT | 0 | 1}` fn stats_persistent(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("STATS_PERSISTENT"), - multispace0, - opt(tag("=")), - multispace0, - DefaultOrZeroOrOne::parse, - )), - |(_, _, _, _, value)| TableOption::StatsAutoRecalc(value), + |x| CommonParser::parse_default_value_with_key(x, "STATS_AUTO_RECALC".to_string()), + TableOption::StatsAutoRecalc, )(i) } /// parse `STATS_SAMPLE_PAGES [=] value` fn stats_sample_pages(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { map( - tuple(( - tag_no_case("STATS_SAMPLE_PAGES "), - multispace0, - opt(tag("=")), - multispace0, - digit1, - )), - |(_, _, _, _, value): (&str, &str, Option<&str>, &str, &str)| { - TableOption::StatsSamplePages(value.parse::().unwrap()) - }, + |x| CommonParser::parse_string_value_with_key(x, "STATS_SAMPLE_PAGES".to_string()), + |value| TableOption::StatsSamplePages(value.parse::().unwrap()), )(i) } @@ -608,26 +500,46 @@ impl TableOption { /// parse `UNION [=] (tbl_name[,tbl_name]...)` fn union(i: &str) -> IResult<&str, TableOption, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("UNION "), - multispace0, - opt(tag("=")), - multispace0, - map( - tuple(( - multispace0, - delimited( - tag("("), - delimited(multispace0, Column::index_col_list, multispace0), - tag(")"), - ), - )), - |(_, value)| value.iter().map(|x| x.name.clone()).collect(), - ), - )), - |(_, _, _, _, union)| TableOption::Union(union), - )(i) + alt(( + map( + tuple(( + tag_no_case("UNION"), + multispace1, + map( + tuple(( + multispace0, + delimited( + tag("("), + delimited(multispace0, Column::index_col_list, multispace0), + tag(")"), + ), + )), + |(_, value)| value.iter().map(|x| x.name.clone()).collect(), + ), + )), + |(_, _, union)| TableOption::Union(union), + ), + map( + tuple(( + tag_no_case("UNION "), + multispace0, + tag("="), + multispace0, + map( + tuple(( + multispace0, + delimited( + tag("("), + delimited(multispace0, Column::index_col_list, multispace0), + tag(")"), + ), + )), + |(_, value)| value.iter().map(|x| x.name.clone()).collect(), + ), + )), + |(_, _, _, _, union)| TableOption::Union(union), + ), + ))(i) } } diff --git a/src/base/trigger.rs b/src/base/trigger.rs index 370905e..1949a96 100644 --- a/src/base/trigger.rs +++ b/src/base/trigger.rs @@ -57,3 +57,50 @@ impl<'a> From<(&'a str, &'a str)> for Trigger { } } } + +#[cfg(test)] +mod tests { + use base::Trigger; + use nom::combinator::into; + + #[test] + fn parse_trigger() { + let str1 = "trigger_name"; + let res1 = Trigger::parse(str1); + let exp1 = Trigger { + name: "trigger_name".to_string(), + schema: None, + }; + assert!(res1.is_ok()); + assert_eq!(res1.unwrap().1, exp1); + + let str2 = "foo.trigger_name"; + let res2 = Trigger::parse(str2); + let exp2 = Trigger { + name: "trigger_name".to_string(), + schema: Some("foo".to_string()), + }; + assert!(res2.is_ok()); + assert_eq!(res2.unwrap().1, exp2); + } + + #[test] + fn from_str() { + let trigger1: Trigger = "trigger_name".into(); + let exp1 = Trigger { + name: "trigger_name".to_string(), + schema: None, + }; + assert_eq!(trigger1, exp1); + } + + #[test] + fn from_tuple_str() { + let trigger2: Trigger = ("foo", "trigger_name").into(); + let exp2 = Trigger { + name: "trigger_name".to_string(), + schema: Some("foo".to_string()), + }; + assert_eq!(trigger2, exp2); + } +} diff --git a/src/dds/alter_database.rs b/src/dds/alter_database.rs index 992545a..2def5e1 100644 --- a/src/dds/alter_database.rs +++ b/src/dds/alter_database.rs @@ -132,18 +132,30 @@ impl AlterDatabaseOption { ); // READ ONLY [=] {DEFAULT | 0 | 1} - let read_only = map( - tuple(( - opt(tag_no_case("READ")), - multispace1, - tag_no_case("ONLY "), - multispace0, - opt(tag("=")), - multispace0, - DefaultOrZeroOrOne::parse, - )), - |x| AlterDatabaseOption::ReadOnly(x.6), - ); + let read_only = alt(( + map( + tuple(( + opt(tag_no_case("READ")), + multispace1, + tag_no_case("ONLY"), + multispace1, + DefaultOrZeroOrOne::parse, + )), + |(_, _, _, _, value)| AlterDatabaseOption::ReadOnly(value), + ), + map( + tuple(( + opt(tag_no_case("READ")), + multispace1, + tag_no_case("ONLY"), + multispace0, + tag("="), + multispace0, + DefaultOrZeroOrOne::parse, + )), + |(_, _, _, _, _, _, value)| AlterDatabaseOption::ReadOnly(value), + ), + )); alt((character, collate, encryption, read_only))(i) } diff --git a/src/dds/drop_logfile_group.rs b/src/dds/drop_logfile_group.rs index 49d79e2..3a64c62 100644 --- a/src/dds/drop_logfile_group.rs +++ b/src/dds/drop_logfile_group.rs @@ -2,18 +2,16 @@ use core::fmt; use std::fmt::Formatter; use std::str; -use nom::bytes::complete::{tag, tag_no_case}; -use nom::character::complete::multispace0; +use nom::bytes::complete::tag_no_case; use nom::character::complete::multispace1; -use nom::combinator::{map, opt}; +use nom::combinator::map; use nom::sequence::tuple; use nom::IResult; use base::error::ParseSQLError; use base::CommonParser; -/// parse `DROP LOGFILE GROUP logfile_group -/// ENGINE [=] engine_name` +/// parse `DROP LOGFILE GROUP logfile_group ENGINE [=] engine_name` #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct DropLogfileGroupStatement { pub logfile_group: String, @@ -31,20 +29,10 @@ impl DropLogfileGroupStatement { multispace1, map(CommonParser::sql_identifier, String::from), multispace1, - map( - tuple(( - tag_no_case("ENGINE"), - multispace0, - opt(tag("=")), - multispace0, - CommonParser::sql_identifier, - )), - |(_, _, _, _, engine)| String::from(engine), - ), - multispace0, + |x| CommonParser::parse_string_value_with_key(x, "ENGINE".to_string()), CommonParser::statement_terminator, )); - let (remaining_input, (_, _, _, _, _, _, logfile_group, _, engine_name, _, _)) = parser(i)?; + let (remaining_input, (_, _, _, _, _, _, logfile_group, _, engine_name, _)) = parser(i)?; Ok(( remaining_input, @@ -71,11 +59,20 @@ mod tests { #[test] fn parse_drop_logfile_group_parser() { - let sqls = ["DROP LOGFILE GROUP logfile_group ENGINE = demo;"]; - let exp_statements = [DropLogfileGroupStatement { - logfile_group: "logfile_group".to_string(), - engine_name: "demo".to_string(), - }]; + let sqls = [ + "DROP LOGFILE GROUP logfile_group ENGINE = demo;", + "DROP LOGFILE GROUP logfile_group ENGINE bar;", + ]; + let exp_statements = [ + DropLogfileGroupStatement { + logfile_group: "logfile_group".to_string(), + engine_name: "demo".to_string(), + }, + DropLogfileGroupStatement { + logfile_group: "logfile_group".to_string(), + engine_name: "bar".to_string(), + }, + ]; for i in 0..sqls.len() { let res = DropLogfileGroupStatement::parse(sqls[i]); diff --git a/src/dds/drop_tablespace.rs b/src/dds/drop_tablespace.rs index 5b1735b..0205ba6 100644 --- a/src/dds/drop_tablespace.rs +++ b/src/dds/drop_tablespace.rs @@ -2,9 +2,8 @@ use core::fmt; use std::fmt::Formatter; use std::str; -use nom::bytes::complete::{tag, tag_no_case}; +use nom::bytes::complete::tag_no_case; use nom::character::complete::multispace0; -use nom::character::complete::multispace1; use nom::combinator::{map, opt}; use nom::sequence::tuple; use nom::IResult; @@ -12,8 +11,7 @@ use nom::IResult; use base::error::ParseSQLError; use base::CommonParser; -/// parse `DROP [UNDO] TABLESPACE tablespace_name -/// [ENGINE [=] engine_name]` +/// parse `DROP [UNDO] TABLESPACE tablespace_name [ENGINE [=] engine_name]` #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct DropTablespaceStatement { pub undo: bool, @@ -30,25 +28,12 @@ impl DropTablespaceStatement { multispace0, tag_no_case("TABLESPACE "), multispace0, - map(CommonParser::sql_identifier, |tablespace_name| { - String::from(tablespace_name) - }), - multispace0, - opt(map( - tuple(( - tag_no_case("ENGINE"), - multispace0, - opt(tag("=")), - multispace0, - CommonParser::sql_identifier, - multispace0, - )), - |(_, _, _, _, engine, _)| String::from(engine), - )), + map(CommonParser::sql_identifier, String::from), multispace0, + opt(|x| CommonParser::parse_string_value_with_key(x, "ENGINE".to_string())), CommonParser::statement_terminator, )); - let (remaining_input, (_, _, opt_undo, _, _, _, tablespace_name, _, engine_name, _, _)) = + let (remaining_input, (_, _, opt_undo, _, _, _, tablespace_name, _, engine_name, _)) = parser(i)?; Ok(( diff --git a/statements/alter_database.txt b/statements/alter_database.txt deleted file mode 100644 index 3401c90..0000000 --- a/statements/alter_database.txt +++ /dev/null @@ -1,9 +0,0 @@ -ALTER {DATABASE | SCHEMA} [db_name] - alter_option ... - -alter_option: { - [DEFAULT] CHARACTER SET [=] charset_name - | [DEFAULT] COLLATE [=] collation_name - | [DEFAULT] ENCRYPTION [=] {'Y' | 'N'} - | READ ONLY [=] {DEFAULT | 0 | 1} -} \ No newline at end of file diff --git a/statements/alter_table.txt b/statements/alter_table.txt deleted file mode 100644 index ab058f4..0000000 --- a/statements/alter_table.txt +++ /dev/null @@ -1,121 +0,0 @@ -ALTER TABLE tbl_name - [alter_option [, alter_option] ...] - [partition_options] - -alter_option: { - table_options - | ADD [COLUMN] col_name column_definition - [FIRST | AFTER col_name] - | ADD [COLUMN] (col_name column_definition,...) - | ADD {INDEX | KEY} [index_name] - [index_type] (key_part,...) [index_option] ... - | ADD {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] - (key_part,...) [index_option] ... - | ADD [CONSTRAINT [symbol]] PRIMARY KEY - [index_type] (key_part,...) - [index_option] ... - | ADD [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY] - [index_name] [index_type] (key_part,...) - [index_option] ... - | ADD [CONSTRAINT [symbol]] FOREIGN KEY - [index_name] (col_name,...) - reference_definition - | ADD [CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED] - | DROP {CHECK | CONSTRAINT} symbol - | ALTER {CHECK | CONSTRAINT} symbol [NOT] ENFORCED - | ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY} - | ALTER [COLUMN] col_name { - SET DEFAULT {literal | (expr)} - | SET {VISIBLE | INVISIBLE} - | DROP DEFAULT - } - | ALTER INDEX index_name {VISIBLE | INVISIBLE} - | CHANGE [COLUMN] old_col_name new_col_name column_definition - [FIRST | AFTER col_name] - | [DEFAULT] CHARACTER SET [=] charset_name [COLLATE [=] collation_name] - | CONVERT TO CHARACTER SET charset_name [COLLATE collation_name] - | {DISABLE | ENABLE} KEYS - | {DISCARD | IMPORT} TABLESPACE - | DROP [COLUMN] col_name - | DROP {INDEX | KEY} index_name - | DROP PRIMARY KEY - | DROP FOREIGN KEY fk_symbol - | FORCE - | LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE} - | MODIFY [COLUMN] col_name column_definition - [FIRST | AFTER col_name] - | ORDER BY col_name [, col_name] ... - | RENAME COLUMN old_col_name TO new_col_name - | RENAME {INDEX | KEY} old_index_name TO new_index_name - | RENAME [TO | AS] new_tbl_name - | {WITHOUT | WITH} VALIDATION -} - -partition_options: - partition_option [partition_option] ... - -partition_option: { - ADD PARTITION (partition_definition) - | DROP PARTITION partition_names - | DISCARD PARTITION {partition_names | ALL} TABLESPACE - | IMPORT PARTITION {partition_names | ALL} TABLESPACE - | TRUNCATE PARTITION {partition_names | ALL} - | COALESCE PARTITION number - | REORGANIZE PARTITION partition_names INTO (partition_definitions) - | EXCHANGE PARTITION partition_name WITH TABLE tbl_name [{WITH | WITHOUT} VALIDATION] - | ANALYZE PARTITION {partition_names | ALL} - | CHECK PARTITION {partition_names | ALL} - | OPTIMIZE PARTITION {partition_names | ALL} - | REBUILD PARTITION {partition_names | ALL} - | REPAIR PARTITION {partition_names | ALL} - | REMOVE PARTITIONING -} - -key_part: {col_name [(length)] | (expr)} [ASC | DESC] - -index_type: - USING {BTREE | HASH} - -index_option: { - KEY_BLOCK_SIZE [=] value - | index_type - | WITH PARSER parser_name - | COMMENT 'string' - | {VISIBLE | INVISIBLE} -} - -table_options: - table_option [[,] table_option] ... - -table_option: { - AutoextendSize [=] value - | AutoIncrement [=] value - | AvgRowLength [=] value - | [DEFAULT] CHARACTER SET [=] charset_name - | CHECKSUM [=] {0 | 1} - | [DEFAULT] COLLATE [=] collation_name - | COMMENT [=] 'string' - | COMPRESSION [=] {'ZLIB' | 'LZ4' | 'NONE'} - | CONNECTION [=] 'connect_string' - | {DATA | INDEX} DIRECTORY [=] 'absolute path to directory' - | DELAY_KEY_WRITE [=] {0 | 1} - | ENCRYPTION [=] {'Y' | 'N'} - | ENGINE [=] engine_name - | ENGINE_ATTRIBUTE [=] 'string' - | INSERT_METHOD [=] { NO | FIRST | LAST } - | KEY_BLOCK_SIZE [=] value - | MAX_ROWS [=] value - | MIN_ROWS [=] value - | PACK_KEYS [=] {0 | 1 | DEFAULT} - | PASSWORD [=] 'string' - | ROW_FORMAT [=] {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT} - | SecondaryEngineAttribute [=] 'string' - | StatsAutoRecalc [=] {DEFAULT | 0 | 1} - | StatsPersistent [=] {DEFAULT | 0 | 1} - | StatsSamplePages [=] value - | TABLESPACE tablespace_name [STORAGE {DISK | MEMORY}] - | UNION [=] (tbl_name[,tbl_name]...) -} - -partition_options: - (see CREATE TABLE options) \ No newline at end of file diff --git a/statements/create_table.txt b/statements/create_table.txt deleted file mode 100644 index 2be8a3d..0000000 --- a/statements/create_table.txt +++ /dev/null @@ -1,165 +0,0 @@ -CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name - (create_definition,...) - [table_options] - [partition_options] - -CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name - [(create_definition,...)] - [table_options] - [partition_options] - [IGNORE | REPLACE] - [AS] query_expression - -CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name - { LIKE old_tbl_name | (LIKE old_tbl_name) } - -create_definition: { - col_name column_definition - | {INDEX | KEY} [index_name] [index_type] (key_part,...) - [index_option] ... - | {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] (key_part,...) - [index_option] ... - | [CONSTRAINT [symbol]] PRIMARY KEY - [index_type] (key_part,...) - [index_option] ... - | [CONSTRAINT [symbol]] UNIQUE [INDEX | KEY] - [index_name] [index_type] (key_part,...) - [index_option] ... - | [CONSTRAINT [symbol]] FOREIGN KEY - [index_name] (col_name,...) - reference_definition - | check_constraint_definition -} - -column_definition: { - data_type [NOT NULL | NULL] [DEFAULT {literal | (expr)} ] - [VISIBLE | INVISIBLE] - [AUTO_INCREMENT] [UNIQUE [KEY]] [[PRIMARY] KEY] - [COMMENT 'string'] - [COLLATE collation_name] - [COLUMN_FORMAT {FIXED | DYNAMIC | DEFAULT}] - [ENGINE_ATTRIBUTE [=] 'string'] - [SECONDARY_ENGINE_ATTRIBUTE [=] 'string'] - [STORAGE {DISK | MEMORY}] - [reference_definition] - [check_constraint_definition] - | data_type - [COLLATE collation_name] - [GENERATED ALWAYS] AS (expr) - [VIRTUAL | STORED] [NOT NULL | NULL] - [VISIBLE | INVISIBLE] - [UNIQUE [KEY]] [[PRIMARY] KEY] - [COMMENT 'string'] - [reference_definition] - [check_constraint_definition] -} - -data_type: - (see Chapter 13, Data Types) - -key_part: {col_name [(length)] | (expr)} [ASC | DESC] - -index_type: - USING {BTREE | HASH} - -index_option: { - KEY_BLOCK_SIZE [=] value - | index_type - | WITH PARSER parser_name - | COMMENT 'string' - | {VISIBLE | INVISIBLE} - |ENGINE_ATTRIBUTE [=] 'string' - |SECONDARY_ENGINE_ATTRIBUTE [=] 'string' -} - -check_constraint_definition: - [CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED] - -reference_definition: - REFERENCES tbl_name (key_part,...) - [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] - [ON DELETE reference_option] - [ON UPDATE reference_option] - -reference_option: - RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT - -table_options: - table_option [[,] table_option] ... - -table_option: { - AUTOEXTEND_SIZE [=] value - | AUTO_INCREMENT [=] value - | AVG_ROW_LENGTH [=] value - | [DEFAULT] CHARACTER SET [=] charset_name - | CHECKSUM [=] {0 | 1} - | [DEFAULT] COLLATE [=] collation_name - | COMMENT [=] 'string' - | COMPRESSION [=] {'ZLIB' | 'LZ4' | 'NONE'} - | CONNECTION [=] 'connect_string' - | {DATA | INDEX} DIRECTORY [=] 'absolute path to directory' - | DELAY_KEY_WRITE [=] {0 | 1} - | ENCRYPTION [=] {'Y' | 'N'} - | ENGINE [=] engine_name - | ENGINE_ATTRIBUTE [=] 'string' - | INSERT_METHOD [=] { NO | FIRST | LAST } - | KEY_BLOCK_SIZE [=] value - | MAX_ROWS [=] value - | MIN_ROWS [=] value - | PACK_KEYS [=] {0 | 1 | DEFAULT} - | PASSWORD [=] 'string' - | ROW_FORMAT [=] {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT} - | START TRANSACTION - | SECONDARY_ENGINE_ATTRIBUTE [=] 'string' - | STATS_AUTO_RECALC [=] {DEFAULT | 0 | 1} - | STATS_PERSISTENT [=] {DEFAULT | 0 | 1} - | STATS_SAMPLE_PAGES [=] value - | tablespace_option - | UNION [=] (tbl_name[,tbl_name]...) -} - -partition_options: - PARTITION BY - { [LINEAR] HASH(expr) - | [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list) - | RANGE{(expr) | COLUMNS(column_list)} - | LIST{(expr) | COLUMNS(column_list)} } - [PARTITIONS num] - [SUBPARTITION BY - { [LINEAR] HASH(expr) - | [LINEAR] KEY [ALGORITHM={1 | 2}] (column_list) } - [SUBPARTITIONS num] - ] - [(partition_definition [, partition_definition] ...)] - -partition_definition: - PARTITION partition_name - [VALUES - {LESS THAN {(expr | value_list) | MAXVALUE} - | - IN (value_list)}] - [[STORAGE] ENGINE [=] engine_name] - [COMMENT [=] 'string' ] - [DATA DIRECTORY [=] 'data_dir'] - [INDEX DIRECTORY [=] 'index_dir'] - [MAX_ROWS [=] max_number_of_rows] - [MIN_ROWS [=] min_number_of_rows] - [TABLESPACE [=] tablespace_name] - [(subpartition_definition [, subpartition_definition] ...)] - -subpartition_definition: - SUBPARTITION logical_name - [[STORAGE] ENGINE [=] engine_name] - [COMMENT [=] 'string' ] - [DATA DIRECTORY [=] 'data_dir'] - [INDEX DIRECTORY [=] 'index_dir'] - [MAX_ROWS [=] max_number_of_rows] - [MIN_ROWS [=] min_number_of_rows] - [TABLESPACE [=] tablespace_name] - -tablespace_option: - TABLESPACE tablespace_name [STORAGE DISK] - | [TABLESPACE tablespace_name] STORAGE MEMORY - -query_expression: - SELECT ... (Some valid select or union statement) \ No newline at end of file diff --git a/statements/drop_database.txt b/statements/drop_database.txt deleted file mode 100644 index 246cf1d..0000000 --- a/statements/drop_database.txt +++ /dev/null @@ -1 +0,0 @@ -DROP {DATABASE | SCHEMA} [IF EXISTS] db_name \ No newline at end of file diff --git a/statements/drop_table.txt b/statements/drop_table.txt deleted file mode 100644 index 69895d1..0000000 --- a/statements/drop_table.txt +++ /dev/null @@ -1,3 +0,0 @@ -DROP [TEMPORARY] TABLE [IF EXISTS] - tbl_name [, tbl_name] ... - [RESTRICT | CASCADE] \ No newline at end of file diff --git a/statements/rename_table.txt b/statements/rename_table.txt deleted file mode 100644 index ad7a4b5..0000000 --- a/statements/rename_table.txt +++ /dev/null @@ -1,3 +0,0 @@ -RENAME TABLE -tbl_name TO new_tbl_name -[, tbl_name2 TO new_tbl_name2] ... \ No newline at end of file diff --git a/statements/truncate_table.txt b/statements/truncate_table.txt deleted file mode 100644 index f050f28..0000000 --- a/statements/truncate_table.txt +++ /dev/null @@ -1 +0,0 @@ -TRUNCATE [TABLE] tbl_name \ No newline at end of file diff --git a/tests/test_ddl.rs b/tests/test_ddl.rs index f028353..14513b6 100644 --- a/tests/test_ddl.rs +++ b/tests/test_ddl.rs @@ -2,6 +2,8 @@ extern crate sqlparser_mysql; use sqlparser_mysql::dds::{AlterTableStatement, CreateTableStatement}; +// FIXME should assert_eq(parse_result, exp) + #[test] fn parse_create_table() { let create_sqls = vec![ @@ -37,13 +39,12 @@ fn parse_create_table() { "CREATE TABLE author ( a_id int not null, a_fname varchar(20), a_lname varchar(20), a_mname varchar(20), a_dob date, a_bio int, PRIMARY KEY(a_id))", "CREATE TABLE customer ( c_id int not null, c_uname varchar(20), c_passwd varchar(20), c_fname varchar(17), c_lname varchar(17), c_addr_id int, c_phone varchar(18), c_email varchar(50), c_since date, c_last_login date, c_login timestamp, c_expiration timestamp, c_discount real, c_balance double, c_ytd_pmt double, c_birthdate date, c_data int, PRIMARY KEY(c_id))", "CREATE TABLE item ( i_id int not null, i_title varchar(60), i_a_id int, i_pub_date date, i_publisher varchar(60), i_subject varchar(60), i_desc text, i_related1 int, i_related2 int, i_related3 int, i_related4 int, i_related5 int, i_thumbnail varchar(40), i_image varchar(40), i_srp double, i_cost double, i_avail date, i_stock int, i_isbn char(13), i_page int, i_backing varchar(15), i_dimensions varchar(25), PRIMARY KEY(i_id))", - "CREATE TABLE user (user_id int(5) unsigned NOT NULL auto_increment,user_name varchar(255) binary NOT NULL default '',user_rights tinyblob NOT NULL default '',user_password tinyblob NOT NULL default '',user_newpassword tinyblob NOT NULL default '',user_email tinytext NOT NULL default '',user_options blob NOT NULL default '',user_touched char(14) binary NOT NULL default '',UNIQUE KEY user_id (user_id)) ENGINE=MyISAM PACK_KEYS=1;", "CREATE TABLE `admin_assert` (`assert_id` int(10) unsigned NOT NULL Auto_Increment COMMENT 'Assert ID',`assert_type` varchar(20) DEFAULT NULL COMMENT 'Assert Type',`assert_data` text COMMENT 'Assert Data',PRIMARY KEY (`assert_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;", - "CREATE TABLE `postcode_city` (`id` int(10) unsigned NOT NULL Auto_Increment COMMENT 'Id',`country_code` varchar(5) NOT NULL COMMENT 'Country Code',`postcode` varchar(20) NOT NULL COMMENT 'Postcode',`city` text NOT NULL COMMENT 'City',PRIMARY KEY (`id`)) ENGINE=InnoDB Auto_Increment=52142 DEFAULT CHARSET=utf8 COMMENT='Postcode -> City';", + "CREATE TABLE user (user_id int(5) unsigned NOT NULL auto_increment,user_name varchar(255) binary NOT NULL default '',user_rights tinyblob NOT NULL default '',user_password tinyblob NOT NULL default '',user_newpassword tinyblob NOT NULL default '',user_email tinytext NOT NULL default '',user_options blob NOT NULL default '',user_touched char(14) binary NOT NULL default '',UNIQUE KEY user_id (user_id)) ENGINE=MyISAM PACK_KEYS=1;", + "CREATE TABLE `postcode_city` (`id` int(10) unsigned NOT NULL Auto_Increment COMMENT 'Id',`country_code` varchar(5) NOT NULL COMMENT 'Country Code',`postcode` varchar(20) NOT NULL COMMENT 'Postcode',`city` text NOT NULL COMMENT 'City',PRIMARY KEY (`id`)) Auto_Increment=52142 DEFAULT CHARSET=utf8 COMMENT='Postcode -> City';", ]; - for i in 0..create_sqls.len() { - println!("{}/{}", i + 1, create_sqls.len()); - let res = CreateTableStatement::parse(create_sqls[i]); + for sql in create_sqls { + let res = CreateTableStatement::parse(sql); println!("{:?}", res); assert!(res.is_ok()); } @@ -110,11 +111,8 @@ fn parse_alter_table() { "ALTER TABLE `process_template_config` ADD template_admin_approver json NULL DEFAULT NULL COMMENT '模板管理员';" ]; - for i in 0..alter_sqls.len() { - println!("{}/{}", i + 1, alter_sqls.len()); - let res = AlterTableStatement::parse(alter_sqls[i]); - // res.unwrap(); - // println!("{:?}", res); + for sql in alter_sqls { + let res = AlterTableStatement::parse(sql); println!("{:?}", res); assert!(res.is_ok()); }