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#[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 map(CommonParser::sql_identifier, String::from),
77 multispace0,
78 KeyPart::parse, multispace0,
80 opt(MatchType::parse), 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}