1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use core::fmt;
use std::fmt::Formatter;
use std::str;

use nom::bytes::complete::tag_no_case;
use nom::character::complete::multispace0;
use nom::character::complete::multispace1;
use nom::combinator::{map, opt};
use nom::multi::many0;
use nom::sequence::{delimited, terminated, tuple};
use nom::IResult;

use base::error::ParseSQLError;
use base::CommonParser;

/// parse `DROP VIEW [IF EXISTS]
///     view_name [, view_name] ...
///     [RESTRICT | CASCADE]`
#[derive(Default, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct DropViewStatement {
    pub if_exists: bool,
    /// A name of a table, view, custom type, etc., possibly multipart, i.e. db.schema.obj
    pub views: Vec<String>,
    pub if_restrict: bool,
    pub if_cascade: bool,
}

impl DropViewStatement {
    /// DROP VIEW [IF EXISTS]
    ///     view_name [, view_name] ...
    ///     [RESTRICT | CASCADE]
    pub fn parse(i: &str) -> IResult<&str, DropViewStatement, ParseSQLError<&str>> {
        let mut parser = tuple((
            tag_no_case("DROP "),
            multispace0,
            tag_no_case("VIEW "),
            CommonParser::parse_if_exists,
            multispace0,
            map(
                many0(terminated(
                    CommonParser::sql_identifier,
                    opt(CommonParser::ws_sep_comma),
                )),
                |x| x.iter().map(|v| String::from(*v)).collect::<Vec<String>>(),
            ),
            opt(delimited(multispace1, tag_no_case("RESTRICT"), multispace0)),
            opt(delimited(multispace1, tag_no_case("CASCADE"), multispace0)),
            CommonParser::statement_terminator,
        ));
        let (
            remaining_input,
            (_, _, _, opt_if_exists, _, views, opt_if_restrict, opt_if_cascade, _),
        ) = parser(i)?;

        Ok((
            remaining_input,
            DropViewStatement {
                views,
                if_exists: opt_if_exists.is_some(),
                if_restrict: opt_if_restrict.is_some(),
                if_cascade: opt_if_cascade.is_some(),
            },
        ))
    }
}

impl fmt::Display for DropViewStatement {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "DROP VIEW")?;
        if self.if_exists {
            write!(f, " IF EXISTS")?;
        }

        let view_name = self.views.join(", ");
        write!(f, " {}", view_name)?;

        if self.if_restrict {
            write!(f, " RESTRICT")?;
        }
        if self.if_cascade {
            write!(f, " CASCADE")?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use dds::drop_view::DropViewStatement;

    #[test]
    fn parse_drop_view() {
        let sqls = [
            "DROP VIEW view_name;",
            "DROP VIEW IF EXISTS view_name;",
            "DROP VIEW view_name CASCADE;",
            "DROP VIEW  view_name1, view_name2;",
            "DROP VIEW  view_name1, view_name2 RESTRICT;",
        ];

        let exp_statements = [
            DropViewStatement {
                if_exists: false,
                views: vec!["view_name".to_string()],
                if_restrict: false,
                if_cascade: false,
            },
            DropViewStatement {
                if_exists: true,
                views: vec!["view_name".to_string()],
                if_restrict: false,
                if_cascade: false,
            },
            DropViewStatement {
                if_exists: false,
                views: vec!["view_name".to_string()],
                if_restrict: false,
                if_cascade: true,
            },
            DropViewStatement {
                if_exists: false,
                views: vec!["view_name1".to_string(), "view_name2".to_string()],
                if_restrict: false,
                if_cascade: false,
            },
            DropViewStatement {
                if_exists: false,
                views: vec!["view_name1".to_string(), "view_name2".to_string()],
                if_restrict: true,
                if_cascade: false,
            },
        ];

        for i in 0..sqls.len() {
            let res = DropViewStatement::parse(sqls[i]);
            assert!(res.is_ok());
            assert_eq!(res.unwrap().1, exp_statements[i]);
        }
    }
}