sqlparser_mysql/base/
reference_definition.rs

1use nom::bytes::complete::tag_no_case;
2use nom::character::complete::{multispace0, multispace1};
3use nom::combinator::{map, opt};
4use nom::sequence::tuple;
5use nom::IResult;
6use std::fmt::{Display, Formatter};
7
8use base::error::ParseSQLError;
9use base::reference_type::ReferenceType;
10use base::{CommonParser, KeyPart, MatchType};
11
12/// reference_definition:
13///     `REFERENCES tbl_name (key_part,...)
14///       [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
15///       [ON DELETE reference_option]
16///       [ON UPDATE reference_option]`
17#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
18pub struct ReferenceDefinition {
19    pub tbl_name: String,
20    pub key_part: Vec<KeyPart>,
21    pub match_type: Option<MatchType>,
22    pub on_delete: Option<ReferenceType>,
23    pub on_update: Option<ReferenceType>,
24}
25
26impl Display for ReferenceDefinition {
27    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
28        let key_part = self
29            .key_part
30            .iter()
31            .map(|x| x.to_string())
32            .collect::<Vec<String>>()
33            .join(" ");
34        write!(f, "REFERENCES {} ({})", self.tbl_name, key_part);
35        if let Some(match_type) = &self.match_type {
36            write!(f, " {}", match_type);
37        }
38        if let Some(on_delete) = &self.on_delete {
39            write!(f, " {}", on_delete);
40        }
41        if let Some(on_update) = &self.on_update {
42            write!(f, " {}", on_update);
43        }
44
45        Ok(())
46    }
47}
48
49impl ReferenceDefinition {
50    pub fn parse(i: &str) -> IResult<&str, ReferenceDefinition, ParseSQLError<&str>> {
51        let opt_on_delete = opt(map(
52            tuple((
53                tag_no_case("ON"),
54                multispace1,
55                tag_no_case("DELETE"),
56                multispace1,
57                ReferenceType::parse,
58            )),
59            |(_, _, _, _, reference_type)| reference_type,
60        ));
61        let opt_on_update = opt(map(
62            tuple((
63                tag_no_case("ON"),
64                multispace1,
65                tag_no_case("UPDATE"),
66                multispace1,
67                ReferenceType::parse,
68            )),
69            |(_, _, _, _, reference_type)| reference_type,
70        ));
71
72        map(
73            tuple((
74                tuple((multispace0, tag_no_case("REFERENCES"), multispace1)),
75                // tbl_name
76                map(CommonParser::sql_identifier, String::from),
77                multispace0,
78                KeyPart::parse, // (key_part,...)
79                multispace0,
80                opt(MatchType::parse), // [MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]
81                multispace0,
82                opt_on_delete,
83                multispace0,
84                opt_on_update,
85                multispace0,
86            )),
87            |(_, tbl_name, _, key_part, _, match_type, _, on_delete, _, on_update, _)| {
88                ReferenceDefinition {
89                    tbl_name,
90                    key_part,
91                    match_type,
92                    on_delete,
93                    on_update,
94                }
95            },
96        )(i)
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use base::reference_type::ReferenceType;
103    use base::{KeyPart, KeyPartType, ReferenceDefinition};
104
105    #[test]
106    fn parse_reference_definition() {
107        let str1 = "references tbl_name (col_name1, col_name2)";
108        let res1 = ReferenceDefinition::parse(str1);
109        let exp1 = ReferenceDefinition {
110            tbl_name: "tbl_name".to_string(),
111            key_part: vec![
112                KeyPart {
113                    r#type: KeyPartType::ColumnNameWithLength {
114                        col_name: "col_name1".to_string(),
115                        length: None,
116                    },
117                    order: None,
118                },
119                KeyPart {
120                    r#type: KeyPartType::ColumnNameWithLength {
121                        col_name: "col_name2".to_string(),
122                        length: None,
123                    },
124                    order: None,
125                },
126            ],
127            match_type: None,
128            on_update: None,
129            on_delete: None,
130        };
131        assert!(res1.is_ok());
132        assert_eq!(res1.unwrap().1, exp1);
133
134        let str2 = "references tbl_name (col_name1) ON DELETE set null";
135        let res2 = ReferenceDefinition::parse(str2);
136        let exp2 = ReferenceDefinition {
137            tbl_name: "tbl_name".to_string(),
138            key_part: vec![KeyPart {
139                r#type: KeyPartType::ColumnNameWithLength {
140                    col_name: "col_name1".to_string(),
141                    length: None,
142                },
143                order: None,
144            }],
145            match_type: None,
146            on_update: None,
147            on_delete: Some(ReferenceType::SetNull),
148        };
149        assert!(res2.is_ok());
150        assert_eq!(res2.unwrap().1, exp2);
151    }
152}