sqlparser_mysql/base/
column.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Display};
3use std::str;
4use std::str::FromStr;
5
6use nom::branch::alt;
7use nom::bytes::complete::{tag, tag_no_case, take_until};
8use nom::character::complete::{alphanumeric1, digit1, multispace0, multispace1};
9use nom::combinator::{map, opt};
10use nom::multi::{many0, separated_list0};
11use nom::sequence::{delimited, pair, preceded, terminated, tuple};
12use nom::IResult;
13
14use base::error::ParseSQLErrorKind;
15use base::{CaseWhenExpression, CommonParser, DataType, DisplayUtil, Literal, ParseSQLError, Real};
16
17#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
18pub enum FunctionExpression {
19    Avg(FunctionArgument, bool),
20    Count(FunctionArgument, bool),
21    CountStar,
22    Sum(FunctionArgument, bool),
23    Max(FunctionArgument),
24    Min(FunctionArgument),
25    GroupConcat(FunctionArgument, String),
26    Generic(String, FunctionArguments),
27}
28
29impl FunctionExpression {
30    pub fn parse(i: &str) -> IResult<&str, FunctionExpression, ParseSQLError<&str>> {
31        let delim_group_concat_fx = delimited(tag("("), Self::group_concat_fx, tag(")"));
32        alt((
33            map(tag_no_case("COUNT(*)"), |_| FunctionExpression::CountStar),
34            map(
35                preceded(tag_no_case("COUNT"), FunctionArgument::delim_fx_args),
36                |args| FunctionExpression::Count(args.0.clone(), args.1),
37            ),
38            map(
39                preceded(tag_no_case("SUM"), FunctionArgument::delim_fx_args),
40                |args| FunctionExpression::Sum(args.0.clone(), args.1),
41            ),
42            map(
43                preceded(tag_no_case("AVG"), FunctionArgument::delim_fx_args),
44                |args| FunctionExpression::Avg(args.0.clone(), args.1),
45            ),
46            map(
47                preceded(tag_no_case("MAX"), FunctionArgument::delim_fx_args),
48                |args| FunctionExpression::Max(args.0.clone()),
49            ),
50            map(
51                preceded(tag_no_case("MIN"), FunctionArgument::delim_fx_args),
52                |args| FunctionExpression::Min(args.0.clone()),
53            ),
54            map(
55                preceded(tag_no_case("GROUP_CONCAT"), delim_group_concat_fx),
56                |spec| {
57                    let (ref col, ref sep) = spec;
58                    let sep = match *sep {
59                        // default separator is a comma, see MySQL manual ยง5.7
60                        None => String::from(","),
61                        Some(s) => String::from(s),
62                    };
63                    FunctionExpression::GroupConcat(FunctionArgument::Column(col.clone()), sep)
64                },
65            ),
66            map(
67                tuple((
68                    CommonParser::sql_identifier,
69                    multispace0,
70                    tag("("),
71                    separated_list0(
72                        tag(","),
73                        delimited(multispace0, FunctionArgument::parse, multispace0),
74                    ),
75                    tag(")"),
76                )),
77                |tuple| {
78                    let (name, _, _, arguments, _) = tuple;
79                    FunctionExpression::Generic(
80                        name.to_string(),
81                        FunctionArguments::from(arguments),
82                    )
83                },
84            ),
85        ))(i)
86    }
87
88    fn group_concat_fx_helper(i: &str) -> IResult<&str, &str, ParseSQLError<&str>> {
89        let ws_sep = preceded(multispace0, tag_no_case("separator"));
90        let (remaining_input, sep) = delimited(
91            ws_sep,
92            delimited(tag("'"), opt(alphanumeric1), tag("'")),
93            multispace0,
94        )(i)?;
95
96        Ok((remaining_input, sep.unwrap_or("")))
97    }
98
99    fn group_concat_fx(i: &str) -> IResult<&str, (Column, Option<&str>), ParseSQLError<&str>> {
100        pair(Column::without_alias, opt(Self::group_concat_fx_helper))(i)
101    }
102}
103
104impl Display for FunctionExpression {
105    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106        match *self {
107            FunctionExpression::Avg(ref col, d) if d => write!(f, "avg(distinct {})", col),
108            FunctionExpression::Count(ref col, d) if d => write!(f, "count(distinct {})", col),
109            FunctionExpression::Sum(ref col, d) if d => write!(f, "sum(distinct {})", col),
110            FunctionExpression::Avg(ref col, _) => write!(f, "avg({})", col),
111            FunctionExpression::Count(ref col, _) => write!(f, "count({})", col),
112            FunctionExpression::CountStar => write!(f, "count(*)"),
113            FunctionExpression::Sum(ref col, _) => write!(f, "sum({})", col),
114            FunctionExpression::Max(ref col) => write!(f, "max({})", col),
115            FunctionExpression::Min(ref col) => write!(f, "min({})", col),
116            FunctionExpression::GroupConcat(ref col, ref s) => {
117                write!(f, "group_concat({}, {})", col, s)
118            }
119            FunctionExpression::Generic(ref name, ref args) => write!(f, "{}({})", name, args),
120        }
121    }
122}
123
124#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
125pub struct FunctionArguments {
126    pub arguments: Vec<FunctionArgument>,
127}
128
129impl Display for FunctionArguments {
130    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
131        write!(
132            f,
133            "{}",
134            self.arguments
135                .iter()
136                .map(|arg| format!("{}", arg))
137                .collect::<Vec<String>>()
138                .join(",")
139        )?;
140        Ok(())
141    }
142}
143
144impl From<Vec<FunctionArgument>> for FunctionArguments {
145    fn from(args: Vec<FunctionArgument>) -> FunctionArguments {
146        FunctionArguments { arguments: args }
147    }
148}
149
150#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
151pub enum FunctionArgument {
152    Column(Column),
153    Conditional(CaseWhenExpression),
154}
155
156impl FunctionArgument {
157    // Parses the argument for an aggregation function
158    pub fn parse(i: &str) -> IResult<&str, FunctionArgument, ParseSQLError<&str>> {
159        alt((
160            map(CaseWhenExpression::parse, FunctionArgument::Conditional),
161            map(Column::without_alias, FunctionArgument::Column),
162        ))(i)
163    }
164
165    // Parses the arguments for an aggregation function, and also returns whether the distinct flag is
166    // present.
167    fn function_arguments(i: &str) -> IResult<&str, (FunctionArgument, bool), ParseSQLError<&str>> {
168        let distinct_parser = opt(tuple((tag_no_case("distinct"), multispace1)));
169        let (remaining_input, (distinct, args)) =
170            tuple((distinct_parser, FunctionArgument::parse))(i)?;
171        Ok((remaining_input, (args, distinct.is_some())))
172    }
173
174    pub fn delim_fx_args(i: &str) -> IResult<&str, (FunctionArgument, bool), ParseSQLError<&str>> {
175        delimited(tag("("), Self::function_arguments, tag(")"))(i)
176    }
177}
178
179impl Display for FunctionArgument {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        match *self {
182            FunctionArgument::Column(ref col) => write!(f, "{}", col)?,
183            FunctionArgument::Conditional(ref e) => {
184                write!(f, "{}", e)?;
185            }
186        }
187        Ok(())
188    }
189}
190
191#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
192pub struct Column {
193    pub name: String,
194    pub alias: Option<String>,
195    pub table: Option<String>,
196    pub function: Option<Box<FunctionExpression>>,
197}
198
199impl Column {
200    pub fn index_col_list(i: &str) -> IResult<&str, Vec<Column>, ParseSQLError<&str>> {
201        many0(map(
202            terminated(
203                CommonParser::index_col_name,
204                opt(CommonParser::ws_sep_comma),
205            ),
206            // XXX(malte): ignores length and order
207            |e| e.0,
208        ))(i)
209    }
210
211    // Parse rule for a comma-separated list of fields without aliases.
212    pub fn field_list(i: &str) -> IResult<&str, Vec<Column>, ParseSQLError<&str>> {
213        many0(terminated(
214            Column::without_alias,
215            opt(CommonParser::ws_sep_comma),
216        ))(i)
217    }
218
219    // Parses a SQL column identifier in the column format
220    pub fn without_alias(i: &str) -> IResult<&str, Column, ParseSQLError<&str>> {
221        let table_parser = pair(
222            opt(terminated(CommonParser::sql_identifier, tag("."))),
223            CommonParser::sql_identifier,
224        );
225        alt((
226            map(FunctionExpression::parse, |f| Column {
227                name: format!("{}", f),
228                alias: None,
229                table: None,
230                function: Some(Box::new(f)),
231            }),
232            map(table_parser, |tup| Column {
233                name: tup.1.to_string(),
234                alias: None,
235                table: tup.0.map(|t| t.to_string()),
236                function: None,
237            }),
238        ))(i)
239    }
240
241    // Parses a SQL column identifier in the table.column format
242    pub fn parse(i: &str) -> IResult<&str, Column, ParseSQLError<&str>> {
243        let col_func_no_table = map(
244            pair(FunctionExpression::parse, opt(CommonParser::as_alias)),
245            |tup| Column {
246                name: match tup.1 {
247                    None => format!("{}", tup.0),
248                    Some(a) => String::from(a),
249                },
250                alias: tup.1.map(String::from),
251                table: None,
252                function: Some(Box::new(tup.0)),
253            },
254        );
255        let col_w_table = map(
256            tuple((
257                opt(terminated(CommonParser::sql_identifier, tag("."))),
258                CommonParser::sql_identifier,
259                opt(CommonParser::as_alias),
260            )),
261            |tup| Column {
262                name: tup.1.to_string(),
263                alias: tup.2.map(String::from),
264                table: tup.0.map(|t| t.to_string()),
265                function: None,
266            },
267        );
268        alt((col_func_no_table, col_w_table))(i)
269    }
270}
271
272impl fmt::Display for Column {
273    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
274        if let Some(ref table) = self.table {
275            write!(
276                f,
277                "{}.{}",
278                DisplayUtil::escape_if_keyword(table),
279                DisplayUtil::escape_if_keyword(&self.name)
280            )?;
281        } else if let Some(ref function) = self.function {
282            write!(f, "{}", *function)?;
283        } else {
284            write!(f, "{}", DisplayUtil::escape_if_keyword(&self.name))?;
285        }
286        if let Some(ref alias) = self.alias {
287            write!(f, " AS {}", DisplayUtil::escape_if_keyword(alias))?;
288        }
289        Ok(())
290    }
291}
292
293impl From<String> for Column {
294    fn from(value: String) -> Self {
295        match value.find('.') {
296            None => Column {
297                name: value,
298                alias: None,
299                table: None,
300                function: None,
301            },
302            Some(i) => Column {
303                name: String::from(&value[i + 1..]),
304                alias: None,
305                table: Some(String::from(&value[0..i])),
306                function: None,
307            },
308        }
309    }
310}
311
312impl<'a> From<&'a str> for Column {
313    fn from(c: &str) -> Column {
314        match c.find('.') {
315            None => Column {
316                name: String::from(c),
317                alias: None,
318                table: None,
319                function: None,
320            },
321            Some(i) => Column {
322                name: String::from(&c[i + 1..]),
323                alias: None,
324                table: Some(String::from(&c[0..i])),
325                function: None,
326            },
327        }
328    }
329}
330
331impl Ord for Column {
332    fn cmp(&self, other: &Column) -> Ordering {
333        if self.table.is_some() && other.table.is_some() {
334            match self.table.cmp(&other.table) {
335                Ordering::Equal => self.name.cmp(&other.name),
336                x => x,
337            }
338        } else {
339            self.name.cmp(&other.name)
340        }
341    }
342}
343
344#[allow(clippy::non_canonical_partial_ord_impl)]
345impl PartialOrd for Column {
346    fn partial_cmp(&self, other: &Column) -> Option<Ordering> {
347        if self.table.is_some() && other.table.is_some() {
348            match self.table.cmp(&other.table) {
349                Ordering::Equal => Some(self.name.cmp(&other.name)),
350                x => Some(x),
351            }
352        } else if self.table.is_none() && other.table.is_none() {
353            Some(self.name.cmp(&other.name))
354        } else {
355            None
356        }
357    }
358}
359
360#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
361pub enum ColumnConstraint {
362    NotNull,
363    Null,
364    CharacterSet(String),
365    Collation(String),
366    DefaultValue(Literal),
367    AutoIncrement,
368    PrimaryKey,
369    Unique,
370    OnUpdate(Literal),
371}
372
373impl ColumnConstraint {
374    pub fn parse(i: &str) -> IResult<&str, Option<ColumnConstraint>, ParseSQLError<&str>> {
375        let not_null = map(
376            delimited(
377                multispace0,
378                tuple((tag_no_case("NOT"), multispace1, tag_no_case("NULL"))),
379                multispace0,
380            ),
381            |_| Some(ColumnConstraint::NotNull),
382        );
383        let null = map(
384            delimited(multispace0, tag_no_case("NULL"), multispace0),
385            |_| Some(ColumnConstraint::Null),
386        );
387        let auto_increment = map(
388            delimited(multispace0, tag_no_case("AUTO_INCREMENT"), multispace0),
389            |_| Some(ColumnConstraint::AutoIncrement),
390        );
391        let primary_key = map(
392            delimited(
393                multispace0,
394                tuple((tag_no_case("PRIMARY"), multispace1, tag_no_case("KEY"))),
395                multispace0,
396            ),
397            |_| Some(ColumnConstraint::PrimaryKey),
398        );
399        let unique = map(
400            delimited(multispace0, tag_no_case("UNIQUE"), multispace0),
401            |_| Some(ColumnConstraint::Unique),
402        );
403        let character_set = map(
404            preceded(
405                delimited(
406                    multispace0,
407                    tuple((tag_no_case("CHARACTER"), multispace1, tag_no_case("SET"))),
408                    multispace1,
409                ),
410                alt((
411                    CommonParser::sql_identifier,
412                    delimited(tag("'"), CommonParser::sql_identifier, tag("'")),
413                    delimited(tag("\""), CommonParser::sql_identifier, tag("\"")),
414                )),
415            ),
416            |cs| {
417                let char_set = cs.to_owned();
418                Some(ColumnConstraint::CharacterSet(char_set))
419            },
420        );
421        let charset = map(
422            preceded(
423                delimited(multispace0, tag_no_case("CHARSET"), multispace1),
424                alt((
425                    CommonParser::sql_identifier,
426                    delimited(tag("'"), CommonParser::sql_identifier, tag("'")),
427                    delimited(tag("\""), CommonParser::sql_identifier, tag("\"")),
428                )),
429            ),
430            |cs| {
431                let char_set = cs.to_owned();
432                Some(ColumnConstraint::CharacterSet(char_set))
433            },
434        );
435        let collate = map(
436            preceded(
437                delimited(multispace0, tag_no_case("COLLATE"), multispace1),
438                alt((
439                    CommonParser::sql_identifier,
440                    delimited(tag("'"), CommonParser::sql_identifier, tag("'")),
441                    delimited(tag("\""), CommonParser::sql_identifier, tag("\"")),
442                )),
443            ),
444            |c| {
445                let collation = c.to_owned();
446                Some(ColumnConstraint::Collation(collation))
447            },
448        );
449        // https://dev.mysql.com/doc/refman/5.7/en/timestamp-initialization.html
450        // for timestamp only, part of constraint
451        let on_update = map(
452            tuple((
453                tag_no_case("ON"),
454                multispace1,
455                tag_no_case("UPDATE"),
456                multispace1,
457                tag_no_case("CURRENT_TIMESTAMP"),
458                opt(CommonParser::delim_digit),
459            )),
460            |_| Some(ColumnConstraint::OnUpdate(Literal::CurrentTimestamp)),
461        );
462
463        alt((
464            not_null,
465            null,
466            auto_increment,
467            Self::default,
468            primary_key,
469            unique,
470            character_set,
471            charset,
472            collate,
473            on_update,
474        ))(i)
475    }
476
477    fn default(i: &str) -> IResult<&str, Option<ColumnConstraint>, ParseSQLError<&str>> {
478        let (remaining_input, (_, _, _, def, _)) = tuple((
479            multispace0,
480            tag_no_case("DEFAULT"),
481            multispace1,
482            alt((
483                map(delimited(tag("'"), take_until("'"), tag("'")), |s| {
484                    Literal::String(String::from(s))
485                }),
486                map(delimited(tag("\""), take_until("\""), tag("\"")), |s| {
487                    Literal::String(String::from(s))
488                }),
489                map(tuple((digit1, tag("."), digit1)), |(i, _, f)| {
490                    Literal::FixedPoint(Real {
491                        integral: i32::from_str(i).unwrap(),
492                        fractional: i32::from_str(f).unwrap(),
493                    })
494                }),
495                map(tuple((opt(tag("-")), digit1)), |d: (Option<&str>, &str)| {
496                    let d_i64: i64 = d.1.parse().unwrap();
497                    if d.0.is_some() {
498                        Literal::Integer(-d_i64)
499                    } else {
500                        Literal::Integer(d_i64)
501                    }
502                }),
503                map(tag("''"), |_| Literal::String(String::from(""))),
504                map(tag_no_case("NULL"), |_| Literal::Null),
505                map(tag_no_case("FALSE"), |_| Literal::Bool(false)),
506                map(tag_no_case("TRUE"), |_| Literal::Bool(true)),
507                map(
508                    tuple((
509                        tag_no_case("CURRENT_TIMESTAMP"),
510                        opt(CommonParser::delim_digit),
511                    )),
512                    |_| Literal::CurrentTimestamp,
513                ),
514            )),
515            multispace0,
516        ))(i)?;
517
518        Ok((remaining_input, Some(ColumnConstraint::DefaultValue(def))))
519    }
520}
521
522impl fmt::Display for ColumnConstraint {
523    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
524        match *self {
525            ColumnConstraint::NotNull => write!(f, "NOT NULL"),
526            ColumnConstraint::Null => write!(f, "NULL"),
527            ColumnConstraint::CharacterSet(ref charset) => write!(f, "CHARACTER SET {}", charset),
528            ColumnConstraint::Collation(ref collation) => write!(f, "COLLATE {}", collation),
529            ColumnConstraint::DefaultValue(ref literal) => {
530                write!(f, "DEFAULT {}", literal)
531            }
532            ColumnConstraint::AutoIncrement => write!(f, "AutoIncrement"),
533            ColumnConstraint::PrimaryKey => write!(f, "PRIMARY KEY"),
534            ColumnConstraint::Unique => write!(f, "UNIQUE"),
535            ColumnConstraint::OnUpdate(ref ts) => write!(f, "ON UPDATE CURRENT_TIMESTAMP"),
536        }
537    }
538}
539
540#[derive(Clone, Hash, PartialEq, Eq, Debug, Serialize, Deserialize)]
541pub enum ColumnPosition {
542    First,
543    After(Column),
544}
545
546impl ColumnPosition {
547    pub fn parse(i: &str) -> IResult<&str, ColumnPosition, ParseSQLError<&str>> {
548        alt((
549            map(
550                tuple((multispace0, tag_no_case("FIRST"), multispace0)),
551                |_| ColumnPosition::First,
552            ),
553            map(
554                tuple((
555                    multispace0,
556                    tag_no_case("AFTER"),
557                    multispace1,
558                    CommonParser::sql_identifier,
559                )),
560                |(_, _, _, identifier)| ColumnPosition::After(String::from(identifier).into()),
561            ),
562        ))(i)
563    }
564}
565
566impl Display for ColumnPosition {
567    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
568        match self {
569            ColumnPosition::First => Ok(write!(f, "FIRST")?),
570            ColumnPosition::After(column) => {
571                let column_name = match &column.table {
572                    Some(table) => format!("{}.{}", table, &column.name),
573                    None => column.name.to_string(),
574                };
575                Ok(write!(f, "AFTER {column_name}")?)
576            }
577        }
578    }
579}
580
581/// stands for column definition include
582/// - column: column metadata include name alias
583/// - data_type: data type for this column
584/// - constraints: collection of constraint, like primary key, not null
585/// - comment: column definition comment
586/// - position: column position info, like FIRST or AFTER other_column
587#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
588pub struct ColumnSpecification {
589    pub column: Column,
590    pub data_type: DataType,
591    pub constraints: Vec<ColumnConstraint>,
592    pub comment: Option<String>,
593    pub position: Option<ColumnPosition>,
594}
595
596impl ColumnSpecification {
597    pub fn parse(i: &str) -> IResult<&str, ColumnSpecification, ParseSQLError<&str>> {
598        let mut parser = tuple((
599            Column::without_alias,
600            opt(delimited(
601                multispace1,
602                DataType::type_identifier,
603                multispace0,
604            )),
605            many0(ColumnConstraint::parse),
606            opt(CommonParser::parse_comment),
607            opt(ColumnPosition::parse),
608            opt(CommonParser::ws_sep_comma),
609        ));
610
611        match parser(i) {
612            Ok((input, (column, field_type, constraints, comment, position, _))) => {
613                if field_type.is_none() {
614                    let error = ParseSQLError {
615                        errors: vec![(i, ParseSQLErrorKind::Context("data type is empty"))],
616                    };
617                    return Err(nom::Err::Error(error));
618                }
619
620                let sql_type = field_type.unwrap();
621                Ok((
622                    input,
623                    ColumnSpecification {
624                        column,
625                        data_type: sql_type,
626                        constraints: constraints.into_iter().flatten().collect(),
627                        comment,
628                        position,
629                    },
630                ))
631            }
632            Err(err) => Err(err),
633        }
634    }
635
636    pub fn new(column: Column, sql_type: DataType) -> ColumnSpecification {
637        ColumnSpecification {
638            column,
639            data_type: sql_type,
640            constraints: vec![],
641            comment: None,
642            position: None,
643        }
644    }
645
646    pub fn with_constraints(
647        column: Column,
648        sql_type: DataType,
649        constraints: Vec<ColumnConstraint>,
650    ) -> ColumnSpecification {
651        ColumnSpecification {
652            column,
653            data_type: sql_type,
654            constraints,
655            comment: None,
656            position: None,
657        }
658    }
659}
660
661impl fmt::Display for ColumnSpecification {
662    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
663        write!(
664            f,
665            "{} {}",
666            DisplayUtil::escape_if_keyword(&self.column.name),
667            self.data_type
668        )?;
669        for constraint in self.constraints.iter() {
670            write!(f, " {}", constraint)?;
671        }
672        if let Some(ref comment) = self.comment {
673            write!(f, " COMMENT '{}'", comment)?;
674        }
675        if let Some(ref position) = self.position {
676            write!(f, " {}", position)?;
677        }
678        Ok(())
679    }
680}
681
682#[cfg(test)]
683mod tests {
684    use super::*;
685
686    #[test]
687    fn column_from_str() {
688        let s = "table.col";
689        let c = Column::from(s);
690
691        assert_eq!(
692            c,
693            Column {
694                name: String::from("col"),
695                alias: None,
696                table: Some(String::from("table")),
697                function: None,
698            }
699        );
700    }
701
702    #[test]
703    fn print_function_column() {
704        let c1 = Column {
705            name: "".into(), // must be present, but will be ignored
706            alias: Some("foo".into()),
707            table: None,
708            function: Some(Box::new(FunctionExpression::CountStar)),
709        };
710        let c2 = Column {
711            name: "".into(), // must be present, but will be ignored
712            alias: None,
713            table: None,
714            function: Some(Box::new(FunctionExpression::CountStar)),
715        };
716        let c3 = Column {
717            name: "".into(), // must be present, but will be ignored
718            alias: None,
719            table: None,
720            function: Some(Box::new(FunctionExpression::Sum(
721                FunctionArgument::Column(Column::from("mytab.foo")),
722                false,
723            ))),
724        };
725
726        assert_eq!(format!("{}", c1), "count(*) AS foo");
727        assert_eq!(format!("{}", c2), "count(*)");
728        assert_eq!(format!("{}", c3), "sum(mytab.foo)");
729    }
730
731    #[test]
732    fn simple_generic_function() {
733        let list = [
734            "coalesce(a,b,c)",
735            "coalesce (a,b,c)",
736            "coalesce(a ,b,c)",
737            "coalesce(a, b,c)",
738        ];
739        for part in list.iter() {
740            let res = FunctionExpression::parse(part);
741            let expected = FunctionExpression::Generic(
742                "coalesce".to_string(),
743                FunctionArguments::from(vec![
744                    FunctionArgument::Column(Column::from("a")),
745                    FunctionArgument::Column(Column::from("b")),
746                    FunctionArgument::Column(Column::from("c")),
747                ]),
748            );
749            assert_eq!(res, Ok(("", expected)));
750        }
751    }
752
753    #[test]
754    fn parse_function_expression() {
755        let str1 = "count(*)";
756        let res1 = FunctionExpression::parse(str1);
757        assert!(res1.is_ok());
758        assert_eq!(res1.unwrap().1, FunctionExpression::CountStar);
759
760        let str2 = "max(addr_id)";
761        let res2 = FunctionExpression::parse(str2);
762        let expected = FunctionExpression::Max(FunctionArgument::Column(Column::from("addr_id")));
763        assert_eq!(res2.unwrap().1, expected);
764
765        let str3 = "count(num)";
766        let res3 = FunctionExpression::parse(str3);
767        assert!(res3.is_ok());
768        let expected =
769            FunctionExpression::Count(FunctionArgument::Column(Column::from("num")), false);
770        assert_eq!(res3.unwrap().1, expected);
771    }
772
773    #[test]
774    fn parse_column_constraint() {
775        let str1 = "NOT null ";
776        let res1 = ColumnConstraint::parse(str1);
777        assert!(res1.is_ok());
778        assert_eq!(res1.unwrap().1.unwrap(), ColumnConstraint::NotNull);
779
780        let str2 = "AUTO_INCREMENT";
781        let res2 = ColumnConstraint::parse(str2);
782        assert!(res2.is_ok());
783        assert_eq!(res2.unwrap().1.unwrap(), ColumnConstraint::AutoIncrement);
784
785        let str3 = "CHARACTER SET utf8";
786        let res3 = ColumnConstraint::parse(str3);
787        assert!(res3.is_ok());
788        assert_eq!(
789            res3.unwrap().1.unwrap(),
790            ColumnConstraint::CharacterSet("utf8".to_string())
791        );
792    }
793
794    #[test]
795    fn parse_column_position() {
796        let parts = [
797            "FIRST",
798            " FIRST",
799            " FIRST ",
800            "AFTER foo",
801            " AFTER foo ",
802            "  AFTER  foo ",
803        ];
804        let positions = vec![
805            ColumnPosition::First,
806            ColumnPosition::First,
807            ColumnPosition::First,
808            ColumnPosition::After("foo".into()),
809            ColumnPosition::After("foo".into()),
810            ColumnPosition::After("foo".into()),
811        ];
812        for i in 0..parts.len() {
813            let res = ColumnPosition::parse(parts[i]);
814            assert!(res.is_ok());
815            assert_eq!(res.unwrap().1, positions[i])
816        }
817    }
818
819    #[test]
820    fn parse_column() {
821        let str1 = "some_column VARCHAR(255) FIRST;";
822        let res1 = ColumnSpecification::parse(str1);
823        let expected = ColumnSpecification {
824            column: "some_column".into(),
825            data_type: DataType::Varchar(255),
826            constraints: vec![],
827            comment: None,
828            position: Some(ColumnPosition::First),
829        };
830        assert!(res1.is_ok());
831        assert_eq!(res1.unwrap().1, expected);
832
833        let str2 = "another_column int not null auto_increment primary key After age;";
834        let res2 = ColumnSpecification::parse(str2);
835        let expected = ColumnSpecification {
836            column: "another_column".into(),
837            data_type: DataType::Int(32),
838            constraints: vec![
839                ColumnConstraint::NotNull,
840                ColumnConstraint::AutoIncrement,
841                ColumnConstraint::PrimaryKey,
842            ],
843            comment: None,
844            position: Some(ColumnPosition::After("age".into())),
845        };
846        assert!(res2.is_ok());
847        assert_eq!(res2.unwrap().1, expected);
848    }
849}