sea_schema/mysql/parser/
index.rs

1use crate::mysql::def::*;
2use crate::mysql::query::IndexQueryResult;
3use crate::Name;
4
5pub struct IndexQueryResultParser {
6    curr: Option<IndexInfo>,
7    results: Box<dyn Iterator<Item = IndexQueryResult>>,
8}
9
10/// IndexQueryResult must be sorted by (TableName, IndexName, SeqInIndex)
11pub fn parse_index_query_results(
12    results: Box<dyn Iterator<Item = IndexQueryResult>>,
13) -> impl Iterator<Item = IndexInfo> {
14    IndexQueryResultParser {
15        curr: None,
16        results,
17    }
18}
19
20impl Iterator for IndexQueryResultParser {
21    type Item = IndexInfo;
22
23    fn next(&mut self) -> Option<Self::Item> {
24        for result in self.results.by_ref() {
25            let mut index = parse_index_query_result(result);
26            if let Some(curr) = &mut self.curr {
27                // group by `index.name`, consolidate to `index.parts`
28                if curr.name == index.name {
29                    curr.parts.push(index.parts.pop().unwrap());
30                    curr.functional |= index.functional;
31                } else {
32                    let prev = self.curr.take();
33                    self.curr = Some(index);
34                    return prev;
35                }
36            } else {
37                self.curr = Some(index);
38            }
39        }
40        self.curr.take()
41    }
42}
43
44pub fn parse_index_query_result(mut result: IndexQueryResult) -> IndexInfo {
45    IndexInfo {
46        unique: match result.non_unique {
47            0 => true,
48            1 => false,
49            _ => unimplemented!(),
50        },
51        name: result.index_name,
52        parts: vec![IndexPart {
53            column: if result.column_name.is_some() {
54                result.column_name.take().unwrap()
55            } else if result.expression.is_some() {
56                result.expression.take().unwrap()
57            } else {
58                panic!("index column error")
59            },
60            order: match result.collation {
61                Some(collation) => match collation.as_str() {
62                    "A" => IndexOrder::Ascending,
63                    "D" => IndexOrder::Descending,
64                    _ => unimplemented!(),
65                },
66                None => IndexOrder::Unordered,
67            },
68            sub_part: result.sub_part.map(|v| v as u32),
69        }],
70        nullable: matches!(result.nullable.as_str(), "YES"),
71        idx_type: IndexType::from_str(result.index_type.as_str()).unwrap(),
72        comment: result.index_comment,
73        functional: result.expression.is_some(),
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_1() {
83        assert_eq!(
84            parse_index_query_results(Box::new(
85                vec![IndexQueryResult {
86                    non_unique: 0,
87                    index_name: "PRIMARY".to_owned(),
88                    column_name: Some("film_id".to_owned()),
89                    collation: Some("A".to_owned()),
90                    sub_part: None,
91                    nullable: "".to_owned(),
92                    index_type: "BTREE".to_owned(),
93                    index_comment: "".to_owned(),
94                    expression: None
95                }]
96                .into_iter()
97            ))
98            .collect::<Vec<IndexInfo>>(),
99            vec![IndexInfo {
100                unique: true,
101                name: "PRIMARY".to_owned(),
102                parts: vec![IndexPart {
103                    column: "film_id".to_owned(),
104                    order: IndexOrder::Ascending,
105                    sub_part: None,
106                },],
107                nullable: false,
108                idx_type: IndexType::BTree,
109                comment: "".to_owned(),
110                functional: false,
111            }]
112        );
113    }
114
115    #[test]
116    fn test_2() {
117        assert_eq!(
118            parse_index_query_results(Box::new(
119                vec![IndexQueryResult {
120                    non_unique: 1,
121                    index_name: "idx_title".to_owned(),
122                    column_name: Some("title".to_owned()),
123                    collation: Some("A".to_owned()),
124                    sub_part: None,
125                    nullable: "".to_owned(),
126                    index_type: "BTREE".to_owned(),
127                    index_comment: "".to_owned(),
128                    expression: None
129                }]
130                .into_iter()
131            ))
132            .collect::<Vec<IndexInfo>>(),
133            vec![IndexInfo {
134                unique: false,
135                name: "idx_title".to_owned(),
136                parts: vec![IndexPart {
137                    column: "title".to_owned(),
138                    order: IndexOrder::Ascending,
139                    sub_part: None,
140                },],
141                nullable: false,
142                idx_type: IndexType::BTree,
143                comment: "".to_owned(),
144                functional: false,
145            }]
146        );
147    }
148
149    #[test]
150    fn test_3() {
151        assert_eq!(
152            parse_index_query_results(Box::new(
153                vec![
154                    IndexQueryResult {
155                        non_unique: 0,
156                        index_name: "rental_date".to_owned(),
157                        column_name: Some("rental_date".to_owned()),
158                        collation: Some("A".to_owned()),
159                        sub_part: None,
160                        nullable: "".to_owned(),
161                        index_type: "BTREE".to_owned(),
162                        index_comment: "".to_owned(),
163                        expression: None
164                    },
165                    IndexQueryResult {
166                        non_unique: 0,
167                        index_name: "rental_date".to_owned(),
168                        column_name: Some("inventory_id".to_owned()),
169                        collation: Some("D".to_owned()),
170                        sub_part: None,
171                        nullable: "".to_owned(),
172                        index_type: "BTREE".to_owned(),
173                        index_comment: "".to_owned(),
174                        expression: None
175                    },
176                    IndexQueryResult {
177                        non_unique: 0,
178                        index_name: "rental_date".to_owned(),
179                        column_name: Some("customer_id".to_owned()),
180                        collation: Some("A".to_owned()),
181                        sub_part: None,
182                        nullable: "".to_owned(),
183                        index_type: "BTREE".to_owned(),
184                        index_comment: "".to_owned(),
185                        expression: None
186                    },
187                ]
188                .into_iter()
189            ))
190            .collect::<Vec<IndexInfo>>(),
191            vec![IndexInfo {
192                unique: true,
193                name: "rental_date".to_owned(),
194                parts: vec![
195                    IndexPart {
196                        column: "rental_date".to_owned(),
197                        order: IndexOrder::Ascending,
198                        sub_part: None,
199                    },
200                    IndexPart {
201                        column: "inventory_id".to_owned(),
202                        order: IndexOrder::Descending,
203                        sub_part: None,
204                    },
205                    IndexPart {
206                        column: "customer_id".to_owned(),
207                        order: IndexOrder::Ascending,
208                        sub_part: None,
209                    },
210                ],
211                nullable: false,
212                idx_type: IndexType::BTree,
213                comment: "".to_owned(),
214                functional: false
215            }]
216        );
217    }
218
219    #[test]
220    fn test_4() {
221        assert_eq!(
222            parse_index_query_results(Box::new(
223                vec![IndexQueryResult {
224                    non_unique: 1,
225                    index_name: "idx_location".to_owned(),
226                    column_name: Some("location".to_owned()),
227                    collation: Some("A".to_owned()),
228                    sub_part: Some(32),
229                    nullable: "".to_owned(),
230                    index_type: "SPATIAL".to_owned(),
231                    index_comment: "".to_owned(),
232                    expression: None
233                }]
234                .into_iter()
235            ))
236            .collect::<Vec<IndexInfo>>(),
237            vec![IndexInfo {
238                unique: false,
239                name: "idx_location".to_owned(),
240                parts: vec![IndexPart {
241                    column: "location".to_owned(),
242                    order: IndexOrder::Ascending,
243                    sub_part: Some(32),
244                },],
245                nullable: false,
246                idx_type: IndexType::Spatial,
247                comment: "".to_owned(),
248                functional: false,
249            }]
250        );
251    }
252}