diff --git a/README.md b/README.md index acdb8d8..2295d45 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,190 @@ > A SQL parser for MySQL with nom. Written in Rust. -## Data Definition Statements +## Quick Start + +### Example parsing SQL +```rust +use sqlparser_mysql::parser::Parser; +use sqlparser_mysql::parser::ParseConfig; + +let config = ParseConfig::default(); +let sql = "SELECT a, b, 123, myfunc(b) \ + FROM table_1 \ + WHERE a > b AND b < 100 \ + ORDER BY a DESC, b"; +// parse to a Statement +let ast = Parser::parse(&config, sql).unwrap(); + +println!("AST: {:#?}", ast); +``` +The output should be: +```rust +AST: Select( + SelectStatement { + tables: [ + Table { + name: "table_1", + alias: None, + schema: None, + }, + ], + distinct: false, + fields: [ + Col( + Column { + name: "a", + alias: None, + table: None, + function: None, + }, + ), + Col( + Column { + name: "b", + alias: None, + table: None, + function: None, + }, + ), + Value( + Literal( + LiteralExpression { + value: Integer( + 123, + ), + alias: None, + }, + ), + ), + Col( + Column { + name: "myfunc(b)", + alias: None, + table: None, + function: Some( + Generic( + "myfunc", + FunctionArguments { + arguments: [ + Column( + Column { + name: "b", + alias: None, + table: None, + function: None, + }, + ), + ], + }, + ), + ), + }, + ), + ], + join: [], + where_clause: Some( + LogicalOp( + ConditionTree { + operator: And, + left: ComparisonOp( + ConditionTree { + operator: Greater, + left: Base( + Field( + Column { + name: "a", + alias: None, + table: None, + function: None, + }, + ), + ), + right: Base( + Field( + Column { + name: "b", + alias: None, + table: None, + function: None, + }, + ), + ), + }, + ), + right: ComparisonOp( + ConditionTree { + operator: Less, + left: Base( + Field( + Column { + name: "b", + alias: None, + table: None, + function: None, + }, + ), + ), + right: Base( + Literal( + Integer( + 100, + ), + ), + ), + }, + ), + }, + ), + ), + group_by: None, + order: Some( + OrderClause { + columns: [ + ( + Column { + name: "a", + alias: None, + table: None, + function: None, + }, + Desc, + ), + ( + Column { + name: "b", + alias: None, + table: None, + function: None, + }, + Asc, + ), + ], + }, + ), + limit: None, + }, +) +``` + +### Creating SQL text from AST +```rust +use sqlparser_mysql::parser::Parser; +use sqlparser_mysql::parser::ParseConfig; + +let sql = "SELECT a FROM table_1"; +let config = ParseConfig::default(); + +// parse to a Statement +let ast = Parser::parse(&config, sql).unwrap(); + +// The original SQL text can be generated from the AST +assert_eq!(ast.to_string(), sql); +``` + +## Supported Statements + +### Data Definition Statements [MySQL Doc](https://dev.mysql.com/doc/refman/8.0/en/sql-data-definition-statements.html) @@ -44,5 +227,5 @@ - [x] RENAME TABLE Statement - [x] TRUNCATE TABLE Statement -## Database Administration Statements +### Database Administration Statements - [x] SET Statements \ No newline at end of file diff --git a/src/base/column.rs b/src/base/column.rs index 1400546..7b7e410 100644 --- a/src/base/column.rs +++ b/src/base/column.rs @@ -12,8 +12,7 @@ use nom::sequence::{delimited, pair, preceded, terminated, tuple}; use nom::IResult; use base::error::ParseSQLErrorKind; -use base::keywords::escape_if_keyword; -use base::{CaseWhenExpression, CommonParser, DataType, Literal, ParseSQLError, Real}; +use base::{CaseWhenExpression, CommonParser, DataType, DisplayUtil, Literal, ParseSQLError, Real}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum FunctionExpression { @@ -278,16 +277,16 @@ impl fmt::Display for Column { write!( f, "{}.{}", - escape_if_keyword(table), - escape_if_keyword(&self.name) + DisplayUtil::escape_if_keyword(table), + DisplayUtil::escape_if_keyword(&self.name) )?; } else if let Some(ref function) = self.function { write!(f, "{}", *function)?; } else { - write!(f, "{}", escape_if_keyword(&self.name))?; + write!(f, "{}", DisplayUtil::escape_if_keyword(&self.name))?; } if let Some(ref alias) = self.alias { - write!(f, " AS {}", escape_if_keyword(alias))?; + write!(f, " AS {}", DisplayUtil::escape_if_keyword(alias))?; } Ok(()) } @@ -609,7 +608,7 @@ impl fmt::Display for ColumnSpecification { write!( f, "{} {}", - escape_if_keyword(&self.column.name), + DisplayUtil::escape_if_keyword(&self.column.name), self.sql_type )?; for constraint in self.constraints.iter() { diff --git a/src/base/common_parser.rs b/src/base/common_parser.rs index ba5cbec..13417d8 100644 --- a/src/base/common_parser.rs +++ b/src/base/common_parser.rs @@ -10,13 +10,194 @@ use nom::sequence::{delimited, pair, preceded, terminated, tuple}; use nom::{IResult, InputLength, Parser}; use base::column::Column; -use base::keywords::sql_keyword; use base::{OrderType, ParseSQLError}; /// collection of common used parsers pub struct CommonParser; impl CommonParser { + fn keyword_follow_char(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + peek(alt(( + tag(" "), + tag("\n"), + tag(";"), + tag("("), + tag(")"), + tag("\t"), + tag(","), + tag("="), + CommonParser::eof, + )))(i) + } + + fn keywords_part_1(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + terminated(tag_no_case("ABORT"), Self::keyword_follow_char), + terminated(tag_no_case("ACTION"), Self::keyword_follow_char), + terminated(tag_no_case("ADD"), Self::keyword_follow_char), + terminated(tag_no_case("AFTER"), Self::keyword_follow_char), + terminated(tag_no_case("ALL"), Self::keyword_follow_char), + terminated(tag_no_case("ALTER"), Self::keyword_follow_char), + terminated(tag_no_case("ANALYZE"), Self::keyword_follow_char), + terminated(tag_no_case("AND"), Self::keyword_follow_char), + terminated(tag_no_case("AS"), Self::keyword_follow_char), + terminated(tag_no_case("ASC"), Self::keyword_follow_char), + terminated(tag_no_case("ATTACH"), Self::keyword_follow_char), + terminated(tag_no_case("AUTOINCREMENT"), Self::keyword_follow_char), + terminated(tag_no_case("BEFORE"), Self::keyword_follow_char), + terminated(tag_no_case("BEGIN"), Self::keyword_follow_char), + terminated(tag_no_case("BETWEEN"), Self::keyword_follow_char), + terminated(tag_no_case("BY"), Self::keyword_follow_char), + terminated(tag_no_case("CASCADE"), Self::keyword_follow_char), + terminated(tag_no_case("CASE"), Self::keyword_follow_char), + terminated(tag_no_case("CAST"), Self::keyword_follow_char), + terminated(tag_no_case("CHECK"), Self::keyword_follow_char), + terminated(tag_no_case("COLLATE"), Self::keyword_follow_char), + ))(i) + } + + fn keywords_part_2(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + terminated(tag_no_case("COLUMN"), Self::keyword_follow_char), + terminated(tag_no_case("COMMIT"), Self::keyword_follow_char), + terminated(tag_no_case("CONFLICT"), Self::keyword_follow_char), + terminated(tag_no_case("CONSTRAINT"), Self::keyword_follow_char), + terminated(tag_no_case("CREATE"), Self::keyword_follow_char), + terminated(tag_no_case("CROSS"), Self::keyword_follow_char), + terminated(tag_no_case("CURRENT_DATE"), Self::keyword_follow_char), + terminated(tag_no_case("CURRENT_TIME"), Self::keyword_follow_char), + terminated(tag_no_case("CURRENT_TIMESTAMP"), Self::keyword_follow_char), + terminated(tag_no_case("DATABASE"), Self::keyword_follow_char), + terminated(tag_no_case("DEFAULT"), Self::keyword_follow_char), + terminated(tag_no_case("DEFERRABLE"), Self::keyword_follow_char), + terminated(tag_no_case("DEFERRED"), Self::keyword_follow_char), + terminated(tag_no_case("DELETE"), Self::keyword_follow_char), + terminated(tag_no_case("DESC"), Self::keyword_follow_char), + terminated(tag_no_case("DETACH"), Self::keyword_follow_char), + terminated(tag_no_case("DISTINCT"), Self::keyword_follow_char), + terminated(tag_no_case("DROP"), Self::keyword_follow_char), + terminated(tag_no_case("EACH"), Self::keyword_follow_char), + terminated(tag_no_case("ELSE"), Self::keyword_follow_char), + terminated(tag_no_case("END"), Self::keyword_follow_char), + ))(i) + } + + fn keywords_part_3(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + terminated(tag_no_case("ESCAPE"), Self::keyword_follow_char), + terminated(tag_no_case("EXCEPT"), Self::keyword_follow_char), + terminated(tag_no_case("EXCLUSIVE"), Self::keyword_follow_char), + terminated(tag_no_case("EXISTS"), Self::keyword_follow_char), + terminated(tag_no_case("EXPLAIN"), Self::keyword_follow_char), + terminated(tag_no_case("FAIL"), Self::keyword_follow_char), + terminated(tag_no_case("FOR"), Self::keyword_follow_char), + terminated(tag_no_case("FOREIGN"), Self::keyword_follow_char), + terminated(tag_no_case("FROM"), Self::keyword_follow_char), + terminated(tag_no_case("FULL"), Self::keyword_follow_char), + terminated(tag_no_case("FULLTEXT"), Self::keyword_follow_char), + terminated(tag_no_case("GLOB"), Self::keyword_follow_char), + terminated(tag_no_case("GROUP"), Self::keyword_follow_char), + terminated(tag_no_case("HAVING"), Self::keyword_follow_char), + terminated(tag_no_case("IF"), Self::keyword_follow_char), + terminated(tag_no_case("IGNORE"), Self::keyword_follow_char), + terminated(tag_no_case("IMMEDIATE"), Self::keyword_follow_char), + terminated(tag_no_case("IN"), Self::keyword_follow_char), + terminated(tag_no_case("INDEX"), Self::keyword_follow_char), + terminated(tag_no_case("INDEXED"), Self::keyword_follow_char), + terminated(tag_no_case("INITIALLY"), Self::keyword_follow_char), + ))(i) + } + + fn keywords_part_4(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + terminated(tag_no_case("INNER"), Self::keyword_follow_char), + terminated(tag_no_case("INSERT"), Self::keyword_follow_char), + terminated(tag_no_case("INSTEAD"), Self::keyword_follow_char), + terminated(tag_no_case("INTERSECT"), Self::keyword_follow_char), + terminated(tag_no_case("INTO"), Self::keyword_follow_char), + terminated(tag_no_case("IS"), Self::keyword_follow_char), + terminated(tag_no_case("ISNULL"), Self::keyword_follow_char), + terminated(tag_no_case("ORDER"), Self::keyword_follow_char), + terminated(tag_no_case("JOIN"), Self::keyword_follow_char), + terminated(tag_no_case("KEY"), Self::keyword_follow_char), + terminated(tag_no_case("LEFT"), Self::keyword_follow_char), + terminated(tag_no_case("LIKE"), Self::keyword_follow_char), + terminated(tag_no_case("LIMIT"), Self::keyword_follow_char), + terminated(tag_no_case("MATCH"), Self::keyword_follow_char), + terminated(tag_no_case("NATURAL"), Self::keyword_follow_char), + terminated(tag_no_case("NO"), Self::keyword_follow_char), + terminated(tag_no_case("NOT"), Self::keyword_follow_char), + terminated(tag_no_case("NOTNULL"), Self::keyword_follow_char), + terminated(tag_no_case("NULL"), Self::keyword_follow_char), + terminated(tag_no_case("OF"), Self::keyword_follow_char), + terminated(tag_no_case("OFFSET"), Self::keyword_follow_char), + ))(i) + } + + fn keywords_part_5(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + terminated(tag_no_case("ON"), Self::keyword_follow_char), + terminated(tag_no_case("OR"), Self::keyword_follow_char), + terminated(tag_no_case("OUTER"), Self::keyword_follow_char), + terminated(tag_no_case("PLAN"), Self::keyword_follow_char), + terminated(tag_no_case("PRAGMA"), Self::keyword_follow_char), + terminated(tag_no_case("PRIMARY"), Self::keyword_follow_char), + terminated(tag_no_case("QUERY"), Self::keyword_follow_char), + terminated(tag_no_case("RAISE"), Self::keyword_follow_char), + terminated(tag_no_case("RECURSIVE"), Self::keyword_follow_char), + terminated(tag_no_case("REFERENCES"), Self::keyword_follow_char), + terminated(tag_no_case("REGEXP"), Self::keyword_follow_char), + terminated(tag_no_case("REINDEX"), Self::keyword_follow_char), + terminated(tag_no_case("RELEASE"), Self::keyword_follow_char), + terminated(tag_no_case("RENAME"), Self::keyword_follow_char), + terminated(tag_no_case("REPLACE"), Self::keyword_follow_char), + terminated(tag_no_case("RESTRICT"), Self::keyword_follow_char), + terminated(tag_no_case("RIGHT"), Self::keyword_follow_char), + terminated(tag_no_case("ROLLBACK"), Self::keyword_follow_char), + terminated(tag_no_case("ROW"), Self::keyword_follow_char), + terminated(tag_no_case("SAVEPOINT"), Self::keyword_follow_char), + terminated(tag_no_case("SELECT"), Self::keyword_follow_char), + ))(i) + } + + fn keywords_part_6(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + terminated(tag_no_case("SET"), Self::keyword_follow_char), + terminated(tag_no_case("SPATIAL"), Self::keyword_follow_char), + terminated(tag_no_case("TABLE"), Self::keyword_follow_char), + terminated(tag_no_case("TEMP"), Self::keyword_follow_char), + terminated(tag_no_case("TEMPORARY"), Self::keyword_follow_char), + terminated(tag_no_case("THEN"), Self::keyword_follow_char), + terminated(tag_no_case("TO"), Self::keyword_follow_char), + terminated(tag_no_case("TRANSACTION"), Self::keyword_follow_char), + terminated(tag_no_case("TRIGGER"), Self::keyword_follow_char), + terminated(tag_no_case("UNION"), Self::keyword_follow_char), + terminated(tag_no_case("UNIQUE"), Self::keyword_follow_char), + terminated(tag_no_case("UPDATE"), Self::keyword_follow_char), + terminated(tag_no_case("USING"), Self::keyword_follow_char), + terminated(tag_no_case("VACUUM"), Self::keyword_follow_char), + terminated(tag_no_case("VALUES"), Self::keyword_follow_char), + terminated(tag_no_case("VIEW"), Self::keyword_follow_char), + terminated(tag_no_case("VIRTUAL"), Self::keyword_follow_char), + terminated(tag_no_case("WHEN"), Self::keyword_follow_char), + terminated(tag_no_case("WHERE"), Self::keyword_follow_char), + terminated(tag_no_case("WITH"), Self::keyword_follow_char), + terminated(tag_no_case("WITHOUT"), Self::keyword_follow_char), + ))(i) + } + + // Matches any SQL reserved keyword + pub fn sql_keyword(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + Self::keywords_part_1, + Self::keywords_part_2, + Self::keywords_part_3, + Self::keywords_part_4, + Self::keywords_part_5, + Self::keywords_part_6, + ))(i) + } + /// `[index_name]` pub fn opt_index_name(i: &str) -> IResult<&str, Option, ParseSQLError<&str>> { opt(map( @@ -92,7 +273,7 @@ impl CommonParser { alt(( alt(( preceded( - not(peek(sql_keyword)), + not(peek(CommonParser::sql_keyword)), recognize(pair(alpha1, take_while(Self::is_sql_identifier))), ), recognize(pair(tag("_"), take_while1(Self::is_sql_identifier))), diff --git a/src/base/display_util.rs b/src/base/display_util.rs new file mode 100644 index 0000000..27d832b --- /dev/null +++ b/src/base/display_util.rs @@ -0,0 +1,13 @@ +use base::CommonParser; + +pub struct DisplayUtil; + +impl DisplayUtil { + pub fn escape_if_keyword(s: &str) -> String { + if CommonParser::sql_keyword(s).is_ok() { + format!("`{}`", s) + } else { + s.to_owned() + } + } +} diff --git a/src/base/field.rs b/src/base/field.rs index 936d81e..4c02f5d 100644 --- a/src/base/field.rs +++ b/src/base/field.rs @@ -12,10 +12,9 @@ use nom::IResult; use base::arithmetic::ArithmeticExpression; use base::column::Column; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; use base::literal::LiteralExpression; use base::table::Table; -use base::{CommonParser, Literal}; +use base::{CommonParser, DisplayUtil, Literal}; #[derive(Default, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum FieldDefinitionExpression { @@ -53,7 +52,7 @@ impl Display for FieldDefinitionExpression { match *self { FieldDefinitionExpression::All => write!(f, "*"), FieldDefinitionExpression::AllInTable(ref table) => { - write!(f, "{}.*", escape_if_keyword(table)) + write!(f, "{}.*", DisplayUtil::escape_if_keyword(table)) } FieldDefinitionExpression::Col(ref col) => write!(f, "{}", col), FieldDefinitionExpression::Value(ref val) => write!(f, "{}", val), diff --git a/src/base/keywords.rs b/src/base/keywords.rs deleted file mode 100644 index 7ba3b0b..0000000 --- a/src/base/keywords.rs +++ /dev/null @@ -1,198 +0,0 @@ -use nom::branch::alt; -use nom::bytes::complete::{tag, tag_no_case}; -use nom::combinator::peek; -use nom::sequence::terminated; -use nom::IResult; - -use base::error::ParseSQLError; -use base::CommonParser; - -fn keyword_follow_char(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - peek(alt(( - tag(" "), - tag("\n"), - tag(";"), - tag("("), - tag(")"), - tag("\t"), - tag(","), - tag("="), - CommonParser::eof, - )))(i) -} - -fn keywords_part_1(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - terminated(tag_no_case("ABORT"), keyword_follow_char), - terminated(tag_no_case("ACTION"), keyword_follow_char), - terminated(tag_no_case("ADD"), keyword_follow_char), - terminated(tag_no_case("AFTER"), keyword_follow_char), - terminated(tag_no_case("ALL"), keyword_follow_char), - terminated(tag_no_case("ALTER"), keyword_follow_char), - terminated(tag_no_case("ANALYZE"), keyword_follow_char), - terminated(tag_no_case("AND"), keyword_follow_char), - terminated(tag_no_case("AS"), keyword_follow_char), - terminated(tag_no_case("ASC"), keyword_follow_char), - terminated(tag_no_case("ATTACH"), keyword_follow_char), - terminated(tag_no_case("AUTOINCREMENT"), keyword_follow_char), - terminated(tag_no_case("BEFORE"), keyword_follow_char), - terminated(tag_no_case("BEGIN"), keyword_follow_char), - terminated(tag_no_case("BETWEEN"), keyword_follow_char), - terminated(tag_no_case("BY"), keyword_follow_char), - terminated(tag_no_case("CASCADE"), keyword_follow_char), - terminated(tag_no_case("CASE"), keyword_follow_char), - terminated(tag_no_case("CAST"), keyword_follow_char), - terminated(tag_no_case("CHECK"), keyword_follow_char), - terminated(tag_no_case("COLLATE"), keyword_follow_char), - ))(i) -} - -fn keywords_part_2(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - terminated(tag_no_case("COLUMN"), keyword_follow_char), - terminated(tag_no_case("COMMIT"), keyword_follow_char), - terminated(tag_no_case("CONFLICT"), keyword_follow_char), - terminated(tag_no_case("CONSTRAINT"), keyword_follow_char), - terminated(tag_no_case("CREATE"), keyword_follow_char), - terminated(tag_no_case("CROSS"), keyword_follow_char), - terminated(tag_no_case("CURRENT_DATE"), keyword_follow_char), - terminated(tag_no_case("CURRENT_TIME"), keyword_follow_char), - terminated(tag_no_case("CURRENT_TIMESTAMP"), keyword_follow_char), - terminated(tag_no_case("DATABASE"), keyword_follow_char), - terminated(tag_no_case("DEFAULT"), keyword_follow_char), - terminated(tag_no_case("DEFERRABLE"), keyword_follow_char), - terminated(tag_no_case("DEFERRED"), keyword_follow_char), - terminated(tag_no_case("DELETE"), keyword_follow_char), - terminated(tag_no_case("DESC"), keyword_follow_char), - terminated(tag_no_case("DETACH"), keyword_follow_char), - terminated(tag_no_case("DISTINCT"), keyword_follow_char), - terminated(tag_no_case("DROP"), keyword_follow_char), - terminated(tag_no_case("EACH"), keyword_follow_char), - terminated(tag_no_case("ELSE"), keyword_follow_char), - terminated(tag_no_case("END"), keyword_follow_char), - ))(i) -} - -fn keywords_part_3(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - terminated(tag_no_case("ESCAPE"), keyword_follow_char), - terminated(tag_no_case("EXCEPT"), keyword_follow_char), - terminated(tag_no_case("EXCLUSIVE"), keyword_follow_char), - terminated(tag_no_case("EXISTS"), keyword_follow_char), - terminated(tag_no_case("EXPLAIN"), keyword_follow_char), - terminated(tag_no_case("FAIL"), keyword_follow_char), - terminated(tag_no_case("FOR"), keyword_follow_char), - terminated(tag_no_case("FOREIGN"), keyword_follow_char), - terminated(tag_no_case("FROM"), keyword_follow_char), - terminated(tag_no_case("FULL"), keyword_follow_char), - terminated(tag_no_case("FULLTEXT"), keyword_follow_char), - terminated(tag_no_case("GLOB"), keyword_follow_char), - terminated(tag_no_case("GROUP"), keyword_follow_char), - terminated(tag_no_case("HAVING"), keyword_follow_char), - terminated(tag_no_case("IF"), keyword_follow_char), - terminated(tag_no_case("IGNORE"), keyword_follow_char), - terminated(tag_no_case("IMMEDIATE"), keyword_follow_char), - terminated(tag_no_case("IN"), keyword_follow_char), - terminated(tag_no_case("INDEX"), keyword_follow_char), - terminated(tag_no_case("INDEXED"), keyword_follow_char), - terminated(tag_no_case("INITIALLY"), keyword_follow_char), - ))(i) -} - -fn keywords_part_4(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - terminated(tag_no_case("INNER"), keyword_follow_char), - terminated(tag_no_case("INSERT"), keyword_follow_char), - terminated(tag_no_case("INSTEAD"), keyword_follow_char), - terminated(tag_no_case("INTERSECT"), keyword_follow_char), - terminated(tag_no_case("INTO"), keyword_follow_char), - terminated(tag_no_case("IS"), keyword_follow_char), - terminated(tag_no_case("ISNULL"), keyword_follow_char), - terminated(tag_no_case("ORDER"), keyword_follow_char), - terminated(tag_no_case("JOIN"), keyword_follow_char), - terminated(tag_no_case("KEY"), keyword_follow_char), - terminated(tag_no_case("LEFT"), keyword_follow_char), - terminated(tag_no_case("LIKE"), keyword_follow_char), - terminated(tag_no_case("LIMIT"), keyword_follow_char), - terminated(tag_no_case("MATCH"), keyword_follow_char), - terminated(tag_no_case("NATURAL"), keyword_follow_char), - terminated(tag_no_case("NO"), keyword_follow_char), - terminated(tag_no_case("NOT"), keyword_follow_char), - terminated(tag_no_case("NOTNULL"), keyword_follow_char), - terminated(tag_no_case("NULL"), keyword_follow_char), - terminated(tag_no_case("OF"), keyword_follow_char), - terminated(tag_no_case("OFFSET"), keyword_follow_char), - ))(i) -} - -fn keywords_part_5(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - terminated(tag_no_case("ON"), keyword_follow_char), - terminated(tag_no_case("OR"), keyword_follow_char), - terminated(tag_no_case("OUTER"), keyword_follow_char), - terminated(tag_no_case("PLAN"), keyword_follow_char), - terminated(tag_no_case("PRAGMA"), keyword_follow_char), - terminated(tag_no_case("PRIMARY"), keyword_follow_char), - terminated(tag_no_case("QUERY"), keyword_follow_char), - terminated(tag_no_case("RAISE"), keyword_follow_char), - terminated(tag_no_case("RECURSIVE"), keyword_follow_char), - terminated(tag_no_case("REFERENCES"), keyword_follow_char), - terminated(tag_no_case("REGEXP"), keyword_follow_char), - terminated(tag_no_case("REINDEX"), keyword_follow_char), - terminated(tag_no_case("RELEASE"), keyword_follow_char), - terminated(tag_no_case("RENAME"), keyword_follow_char), - terminated(tag_no_case("REPLACE"), keyword_follow_char), - terminated(tag_no_case("RESTRICT"), keyword_follow_char), - terminated(tag_no_case("RIGHT"), keyword_follow_char), - terminated(tag_no_case("ROLLBACK"), keyword_follow_char), - terminated(tag_no_case("ROW"), keyword_follow_char), - terminated(tag_no_case("SAVEPOINT"), keyword_follow_char), - terminated(tag_no_case("SELECT"), keyword_follow_char), - ))(i) -} - -fn keywords_part_6(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - terminated(tag_no_case("SET"), keyword_follow_char), - terminated(tag_no_case("SPATIAL"), keyword_follow_char), - terminated(tag_no_case("TABLE"), keyword_follow_char), - terminated(tag_no_case("TEMP"), keyword_follow_char), - terminated(tag_no_case("TEMPORARY"), keyword_follow_char), - terminated(tag_no_case("THEN"), keyword_follow_char), - terminated(tag_no_case("TO"), keyword_follow_char), - terminated(tag_no_case("TRANSACTION"), keyword_follow_char), - terminated(tag_no_case("TRIGGER"), keyword_follow_char), - terminated(tag_no_case("UNION"), keyword_follow_char), - terminated(tag_no_case("UNIQUE"), keyword_follow_char), - terminated(tag_no_case("UPDATE"), keyword_follow_char), - terminated(tag_no_case("USING"), keyword_follow_char), - terminated(tag_no_case("VACUUM"), keyword_follow_char), - terminated(tag_no_case("VALUES"), keyword_follow_char), - terminated(tag_no_case("VIEW"), keyword_follow_char), - terminated(tag_no_case("VIRTUAL"), keyword_follow_char), - terminated(tag_no_case("WHEN"), keyword_follow_char), - terminated(tag_no_case("WHERE"), keyword_follow_char), - terminated(tag_no_case("WITH"), keyword_follow_char), - terminated(tag_no_case("WITHOUT"), keyword_follow_char), - ))(i) -} - -// Matches any SQL reserved keyword -pub fn sql_keyword(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - keywords_part_1, - keywords_part_2, - keywords_part_3, - keywords_part_4, - keywords_part_5, - keywords_part_6, - ))(i) -} - -pub fn escape_if_keyword(s: &str) -> String { - if sql_keyword(s).is_ok() { - format!("`{}`", s) - } else { - s.to_owned() - } -} diff --git a/src/base/mod.rs b/src/base/mod.rs index a968250..974aca3 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -3,6 +3,7 @@ pub use self::common_parser::CommonParser; pub use self::compression_type::CompressionType; pub use self::data_type::DataType; pub use self::default_or_zero_or_one::DefaultOrZeroOrOne; +pub use self::display_util::DisplayUtil; pub use self::error::ParseSQLError; pub use self::field::{FieldDefinitionExpression, FieldValueExpression}; pub use self::insert_method_type::InsertMethodType; @@ -51,13 +52,10 @@ pub mod visible_type; pub mod arithmetic; pub mod index_option; -pub mod table_option; - -#[macro_use] -pub mod keywords; mod key_part; mod partition_definition; mod reference_definition; +pub mod table_option; pub mod condition; @@ -65,4 +63,5 @@ mod order; pub mod case; +mod display_util; mod join; diff --git a/src/base/order.rs b/src/base/order.rs index f504e93..80f4fed 100644 --- a/src/base/order.rs +++ b/src/base/order.rs @@ -11,8 +11,7 @@ use nom::IResult; use base::column::Column; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; -use base::CommonParser; +use base::{CommonParser, DisplayUtil}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct OrderClause { @@ -56,7 +55,7 @@ impl fmt::Display for OrderClause { "{}", self.columns .iter() - .map(|(c, o)| format!("{} {}", escape_if_keyword(&c.name), o)) + .map(|(c, o)| format!("{} {}", DisplayUtil::escape_if_keyword(&c.name), o)) .collect::>() .join(", ") ) diff --git a/src/base/table.rs b/src/base/table.rs index 8f6711d..faeb7fe 100644 --- a/src/base/table.rs +++ b/src/base/table.rs @@ -9,8 +9,7 @@ use nom::sequence::{pair, terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; -use base::CommonParser; +use base::{CommonParser, DisplayUtil}; /// **Table Definition** #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -96,11 +95,11 @@ impl Table { impl fmt::Display for Table { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref schema) = self.schema { - write!(f, "{}.", escape_if_keyword(schema))?; + write!(f, "{}.", DisplayUtil::escape_if_keyword(schema))?; } - write!(f, "{}", escape_if_keyword(&self.name))?; + write!(f, "{}", DisplayUtil::escape_if_keyword(&self.name))?; if let Some(ref alias) = self.alias { - write!(f, " AS {}", escape_if_keyword(alias))?; + write!(f, " AS {}", DisplayUtil::escape_if_keyword(alias))?; } Ok(()) } diff --git a/src/base/table_key.rs b/src/base/table_key.rs index f31371c..12eddeb 100644 --- a/src/base/table_key.rs +++ b/src/base/table_key.rs @@ -1,7 +1,7 @@ use std::fmt; use base::column::Column; -use base::keywords::escape_if_keyword; +use base::DisplayUtil; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum TableKey { @@ -21,7 +21,7 @@ impl fmt::Display for TableKey { "({})", columns .iter() - .map(|c| escape_if_keyword(&c.name)) + .map(|c| DisplayUtil::escape_if_keyword(&c.name)) .collect::>() .join(", ") ) @@ -29,14 +29,14 @@ impl fmt::Display for TableKey { TableKey::UniqueKey(ref name, ref columns) => { write!(f, "UNIQUE KEY ")?; if let Some(ref name) = *name { - write!(f, "{} ", escape_if_keyword(name))?; + write!(f, "{} ", DisplayUtil::escape_if_keyword(name))?; } write!( f, "({})", columns .iter() - .map(|c| escape_if_keyword(&c.name)) + .map(|c| DisplayUtil::escape_if_keyword(&c.name)) .collect::>() .join(", ") ) @@ -44,26 +44,26 @@ impl fmt::Display for TableKey { TableKey::FulltextKey(ref name, ref columns) => { write!(f, "FULLTEXT KEY ")?; if let Some(ref name) = *name { - write!(f, "{} ", escape_if_keyword(name))?; + write!(f, "{} ", DisplayUtil::escape_if_keyword(name))?; } write!( f, "({})", columns .iter() - .map(|c| escape_if_keyword(&c.name)) + .map(|c| DisplayUtil::escape_if_keyword(&c.name)) .collect::>() .join(", ") ) } TableKey::Key(ref name, ref columns) => { - write!(f, "KEY {} ", escape_if_keyword(name))?; + write!(f, "KEY {} ", DisplayUtil::escape_if_keyword(name))?; write!( f, "({})", columns .iter() - .map(|c| escape_if_keyword(&c.name)) + .map(|c| DisplayUtil::escape_if_keyword(&c.name)) .collect::>() .join(", ") ) diff --git a/src/base/trigger.rs b/src/base/trigger.rs index 3825a20..370905e 100644 --- a/src/base/trigger.rs +++ b/src/base/trigger.rs @@ -7,8 +7,7 @@ use nom::sequence::{pair, tuple}; use nom::IResult; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; -use base::CommonParser; +use base::{CommonParser, DisplayUtil}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct Trigger { @@ -34,9 +33,9 @@ impl Trigger { impl fmt::Display for Trigger { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref schema) = self.schema { - write!(f, "{}.", escape_if_keyword(schema))?; + write!(f, "{}.", DisplayUtil::escape_if_keyword(schema))?; } - write!(f, "{}", escape_if_keyword(&self.name))?; + write!(f, "{}", DisplayUtil::escape_if_keyword(&self.name))?; Ok(()) } } diff --git a/src/dms/delete.rs b/src/dms/delete.rs index 949896e..618a614 100644 --- a/src/dms/delete.rs +++ b/src/dms/delete.rs @@ -8,9 +8,8 @@ use nom::IResult; use base::condition::ConditionExpression; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; use base::table::Table; -use base::CommonParser; +use base::{CommonParser, DisplayUtil}; // FIXME TODO /// DELETE \[LOW_PRIORITY] \[QUICK] \[IGNORE] FROM tbl_name \[\[AS] tbl_alias] @@ -47,7 +46,7 @@ impl DeleteStatement { impl fmt::Display for DeleteStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "DELETE FROM ")?; - write!(f, "{}", escape_if_keyword(&self.table.name))?; + write!(f, "{}", DisplayUtil::escape_if_keyword(&self.table.name))?; if let Some(ref where_clause) = self.where_clause { write!(f, " WHERE ")?; write!(f, "{}", where_clause)?; diff --git a/src/dms/insert.rs b/src/dms/insert.rs index 8d0447e..88854f5 100644 --- a/src/dms/insert.rs +++ b/src/dms/insert.rs @@ -10,9 +10,8 @@ use nom::IResult; use base::column::Column; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; use base::table::Table; -use base::{CommonParser, FieldValueExpression, Literal}; +use base::{CommonParser, DisplayUtil, FieldValueExpression, Literal}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct InsertStatement { @@ -100,7 +99,11 @@ impl InsertStatement { impl fmt::Display for InsertStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "INSERT INTO {}", escape_if_keyword(&self.table.name))?; + write!( + f, + "INSERT INTO {}", + DisplayUtil::escape_if_keyword(&self.table.name) + )?; if let Some(ref fields) = self.fields { write!( f, diff --git a/src/dms/update.rs b/src/dms/update.rs index 1e4a50f..0c8f78e 100644 --- a/src/dms/update.rs +++ b/src/dms/update.rs @@ -9,9 +9,8 @@ use nom::IResult; use base::column::Column; use base::condition::ConditionExpression; use base::error::ParseSQLError; -use base::keywords::escape_if_keyword; use base::table::Table; -use base::{CommonParser, FieldValueExpression}; +use base::{CommonParser, DisplayUtil, FieldValueExpression}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct UpdateStatement { @@ -47,7 +46,11 @@ impl UpdateStatement { impl fmt::Display for UpdateStatement { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "UPDATE {} ", escape_if_keyword(&self.table.name))?; + write!( + f, + "UPDATE {} ", + DisplayUtil::escape_if_keyword(&self.table.name) + )?; assert!(!self.fields.is_empty()); write!( f,