diff --git a/src/base/algorithm_type.rs b/src/base/algorithm_type.rs new file mode 100644 index 0000000..1590e4c --- /dev/null +++ b/src/base/algorithm_type.rs @@ -0,0 +1,39 @@ +use nom::branch::alt; +use nom::bytes::complete::{tag, tag_no_case}; +use nom::character::complete::multispace0; +use nom::combinator::{map, opt}; +use nom::sequence::tuple; +use nom::IResult; + +use base::ParseSQLError; + +/// ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum AlgorithmType { + Instant, // alter table only + Default, + Inplace, + Copy, +} + +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), + )), + )), + |x| x.4, + )(i) + } +} diff --git a/src/common/arithmetic.rs b/src/base/arithmetic.rs similarity index 97% rename from src/common/arithmetic.rs rename to src/base/arithmetic.rs index 69595ce..9e3b0bd 100644 --- a/src/common/arithmetic.rs +++ b/src/base/arithmetic.rs @@ -14,8 +14,7 @@ use nom::{ use base::column::Column; use base::error::ParseSQLErrorKind; -use base::{DataType, Literal, ParseSQLError}; -use common::as_alias; +use base::{CommonParser, DataType, Literal, ParseSQLError}; #[derive(Debug, Clone, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ArithmeticOperator { @@ -41,6 +40,17 @@ impl ArithmeticOperator { } } +impl fmt::Display for ArithmeticOperator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ArithmeticOperator::Add => write!(f, "+"), + ArithmeticOperator::Subtract => write!(f, "-"), + ArithmeticOperator::Multiply => write!(f, "*"), + ArithmeticOperator::Divide => write!(f, "/"), + } + } +} + #[derive(Debug, Clone, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ArithmeticBase { Column(Column), @@ -66,6 +76,16 @@ impl ArithmeticBase { } } +impl fmt::Display for ArithmeticBase { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ArithmeticBase::Column(ref col) => write!(f, "{}", col), + ArithmeticBase::Scalar(ref lit) => write!(f, "{}", lit.to_string()), + ArithmeticBase::Bracketed(ref ari) => write!(f, "({})", ari), + } + } +} + #[derive(Debug, Clone, Deserialize, Eq, Hash, PartialEq, Serialize)] pub enum ArithmeticItem { Base(ArithmeticBase), @@ -156,6 +176,15 @@ impl ArithmeticItem { } } +impl fmt::Display for ArithmeticItem { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ArithmeticItem::Base(ref b) => write!(f, "{}", b), + ArithmeticItem::Expr(ref expr) => write!(f, "{}", expr), + } + } +} + #[derive(Debug, Clone, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct Arithmetic { pub op: ArithmeticOperator, @@ -186,6 +215,12 @@ impl Arithmetic { } } +impl fmt::Display for Arithmetic { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{} {} {}", self.left, self.op, self.right) + } +} + #[derive(Debug, Clone, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct ArithmeticExpression { pub ari: Arithmetic, @@ -195,7 +230,7 @@ pub struct ArithmeticExpression { impl ArithmeticExpression { pub fn parse(i: &str) -> IResult<&str, ArithmeticExpression, ParseSQLError<&str>> { map( - pair(Arithmetic::parse, opt(as_alias)), + pair(Arithmetic::parse, opt(CommonParser::as_alias)), |(ari, opt_alias)| ArithmeticExpression { ari, alias: opt_alias.map(String::from), @@ -222,42 +257,6 @@ impl ArithmeticExpression { } } -impl fmt::Display for ArithmeticOperator { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ArithmeticOperator::Add => write!(f, "+"), - ArithmeticOperator::Subtract => write!(f, "-"), - ArithmeticOperator::Multiply => write!(f, "*"), - ArithmeticOperator::Divide => write!(f, "/"), - } - } -} - -impl fmt::Display for ArithmeticBase { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ArithmeticBase::Column(ref col) => write!(f, "{}", col), - ArithmeticBase::Scalar(ref lit) => write!(f, "{}", lit.to_string()), - ArithmeticBase::Bracketed(ref ari) => write!(f, "({})", ari), - } - } -} - -impl fmt::Display for ArithmeticItem { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ArithmeticItem::Base(ref b) => write!(f, "{}", b), - ArithmeticItem::Expr(ref expr) => write!(f, "{}", expr), - } - } -} - -impl fmt::Display for Arithmetic { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "{} {} {}", self.left, self.op, self.right) - } -} - impl fmt::Display for ArithmeticExpression { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.alias { @@ -269,9 +268,9 @@ impl fmt::Display for ArithmeticExpression { #[cfg(test)] mod tests { + use base::arithmetic::ArithmeticBase::Scalar; + use base::arithmetic::ArithmeticOperator::{Add, Divide, Multiply, Subtract}; use base::column::{Column, FunctionArgument, FunctionExpression}; - use common::arithmetic::ArithmeticBase::Scalar; - use common::arithmetic::ArithmeticOperator::{Add, Divide, Multiply, Subtract}; use super::*; @@ -488,9 +487,5 @@ mod tests { let qs = "56"; let res = Arithmetic::parse(qs); assert!(res.is_err()); - // assert_eq!( - // nom::Err::Error(nom::error::Error::new(qs, ErrorKind::Tag)), - // res.err().unwrap() - // ); } } diff --git a/src/common/case.rs b/src/base/case.rs similarity index 69% rename from src/common/case.rs rename to src/base/case.rs index e39074b..22757d4 100644 --- a/src/common/case.rs +++ b/src/base/case.rs @@ -7,9 +7,9 @@ use nom::sequence::{delimited, terminated, tuple}; use nom::IResult; use base::column::Column; +use base::condition::ConditionExpression; use base::error::ParseSQLError; use base::Literal; -use common::condition::ConditionExpression; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct CaseWhenExpression { @@ -20,31 +20,30 @@ pub struct CaseWhenExpression { impl CaseWhenExpression { pub fn parse(i: &str) -> IResult<&str, CaseWhenExpression, ParseSQLError<&str>> { - let (remaining_input, (_, _, _, _, condition, _, _, _, column, _, else_val, _)) = - tuple(( - tag_no_case("CASE"), - multispace1, - tag_no_case("WHEN"), + let (input, (_, _, _, _, condition, _, _, _, column, _, else_val, _)) = tuple(( + tag_no_case("CASE"), + multispace1, + tag_no_case("WHEN"), + multispace0, + ConditionExpression::condition_expr, + multispace0, + tag_no_case("THEN"), + multispace0, + Column::without_alias, + multispace0, + opt(delimited( + terminated(tag_no_case("ELSE"), multispace0), + Literal::parse, multispace0, - ConditionExpression::condition_expr, - multispace0, - tag_no_case("THEN"), - multispace0, - Column::without_alias, - multispace0, - opt(delimited( - terminated(tag_no_case("ELSE"), multispace0), - Literal::parse, - multispace0, - )), - tag_no_case("END"), - ))(i)?; + )), + tag_no_case("END"), + ))(i)?; let then_expr = ColumnOrLiteral::Column(column); let else_expr = else_val.map(ColumnOrLiteral::Literal); Ok(( - remaining_input, + input, CaseWhenExpression { condition, then_expr, diff --git a/src/base/check_constraint.rs b/src/base/check_constraint.rs new file mode 100644 index 0000000..2c5a6a5 --- /dev/null +++ b/src/base/check_constraint.rs @@ -0,0 +1,7 @@ +/// `[CONSTRAINT [symbol]] CHECK (expr) [[NOT] ENFORCED]` +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub struct CheckConstraintDefinition { + pub symbol: Option, + pub expr: String, + pub enforced: bool, +} diff --git a/src/base/column.rs b/src/base/column.rs index 462f561..1400546 100644 --- a/src/base/column.rs +++ b/src/base/column.rs @@ -12,11 +12,8 @@ use nom::sequence::{delimited, pair, preceded, terminated, tuple}; use nom::IResult; use base::error::ParseSQLErrorKind; -use base::{DataType, Literal, ParseSQLError, Real}; -use common::case::CaseWhenExpression; -use common::index_col_name; -use common::keywords::escape_if_keyword; -use common::{as_alias, delim_digit, parse_comment, sql_identifier, ws_sep_comma}; +use base::keywords::escape_if_keyword; +use base::{CaseWhenExpression, CommonParser, DataType, Literal, ParseSQLError, Real}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum FunctionExpression { @@ -69,7 +66,7 @@ impl FunctionExpression { ), map( tuple(( - sql_identifier, + CommonParser::sql_identifier, multispace0, tag("("), separated_list0( @@ -205,7 +202,10 @@ pub struct Column { impl Column { pub fn index_col_list(i: &str) -> IResult<&str, Vec, ParseSQLError<&str>> { many0(map( - terminated(index_col_name, opt(ws_sep_comma)), + terminated( + CommonParser::index_col_name, + opt(CommonParser::ws_sep_comma), + ), // XXX(malte): ignores length and order |e| e.0, ))(i) @@ -213,12 +213,18 @@ impl Column { // Parse rule for a comma-separated list of fields without aliases. pub fn field_list(i: &str) -> IResult<&str, Vec, ParseSQLError<&str>> { - many0(terminated(Column::without_alias, opt(ws_sep_comma)))(i) + many0(terminated( + Column::without_alias, + opt(CommonParser::ws_sep_comma), + ))(i) } // Parses a SQL column identifier in the column format pub fn without_alias(i: &str) -> IResult<&str, Column, ParseSQLError<&str>> { - let table_parser = pair(opt(terminated(sql_identifier, tag("."))), sql_identifier); + let table_parser = pair( + opt(terminated(CommonParser::sql_identifier, tag("."))), + CommonParser::sql_identifier, + ); alt(( map(FunctionExpression::parse, |f| Column { name: format!("{}", f), @@ -237,8 +243,9 @@ impl Column { // Parses a SQL column identifier in the table.column format pub fn parse(i: &str) -> IResult<&str, Column, ParseSQLError<&str>> { - let col_func_no_table = map(pair(FunctionExpression::parse, opt(as_alias)), |tup| { - Column { + let col_func_no_table = map( + pair(FunctionExpression::parse, opt(CommonParser::as_alias)), + |tup| Column { name: match tup.1 { None => format!("{}", tup.0), Some(a) => String::from(a), @@ -246,13 +253,13 @@ impl Column { alias: tup.1.map(String::from), table: None, function: Some(Box::new(tup.0)), - } - }); + }, + ); let col_w_table = map( tuple(( - opt(terminated(sql_identifier, tag("."))), - sql_identifier, - opt(as_alias), + opt(terminated(CommonParser::sql_identifier, tag("."))), + CommonParser::sql_identifier, + opt(CommonParser::as_alias), )), |tup| Column { name: tup.1.to_string(), @@ -392,9 +399,9 @@ impl ColumnConstraint { preceded( delimited(multispace0, tag_no_case("CHARACTER SET"), multispace1), alt(( - sql_identifier, - delimited(tag("'"), sql_identifier, tag("'")), - delimited(tag("\""), sql_identifier, tag("\"")), + CommonParser::sql_identifier, + delimited(tag("'"), CommonParser::sql_identifier, tag("'")), + delimited(tag("\""), CommonParser::sql_identifier, tag("\"")), )), ), |cs| { @@ -406,9 +413,9 @@ impl ColumnConstraint { preceded( delimited(multispace0, tag_no_case("COLLATE"), multispace1), alt(( - sql_identifier, - delimited(tag("'"), sql_identifier, tag("'")), - delimited(tag("\""), sql_identifier, tag("\"")), + CommonParser::sql_identifier, + delimited(tag("'"), CommonParser::sql_identifier, tag("'")), + delimited(tag("\""), CommonParser::sql_identifier, tag("\"")), )), ), |c| { @@ -425,7 +432,7 @@ impl ColumnConstraint { tag_no_case("UPDATE"), multispace1, tag_no_case("CURRENT_TIMESTAMP"), - opt(delim_digit), + opt(CommonParser::delim_digit), )), |_| Some(ColumnConstraint::OnUpdate(Literal::CurrentTimestamp)), ); @@ -474,7 +481,10 @@ impl ColumnConstraint { map(tag_no_case("FALSE"), |_| Literal::Bool(false)), map(tag_no_case("TRUE"), |_| Literal::Bool(true)), map( - tuple((tag_no_case("CURRENT_TIMESTAMP"), opt(delim_digit))), + tuple(( + tag_no_case("CURRENT_TIMESTAMP"), + opt(CommonParser::delim_digit), + )), |_| Literal::CurrentTimestamp, ), )), @@ -521,7 +531,7 @@ impl ColumnPosition { multispace0, tag_no_case("AFTER"), multispace1, - sql_identifier, + CommonParser::sql_identifier, )), |(_, _, _, identifier)| ColumnPosition::After(String::from(identifier).into()), ), @@ -563,9 +573,9 @@ impl ColumnSpecification { multispace0, )), many0(ColumnConstraint::parse), - opt(parse_comment), + opt(CommonParser::parse_comment), opt(ColumnPosition::parse), - opt(ws_sep_comma), + opt(CommonParser::ws_sep_comma), )); match parser(i) { diff --git a/src/base/common_parser.rs b/src/base/common_parser.rs new file mode 100644 index 0000000..ba5cbec --- /dev/null +++ b/src/base/common_parser.rs @@ -0,0 +1,239 @@ +use std::str::FromStr; + +use nom::branch::alt; +use nom::bytes::complete::{tag, tag_no_case, take_until, take_while, take_while1}; +use nom::character::complete::{alpha1, digit1, line_ending, multispace0, multispace1}; +use nom::character::is_alphanumeric; +use nom::combinator::{map, not, opt, peek, recognize}; +use nom::error::{ErrorKind, ParseError}; +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 { + /// `[index_name]` + pub fn opt_index_name(i: &str) -> IResult<&str, Option, ParseSQLError<&str>> { + opt(map( + delimited(multispace1, CommonParser::sql_identifier, multispace0), + String::from, + ))(i) + } + + #[allow(clippy::type_complexity)] + pub fn index_col_name( + i: &str, + ) -> IResult<&str, (Column, Option, Option), ParseSQLError<&str>> { + let (remaining_input, (column, len_u8, order)) = tuple(( + terminated(Column::without_alias, multispace0), + opt(delimited(tag("("), digit1, tag(")"))), + opt(OrderType::parse), + ))(i)?; + let len = len_u8.map(|l| u16::from_str(l).unwrap()); + + Ok((remaining_input, (column, len, order))) + } + + #[inline] + fn is_sql_identifier(chr: char) -> bool { + is_alphanumeric(chr as u8) || chr == '_' || chr == '@' + } + + /// first and third are opt + pub fn opt_delimited, F, G, H>( + mut first: F, + mut second: G, + mut third: H, + ) -> impl FnMut(I) -> IResult + where + F: Parser, + G: Parser, + H: Parser, + { + move |input: I| { + let inp = input.clone(); + match second.parse(input) { + Ok((i, o)) => Ok((i, o)), + _ => { + let (inp, _) = first.parse(inp)?; + let (inp, o2) = second.parse(inp)?; + third.parse(inp).map(|(i, _)| (i, o2)) + } + } + } + } + + fn precision_helper(i: &str) -> IResult<&str, (u8, Option), ParseSQLError<&str>> { + let (remaining_input, (m, d)) = tuple(( + digit1, + opt(preceded(tag(","), preceded(multispace0, digit1))), + ))(i)?; + + Ok(( + remaining_input, + (m.parse().unwrap(), d.map(|r| r.parse().unwrap())), + )) + } + + pub fn precision(i: &str) -> IResult<&str, (u8, Option), ParseSQLError<&str>> { + delimited(tag("("), Self::precision_helper, tag(")"))(i) + } + + pub fn delim_digit(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + delimited(tag("("), digit1, tag(")"))(i) + } + + pub fn sql_identifier(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + alt(( + alt(( + preceded( + not(peek(sql_keyword)), + recognize(pair(alpha1, take_while(Self::is_sql_identifier))), + ), + recognize(pair(tag("_"), take_while1(Self::is_sql_identifier))), + // variable only + recognize(pair(tag("@"), take_while1(Self::is_sql_identifier))), + )), + delimited(tag("`"), take_while1(Self::is_sql_identifier), tag("`")), + delimited(tag("["), take_while1(Self::is_sql_identifier), tag("]")), + ))(i) + } + + // Parse an unsigned integer. + pub fn unsigned_number(i: &str) -> IResult<&str, u64, ParseSQLError<&str>> { + map(digit1, |d| FromStr::from_str(d).unwrap())(i) + } + + pub fn eof>(input: I) -> IResult { + if input.input_len() == 0 { + Ok((input, input)) + } else { + Err(nom::Err::Error(E::from_error_kind(input, ErrorKind::Eof))) + } + } + + // Parse a terminator that ends a SQL statement. + pub fn statement_terminator(i: &str) -> IResult<&str, (), ParseSQLError<&str>> { + let (remaining_input, _) = delimited( + multispace0, + alt((tag(";"), line_ending, CommonParser::eof)), + multispace0, + )(i)?; + Ok((remaining_input, ())) + } + + // Parse rule for AS-based aliases for SQL entities. + pub fn as_alias(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + map( + tuple(( + multispace1, + opt(pair(tag_no_case("AS"), multispace1)), + // FIXME as can starts with number + CommonParser::sql_identifier, + )), + |a| a.2, + )(i) + } + + pub fn ws_sep_comma(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + delimited(multispace0, tag(","), multispace0)(i) + } + + pub(crate) fn ws_sep_equals(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { + delimited(multispace0, tag("="), multispace0)(i) + } + + /// Parse rule for a comment part. + /// COMMENT 'comment content' + /// or + /// COMMENT "comment content" + pub fn parse_comment(i: &str) -> IResult<&str, String, ParseSQLError<&str>> { + alt(( + map( + preceded( + delimited(multispace0, tag_no_case("COMMENT"), multispace1), + delimited(tag("'"), take_until("'"), tag("'")), + ), + String::from, + ), + map( + preceded( + delimited(multispace0, tag_no_case("COMMENT"), multispace1), + delimited(tag("\""), take_until("\""), tag("\"")), + ), + String::from, + ), + ))(i) + } + + /// IF EXISTS + pub fn parse_if_exists(i: &str) -> IResult<&str, Option<&str>, ParseSQLError<&str>> { + opt(delimited( + multispace0, + delimited(tag_no_case("IF"), multispace1, tag_no_case("EXISTS")), + multispace0, + ))(i) + } +} + +#[cfg(test)] +mod tests { + use nom::bytes::complete::tag; + use nom::IResult; + + use base::CommonParser; + + #[test] + fn sql_identifiers() { + let id1 = "foo"; + let id2 = "f_o_o"; + let id3 = "foo12"; + let id4 = ":fo oo"; + let id5 = "primary "; + let id6 = "`primary`"; + + assert!(CommonParser::sql_identifier(id1).is_ok()); + assert!(CommonParser::sql_identifier(id2).is_ok()); + assert!(CommonParser::sql_identifier(id3).is_ok()); + assert!(CommonParser::sql_identifier(id4).is_err()); + assert!(CommonParser::sql_identifier(id5).is_err()); + assert!(CommonParser::sql_identifier(id6).is_ok()); + } + + fn test_opt_delimited_fn_call(i: &str) -> IResult<&str, &str> { + CommonParser::opt_delimited(tag("("), tag("abc"), tag(")"))(i) + } + + #[test] + fn opt_delimited_tests() { + // let ok1 = IResult::Ok(("".as_bytes(), "abc".as_bytes())); + assert_eq!(test_opt_delimited_fn_call("abc"), IResult::Ok(("", "abc"))); + assert_eq!( + test_opt_delimited_fn_call("(abc)"), + IResult::Ok(("", "abc")) + ); + assert!(test_opt_delimited_fn_call("(abc").is_err()); + assert_eq!( + test_opt_delimited_fn_call("abc)"), + IResult::Ok((")", "abc")) + ); + assert!(test_opt_delimited_fn_call("ab").is_err()); + } + + #[test] + fn comment_data() { + let res = CommonParser::parse_comment(" COMMENT 'test'"); + assert_eq!(res.unwrap().1, "test"); + } + + #[test] + fn terminated_by_semicolon() { + let res = CommonParser::statement_terminator(" ; "); + assert_eq!(res, Ok(("", ()))); + } +} diff --git a/src/base/compression_type.rs b/src/base/compression_type.rs new file mode 100644 index 0000000..324b1c7 --- /dev/null +++ b/src/base/compression_type.rs @@ -0,0 +1,46 @@ +use nom::branch::alt; +use nom::bytes::complete::{tag, tag_no_case}; +use nom::combinator::map; +use nom::sequence::delimited; +use nom::IResult; + +use base::ParseSQLError; + +/// {'ZLIB' | 'LZ4' | 'NONE'} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum CompressionType { + ZLIB, + LZ4, + NONE, +} + +impl CompressionType { + pub fn parse(i: &str) -> IResult<&str, CompressionType, ParseSQLError<&str>> { + alt(( + map( + delimited( + alt((tag("'"), tag("\""))), + tag_no_case("ZLIB"), + alt((tag("'"), tag("\""))), + ), + |_| CompressionType::ZLIB, + ), + map( + delimited( + alt((tag("'"), tag("\""))), + tag_no_case("LZ4"), + alt((tag("'"), tag("\""))), + ), + |_| CompressionType::LZ4, + ), + map( + delimited( + alt((tag("'"), tag("\""))), + tag_no_case("NONE"), + alt((tag("'"), tag("\""))), + ), + |_| CompressionType::NONE, + ), + ))(i) + } +} diff --git a/src/common/condition.rs b/src/base/condition.rs similarity index 98% rename from src/common/condition.rs rename to src/base/condition.rs index d646c1b..89c06cb 100644 --- a/src/common/condition.rs +++ b/src/base/condition.rs @@ -9,11 +9,11 @@ use nom::combinator::{map, opt}; use nom::sequence::{delimited, pair, preceded, separated_pair, terminated, tuple}; use nom::IResult; +use base::arithmetic::ArithmeticExpression; use base::column::Column; use base::error::ParseSQLError; use base::{Literal, Operator}; -use common::arithmetic::ArithmeticExpression; -use dms::select::{BetweenAndClause, SelectStatement}; +use dms::{BetweenAndClause, SelectStatement}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum ConditionBase { @@ -370,14 +370,13 @@ impl fmt::Display for ConditionExpression { #[cfg(test)] mod tests { - use base::table::Table; - use base::{FieldDefinitionExpression, ItemPlaceholder}; - use common::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; - use common::condition::ConditionBase::{Field, LiteralList, NestedSelect}; - use common::condition::ConditionExpression::{ + use base::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; + use base::condition::ConditionBase::{Field, LiteralList, NestedSelect}; + use base::condition::ConditionExpression::{ Base, Bracketed, ComparisonOp, LogicalOp, NegationOp, }; - use dms::select::SelectStatement; + use base::table::Table; + use base::{FieldDefinitionExpression, ItemPlaceholder}; use super::*; diff --git a/src/base/data_type.rs b/src/base/data_type.rs index c078fbe..e58ed07 100644 --- a/src/base/data_type.rs +++ b/src/base/data_type.rs @@ -9,8 +9,7 @@ use nom::sequence::{delimited, preceded, terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use base::Literal; -use common::{delim_digit, precision}; +use base::{CommonParser, Literal}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum DataType { @@ -101,18 +100,21 @@ impl DataType { map( tuple(( tag_no_case("char"), - delim_digit, + CommonParser::delim_digit, multispace0, opt(tag_no_case("binary")), )), |t| DataType::Char(Self::len_as_u16(t.1)), ), - map(preceded(tag_no_case("datetime"), opt(delim_digit)), |fsp| { - DataType::DateTime(match fsp { - Some(fsp) => Self::len_as_u16(fsp), - None => 0, - }) - }), + map( + preceded(tag_no_case("datetime"), opt(CommonParser::delim_digit)), + |fsp| { + DataType::DateTime(match fsp { + Some(fsp) => Self::len_as_u16(fsp), + None => 0, + }) + }, + ), map(tag_no_case("date"), |_| DataType::Date), map( tuple((tag_no_case("double"), multispace0, Self::opt_signed)), @@ -132,7 +134,7 @@ impl DataType { tuple(( tag_no_case("float"), multispace0, - opt(precision), + opt(CommonParser::precision), multispace0, )), |_| DataType::Float, @@ -145,13 +147,17 @@ impl DataType { map(tag_no_case("json"), |_| DataType::Json), map(tag_no_case("uuid"), |_| DataType::Uuid), map( - tuple((tag_no_case("timestamp"), opt(delim_digit), multispace0)), + tuple(( + tag_no_case("timestamp"), + opt(CommonParser::delim_digit), + multispace0, + )), |_| DataType::Timestamp, ), map( tuple(( tag_no_case("varchar"), - delim_digit, + CommonParser::delim_digit, multispace0, opt(tag_no_case("binary")), )), @@ -164,7 +170,11 @@ impl DataType { fn type_identifier_second_half(i: &str) -> IResult<&str, DataType, ParseSQLError<&str>> { alt(( map( - tuple((tag_no_case("binary"), delim_digit, multispace0)), + tuple(( + tag_no_case("binary"), + CommonParser::delim_digit, + multispace0, + )), |t| DataType::Binary(Self::len_as_u16(t.1)), ), map(tag_no_case("blob"), |_| DataType::Blob), @@ -175,7 +185,11 @@ impl DataType { map(tag_no_case("tinyblob"), |_| DataType::Tinyblob), map(tag_no_case("tinytext"), |_| DataType::Tinytext), map( - tuple((tag_no_case("varbinary"), delim_digit, multispace0)), + tuple(( + tag_no_case("varbinary"), + CommonParser::delim_digit, + multispace0, + )), |t| DataType::Varbinary(Self::len_as_u16(t.1)), ), ))(i) @@ -187,7 +201,7 @@ impl DataType { let (remaining_input, (_, _, len, _, signed)) = tuple(( tag_no_case("tinyint"), multispace0, - opt(delim_digit), + opt(CommonParser::delim_digit), multispace0, Self::opt_signed, ))(i)?; @@ -219,7 +233,7 @@ impl DataType { let (remaining_input, (_, _, len, _, signed)) = tuple(( tag_no_case("bigint"), multispace0, - opt(delim_digit), + opt(CommonParser::delim_digit), multispace0, Self::opt_signed, ))(i)?; @@ -255,7 +269,7 @@ impl DataType { tag_no_case("smallint"), )), multispace0, - opt(delim_digit), + opt(CommonParser::delim_digit), multispace0, Self::opt_signed, ))(i)?; @@ -287,7 +301,7 @@ impl DataType { fn decimal_or_numeric(i: &str) -> IResult<&str, DataType, ParseSQLError<&str>> { let (remaining_input, precision) = delimited( alt((tag_no_case("decimal"), tag_no_case("numeric"))), - opt(precision), + opt(CommonParser::precision), multispace0, )(i)?; diff --git a/src/base/default_or_zero_or_one.rs b/src/base/default_or_zero_or_one.rs new file mode 100644 index 0000000..1d4fb3f --- /dev/null +++ b/src/base/default_or_zero_or_one.rs @@ -0,0 +1,38 @@ +use core::fmt; +use std::fmt::Formatter; + +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// {DEFAULT | 0 | 1} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum DefaultOrZeroOrOne { + Default, + Zero, + One, +} + +impl DefaultOrZeroOrOne { + pub fn parse(i: &str) -> IResult<&str, DefaultOrZeroOrOne, ParseSQLError<&str>> { + alt(( + map(tag_no_case("0"), |_| DefaultOrZeroOrOne::Zero), + map(tag_no_case("1"), |_| DefaultOrZeroOrOne::One), + map(tag_no_case("DEFAULT"), |_| DefaultOrZeroOrOne::Default), + ))(i) + } +} + +impl fmt::Display for DefaultOrZeroOrOne { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + DefaultOrZeroOrOne::Default => write!(f, "DEFAULT")?, + DefaultOrZeroOrOne::Zero => write!(f, "1")?, + DefaultOrZeroOrOne::One => write!(f, "0")?, + } + Ok(()) + } +} diff --git a/src/base/field.rs b/src/base/field.rs index 9345641..936d81e 100644 --- a/src/base/field.rs +++ b/src/base/field.rs @@ -9,14 +9,13 @@ use nom::multi::{many0, many1}; use nom::sequence::{delimited, separated_pair, terminated}; 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::Literal; -use common::arithmetic::ArithmeticExpression; -use common::keywords::escape_if_keyword; -use common::ws_sep_comma; +use base::{CommonParser, Literal}; #[derive(Default, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum FieldDefinitionExpression { @@ -44,7 +43,7 @@ impl FieldDefinitionExpression { }), map(Column::parse, FieldDefinitionExpression::Col), )), - opt(ws_sep_comma), + opt(CommonParser::ws_sep_comma), ))(i) } } @@ -96,7 +95,10 @@ impl FieldValueExpression { pub fn assignment_expr_list( i: &str, ) -> IResult<&str, Vec<(Column, FieldValueExpression)>, ParseSQLError<&str>> { - many1(terminated(Self::assignment_expr, opt(ws_sep_comma)))(i) + many1(terminated( + Self::assignment_expr, + opt(CommonParser::ws_sep_comma), + ))(i) } } diff --git a/src/base/fulltext_or_spatial_type.rs b/src/base/fulltext_or_spatial_type.rs new file mode 100644 index 0000000..b99fd57 --- /dev/null +++ b/src/base/fulltext_or_spatial_type.rs @@ -0,0 +1,23 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// {FULLTEXT | SPATIAL} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum FulltextOrSpatialType { + Fulltext, + Spatial, +} + +impl FulltextOrSpatialType { + /// {FULLTEXT | SPATIAL} + pub fn parse(i: &str) -> IResult<&str, FulltextOrSpatialType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("FULLTEXT"), |_| FulltextOrSpatialType::Fulltext), + map(tag_no_case("SPATIAL"), |_| FulltextOrSpatialType::Spatial), + ))(i) + } +} diff --git a/src/common/index_option.rs b/src/base/index_option.rs similarity index 94% rename from src/common/index_option.rs rename to src/base/index_option.rs index 71f1f1e..5b53241 100644 --- a/src/common/index_option.rs +++ b/src/base/index_option.rs @@ -7,8 +7,9 @@ use nom::sequence::{delimited, preceded, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{index_option, IndexType, VisibleType}; -use common::{parse_comment, sql_identifier}; +use base::index_type::IndexType; +use base::visible_type::VisibleType; +use base::CommonParser; /// index_option: { /// KEY_BLOCK_SIZE [=] value @@ -43,7 +44,7 @@ impl IndexOption { map(Self::key_block_size, IndexOption::KeyBlockSize), map(IndexType::parse, IndexOption::IndexType), map(Self::with_parser, IndexOption::WithParser), - map(parse_comment, IndexOption::Comment), + map(CommonParser::parse_comment, IndexOption::Comment), map(VisibleType::parse, IndexOption::VisibleType), map(Self::engine_attribute, IndexOption::EngineAttribute), map( @@ -53,7 +54,7 @@ impl IndexOption { ))(i) } - /// [index_option] + /// `[index_option]` /// index_option: { /// KEY_BLOCK_SIZE [=] value /// | index_type @@ -91,7 +92,7 @@ impl IndexOption { multispace1, tag_no_case("PARSER"), multispace1, - sql_identifier, + CommonParser::sql_identifier, multispace0, )), |(_, _, _, _, _, parser_name, _)| String::from(parser_name), diff --git a/src/base/index_or_key_type.rs b/src/base/index_or_key_type.rs new file mode 100644 index 0000000..8dd421d --- /dev/null +++ b/src/base/index_or_key_type.rs @@ -0,0 +1,23 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// {INDEX | KEY} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum IndexOrKeyType { + Index, + Key, +} + +impl IndexOrKeyType { + /// {INDEX | KEY} + pub fn parse(i: &str) -> IResult<&str, IndexOrKeyType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("KEY"), |_| IndexOrKeyType::Key), + map(tag_no_case("INDEX"), |_| IndexOrKeyType::Index), + ))(i) + } +} diff --git a/src/base/index_type.rs b/src/base/index_type.rs new file mode 100644 index 0000000..e9b889f --- /dev/null +++ b/src/base/index_type.rs @@ -0,0 +1,53 @@ +use core::fmt; +use std::fmt::Formatter; + +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::character::complete::{multispace0, multispace1}; +use nom::combinator::{map, opt}; +use nom::sequence::{delimited, tuple}; +use nom::IResult; + +use base::ParseSQLError; + +/// USING {BTREE | HASH} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum IndexType { + Btree, + Hash, +} + +impl IndexType { + pub fn parse(i: &str) -> IResult<&str, IndexType, ParseSQLError<&str>> { + map( + tuple(( + tag_no_case("USING"), + multispace1, + alt(( + map(tag_no_case("BTREE"), |_| IndexType::Btree), + map(tag_no_case("HASH"), |_| IndexType::Hash), + )), + )), + |x| x.2, + )(i) + } + + /// `[index_type]` + /// USING {BTREE | HASH} + pub fn opt_index_type(i: &str) -> IResult<&str, Option, ParseSQLError<&str>> { + opt(map( + delimited(multispace1, IndexType::parse, multispace0), + |x| x, + ))(i) + } +} + +impl fmt::Display for IndexType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + IndexType::Btree => write!(f, " USING BTREE")?, + IndexType::Hash => write!(f, " USING HASH")?, + }; + Ok(()) + } +} diff --git a/src/base/insert_method_type.rs b/src/base/insert_method_type.rs new file mode 100644 index 0000000..d6acd97 --- /dev/null +++ b/src/base/insert_method_type.rs @@ -0,0 +1,24 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// { NO | FIRST | LAST } +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum InsertMethodType { + No, + First, + Last, +} + +impl InsertMethodType { + pub fn parse(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/item_placeholder.rs b/src/base/item_placeholder.rs index 1f4f582..269cf19 100644 --- a/src/base/item_placeholder.rs +++ b/src/base/item_placeholder.rs @@ -3,8 +3,11 @@ use std::fmt::Display; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum ItemPlaceholder { + /// ? QuestionMark, + /// $ DollarNumber(i32), + /// : ColonNumber(i32), } diff --git a/src/common/join.rs b/src/base/join.rs similarity index 95% rename from src/common/join.rs rename to src/base/join.rs index abe7de9..49cab75 100644 --- a/src/common/join.rs +++ b/src/base/join.rs @@ -9,11 +9,11 @@ use nom::sequence::{delimited, preceded, terminated, tuple}; use nom::IResult; use base::column::Column; +use base::condition::ConditionExpression; use base::error::ParseSQLError; use base::table::Table; -use common::as_alias; -use common::condition::ConditionExpression; -use dms::select::{JoinClause, SelectStatement}; +use base::CommonParser; +use dms::{JoinClause, SelectStatement}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum JoinRightSide { @@ -32,7 +32,7 @@ impl JoinRightSide { let nested_select = map( tuple(( delimited(tag("("), SelectStatement::nested_selection, tag(")")), - opt(as_alias), + opt(CommonParser::as_alias), )), |t| JoinRightSide::NestedSelect(Box::new(t.0), t.1.map(String::from)), ); @@ -182,11 +182,11 @@ impl fmt::Display for JoinConstraint { #[cfg(test)] mod tests { + use base::condition::ConditionBase::Field; + use base::condition::ConditionExpression::Base; + use base::condition::{ConditionExpression, ConditionTree}; use base::Operator; - use common::condition::ConditionBase::Field; - use common::condition::ConditionExpression::Base; - use common::condition::{ConditionExpression, ConditionTree}; - use dms::select::JoinClause; + use dms::JoinClause; use super::*; diff --git a/src/common/key_part.rs b/src/base/key_part.rs similarity index 91% rename from src/common/key_part.rs rename to src/base/key_part.rs index f515cbc..1413718 100644 --- a/src/common/key_part.rs +++ b/src/base/key_part.rs @@ -7,8 +7,7 @@ use nom::sequence::{delimited, preceded, terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::OrderType; -use common::{sql_identifier, ws_sep_comma}; +use base::{CommonParser, OrderType}; /// key_part: {col_name \[(length)] | (expr)} \[ASC | DESC] #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -42,7 +41,10 @@ impl KeyPart { tag("("), delimited( multispace0, - many1(map(terminated(Self::parse, opt(ws_sep_comma)), |e| e)), + many1(map( + terminated(Self::parse, opt(CommonParser::ws_sep_comma)), + |e| e, + )), multispace0, ), tag(")"), @@ -71,7 +73,7 @@ impl KeyPartType { // {col_name [(length)] let col_name_with_length = tuple(( multispace0, - sql_identifier, + CommonParser::sql_identifier, multispace0, opt(delimited( tag("("), diff --git a/src/common/keywords.rs b/src/base/keywords.rs similarity index 99% rename from src/common/keywords.rs rename to src/base/keywords.rs index 63ad1dc..7ba3b0b 100644 --- a/src/common/keywords.rs +++ b/src/base/keywords.rs @@ -5,7 +5,7 @@ use nom::sequence::terminated; use nom::IResult; use base::error::ParseSQLError; -use common::eof; +use base::CommonParser; fn keyword_follow_char(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { peek(alt(( @@ -17,7 +17,7 @@ fn keyword_follow_char(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { tag("\t"), tag(","), tag("="), - eof, + CommonParser::eof, )))(i) } diff --git a/src/base/literal.rs b/src/base/literal.rs index d5b6dad..7d4650e 100644 --- a/src/base/literal.rs +++ b/src/base/literal.rs @@ -10,8 +10,7 @@ use nom::sequence::{delimited, pair, preceded, tuple}; use nom::IResult; use base::error::ParseSQLError; -use base::ItemPlaceholder; -use common::{as_alias, opt_delimited, ws_sep_comma}; +use base::{CommonParser, ItemPlaceholder}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum Literal { @@ -124,12 +123,12 @@ impl Literal { Self::float_literal, Self::integer_literal, Self::string_literal, - map(tag_no_case("null"), |_| Literal::Null), - map(tag_no_case("current_timestamp"), |_| { + map(tag_no_case("NULL"), |_| Literal::Null), + map(tag_no_case("CURRENT_TIMESTAMP"), |_| { Literal::CurrentTimestamp }), - map(tag_no_case("current_date"), |_| Literal::CurrentDate), - map(tag_no_case("current_time"), |_| Literal::CurrentTime), + map(tag_no_case("CURRENT_DATE"), |_| Literal::CurrentDate), + map(tag_no_case("CURRENT_TIME"), |_| Literal::CurrentTime), map(tag("?"), |_| { Literal::Placeholder(ItemPlaceholder::QuestionMark) }), @@ -146,7 +145,11 @@ impl Literal { // Parse a list of values (e.g., for INSERT syntax). pub fn value_list(i: &str) -> IResult<&str, Vec, ParseSQLError<&str>> { - many0(delimited(multispace0, Literal::parse, opt(ws_sep_comma)))(i) + many0(delimited( + multispace0, + Literal::parse, + opt(CommonParser::ws_sep_comma), + ))(i) } } @@ -225,8 +228,8 @@ impl LiteralExpression { pub fn parse(i: &str) -> IResult<&str, LiteralExpression, ParseSQLError<&str>> { map( pair( - opt_delimited(tag("("), Literal::parse, tag(")")), - opt(as_alias), + CommonParser::opt_delimited(tag("("), Literal::parse, tag(")")), + opt(CommonParser::as_alias), ), |p| LiteralExpression { value: p.0, diff --git a/src/base/lock_type.rs b/src/base/lock_type.rs new file mode 100644 index 0000000..c52cadb --- /dev/null +++ b/src/base/lock_type.rs @@ -0,0 +1,40 @@ +use nom::branch::alt; +use nom::bytes::complete::{tag, tag_no_case}; +use nom::character::complete::multispace0; +use nom::combinator::{map, opt}; +use nom::sequence::tuple; +use nom::IResult; + +use base::ParseSQLError; + +/// LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum LockType { + Default, + None, + Shared, + Exclusive, +} + +impl LockType { + /// lock_option: + /// LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE} + 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), + )), + multispace0, + )), + |x| x.4, + )(i) + } +} diff --git a/src/base/match_type.rs b/src/base/match_type.rs new file mode 100644 index 0000000..f070489 --- /dev/null +++ b/src/base/match_type.rs @@ -0,0 +1,34 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::character::complete::multispace1; +use nom::combinator::map; +use nom::sequence::tuple; +use nom::IResult; + +use base::ParseSQLError; + +/// `[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]` +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum MatchType { + Full, + Partial, + Simple, +} + +impl MatchType { + /// [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] + pub fn parse(i: &str) -> IResult<&str, MatchType, ParseSQLError<&str>> { + map( + tuple(( + tag_no_case("MATCH"), + multispace1, + alt(( + map(tag_no_case("FULL"), |_| MatchType::Full), + map(tag_no_case("PARTIAL"), |_| MatchType::Partial), + map(tag_no_case("SIMPLE"), |_| MatchType::Simple), + )), + )), + |x| x.2, + )(i) + } +} diff --git a/src/base/mod.rs b/src/base/mod.rs index dc37179..a968250 100644 --- a/src/base/mod.rs +++ b/src/base/mod.rs @@ -1,20 +1,68 @@ +pub use self::case::{CaseWhenExpression, ColumnOrLiteral}; +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::error::ParseSQLError; pub use self::field::{FieldDefinitionExpression, FieldValueExpression}; +pub use self::insert_method_type::InsertMethodType; pub use self::item_placeholder::ItemPlaceholder; +pub use self::join::{JoinConstraint, JoinOperator, JoinRightSide}; +pub use self::key_part::{KeyPart, KeyPartType}; pub use self::literal::{Literal, LiteralExpression, Real}; +pub use self::match_type::MatchType; pub use self::operator::Operator; +pub use self::order::OrderClause; +pub use self::order::OrderType; +pub use self::partition_definition::PartitionDefinition; +pub use self::reference_definition::ReferenceDefinition; +pub use self::row_format_type::RowFormatType; pub use self::table_key::TableKey; +pub use self::tablespace_type::TablespaceType; pub mod column; pub mod table; pub mod trigger; -mod data_type; +pub mod algorithm_type; +pub mod check_constraint; +pub mod common_parser; +pub mod compression_type; +pub mod data_type; +pub mod default_or_zero_or_one; pub mod error; -mod field; -mod item_placeholder; -mod literal; -mod operator; -mod table_key; +pub mod field; +pub mod fulltext_or_spatial_type; +pub mod index_or_key_type; +pub mod index_type; +pub mod insert_method_type; +pub mod item_placeholder; +pub mod literal; +pub mod lock_type; +pub mod match_type; +pub mod operator; +pub mod reference_type; +pub mod row_format_type; +pub mod table_key; +pub mod tablespace_type; +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 condition; + +mod order; + +pub mod case; + +mod join; diff --git a/src/common/order.rs b/src/base/order.rs similarity index 96% rename from src/common/order.rs rename to src/base/order.rs index 9dc35a2..f504e93 100644 --- a/src/common/order.rs +++ b/src/base/order.rs @@ -11,8 +11,8 @@ use nom::IResult; use base::column::Column; use base::error::ParseSQLError; -use common::keywords::escape_if_keyword; -use common::ws_sep_comma; +use base::keywords::escape_if_keyword; +use base::CommonParser; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct OrderClause { @@ -38,7 +38,7 @@ impl OrderClause { let (remaining_input, (field_name, ordering, _)) = tuple(( Column::without_alias, opt(preceded(multispace0, OrderType::parse)), - opt(ws_sep_comma), + opt(CommonParser::ws_sep_comma), ))(i)?; Ok(( diff --git a/src/common/partition_definition.rs b/src/base/partition_definition.rs similarity index 100% rename from src/common/partition_definition.rs rename to src/base/partition_definition.rs diff --git a/src/common/reference_definition.rs b/src/base/reference_definition.rs similarity index 93% rename from src/common/reference_definition.rs rename to src/base/reference_definition.rs index f13e98f..1d73c07 100644 --- a/src/common/reference_definition.rs +++ b/src/base/reference_definition.rs @@ -5,8 +5,8 @@ use nom::sequence::tuple; use nom::IResult; use base::error::ParseSQLError; -use common::{sql_identifier, KeyPart}; -use common::{MatchType, ReferenceType}; +use base::reference_type::ReferenceType; +use base::{CommonParser, KeyPart, MatchType}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct ReferenceDefinition { @@ -48,7 +48,7 @@ impl ReferenceDefinition { tuple(( tuple((multispace0, tag_no_case("REFERENCES"), multispace1)), // tbl_name - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, KeyPart::key_part_list, // (key_part,...) multispace0, diff --git a/src/base/reference_type.rs b/src/base/reference_type.rs new file mode 100644 index 0000000..d9f1758 --- /dev/null +++ b/src/base/reference_type.rs @@ -0,0 +1,42 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::character::complete::multispace1; +use nom::combinator::map; +use nom::sequence::tuple; +use nom::IResult; + +use base::ParseSQLError; + +/// reference_option: +/// RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum ReferenceType { + Restrict, + Cascade, + SetNull, + NoAction, + SetDefault, +} + +impl ReferenceType { + /// reference_option: + /// RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT + pub fn parse(i: &str) -> IResult<&str, ReferenceType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("RESTRICT"), |_| ReferenceType::Restrict), + map(tag_no_case("CASCADE"), |_| ReferenceType::Cascade), + map( + tuple((tag_no_case("SET"), multispace1, tag_no_case("NULL"))), + |_| ReferenceType::SetNull, + ), + map( + tuple((tag_no_case("NO"), multispace1, tag_no_case("ACTION"))), + |_| ReferenceType::NoAction, + ), + map( + tuple((tag_no_case("SET"), multispace1, tag_no_case("DEFAULT"))), + |_| ReferenceType::SetDefault, + ), + ))(i) + } +} diff --git a/src/base/row_format_type.rs b/src/base/row_format_type.rs new file mode 100644 index 0000000..47d7215 --- /dev/null +++ b/src/base/row_format_type.rs @@ -0,0 +1,30 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum RowFormatType { + Default, + Dynamic, + Fixed, + Compressed, + Redundant, + Compact, +} + +impl RowFormatType { + pub fn parse(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 315c2f7..8f6711d 100644 --- a/src/base/table.rs +++ b/src/base/table.rs @@ -9,8 +9,8 @@ use nom::sequence::{pair, terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::keywords::escape_if_keyword; -use common::{as_alias, sql_identifier, ws_sep_comma}; +use base::keywords::escape_if_keyword; +use base::CommonParser; /// **Table Definition** #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -27,16 +27,19 @@ impl Table { // Parse list of table names. // XXX(malte): add support for aliases pub fn table_list(i: &str) -> IResult<&str, Vec, ParseSQLError<&str>> { - many0(terminated(Table::schema_table_reference, opt(ws_sep_comma)))(i) + many0(terminated( + Table::schema_table_reference, + opt(CommonParser::ws_sep_comma), + ))(i) } // Parse a reference to a named schema.table, with an optional alias pub fn schema_table_reference(i: &str) -> IResult<&str, Table, ParseSQLError<&str>> { map( tuple(( - opt(pair(sql_identifier, tag("."))), - sql_identifier, - opt(as_alias), + opt(pair(CommonParser::sql_identifier, tag("."))), + CommonParser::sql_identifier, + opt(CommonParser::as_alias), )), |tup| Table { name: String::from(tup.1), @@ -48,17 +51,23 @@ impl Table { // Parse a reference to a named table, with an optional alias pub fn table_reference(i: &str) -> IResult<&str, Table, ParseSQLError<&str>> { - map(pair(sql_identifier, opt(as_alias)), |tup| Table { - name: String::from(tup.0), - alias: tup.1.map(String::from), - schema: None, - })(i) + map( + pair(CommonParser::sql_identifier, opt(CommonParser::as_alias)), + |tup| Table { + name: String::from(tup.0), + alias: tup.1.map(String::from), + schema: None, + }, + )(i) } /// table alias not allowed in DROP/TRUNCATE/RENAME TABLE statement pub fn without_alias(i: &str) -> IResult<&str, Table, ParseSQLError<&str>> { map( - tuple((opt(pair(sql_identifier, tag("."))), sql_identifier)), + tuple(( + opt(pair(CommonParser::sql_identifier, tag("."))), + CommonParser::sql_identifier, + )), |tup| Table { name: String::from(tup.1), alias: None, diff --git a/src/base/table_key.rs b/src/base/table_key.rs index 02af611..f31371c 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 common::keywords::escape_if_keyword; +use base::keywords::escape_if_keyword; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum TableKey { diff --git a/src/common/table_option.rs b/src/base/table_option.rs similarity index 97% rename from src/common/table_option.rs rename to src/base/table_option.rs index 5469bca..8287a8e 100644 --- a/src/common/table_option.rs +++ b/src/base/table_option.rs @@ -1,15 +1,15 @@ 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, value}; +use nom::combinator::{map, opt}; use nom::sequence::{delimited, tuple}; use nom::{IResult, Parser}; use base::column::Column; use base::error::ParseSQLError; -use common::sql_identifier; -use common::{ - CompressionType, DefaultOrZeroOrOne, InsertMethodType, RowFormatType, TablespaceType, +use base::{ + CommonParser, CompressionType, DefaultOrZeroOrOne, InsertMethodType, RowFormatType, + TablespaceType, }; /// table_option: { @@ -213,7 +213,7 @@ impl TableOption { opt(tag("=")), multispace0, )), - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, )), |(_, _, _, charset_name, _)| TableOption::DefaultCharacterSet(charset_name), @@ -232,7 +232,7 @@ impl TableOption { opt(tag("=")), multispace0, )), - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, )), |(_, _, _, charset_name, _)| TableOption::DefaultCharset(charset_name), @@ -266,7 +266,7 @@ impl TableOption { multispace1, opt(tag("=")), multispace0, - sql_identifier, + CommonParser::sql_identifier, multispace0, )), |(_, _, _, _, collation_name, _)| String::from(collation_name), @@ -402,7 +402,7 @@ impl TableOption { multispace0, opt(tag("=")), multispace0, - sql_identifier, + CommonParser::sql_identifier, multispace0, )), |(_, _, _, _, engine, _)| TableOption::Engine(String::from(engine)), @@ -622,7 +622,7 @@ impl TableOption { tuple(( tag_no_case("TABLESPACE"), multispace1, - map(sql_identifier, String::from), // tablespace_name + map(CommonParser::sql_identifier, String::from), // tablespace_name multispace0, opt(map( tuple((tag_no_case("STORAGE"), multispace0, TablespaceType::parse)), diff --git a/src/base/tablespace_type.rs b/src/base/tablespace_type.rs new file mode 100644 index 0000000..4b7ace4 --- /dev/null +++ b/src/base/tablespace_type.rs @@ -0,0 +1,22 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// STORAGE {DISK | MEMORY} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum TablespaceType { + StorageDisk, + StorageMemory, +} + +impl TablespaceType { + pub fn parse(i: &str) -> IResult<&str, TablespaceType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("DISK"), |_| TablespaceType::StorageDisk), + map(tag_no_case("MEMORY"), |_| TablespaceType::StorageMemory), + ))(i) + } +} diff --git a/src/base/trigger.rs b/src/base/trigger.rs index fb413c1..3825a20 100644 --- a/src/base/trigger.rs +++ b/src/base/trigger.rs @@ -7,8 +7,8 @@ use nom::sequence::{pair, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::keywords::escape_if_keyword; -use common::sql_identifier; +use base::keywords::escape_if_keyword; +use base::CommonParser; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct Trigger { @@ -19,7 +19,10 @@ pub struct Trigger { impl Trigger { pub fn parse(i: &str) -> IResult<&str, Trigger, ParseSQLError<&str>> { map( - tuple((opt(pair(sql_identifier, tag("."))), sql_identifier)), + tuple(( + opt(pair(CommonParser::sql_identifier, tag("."))), + CommonParser::sql_identifier, + )), |tup| Trigger { name: String::from(tup.1), schema: tup.0.map(|(schema, _)| String::from(schema)), diff --git a/src/base/visible_type.rs b/src/base/visible_type.rs new file mode 100644 index 0000000..b36ff0e --- /dev/null +++ b/src/base/visible_type.rs @@ -0,0 +1,22 @@ +use nom::branch::alt; +use nom::bytes::complete::tag_no_case; +use nom::combinator::map; +use nom::IResult; + +use base::ParseSQLError; + +/// {VISIBLE | INVISIBLE} +#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum VisibleType { + Visible, + Invisible, +} + +impl VisibleType { + pub fn parse(i: &str) -> IResult<&str, VisibleType, ParseSQLError<&str>> { + alt(( + map(tag_no_case("VISIBLE"), |_| VisibleType::Visible), + map(tag_no_case("INVISIBLE"), |_| VisibleType::Invisible), + ))(i) + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs deleted file mode 100644 index 1af2d07..0000000 --- a/src/common/mod.rs +++ /dev/null @@ -1,606 +0,0 @@ -/// common parsers -use core::fmt; -use std::fmt::Formatter; -use std::str::FromStr; - -use nom::branch::alt; -use nom::bytes::complete::{tag, tag_no_case, take_until, take_while, take_while1}; -use nom::character::complete::{alpha1, digit1, line_ending, multispace0, multispace1}; -use nom::character::is_alphanumeric; -use nom::combinator::{map, not, opt, peek, recognize}; -use nom::error::{ErrorKind, ParseError}; -use nom::sequence::{delimited, pair, preceded, terminated, tuple}; -use nom::{IResult, InputLength, Parser}; - -use base::column::Column; -use base::ParseSQLError; -use common::keywords::sql_keyword; -pub use common::order::{OrderClause, OrderType}; - -pub use self::case::{CaseWhenExpression, ColumnOrLiteral}; -pub use self::join::{JoinConstraint, JoinOperator, JoinRightSide}; -pub use self::key_part::{KeyPart, KeyPartType}; -pub use self::partition_definition::PartitionDefinition; -pub use self::reference_definition::ReferenceDefinition; - -pub mod index_option; -pub mod table_option; - -pub mod arithmetic; - -#[macro_use] -pub mod keywords; -mod key_part; -mod partition_definition; -mod reference_definition; - -pub mod condition; - -mod order; - -pub mod case; - -mod join; - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum AlgorithmType { - Default, - Inplace, - Copy, -} - -impl AlgorithmType { - /// algorithm_option: - /// ALGORITHM [=] {DEFAULT | 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("INPLACE"), |_| AlgorithmType::Inplace), - map(tag_no_case("COPY"), |_| AlgorithmType::Copy), - )), - )), - |x| x.4, - )(i) - } -} - -/// LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum LockType { - Default, - None, - Shared, - Exclusive, -} - -impl LockType { - /// lock_option: - /// LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE} - 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), - )), - multispace0, - )), - |x| x.4, - )(i) - } -} - -/// [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum MatchType { - Full, - Partial, - Simple, -} - -impl MatchType { - /// [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE] - fn parse(i: &str) -> IResult<&str, MatchType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("MATCH"), - multispace1, - alt(( - map(tag_no_case("FULL"), |_| MatchType::Full), - map(tag_no_case("PARTIAL"), |_| MatchType::Partial), - map(tag_no_case("SIMPLE"), |_| MatchType::Simple), - )), - )), - |x| x.2, - )(i) - } -} - -/// reference_option: -/// RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum ReferenceType { - Restrict, - Cascade, - SetNull, - NoAction, - SetDefault, -} - -impl ReferenceType { - /// reference_option: - /// RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT - pub fn parse(i: &str) -> IResult<&str, ReferenceType, ParseSQLError<&str>> { - alt(( - map(tag_no_case("RESTRICT"), |_| ReferenceType::Restrict), - map(tag_no_case("CASCADE"), |_| ReferenceType::Cascade), - map( - tuple((tag_no_case("SET"), multispace1, tag_no_case("NULL"))), - |_| ReferenceType::SetNull, - ), - map( - tuple((tag_no_case("NO"), multispace1, tag_no_case("ACTION"))), - |_| ReferenceType::NoAction, - ), - map( - tuple((tag_no_case("SET"), multispace1, tag_no_case("DEFAULT"))), - |_| ReferenceType::SetDefault, - ), - ))(i) - } -} - -/// {'ZLIB' | 'LZ4' | 'NONE'} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum CompressionType { - ZLIB, - LZ4, - NONE, -} - -impl CompressionType { - fn parse(i: &str) -> IResult<&str, CompressionType, ParseSQLError<&str>> { - alt(( - map( - delimited( - alt((tag("'"), tag("\""))), - tag_no_case("ZLIB"), - alt((tag("'"), tag("\""))), - ), - |_| CompressionType::ZLIB, - ), - map( - delimited( - alt((tag("'"), tag("\""))), - tag_no_case("LZ4"), - alt((tag("'"), tag("\""))), - ), - |_| CompressionType::LZ4, - ), - map( - delimited( - alt((tag("'"), tag("\""))), - tag_no_case("NONE"), - alt((tag("'"), tag("\""))), - ), - |_| CompressionType::NONE, - ), - ))(i) - } -} - -/// { NO | FIRST | LAST } -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum InsertMethodType { - No, - First, - Last, -} - -impl InsertMethodType { - fn parse(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) - } -} - -/// {DEFAULT | DYNAMIC | FIXED | COMPRESSED | REDUNDANT | COMPACT} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum RowFormatType { - Default, - Dynamic, - Fixed, - Compressed, - Redundant, - Compact, -} - -impl RowFormatType { - fn parse(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) - } -} - -/// {DEFAULT | 0 | 1} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum DefaultOrZeroOrOne { - Default, - Zero, - One, -} - -impl DefaultOrZeroOrOne { - pub fn parse(i: &str) -> IResult<&str, DefaultOrZeroOrOne, ParseSQLError<&str>> { - alt(( - map(tag_no_case("0"), |_| DefaultOrZeroOrOne::Zero), - map(tag_no_case("1"), |_| DefaultOrZeroOrOne::One), - map(tag_no_case("DEFAULT"), |_| DefaultOrZeroOrOne::Default), - ))(i) - } -} - -impl fmt::Display for DefaultOrZeroOrOne { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - DefaultOrZeroOrOne::Default => write!(f, "DEFAULT")?, - DefaultOrZeroOrOne::Zero => write!(f, "1")?, - DefaultOrZeroOrOne::One => write!(f, "0")?, - } - Ok(()) - } -} - -/// STORAGE {DISK | MEMORY} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum TablespaceType { - StorageDisk, - StorageMemory, -} - -impl TablespaceType { - pub fn parse(i: &str) -> IResult<&str, TablespaceType, ParseSQLError<&str>> { - alt(( - map(tag_no_case("DISK"), |_| TablespaceType::StorageDisk), - map(tag_no_case("MEMORY"), |_| TablespaceType::StorageMemory), - ))(i) - } -} - -/// {FULLTEXT | SPATIAL} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum FulltextOrSpatialType { - Fulltext, - Spatial, -} - -impl FulltextOrSpatialType { - /// // {FULLTEXT | SPATIAL} - pub fn parse(i: &str) -> IResult<&str, FulltextOrSpatialType, ParseSQLError<&str>> { - alt(( - map(tag_no_case("FULLTEXT"), |_| FulltextOrSpatialType::Fulltext), - map(tag_no_case("SPATIAL"), |_| FulltextOrSpatialType::Spatial), - ))(i) - } -} - -/// {INDEX | KEY} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum IndexOrKeyType { - Index, - Key, -} - -impl IndexOrKeyType { - /// {INDEX | KEY} - pub fn parse(i: &str) -> IResult<&str, IndexOrKeyType, ParseSQLError<&str>> { - alt(( - map(tag_no_case("KEY"), |_| IndexOrKeyType::Key), - map(tag_no_case("INDEX"), |_| IndexOrKeyType::Index), - ))(i) - } -} - -/// \[CONSTRAINT \[symbol]] CHECK (expr) \[\[NOT] ENFORCED] -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct CheckConstraintDefinition { - pub symbol: Option, - pub expr: String, - pub enforced: bool, -} - -/// {VISIBLE | INVISIBLE} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum VisibleType { - Visible, - Invisible, -} - -impl VisibleType { - pub fn parse(i: &str) -> IResult<&str, VisibleType, ParseSQLError<&str>> { - alt(( - map(tag_no_case("VISIBLE"), |_| VisibleType::Visible), - map(tag_no_case("INVISIBLE"), |_| VisibleType::Invisible), - ))(i) - } -} - -/// USING {BTREE | HASH} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum IndexType { - Btree, - Hash, -} - -impl IndexType { - fn parse(i: &str) -> IResult<&str, IndexType, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("USING"), - multispace1, - alt(( - map(tag_no_case("BTREE"), |_| IndexType::Btree), - map(tag_no_case("HASH"), |_| IndexType::Hash), - )), - )), - |x| x.2, - )(i) - } - - /// \[index_type] - /// USING {BTREE | HASH} - pub fn opt_index_type(i: &str) -> IResult<&str, Option, ParseSQLError<&str>> { - opt(map( - delimited(multispace1, IndexType::parse, multispace0), - |x| x, - ))(i) - } -} - -impl fmt::Display for IndexType { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - IndexType::Btree => write!(f, " USING BTREE")?, - IndexType::Hash => write!(f, " USING HASH")?, - }; - Ok(()) - } -} - -/// \[index_name] -pub fn opt_index_name(i: &str) -> IResult<&str, Option, ParseSQLError<&str>> { - opt(map( - delimited(multispace1, sql_identifier, multispace0), - String::from, - ))(i) -} - -#[allow(clippy::type_complexity)] -pub fn index_col_name( - i: &str, -) -> IResult<&str, (Column, Option, Option), ParseSQLError<&str>> { - let (remaining_input, (column, len_u8, order)) = tuple(( - terminated(Column::without_alias, multispace0), - opt(delimited(tag("("), digit1, tag(")"))), - opt(OrderType::parse), - ))(i)?; - let len = len_u8.map(|l| u16::from_str(l).unwrap()); - - Ok((remaining_input, (column, len, order))) -} - -#[inline] -fn is_sql_identifier(chr: char) -> bool { - is_alphanumeric(chr as u8) || chr == '_' || chr == '@' -} - -/// first and third are opt -pub(crate) fn opt_delimited, F, G, H>( - mut first: F, - mut second: G, - mut third: H, -) -> impl FnMut(I) -> IResult -where - F: Parser, - G: Parser, - H: Parser, -{ - move |input: I| { - let inp = input.clone(); - match second.parse(input) { - Ok((i, o)) => Ok((i, o)), - _ => { - let (inp, _) = first.parse(inp)?; - let (inp, o2) = second.parse(inp)?; - third.parse(inp).map(|(i, _)| (i, o2)) - } - } - } -} - -fn precision_helper(i: &str) -> IResult<&str, (u8, Option), ParseSQLError<&str>> { - let (remaining_input, (m, d)) = tuple(( - digit1, - opt(preceded(tag(","), preceded(multispace0, digit1))), - ))(i)?; - - Ok(( - remaining_input, - (m.parse().unwrap(), d.map(|r| r.parse().unwrap())), - )) -} - -pub fn precision(i: &str) -> IResult<&str, (u8, Option), ParseSQLError<&str>> { - delimited(tag("("), precision_helper, tag(")"))(i) -} - -pub fn delim_digit(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - delimited(tag("("), digit1, tag(")"))(i) -} - -pub fn sql_identifier(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - alt(( - alt(( - preceded( - not(peek(sql_keyword)), - recognize(pair(alpha1, take_while(is_sql_identifier))), - ), - recognize(pair(tag("_"), take_while1(is_sql_identifier))), - // variable only - recognize(pair(tag("@"), take_while1(is_sql_identifier))), - )), - delimited(tag("`"), take_while1(is_sql_identifier), tag("`")), - delimited(tag("["), take_while1(is_sql_identifier), tag("]")), - ))(i) -} - -// Parse an unsigned integer. -pub fn unsigned_number(i: &str) -> IResult<&str, u64, ParseSQLError<&str>> { - map(digit1, |d| FromStr::from_str(d).unwrap())(i) -} - -pub(crate) fn eof>(input: I) -> IResult { - if input.input_len() == 0 { - Ok((input, input)) - } else { - Err(nom::Err::Error(E::from_error_kind(input, ErrorKind::Eof))) - } -} - -// Parse a terminator that ends a SQL statement. -pub fn statement_terminator(i: &str) -> IResult<&str, (), ParseSQLError<&str>> { - let (remaining_input, _) = - delimited(multispace0, alt((tag(";"), line_ending, eof)), multispace0)(i)?; - Ok((remaining_input, ())) -} - -// Parse rule for AS-based aliases for SQL entities. -pub fn as_alias(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - map( - tuple(( - multispace1, - opt(pair(tag_no_case("AS"), multispace1)), - // FIXME as can starts with number - sql_identifier, - )), - |a| a.2, - )(i) -} - -pub fn ws_sep_comma(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - delimited(multispace0, tag(","), multispace0)(i) -} - -pub(crate) fn ws_sep_equals(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> { - delimited(multispace0, tag("="), multispace0)(i) -} - -/// Parse rule for a comment part. -/// COMMENT 'comment content' -/// or -/// COMMENT "comment content" -pub fn parse_comment(i: &str) -> IResult<&str, String, ParseSQLError<&str>> { - alt(( - map( - preceded( - delimited(multispace0, tag_no_case("COMMENT"), multispace1), - delimited(tag("'"), take_until("'"), tag("'")), - ), - String::from, - ), - map( - preceded( - delimited(multispace0, tag_no_case("COMMENT"), multispace1), - delimited(tag("\""), take_until("\""), tag("\"")), - ), - String::from, - ), - ))(i) -} - -/// IF EXISTS -pub fn parse_if_exists(i: &str) -> IResult<&str, Option<&str>, ParseSQLError<&str>> { - opt(delimited( - multispace0, - delimited(tag_no_case("IF"), multispace1, tag_no_case("EXISTS")), - multispace0, - ))(i) -} - -#[cfg(test)] -mod tests { - use nom::bytes::complete::tag; - use nom::IResult; - - use common::{opt_delimited, parse_comment, sql_identifier, statement_terminator}; - - #[test] - fn sql_identifiers() { - let id1 = "foo"; - let id2 = "f_o_o"; - let id3 = "foo12"; - let id4 = ":fo oo"; - let id5 = "primary "; - let id6 = "`primary`"; - - assert!(sql_identifier(id1).is_ok()); - assert!(sql_identifier(id2).is_ok()); - assert!(sql_identifier(id3).is_ok()); - assert!(sql_identifier(id4).is_err()); - assert!(sql_identifier(id5).is_err()); - assert!(sql_identifier(id6).is_ok()); - } - - fn test_opt_delimited_fn_call(i: &str) -> IResult<&str, &str> { - opt_delimited(tag("("), tag("abc"), tag(")"))(i) - } - - #[test] - fn opt_delimited_tests() { - // let ok1 = IResult::Ok(("".as_bytes(), "abc".as_bytes())); - assert_eq!(test_opt_delimited_fn_call("abc"), IResult::Ok(("", "abc"))); - assert_eq!( - test_opt_delimited_fn_call("(abc)"), - IResult::Ok(("", "abc")) - ); - assert!(test_opt_delimited_fn_call("(abc").is_err()); - assert_eq!( - test_opt_delimited_fn_call("abc)"), - IResult::Ok((")", "abc")) - ); - assert!(test_opt_delimited_fn_call("ab").is_err()); - } - - #[test] - fn comment_data() { - let res = parse_comment(" COMMENT 'test'"); - assert_eq!(res.unwrap().1, "test"); - } - - #[test] - fn terminated_by_semicolon() { - let res = statement_terminator(" ; "); - assert_eq!(res, Ok(("", ()))); - } -} diff --git a/src/das/mod.rs b/src/das/mod.rs index ef49015..ba6503e 100644 --- a/src/das/mod.rs +++ b/src/das/mod.rs @@ -1 +1,3 @@ -pub mod set_statement; +mod set_statement; + +pub use das::set_statement::SetStatement; diff --git a/src/das/set_statement.rs b/src/das/set_statement.rs index 3a0b7b4..d875ed8 100644 --- a/src/das/set_statement.rs +++ b/src/das/set_statement.rs @@ -6,8 +6,7 @@ use nom::sequence::tuple; use nom::IResult; use base::error::ParseSQLError; -use base::Literal; -use common::{sql_identifier, statement_terminator}; +use base::{CommonParser, Literal}; /// SET variable = expr [, variable = expr] ... /// @@ -31,12 +30,12 @@ impl SetStatement { let (remaining_input, (_, _, var, _, _, _, value, _)) = tuple(( tag_no_case("SET"), multispace1, - sql_identifier, + CommonParser::sql_identifier, multispace0, tag_no_case("="), multispace0, Literal::parse, - statement_terminator, + CommonParser::statement_terminator, ))(i)?; let variable = String::from(var); Ok((remaining_input, SetStatement { variable, value })) diff --git a/src/dds/alter_database.rs b/src/dds/alter_database.rs index 0fe8bd8..d35e8be 100644 --- a/src/dds/alter_database.rs +++ b/src/dds/alter_database.rs @@ -10,8 +10,7 @@ use nom::sequence::{terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::sql_identifier; -use common::DefaultOrZeroOrOne; +use base::{CommonParser, DefaultOrZeroOrOne}; /// ALTER {DATABASE | SCHEMA} \[db_name] /// alter_option ... @@ -37,7 +36,7 @@ impl AlterDatabaseStatement { multispace0, alt((tag_no_case("DATABASE"), tag_no_case("SCHEMA"))), multispace1, - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace1, many1(terminated(AlterDatabaseOption::parse, multispace0)), )), @@ -90,7 +89,7 @@ impl AlterDatabaseOption { opt(tag("=")), multispace0, )), - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, )), |(_, _, _, charset_name, _)| AlterDatabaseOption::CharacterSet(charset_name), @@ -107,7 +106,7 @@ impl AlterDatabaseOption { multispace0, opt(tag("=")), multispace0, - sql_identifier, + CommonParser::sql_identifier, multispace0, )), |(_, _, _, _, collation_name, _)| String::from(collation_name), diff --git a/src/dds/alter_table.rs b/src/dds/alter_table.rs index ab60278..33390ff 100644 --- a/src/dds/alter_table.rs +++ b/src/dds/alter_table.rs @@ -5,22 +5,24 @@ use std::str::FromStr; use nom::branch::alt; use nom::bytes::complete::{tag, tag_no_case, take_until}; use nom::character::complete::{alphanumeric1, anychar, digit1, multispace0, multispace1}; -use nom::combinator::{map, opt, recognize, value}; +use nom::combinator::{map, opt, recognize}; use nom::error::ParseError; use nom::multi::{many0, many1}; use nom::sequence::{delimited, preceded, terminated, tuple}; use nom::{IResult, Parser}; +use base::algorithm_type::AlgorithmType; +use base::check_constraint::CheckConstraintDefinition; use base::column::{Column, ColumnSpecification}; +use base::fulltext_or_spatial_type::FulltextOrSpatialType; +use base::index_option::IndexOption; +use base::index_or_key_type::IndexOrKeyType; +use base::index_type::IndexType; +use base::lock_type::LockType; use base::table::Table; -use base::ParseSQLError; -use common::index_option::IndexOption; -use common::table_option::TableOption; -use common::{ - opt_index_name, CheckConstraintDefinition, FulltextOrSpatialType, IndexOrKeyType, IndexType, - KeyPart, LockType, PartitionDefinition, ReferenceDefinition, VisibleType, -}; -use common::{sql_identifier, statement_terminator, ws_sep_comma}; +use base::table_option::TableOption; +use base::visible_type::VisibleType; +use base::{CommonParser, KeyPart, ParseSQLError, PartitionDefinition, ReferenceDefinition}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct AlterTableStatement { @@ -44,14 +46,18 @@ impl AlterTableStatement { multispace0, // opt(many0(map( - tuple((AlterTableOption::parse, opt(ws_sep_comma), multispace0)), + tuple(( + AlterTableOption::parse, + opt(CommonParser::ws_sep_comma), + multispace0, + )), |x| x.0, ))), opt(many0(terminated( AlterPartitionOption::parse, - opt(ws_sep_comma), + opt(CommonParser::ws_sep_comma), ))), - statement_terminator, + CommonParser::statement_terminator, )); let (remaining_input, (_, table, _, alter_options, partition_options, _)) = parser(i)?; Ok(( @@ -77,39 +83,19 @@ impl fmt::Display for AlterTableStatement { } /////// Alter Table Option -/// ALGORITHM [=] {DEFAULT | INSTANT | INPLACE | COPY} -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub enum AlgorithmType { - DEFAULT, - INSTANT, - INPLACE, - COPY, -} - -impl AlgorithmType { - fn parse(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) - } -} - /// {CHECK | CONSTRAINT} #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum CheckOrConstraintType { - CHECK, - CONSTRAINT, + Check, + Constraint, } impl CheckOrConstraintType { fn parse(i: &str) -> IResult<&str, CheckOrConstraintType, ParseSQLError<&str>> { alt(( - map(tag_no_case("CHECK"), |_| CheckOrConstraintType::CHECK), + map(tag_no_case("CHECK"), |_| CheckOrConstraintType::Check), map(tag_no_case("CONSTRAINT"), |_| { - CheckOrConstraintType::CONSTRAINT + CheckOrConstraintType::Constraint }), ))(i) } @@ -307,7 +293,10 @@ impl AlterTableOption { /// table_option \[\[,] table_option] ... pub fn alter_table_options(i: &str) -> IResult<&str, AlterTableOption, ParseSQLError<&str>> { map( - many1(terminated(TableOption::parse, opt(ws_sep_comma))), + many1(terminated( + TableOption::parse, + opt(CommonParser::ws_sep_comma), + )), |table_options| AlterTableOption::TableOptions { table_options }, )(i) } @@ -323,7 +312,9 @@ impl AlterTableOption { Self::add_check, Self::drop_check_or_constraint, Self::alter_check_or_constraint_enforced, - Self::algorithm_equal_default_or_instant_or_inplace_or_copy, + map(AlgorithmType::parse, |x| AlterTableOption::Algorithm { + algorithm: x, + }), Self::alter_column, Self::alter_index_visibility, Self::change_column, @@ -360,7 +351,7 @@ impl AlterTableOption { tag_no_case("ADD"), opt(preceded( tuple((multispace1, tag_no_case("CONSTRAINT"))), - opt(preceded(multispace1, sql_identifier)), + opt(preceded(multispace1, CommonParser::sql_identifier)), )), )), |(_, x)| x.and_then(|inner| inner.map(String::from)), @@ -429,7 +420,7 @@ impl AlterTableOption { // {INDEX | KEY} IndexOrKeyType::parse, // [index_name] - opt_index_name, + CommonParser::opt_index_name, // [index_type] IndexType::opt_index_type, // (key_part,...) @@ -459,7 +450,7 @@ impl AlterTableOption { // [INDEX | KEY] preceded(multispace1, opt(IndexOrKeyType::parse)), // [index_name] - opt_index_name, + CommonParser::opt_index_name, // (key_part,...) KeyPart::key_part_list, // [index_option] @@ -528,7 +519,7 @@ impl AlterTableOption { |(_, _, _, value)| value, ), // [index_name] - opt_index_name, + CommonParser::opt_index_name, // [index_type] IndexType::opt_index_type, // (key_part,...) @@ -570,7 +561,7 @@ impl AlterTableOption { tag_no_case("KEY"), )), // [index_name] - opt_index_name, + CommonParser::opt_index_name, // (col_name,...) map( tuple(( @@ -640,7 +631,7 @@ impl AlterTableOption { CheckOrConstraintType::parse, // symbol map( - tuple((multispace1, sql_identifier, multispace0)), + tuple((multispace1, CommonParser::sql_identifier, multispace0)), |(_, symbol, _)| String::from(symbol), ), )), @@ -662,7 +653,7 @@ impl AlterTableOption { CheckOrConstraintType::parse, // symbol map( - tuple((multispace1, sql_identifier, multispace1)), + tuple((multispace1, CommonParser::sql_identifier, multispace1)), |(_, symbol, _)| String::from(symbol), ), opt(tag_no_case("NOT ")), @@ -678,23 +669,6 @@ impl AlterTableOption { )(i) } - /// ALGORITHM \[=] {DEFAULT | INSTANT | INPLACE | COPY} - fn algorithm_equal_default_or_instant_or_inplace_or_copy( - i: &str, - ) -> IResult<&str, AlterTableOption, ParseSQLError<&str>> { - map( - tuple(( - tag_no_case("ALGORITHM "), - multispace0, - opt(tag("= ")), - multispace0, - AlgorithmType::parse, - multispace0, - )), - |(_, _, _, _, algorithm, _)| AlterTableOption::Algorithm { algorithm }, - )(i) - } - /// ALTER \[COLUMN] col_name { /// SET DEFAULT {literal | (expr)} /// | SET {VISIBLE | INVISIBLE} @@ -708,7 +682,7 @@ impl AlterTableOption { opt(tag_no_case("COLUMN ")), // col_name map( - tuple((multispace0, sql_identifier, multispace1)), + tuple((multispace0, CommonParser::sql_identifier, multispace1)), |(_, col_name, _)| String::from(col_name), ), AlertColumnOperation::parse, @@ -730,7 +704,7 @@ impl AlterTableOption { opt(tag_no_case("INDEX ")), // index_name map( - tuple((multispace0, sql_identifier, multispace1)), + tuple((multispace0, CommonParser::sql_identifier, multispace1)), |(_, col_name, _)| String::from(col_name), ), VisibleType::parse, @@ -752,7 +726,7 @@ impl AlterTableOption { opt(tag_no_case("COLUMN ")), multispace0, // old_col_name - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace1, ColumnSpecification::parse, multispace0, @@ -779,14 +753,14 @@ impl AlterTableOption { opt(tag("=")), multispace0, )), - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, opt(map( tuple(( multispace0, tag_no_case("COLLATE"), multispace1, - sql_identifier, + CommonParser::sql_identifier, )), |(_, _, _, collation_name)| String::from(collation_name), )), @@ -814,14 +788,14 @@ impl AlterTableOption { tuple(( // CONVERT TO CHARACTER SET prefix, - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, opt(map( tuple(( multispace0, tag_no_case("COLLATE"), multispace1, - sql_identifier, + CommonParser::sql_identifier, )), |(_, _, _, collation_name)| String::from(collation_name), )), @@ -880,7 +854,7 @@ impl AlterTableOption { opt(tag_no_case("COLUMN ")), // col_name map( - tuple((multispace0, sql_identifier, multispace0)), + tuple((multispace0, CommonParser::sql_identifier, multispace0)), |(_, col_name, _)| String::from(col_name), ), multispace0, @@ -898,7 +872,7 @@ impl AlterTableOption { IndexOrKeyType::parse, // [index_name] map( - tuple((multispace1, sql_identifier, multispace0)), + tuple((multispace1, CommonParser::sql_identifier, multispace0)), |(_, index_name, _)| String::from(index_name), ), multispace0, @@ -935,7 +909,7 @@ impl AlterTableOption { multispace1, tag_no_case("KEY"), multispace1, - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, )), |x| AlterTableOption::DropForeignKey { fk_symbol: x.6 }, @@ -982,7 +956,7 @@ impl AlterTableOption { tag_no_case("BY"), multispace1, many0(map( - terminated(Column::without_alias, opt(ws_sep_comma)), + terminated(Column::without_alias, opt(CommonParser::ws_sep_comma)), |e| e.name, )), multispace0, @@ -1000,12 +974,12 @@ impl AlterTableOption { opt(tag_no_case("COLUMN ")), multispace0, // old_col_name - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace1, tag_no_case("TO"), multispace1, // new_col_name - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, )), |(_, _, _, _, old_col_name, _, _, _, new_col_name, _)| AlterTableOption::RenameColumn { @@ -1024,13 +998,13 @@ impl AlterTableOption { IndexOrKeyType::parse, // old_index_name map( - tuple((multispace1, sql_identifier, multispace1)), + tuple((multispace1, CommonParser::sql_identifier, multispace1)), |(_, index_name, _)| String::from(index_name), ), tuple((multispace1, tag_no_case("TO"))), // new_index_name map( - tuple((multispace1, sql_identifier, multispace1)), + tuple((multispace1, CommonParser::sql_identifier, multispace1)), |(_, index_name, _)| String::from(index_name), ), multispace0, @@ -1054,7 +1028,7 @@ impl AlterTableOption { alt((tag_no_case("TO"), tag_no_case("AS"))), // new_tbl_name map( - tuple((multispace1, sql_identifier, multispace0)), + tuple((multispace1, CommonParser::sql_identifier, multispace0)), |(_, index_name, _)| String::from(index_name), ), multispace0, @@ -1167,8 +1141,8 @@ impl AlterPartitionOption { #[cfg(test)] mod tests { use base::column::{ColumnConstraint, ColumnPosition, ColumnSpecification}; + use base::index_option::IndexOption; use base::Literal; - use common::index_option::IndexOption; use dds::alter_table::{AlterTableOption, AlterTableStatement}; #[test] diff --git a/src/dds/create_index.rs b/src/dds/create_index.rs index abb1987..115ba1e 100644 --- a/src/dds/create_index.rs +++ b/src/dds/create_index.rs @@ -5,11 +5,12 @@ use nom::combinator::{map, opt}; use nom::sequence::{terminated, tuple}; use nom::IResult; +use base::algorithm_type::AlgorithmType; use base::error::ParseSQLError; +use base::index_option::IndexOption; +use base::lock_type::LockType; use base::table::Table; -use common::index_option::IndexOption; -use common::{sql_identifier, statement_terminator}; -use common::{AlgorithmType, KeyPart, LockType}; +use base::{CommonParser, KeyPart}; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct CreateIndexStatement { @@ -55,7 +56,9 @@ impl CreateIndexStatement { tuple((tag_no_case("CREATE"), multispace1)), opt(terminated(Index::parse, multispace1)), tuple((tag_no_case("INDEX"), multispace1)), - map(tuple((sql_identifier, multispace1)), |x| String::from(x.0)), + map(tuple((CommonParser::sql_identifier, multispace1)), |x| { + String::from(x.0) + }), opt(terminated(Index::parse, multispace1)), terminated(tag_no_case("ON"), multispace1), terminated(Table::without_alias, multispace1), // tbl_name @@ -64,7 +67,7 @@ impl CreateIndexStatement { multispace0, // [index_option] opt(terminated(AlgorithmType::parse, multispace0)), opt(terminated(LockType::parse, multispace0)), - statement_terminator, + CommonParser::statement_terminator, )), |( _, diff --git a/src/dds/create_table.rs b/src/dds/create_table.rs index bd2869c..190ca8a 100644 --- a/src/dds/create_table.rs +++ b/src/dds/create_table.rs @@ -9,17 +9,17 @@ use nom::multi::many1; use nom::sequence::{delimited, preceded, terminated, tuple}; use nom::IResult; +use base::check_constraint::CheckConstraintDefinition; use base::column::{Column, ColumnSpecification}; use base::error::ParseSQLError; +use base::fulltext_or_spatial_type::FulltextOrSpatialType; +use base::index_option::IndexOption; +use base::index_or_key_type::IndexOrKeyType; +use base::index_type::IndexType; use base::table::Table; -use common::index_option::IndexOption; -use common::table_option::TableOption; -use common::{ - opt_index_name, CheckConstraintDefinition, FulltextOrSpatialType, IndexOrKeyType, IndexType, - ReferenceDefinition, -}; -use common::{sql_identifier, statement_terminator, ws_sep_comma, KeyPart}; -use dms::select::SelectStatement; +use base::table_option::TableOption; +use base::{CommonParser, KeyPart, ReferenceDefinition}; +use dms::SelectStatement; /// **CreateTableStatement** /// [MySQL Doc](https://dev.mysql.com/doc/refman/8.0/en/create-table.html) @@ -148,7 +148,7 @@ impl CreateTableType { multispace0, // [partition_options] opt(CreatePartitionOption::parse), - statement_terminator, + CommonParser::statement_terminator, )), |(x)| { let temporary = x.0 .0; @@ -235,7 +235,7 @@ impl CreateTableType { )), |x| CreateTableType::LikeOldTable { table: x }, ), - statement_terminator, + CommonParser::statement_terminator, )), |(x, _, create_type, _)| { let table = x.2; @@ -254,7 +254,10 @@ impl CreateTableType { /// parse `[table_options]` part fn create_table_options(i: &str) -> IResult<&str, Vec, ParseSQLError<&str>> { map( - many1(terminated(TableOption::parse, opt(ws_sep_comma))), + many1(terminated( + TableOption::parse, + opt(CommonParser::ws_sep_comma), + )), |x| x, )(i) } @@ -393,7 +396,7 @@ impl CreateDefinition { multispace0, CreateDefinition::parse, multispace0, - opt(ws_sep_comma), + opt(CommonParser::ws_sep_comma), multispace0, )), |x| x.1, @@ -409,7 +412,7 @@ impl CreateDefinition { // {INDEX | KEY} IndexOrKeyType::parse, // [index_name] - opt_index_name, + CommonParser::opt_index_name, // [index_type] IndexType::opt_index_type, // (key_part,...) @@ -438,7 +441,7 @@ impl CreateDefinition { // [INDEX | KEY] preceded(multispace1, opt(IndexOrKeyType::parse)), // [index_name] - opt_index_name, + CommonParser::opt_index_name, // (key_part,...) KeyPart::key_part_list, // [index_option] @@ -496,7 +499,7 @@ impl CreateDefinition { )), |(_, _, _, value)| value, ), // UNIQUE [INDEX | KEY] - opt_index_name, // [index_name] + CommonParser::opt_index_name, // [index_name] IndexType::opt_index_type, // [index_type] KeyPart::key_part_list, // (key_part,...) IndexOption::opt_index_option, // [index_option] @@ -535,7 +538,7 @@ impl CreateDefinition { tag_no_case("KEY"), )), // [index_name] - opt_index_name, + CommonParser::opt_index_name, // (col_name,...) map( tuple(( @@ -607,7 +610,7 @@ impl CreateDefinition { map( opt(preceded( tag_no_case("CONSTRAINT"), - opt(preceded(multispace1, sql_identifier)), + opt(preceded(multispace1, CommonParser::sql_identifier)), )), |(x)| x.and_then(|inner| inner.map(String::from)), )(i) diff --git a/src/dds/drop_database.rs b/src/dds/drop_database.rs index 2cf70d3..70bcaa2 100644 --- a/src/dds/drop_database.rs +++ b/src/dds/drop_database.rs @@ -9,7 +9,7 @@ use nom::sequence::tuple; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP {DATABASE | SCHEMA} [IF EXISTS] db_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -24,10 +24,10 @@ impl DropDatabaseStatement { tag_no_case("DROP "), multispace0, alt((tag_no_case("DATABASE "), tag_no_case("SCHEMA "))), - parse_if_exists, + CommonParser::parse_if_exists, multispace0, - sql_identifier, - statement_terminator, + CommonParser::sql_identifier, + CommonParser::statement_terminator, )); let (remaining_input, (_, _, _, opt_if_exists, _, database, _)) = parser(i)?; @@ -55,29 +55,6 @@ impl fmt::Display for DropDatabaseStatement { } } -pub fn drop_database(i: &str) -> IResult<&str, DropDatabaseStatement, ParseSQLError<&str>> { - let mut parser = tuple(( - tag_no_case("DROP "), - multispace0, - alt((tag_no_case("DATABASE "), tag_no_case("SCHEMA "))), - parse_if_exists, - multispace0, - sql_identifier, - statement_terminator, - )); - let (remaining_input, (_, _, _, opt_if_exists, _, database, _)) = parser(i)?; - - let name = String::from(database); - - Ok(( - remaining_input, - DropDatabaseStatement { - name, - if_exists: opt_if_exists.is_some(), - }, - )) -} - #[cfg(test)] mod tests { use dds::drop_database::DropDatabaseStatement; diff --git a/src/dds/drop_event.rs b/src/dds/drop_event.rs index 3894d88..2fb8008 100644 --- a/src/dds/drop_event.rs +++ b/src/dds/drop_event.rs @@ -8,7 +8,7 @@ use nom::sequence::{terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP EVENT [IF EXISTS] event_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -24,10 +24,10 @@ impl DropEventStatement { tuple(( terminated(tag_no_case("DROP"), multispace1), terminated(tag_no_case("EVENT"), multispace1), - parse_if_exists, - map(sql_identifier, String::from), + CommonParser::parse_if_exists, + map(CommonParser::sql_identifier, String::from), multispace0, - statement_terminator, + CommonParser::statement_terminator, )), |x| DropEventStatement { if_exists: x.2.is_some(), diff --git a/src/dds/drop_function.rs b/src/dds/drop_function.rs index 197e384..d46ea44 100644 --- a/src/dds/drop_function.rs +++ b/src/dds/drop_function.rs @@ -8,7 +8,7 @@ use nom::sequence::{terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP FUNCTION [IF EXISTS] sp_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -24,10 +24,10 @@ impl DropFunctionStatement { tuple(( terminated(tag_no_case("DROP"), multispace1), terminated(tag_no_case("FUNCTION"), multispace1), - parse_if_exists, - map(sql_identifier, String::from), + CommonParser::parse_if_exists, + map(CommonParser::sql_identifier, String::from), multispace0, - statement_terminator, + CommonParser::statement_terminator, )), |x| DropFunctionStatement { if_exists: x.2.is_some(), @@ -48,24 +48,6 @@ impl fmt::Display for DropFunctionStatement { } } -/// DROP FUNCTION [IF EXISTS] sp_name -pub fn drop_function(i: &str) -> IResult<&str, DropFunctionStatement, ParseSQLError<&str>> { - map( - tuple(( - terminated(tag_no_case("DROP"), multispace1), - terminated(tag_no_case("FUNCTION"), multispace1), - parse_if_exists, - map(sql_identifier, String::from), - multispace0, - statement_terminator, - )), - |x| DropFunctionStatement { - if_exists: x.2.is_some(), - sp_name: x.3, - }, - )(i) -} - #[cfg(test)] mod tests { use dds::drop_function::DropFunctionStatement; diff --git a/src/dds/drop_index.rs b/src/dds/drop_index.rs index 1aa6a1d..4e7e38d 100644 --- a/src/dds/drop_index.rs +++ b/src/dds/drop_index.rs @@ -4,10 +4,11 @@ use nom::combinator::{map, opt}; use nom::sequence::tuple; use nom::IResult; +use base::algorithm_type::AlgorithmType; use base::error::ParseSQLError; +use base::lock_type::LockType; use base::table::Table; -use common::{sql_identifier, statement_terminator}; -use common::{AlgorithmType, LockType}; +use base::CommonParser; #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct DropIndexStatement { @@ -26,7 +27,12 @@ impl DropIndexStatement { tuple((tag_no_case("DROP"), multispace1)), tuple((tag_no_case("INDEX"), multispace1)), map( - tuple((sql_identifier, multispace1, tag_no_case("ON"), multispace1)), + tuple(( + CommonParser::sql_identifier, + multispace1, + tag_no_case("ON"), + multispace1, + )), |x| String::from(x.0), ), Table::without_alias, // tbl_name @@ -35,7 +41,7 @@ impl DropIndexStatement { multispace0, opt(LockType::parse), // lock_option multispace0, - statement_terminator, + CommonParser::statement_terminator, )), |(_, _, index_name, table, _, algorithm_option, _, lock_option, _, _)| { DropIndexStatement { @@ -51,7 +57,7 @@ impl DropIndexStatement { #[cfg(test)] mod tests { - use common::LockType; + use base::lock_type::LockType; use dds::drop_index::DropIndexStatement; #[test] diff --git a/src/dds/drop_logfile_group.rs b/src/dds/drop_logfile_group.rs index d57a4cd..ee4d78c 100644 --- a/src/dds/drop_logfile_group.rs +++ b/src/dds/drop_logfile_group.rs @@ -10,7 +10,7 @@ use nom::sequence::tuple; use nom::IResult; use base::error::ParseSQLError; -use common::{sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP LOGFILE GROUP logfile_group /// ENGINE [=] engine_name @@ -31,7 +31,7 @@ impl DropLogfileGroupStatement { multispace0, tag_no_case("GROUP"), multispace0, - map(sql_identifier, String::from), + map(CommonParser::sql_identifier, String::from), multispace0, map( tuple(( @@ -39,13 +39,13 @@ impl DropLogfileGroupStatement { multispace1, opt(tag("=")), multispace0, - sql_identifier, + CommonParser::sql_identifier, multispace0, )), |(_, _, _, _, engine, _)| String::from(engine), ), multispace0, - statement_terminator, + CommonParser::statement_terminator, )); let (remaining_input, (_, _, _, _, _, _, logfile_group, _, engine_name, _, _)) = parser(i)?; diff --git a/src/dds/drop_procedure.rs b/src/dds/drop_procedure.rs index d3aa4f7..12d3cc6 100644 --- a/src/dds/drop_procedure.rs +++ b/src/dds/drop_procedure.rs @@ -8,7 +8,7 @@ use nom::sequence::{terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP PROCEDURE [IF EXISTS] sp_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -24,10 +24,10 @@ impl DropProcedureStatement { tuple(( terminated(tag_no_case("DROP"), multispace1), terminated(tag_no_case("PROCEDURE"), multispace1), - parse_if_exists, - map(sql_identifier, String::from), + CommonParser::parse_if_exists, + map(CommonParser::sql_identifier, String::from), multispace0, - statement_terminator, + CommonParser::statement_terminator, )), |x| DropProcedureStatement { if_exists: x.2.is_some(), diff --git a/src/dds/drop_server.rs b/src/dds/drop_server.rs index 0975d79..c407c4c 100644 --- a/src/dds/drop_server.rs +++ b/src/dds/drop_server.rs @@ -8,7 +8,7 @@ use nom::sequence::{terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP SERVER [ IF EXISTS ] server_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -24,10 +24,10 @@ impl DropServerStatement { tuple(( terminated(tag_no_case("DROP"), multispace1), terminated(tag_no_case("SERVER"), multispace1), - parse_if_exists, - map(sql_identifier, String::from), + CommonParser::parse_if_exists, + map(CommonParser::sql_identifier, String::from), multispace0, - statement_terminator, + CommonParser::statement_terminator, )), |x| DropServerStatement { if_exists: x.2.is_some(), diff --git a/src/dds/drop_spatial_reference_system.rs b/src/dds/drop_spatial_reference_system.rs index b133c4c..92a3ab3 100644 --- a/src/dds/drop_spatial_reference_system.rs +++ b/src/dds/drop_spatial_reference_system.rs @@ -9,7 +9,7 @@ use nom::sequence::{terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, statement_terminator}; +use base::CommonParser; /// DROP SPATIAL REFERENCE SYSTEM /// [IF EXISTS] @@ -37,10 +37,10 @@ impl DropSpatialReferenceSystemStatement { terminated(tag_no_case("SPATIAL"), multispace1), terminated(tag_no_case("REFERENCE"), multispace1), terminated(tag_no_case("SYSTEM"), multispace1), - parse_if_exists, + CommonParser::parse_if_exists, complete::u32, multispace0, - statement_terminator, + CommonParser::statement_terminator, )), |x| DropSpatialReferenceSystemStatement { if_exists: x.4.is_some(), diff --git a/src/dds/drop_table.rs b/src/dds/drop_table.rs index ac7a82d..71ef5d6 100644 --- a/src/dds/drop_table.rs +++ b/src/dds/drop_table.rs @@ -12,7 +12,7 @@ use nom::IResult; use base::error::ParseSQLError; use base::table::Table; -use common::{parse_if_exists, statement_terminator, ws_sep_comma}; +use base::CommonParser; /// /// DROP \[TEMPORARY] TABLE \[IF EXISTS] @@ -42,12 +42,15 @@ impl DropTableStatement { )), multispace0, tag_no_case("TABLE "), - parse_if_exists, + CommonParser::parse_if_exists, multispace0, - many0(terminated(Table::without_alias, opt(ws_sep_comma))), + many0(terminated( + Table::without_alias, + opt(CommonParser::ws_sep_comma), + )), opt(delimited(multispace1, tag_no_case("RESTRICT"), multispace0)), opt(delimited(multispace1, tag_no_case("CASCADE"), multispace0)), - statement_terminator, + CommonParser::statement_terminator, )); let ( remaining_input, diff --git a/src/dds/drop_tablespace.rs b/src/dds/drop_tablespace.rs index 564914b..450107d 100644 --- a/src/dds/drop_tablespace.rs +++ b/src/dds/drop_tablespace.rs @@ -10,7 +10,7 @@ use nom::sequence::tuple; use nom::IResult; use base::error::ParseSQLError; -use common::{sql_identifier, statement_terminator}; +use base::CommonParser; /// DROP \[UNDO] TABLESPACE tablespace_name /// \[ENGINE \[=] engine_name] @@ -32,7 +32,7 @@ impl DropTablespaceStatement { multispace0, tag_no_case("TABLESPACE "), multispace0, - map(sql_identifier, |tablespace_name| { + map(CommonParser::sql_identifier, |tablespace_name| { String::from(tablespace_name) }), multispace0, @@ -42,13 +42,13 @@ impl DropTablespaceStatement { multispace1, opt(tag("=")), multispace0, - sql_identifier, + CommonParser::sql_identifier, multispace0, )), |(_, _, _, _, engine, _)| String::from(engine), )), multispace0, - statement_terminator, + CommonParser::statement_terminator, )); let (remaining_input, (_, _, opt_undo, _, _, _, tablespace_name, _, engine_name, _, _)) = parser(i)?; diff --git a/src/dds/drop_trigger.rs b/src/dds/drop_trigger.rs index 652696d..171d0b4 100644 --- a/src/dds/drop_trigger.rs +++ b/src/dds/drop_trigger.rs @@ -9,7 +9,7 @@ use nom::IResult; use base::error::ParseSQLError; use base::trigger::Trigger; -use common::{parse_if_exists, statement_terminator}; +use base::CommonParser; /// DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -25,11 +25,11 @@ impl DropTriggerStatement { tag_no_case("DROP "), multispace0, tag_no_case("TRIGGER "), - parse_if_exists, + CommonParser::parse_if_exists, multispace0, Trigger::parse, multispace0, - statement_terminator, + CommonParser::statement_terminator, )); let (remaining_input, (_, _, _, opt_if_exists, _, trigger_name, _, _)) = parser(i)?; diff --git a/src/dds/drop_view.rs b/src/dds/drop_view.rs index bddfb20..2a3163c 100644 --- a/src/dds/drop_view.rs +++ b/src/dds/drop_view.rs @@ -11,7 +11,7 @@ use nom::sequence::{delimited, terminated, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{parse_if_exists, sql_identifier, statement_terminator, ws_sep_comma}; +use base::CommonParser; /// DROP VIEW [IF EXISTS] /// view_name [, view_name] ... @@ -34,14 +34,18 @@ impl DropViewStatement { tag_no_case("DROP "), multispace0, tag_no_case("VIEW "), - parse_if_exists, + CommonParser::parse_if_exists, multispace0, - map(many0(terminated(sql_identifier, opt(ws_sep_comma))), |x| { - x.iter().map(|v| String::from(*v)).collect::>() - }), + map( + many0(terminated( + CommonParser::sql_identifier, + opt(CommonParser::ws_sep_comma), + )), + |x| x.iter().map(|v| String::from(*v)).collect::>(), + ), opt(delimited(multispace1, tag_no_case("RESTRICT"), multispace0)), opt(delimited(multispace1, tag_no_case("CASCADE"), multispace0)), - statement_terminator, + CommonParser::statement_terminator, )); let ( remaining_input, diff --git a/src/dds/mod.rs b/src/dds/mod.rs index e7a2514..1692839 100644 --- a/src/dds/mod.rs +++ b/src/dds/mod.rs @@ -1,24 +1,43 @@ -pub mod alter_database; -pub mod alter_table; -pub mod create_index; -pub mod create_table; -pub mod drop_database; -pub mod drop_index; -pub mod drop_table; -pub mod rename_table; -pub mod truncate_table; +pub use dds::alter_database::AlterDatabaseStatement; +pub use dds::alter_table::AlterTableStatement; +pub use dds::create_index::CreateIndexStatement; +pub use dds::create_table::CreateTableStatement; +pub use dds::drop_database::DropDatabaseStatement; +pub use dds::drop_event::DropEventStatement; +pub use dds::drop_function::DropFunctionStatement; +pub use dds::drop_index::DropIndexStatement; +pub use dds::drop_logfile_group::DropLogfileGroupStatement; +pub use dds::drop_procedure::DropProcedureStatement; +pub use dds::drop_server::DropServerStatement; +pub use dds::drop_spatial_reference_system::DropSpatialReferenceSystemStatement; +pub use dds::drop_table::DropTableStatement; +pub use dds::drop_tablespace::DropTablespaceStatement; +pub use dds::drop_trigger::DropTriggerStatement; +pub use dds::drop_view::DropViewStatement; +pub use dds::rename_table::RenameTableStatement; +pub use dds::truncate_table::TruncateTableStatement; -pub mod drop_view; +mod alter_database; +mod alter_table; +mod create_index; +mod create_table; +mod drop_database; +mod drop_index; +mod drop_table; +mod rename_table; +mod truncate_table; -pub mod drop_trigger; +mod drop_view; -pub mod drop_server; -pub mod drop_spatial_reference_system; -pub mod drop_tablespace; +mod drop_trigger; -pub mod drop_function; -pub mod drop_procedure; +mod drop_server; +mod drop_spatial_reference_system; +mod drop_tablespace; -pub mod drop_logfile_group; +mod drop_function; +mod drop_procedure; -pub mod drop_event; +mod drop_logfile_group; + +mod drop_event; diff --git a/src/dds/rename_table.rs b/src/dds/rename_table.rs index ea8ec44..51a8b67 100644 --- a/src/dds/rename_table.rs +++ b/src/dds/rename_table.rs @@ -11,7 +11,7 @@ use nom::IResult; use base::error::ParseSQLError; use base::table::Table; -use common::{statement_terminator, ws_sep_comma}; +use base::CommonParser; /// RENAME TABLE // tbl_name TO new_tbl_name @@ -33,9 +33,9 @@ impl RenameTableStatement { multispace0, many0(terminated( Table::schema_table_reference_to_schema_table_reference, - opt(ws_sep_comma), + opt(CommonParser::ws_sep_comma), )), - statement_terminator, + CommonParser::statement_terminator, )); let (remaining_input, (_, _, _, _, table_pairs, _)) = parser(i)?; diff --git a/src/dds/truncate_table.rs b/src/dds/truncate_table.rs index d1f2230..7a477e9 100644 --- a/src/dds/truncate_table.rs +++ b/src/dds/truncate_table.rs @@ -10,7 +10,7 @@ use nom::IResult; use base::error::ParseSQLError; use base::table::Table; -use common::statement_terminator; +use base::CommonParser; /// TRUNCATE \[TABLE] tbl_name #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -27,7 +27,7 @@ impl TruncateTableStatement { opt(tag_no_case("TABLE ")), multispace0, Table::without_alias, - statement_terminator, + CommonParser::statement_terminator, )); let (remaining_input, (_, _, _, _, table, _)) = parser(i)?; diff --git a/src/dms/compound_select.rs b/src/dms/compound_select.rs index 94ce277..04d799c 100644 --- a/src/dms/compound_select.rs +++ b/src/dms/compound_select.rs @@ -10,7 +10,7 @@ use nom::sequence::{delimited, preceded, tuple}; use nom::IResult; use base::error::ParseSQLError; -use common::{opt_delimited, statement_terminator, OrderClause}; +use base::{CommonParser, OrderClause}; use dms::select::{LimitClause, SelectStatement}; // TODO 用于 create 语句的 select @@ -25,12 +25,12 @@ impl CompoundSelectStatement { // Parse compound selection pub fn parse(i: &str) -> IResult<&str, CompoundSelectStatement, ParseSQLError<&str>> { let (remaining_input, (first_select, other_selects, _, order, limit, _)) = tuple(( - opt_delimited(tag("("), SelectStatement::nested_selection, tag(")")), + CommonParser::opt_delimited(tag("("), SelectStatement::nested_selection, tag(")")), many1(Self::other_selects), multispace0, opt(OrderClause::parse), opt(LimitClause::parse), - statement_terminator, + CommonParser::statement_terminator, ))(i)?; let mut selects = vec![(None, first_select)]; @@ -53,7 +53,7 @@ impl CompoundSelectStatement { multispace0, CompoundSelectOperator::parse, multispace1, - opt_delimited( + CommonParser::opt_delimited( tag("("), delimited(multispace0, SelectStatement::nested_selection, multispace0), tag(")"), diff --git a/src/dms/delete.rs b/src/dms/delete.rs index 52c46a5..949896e 100644 --- a/src/dms/delete.rs +++ b/src/dms/delete.rs @@ -6,11 +6,11 @@ use nom::combinator::opt; use nom::sequence::{delimited, tuple}; use nom::IResult; +use base::condition::ConditionExpression; use base::error::ParseSQLError; +use base::keywords::escape_if_keyword; use base::table::Table; -use common::condition::ConditionExpression; -use common::keywords::escape_if_keyword; -use common::statement_terminator; +use base::CommonParser; // FIXME TODO /// DELETE \[LOW_PRIORITY] \[QUICK] \[IGNORE] FROM tbl_name \[\[AS] tbl_alias] @@ -31,7 +31,7 @@ impl DeleteStatement { delimited(multispace1, tag_no_case("FROM"), multispace1), Table::schema_table_reference, opt(ConditionExpression::parse), - statement_terminator, + CommonParser::statement_terminator, ))(i)?; Ok(( @@ -59,11 +59,11 @@ impl fmt::Display for DeleteStatement { #[cfg(test)] mod tests { use base::column::Column; + use base::condition::ConditionBase::Field; + use base::condition::ConditionExpression::{Base, ComparisonOp}; + use base::condition::{ConditionBase, ConditionTree}; use base::Literal; use base::Operator; - use common::condition::ConditionBase::*; - use common::condition::ConditionExpression::*; - use common::condition::ConditionTree; use super::*; @@ -101,7 +101,7 @@ mod tests { let expected_left = Base(Field(Column::from("id"))); let expected_where_cond = Some(ComparisonOp(ConditionTree { left: Box::new(expected_left), - right: Box::new(Base(Literal(Literal::Integer(1)))), + right: Box::new(Base(ConditionBase::Literal(Literal::Integer(1)))), operator: Operator::Equal, })); assert_eq!( diff --git a/src/dms/insert.rs b/src/dms/insert.rs index 13cb124..8d0447e 100644 --- a/src/dms/insert.rs +++ b/src/dms/insert.rs @@ -10,10 +10,9 @@ use nom::IResult; use base::column::Column; use base::error::ParseSQLError; +use base::keywords::escape_if_keyword; use base::table::Table; -use base::{FieldValueExpression, Literal}; -use common::keywords::escape_if_keyword; -use common::{statement_terminator, ws_sep_comma}; +use base::{CommonParser, FieldValueExpression, Literal}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct InsertStatement { @@ -45,7 +44,7 @@ impl InsertStatement { many1(Self::data), opt(Self::on_duplicate), multispace0, - statement_terminator, + CommonParser::statement_terminator, ))(i)?; assert!(table.alias.is_none()); let ignore = ignore_res.is_some(); @@ -74,7 +73,7 @@ impl InsertStatement { delimited( tag("("), Literal::value_list, - preceded(tag(")"), opt(ws_sep_comma)), + preceded(tag(")"), opt(CommonParser::ws_sep_comma)), )(i) } @@ -133,8 +132,8 @@ impl fmt::Display for InsertStatement { #[cfg(test)] mod tests { + use base::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; use base::{FieldValueExpression, ItemPlaceholder}; - use common::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; use {ParseConfig, Parser}; use super::*; diff --git a/src/dms/mod.rs b/src/dms/mod.rs index f23149d..c4fbb8b 100644 --- a/src/dms/mod.rs +++ b/src/dms/mod.rs @@ -1,5 +1,11 @@ -pub mod compound_select; -pub mod delete; -pub mod insert; -pub mod select; -pub mod update; +pub use dms::compound_select::CompoundSelectStatement; +pub use dms::delete::DeleteStatement; +pub use dms::insert::InsertStatement; +pub use dms::select::{BetweenAndClause, JoinClause, SelectStatement}; +pub use dms::update::UpdateStatement; + +mod compound_select; +mod delete; +mod insert; +mod select; +mod update; diff --git a/src/dms/select.rs b/src/dms/select.rs index 3d95180..8afce01 100644 --- a/src/dms/select.rs +++ b/src/dms/select.rs @@ -9,13 +9,12 @@ use nom::sequence::{delimited, terminated, tuple}; use nom::IResult; use base::column::Column; +use base::condition::ConditionExpression; use base::error::ParseSQLError; use base::table::Table; -use base::FieldDefinitionExpression; -use common::condition::ConditionExpression; -use common::{ - sql_identifier, statement_terminator, unsigned_number, JoinConstraint, JoinOperator, - JoinRightSide, OrderClause, +use base::{ + CommonParser, FieldDefinitionExpression, JoinConstraint, JoinOperator, JoinRightSide, + OrderClause, }; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] @@ -33,7 +32,7 @@ pub struct SelectStatement { impl SelectStatement { // Parse rule for a SQL selection query. pub fn parse(i: &str) -> IResult<&str, SelectStatement, ParseSQLError<&str>> { - terminated(Self::nested_selection, statement_terminator)(i) + terminated(Self::nested_selection, CommonParser::statement_terminator)(i) } pub fn nested_selection(i: &str) -> IResult<&str, SelectStatement, ParseSQLError<&str>> { @@ -208,7 +207,7 @@ impl BetweenAndClause { pub fn parse(i: &str) -> IResult<&str, BetweenAndClause, ParseSQLError<&str>> { map( tuple(( - sql_identifier, + CommonParser::sql_identifier, multispace1, tag_no_case("BETWEEN"), multispace1, @@ -246,7 +245,7 @@ impl LimitClause { multispace0, tag_no_case("LIMIT"), multispace1, - unsigned_number, + CommonParser::unsigned_number, opt(Self::offset), ))(i)?; let offset = opt_offset.unwrap_or(0); @@ -259,7 +258,7 @@ impl LimitClause { multispace0, tag_no_case("OFFSET"), multispace1, - unsigned_number, + CommonParser::unsigned_number, ))(i)?; Ok((remaining_input, val)) @@ -278,17 +277,17 @@ impl fmt::Display for LimitClause { #[cfg(test)] mod tests { + use base::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; use base::column::{Column, FunctionArgument, FunctionArguments, FunctionExpression}; + use base::condition::ConditionBase::LiteralList; + use base::condition::ConditionExpression::{Base, ComparisonOp, LogicalOp}; + use base::condition::{ConditionBase, ConditionExpression, ConditionTree}; use base::table::Table; - use base::Literal; - use base::{FieldValueExpression, ItemPlaceholder, Operator}; - use common::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; - use common::case::{CaseWhenExpression, ColumnOrLiteral}; - use common::condition::ConditionBase; - use common::condition::ConditionBase::LiteralList; - use common::condition::ConditionExpression::{Base, ComparisonOp, LogicalOp}; - use common::condition::{ConditionExpression, ConditionTree}; - use common::{JoinConstraint, JoinOperator, JoinRightSide, OrderClause, OrderType}; + use base::{ + CaseWhenExpression, ColumnOrLiteral, FieldValueExpression, ItemPlaceholder, JoinConstraint, + JoinOperator, JoinRightSide, Operator, OrderClause, + }; + use base::{Literal, OrderType}; use super::*; diff --git a/src/dms/update.rs b/src/dms/update.rs index ee25c09..1e4a50f 100644 --- a/src/dms/update.rs +++ b/src/dms/update.rs @@ -7,12 +7,11 @@ use nom::sequence::tuple; 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::FieldValueExpression; -use common::condition::ConditionExpression; -use common::keywords::escape_if_keyword; -use common::statement_terminator; +use base::{CommonParser, FieldValueExpression}; #[derive(Clone, Debug, Default, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct UpdateStatement { @@ -33,7 +32,7 @@ impl UpdateStatement { FieldValueExpression::assignment_expr_list, multispace0, opt(ConditionExpression::parse), - statement_terminator, + CommonParser::statement_terminator, ))(i)?; Ok(( remaining_input, @@ -69,11 +68,10 @@ impl fmt::Display for UpdateStatement { #[cfg(test)] mod tests { + use base::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; + use base::condition::ConditionExpression::{Base, ComparisonOp}; + use base::condition::{ConditionBase, ConditionTree}; use base::{FieldValueExpression, ItemPlaceholder, Literal, LiteralExpression, Operator, Real}; - use common::arithmetic::{ArithmeticBase, ArithmeticExpression, ArithmeticOperator}; - use common::condition::ConditionBase; - use common::condition::ConditionExpression::{Base, ComparisonOp}; - use common::condition::ConditionTree; use super::*; diff --git a/src/lib.rs b/src/lib.rs index fe17cf8..7d31e1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,47 @@ +//! # SQL Parser for MySQL with Rust +//! +//! This crate provides parser that can parse SQL into an Abstract Syntax Tree. +//! +//! # Example parsing SQL +//! +//! ``` +//! 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); +//! ``` +//! +//! # Creating SQL text from AST +//! +//! This crate allows users to recover the original SQL text (with comments +//! removed, normalized whitespace and identifier capitalization), which is +//! useful for tools that analyze and manipulate SQL. +//! +//! ``` +//! 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); +//! ``` +//! +//! [sqlparser-mysql crates.io page]: https://crates.io/crates/sqlparser-mysql + #![allow(unused)] extern crate core; extern crate nom; @@ -10,10 +54,8 @@ extern crate serde_derive; pub use self::parser::*; -pub mod parser; - pub mod base; -pub mod common; -mod das; +pub mod das; pub mod dds; -mod dms; +pub mod dms; +pub mod parser; diff --git a/src/parser.rs b/src/parser.rs index a93a8a4..b96eb3d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2,35 +2,20 @@ use std::fmt; use std::io::BufRead; use std::str; +use das::SetStatement; +use dds::{ + AlterDatabaseStatement, AlterTableStatement, CreateIndexStatement, CreateTableStatement, + DropDatabaseStatement, DropEventStatement, DropFunctionStatement, DropIndexStatement, + DropLogfileGroupStatement, DropProcedureStatement, DropServerStatement, + DropSpatialReferenceSystemStatement, DropTableStatement, DropTablespaceStatement, + DropTriggerStatement, DropViewStatement, RenameTableStatement, TruncateTableStatement, +}; +use dms::{ + CompoundSelectStatement, DeleteStatement, InsertStatement, SelectStatement, UpdateStatement, +}; use nom::branch::alt; use nom::combinator::map; -use nom::{IResult, Offset}; - -use base::error::ParseSQLError; -use das::set_statement::SetStatement; -use dds::alter_database::AlterDatabaseStatement; -use dds::alter_table::AlterTableStatement; -use dds::create_index::CreateIndexStatement; -use dds::create_table::CreateTableStatement; -use dds::drop_database::DropDatabaseStatement; -use dds::drop_event::DropEventStatement; -use dds::drop_function::DropFunctionStatement; -use dds::drop_index::DropIndexStatement; -use dds::drop_logfile_group::DropLogfileGroupStatement; -use dds::drop_procedure::DropProcedureStatement; -use dds::drop_server::DropServerStatement; -use dds::drop_spatial_reference_system::DropSpatialReferenceSystemStatement; -use dds::drop_table::DropTableStatement; -use dds::drop_tablespace::DropTablespaceStatement; -use dds::drop_trigger::DropTriggerStatement; -use dds::drop_view::DropViewStatement; -use dds::rename_table::RenameTableStatement; -use dds::truncate_table::TruncateTableStatement; -use dms::compound_select::CompoundSelectStatement; -use dms::delete::DeleteStatement; -use dms::insert::InsertStatement; -use dms::select::SelectStatement; -use dms::update::UpdateStatement; +use nom::Offset; pub struct Parser; @@ -156,7 +141,6 @@ mod tests { use std::hash::{Hash, Hasher}; use base::table::Table; - use dms::insert::InsertStatement; use super::*;