1use crate::ast::ddl::IndexMethod;
7
8#[derive(Debug, Clone)]
39pub struct IndexMetadata {
40 pub index_id: u32,
42 pub name: String,
44 pub table: String,
46 pub columns: Vec<String>,
48 pub column_indices: Vec<usize>,
50 pub unique: bool,
52 pub method: Option<IndexMethod>,
54 pub options: Vec<(String, String)>,
56}
57
58impl IndexMetadata {
59 pub fn new(
69 index_id: u32,
70 name: impl Into<String>,
71 table: impl Into<String>,
72 columns: Vec<String>,
73 ) -> Self {
74 Self {
75 index_id,
76 name: name.into(),
77 table: table.into(),
78 columns,
79 column_indices: Vec::new(),
80 unique: false,
81 method: None,
82 options: Vec::new(),
83 }
84 }
85
86 pub fn with_column_indices(mut self, indices: Vec<usize>) -> Self {
88 self.column_indices = indices;
89 self
90 }
91
92 pub fn with_unique(mut self, unique: bool) -> Self {
94 self.unique = unique;
95 self
96 }
97
98 pub fn with_method(mut self, method: IndexMethod) -> Self {
100 self.method = Some(method);
101 self
102 }
103
104 pub fn with_option(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
106 self.options.push((key.into(), value.into()));
107 self
108 }
109
110 pub fn with_options(mut self, options: Vec<(String, String)>) -> Self {
112 self.options = options;
113 self
114 }
115
116 pub fn get_option(&self, key: &str) -> Option<&str> {
120 self.options
121 .iter()
122 .find(|(k, _)| k == key)
123 .map(|(_, v)| v.as_str())
124 }
125
126 pub fn covers_column(&self, column: &str) -> bool {
128 self.columns.iter().any(|c| c == column)
129 }
130
131 pub fn is_single_column(&self) -> bool {
133 self.columns.len() == 1
134 }
135
136 pub fn first_column(&self) -> Option<&str> {
140 self.columns.first().map(|s| s.as_str())
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_index_metadata_new() {
150 let index = IndexMetadata::new(1, "idx_users_id", "users", vec!["id".into()]);
151
152 assert_eq!(index.index_id, 1);
153 assert_eq!(index.name, "idx_users_id");
154 assert_eq!(index.table, "users");
155 assert_eq!(index.columns, vec!["id"]);
156 assert!(index.column_indices.is_empty());
157 assert!(!index.unique);
158 assert!(index.method.is_none());
159 assert!(index.options.is_empty());
160 }
161
162 #[test]
163 fn test_index_metadata_with_column_indices() {
164 let index =
165 IndexMetadata::new(1, "idx", "table", vec!["col".into()]).with_column_indices(vec![2]);
166
167 assert_eq!(index.column_indices, vec![2]);
168 }
169
170 #[test]
171 fn test_index_metadata_with_unique() {
172 let index = IndexMetadata::new(1, "idx", "table", vec!["col".into()]).with_unique(true);
173
174 assert!(index.unique);
175 }
176
177 #[test]
178 fn test_index_metadata_composite() {
179 let index = IndexMetadata::new(
180 5,
181 "idx_composite",
182 "orders",
183 vec!["user_id".into(), "order_date".into()],
184 )
185 .with_column_indices(vec![0, 3])
186 .with_unique(true);
187
188 assert_eq!(index.index_id, 5);
189 assert_eq!(index.columns.len(), 2);
190 assert_eq!(index.column_indices, vec![0, 3]);
191 assert!(index.unique);
192 assert!(!index.is_single_column());
193 }
194
195 #[test]
196 fn test_index_metadata_with_method() {
197 let index = IndexMetadata::new(1, "idx_users_name", "users", vec!["name".into()])
198 .with_method(IndexMethod::BTree);
199
200 assert_eq!(index.method, Some(IndexMethod::BTree));
201 }
202
203 #[test]
204 fn test_index_metadata_hnsw_with_options() {
205 let index = IndexMetadata::new(1, "idx_items_embedding", "items", vec!["embedding".into()])
206 .with_method(IndexMethod::Hnsw)
207 .with_option("m", "16")
208 .with_option("ef_construction", "200");
209
210 assert_eq!(index.method, Some(IndexMethod::Hnsw));
211 assert_eq!(index.options.len(), 2);
212 assert_eq!(index.get_option("m"), Some("16"));
213 assert_eq!(index.get_option("ef_construction"), Some("200"));
214 assert_eq!(index.get_option("nonexistent"), None);
215 }
216
217 #[test]
218 fn test_index_metadata_with_options_bulk() {
219 let options = vec![
220 ("m".to_string(), "32".to_string()),
221 ("ef_construction".to_string(), "400".to_string()),
222 ];
223 let index = IndexMetadata::new(1, "idx", "table", vec!["col".into()]).with_options(options);
224
225 assert_eq!(index.options.len(), 2);
226 assert_eq!(index.get_option("m"), Some("32"));
227 }
228
229 #[test]
230 fn test_covers_column() {
231 let index = IndexMetadata::new(1, "idx", "table", vec!["a".into(), "b".into()]);
232
233 assert!(index.covers_column("a"));
234 assert!(index.covers_column("b"));
235 assert!(!index.covers_column("c"));
236 }
237
238 #[test]
239 fn test_is_single_column() {
240 let single = IndexMetadata::new(1, "idx", "table", vec!["a".into()]);
241 let composite = IndexMetadata::new(2, "idx", "table", vec!["a".into(), "b".into()]);
242
243 assert!(single.is_single_column());
244 assert!(!composite.is_single_column());
245 }
246
247 #[test]
248 fn test_first_column() {
249 let index = IndexMetadata::new(1, "idx", "table", vec!["first".into(), "second".into()]);
250 let empty = IndexMetadata::new(2, "idx", "table", vec![]);
251
252 assert_eq!(index.first_column(), Some("first"));
253 assert_eq!(empty.first_column(), None);
254 }
255}