Skip to main content

zql_cli/core/
catalog.rs

1use crate::core::case::{CaseTree, Entity, Payload};
2use crate::db::context::Context;
3use crate::regex;
4use crate::util::regex::get_match;
5use odbc_api::Nullability;
6use std::borrow::Cow;
7use std::collections::BTreeMap;
8
9pub struct Catalog {
10    directives: CaseTree,
11    objects: CaseTree,
12    columns: CaseTree,
13    keywords: CaseTree,
14}
15
16// noinspection DuplicatedCode
17impl Catalog {
18    pub fn new() -> Self {
19        let directives = CaseTree::new(Cow::default());
20        let objects = CaseTree::new(Cow::default());
21        let columns = CaseTree::new(Cow::default());
22        let keywords = CaseTree::new(Cow::default());
23        Self { directives, objects, columns, keywords }
24    }
25
26    pub fn insert_directive(
27        &mut self,
28        verb: Cow<str>,
29        noun: Cow<str>,
30    ) {
31        if let Some(child) = self.directives.insert_child(&[], verb.clone()) {
32            child.set_entity(Entity::Keyword);
33        }
34        if let Some(child) = self.directives.insert_child(&[verb], noun) {
35            child.set_entity(Entity::Keyword);
36        }
37    }
38
39    pub fn insert_database(
40        &mut self,
41        database: Cow<str>,
42        schema: Cow<str>,
43    ) {
44        self.objects.insert_child(&[database], schema);
45    }
46
47    pub fn insert_table(
48        &mut self,
49        database: Cow<str>,
50        schema: Cow<str>,
51        table: Cow<str>,
52    ) {
53        self.objects.insert_child(&[database, schema], table);
54    }
55
56    pub fn insert_column(
57        &mut self,
58        database: Cow<str>,
59        schema: Cow<str>,
60        table: Cow<str>,
61        column: Cow<str>,
62        dtype: Cow<str>,
63        null: Nullability,
64        index: usize,
65    ) {
66        if let Some(child) = self.columns.insert_child(&[database.clone(), schema.clone()], column.clone()) {
67            child.set_payload(dtype.clone(), null, index);
68        }
69        if let Some(child) = self.objects.insert_child(&[database, schema, table], column) {
70            child.set_payload(dtype, null, index);
71        }
72    }
73
74    pub fn insert_procedure(
75        &mut self,
76        database: Cow<str>,
77        schema: Cow<str>,
78        procedure: Cow<str>,
79    ) {
80        if let Some(child) = self.objects.insert_child(&[database, schema], procedure) {
81            child.set_entity(Entity::Procedure);
82        }
83    }
84
85    pub fn insert_keyword(&mut self, keyword: Cow<str>) {
86        if let Some(child) = self.keywords.insert_child(&[], keyword) {
87            child.set_entity(Entity::Keyword);
88        }
89    }
90
91    pub fn insert_function(&mut self, function: Cow<str>) {
92        if let Some(child) = self.keywords.insert_child(&[], function) {
93            child.set_entity(Entity::Function);
94        }
95    }
96
97    pub fn complete_line(&self, context: Context, line: &str, pos: usize) -> (usize, Vec<String>) {
98        let verb_regex = regex!(r"^(:\w*)$");
99        let noun_regex = regex!(r"^(:\w+)\s+(\w*)$");
100        let context = context.to_lowercase();
101        let line = line[..pos].to_lowercase();
102        if line.starts_with(":") {
103            if let Some(captures) = verb_regex.captures(&line) {
104                // Find directive verbs.
105                let verb = get_match(&captures, 1);
106                let verbs = self.directives.get_values(&[]);
107                let candidates = Self::select_candidates(verbs, verb);
108                return (0, candidates);
109            } else if let Some(captures) = noun_regex.captures(&line) {
110                // Find directive nouns for named verb.
111                let verb = get_match(&captures, 1);
112                let noun = get_match(&captures, 2);
113                let offset = get_offset(&line, noun);
114                let nouns = self.directives.get_values(&[verb]);
115                let candidates = Self::select_candidates(nouns, noun);
116                return (offset, candidates);
117            }
118        }
119        if let Some((start, stub)) = line.rsplit_once(char::is_whitespace) {
120            // Find database object from last whitespace.
121            let start = start.len() + 1;
122            let (offset, candidates) = self.complete_stub(context, stub);
123            (start + offset, candidates)
124        } else {
125            // Find database object from start of line.
126            let (offset, candidates) = self.complete_stub(context, &line);
127            (offset, candidates)
128        }
129    }
130
131    fn complete_stub(&self, context: Context, stub: &str) -> (usize, Vec<String>) {
132        let quad_regex = regex!(r"\b(\w+)\.(\w+)\.(\w+)\.(\w*)$");
133        let triple_regex = regex!(r"\b(\w+)\.(\w+)\.(\w*)$");
134        let double_regex = regex!(r"\b(\w+)\.(\w*)$");
135        let single_regex = regex!(r"\b(\w*)$");
136        let (database, schema) = context.as_str();
137        if let Some(captures) = quad_regex.captures(&stub) {
138            let arg1 = get_match(&captures, 1);
139            let arg2 = get_match(&captures, 2);
140            let arg3 = get_match(&captures, 3);
141            let arg4 = get_match(&captures, 4);
142            let offset = get_offset(&stub, arg4);
143            let candidates = if let Some(columns) = self.objects.get_child(&[arg1, arg2, arg3]) {
144                // Find columns in the named table.
145                let columns = columns.get_values(&[]);
146                Self::select_candidates(columns, arg4)
147            } else {
148                // Cannot match database objects.
149                Vec::new()
150            };
151            (offset, candidates)
152        } else if let Some(captures) = triple_regex.captures(&stub) {
153            let arg1 = get_match(&captures, 1);
154            let arg2 = get_match(&captures, 2);
155            let arg3 = get_match(&captures, 3);
156            let offset = get_offset(&stub, arg3);
157            let candidates = if let Some(tables) = self.objects.get_child(&[arg1, arg2]) {
158                // Find tables in the named schema.
159                let tables = tables.get_values(&[]);
160                Self::select_candidates(tables, arg3)
161            } else if let Some(columns) = self.objects.get_child(&[database, arg1, arg2]) {
162                // Find columns in the named table.
163                let columns = columns.get_values(&[]);
164                Self::select_candidates(columns, arg3)
165            } else {
166                // Cannot match database objects.
167                Vec::new()
168            };
169            (offset, candidates)
170        } else if let Some(captures) = double_regex.captures(&stub) {
171            let arg1 = get_match(&captures, 1);
172            let arg2 = get_match(&captures, 2);
173            let offset = get_offset(&stub, arg2);
174            let candidates = if let Some(schemas) = self.objects.get_child(&[arg1]) {
175                // Find schemas in the named database.
176                let schemas = schemas.get_values(&[]);
177                Self::select_candidates(schemas, arg2)
178            } else if let Some(tables) = self.objects.get_child(&[database, arg1]) {
179                // Find tables in the named schema.
180                let tables = tables.get_values(&[]);
181                Self::select_candidates(tables, arg2)
182            } else if let Some(columns) = self.objects.get_child(&[database, schema, arg1]) {
183                // Find columns in the named table.
184                let columns = columns.get_values(&[]);
185                Self::select_candidates(columns, arg2)
186            } else {
187                // Find columns in all tables (for table aliases).
188                let columns = self.columns.get_values(&[database, schema]);
189                Self::select_candidates(columns, arg2)
190            };
191            (offset, candidates)
192        } else if let Some(captures) = single_regex.captures(&stub) {
193            // Find database objects, SQL keywords or functions.
194            let arg = get_match(&captures, 1);
195            let offset = get_offset(&stub, arg);
196            let chained = self.objects.get_values(&[])
197                .chain(self.objects.get_values(&[database]))
198                .chain(self.objects.get_values(&[database, schema]))
199                .chain(self.columns.get_values(&[database, schema]))
200                .chain(self.keywords.get_values(&[]));
201            let candidates = Self::select_candidates(chained, arg);
202            (offset, candidates)
203        } else {
204            // Cannot match database objects.
205            (0, Vec::new())
206        }
207    }
208
209    fn select_candidates<'a, I>(keywords: I, stub: &str) -> Vec<String> where
210        I: Iterator<Item = (&'a String, (&'a String, Entity))>
211    {
212        let candidates = keywords
213            .filter(|(k, _)| k.starts_with(stub))
214            .map(|(k, (v1, v2))| (k.to_string(), (v1.to_string(), v2)))
215            .collect::<BTreeMap<_, _>>();
216        if candidates.len() == 1 {
217            candidates.into_iter().map(Self::append_suffix).collect()
218        } else {
219            let prefix = Self::generate_prefix(&candidates);
220            candidates.into_iter().map(|c| Self::replace_prefix(c, &prefix)).collect()
221        }
222    }
223
224    fn generate_prefix(candidates: &BTreeMap<String, (String, Entity)>) -> String {
225        let filter = |(c1, c2): &(char, char)| c1 == c2;
226        let choose = |(c1, c2): (char, char)| if c1.is_uppercase() { c1 } else { c2 };
227        let mut candidates = candidates.iter();
228        if let Some((key, (value, _))) = candidates.next() {
229            let search = key.to_string();
230            let mut replace = value.to_string();
231            for (key, (value, _)) in candidates {
232                let common = search.chars().zip(key.chars()).take_while(filter).count();
233                let value = &value[0..common];
234                replace = replace.chars().zip(value.chars()).map(choose).collect();
235            }
236            replace
237        } else {
238            String::new()
239        }
240    }
241
242    fn replace_prefix(candidate: (String, (String, Entity)), prefix: &str) -> String {
243        let (_, (mut value, _)) = candidate;
244        let length = prefix.len();
245        value.replace_range(0..length, prefix);
246        value
247    }
248
249    fn append_suffix(candidate: (String, (String, Entity))) -> String {
250        match candidate {
251            (_, (value, Entity::Default)) => value,
252            (_, (value, Entity::Procedure)) => value + "(",
253            (_, (value, Entity::Keyword)) => value + " ",
254            (_, (value, Entity::Function)) => value + "(",
255        }
256    }
257
258    pub fn get_databases(&self, context: Context) -> Vec<(&str, &str, &str)> {
259        let mut values = BTreeMap::new();
260        if let Some(database) = &context.database {
261            for (database_key, database_node) in self.objects.get_nodes(Entity::Default) {
262                let active = if database_node.as_str().eq_ignore_ascii_case(database) { "Y" } else { "" };
263                if context.schema.is_some() {
264                    for (schema_key, schema_node) in database_node.get_nodes(Entity::Default) {
265                        values.insert((database_key, schema_key.as_str()), (
266                            database_node.as_str(),
267                            schema_node.as_str(),
268                            active,
269                        ));
270                    }
271                } else {
272                    values.insert((database_key, ""), (database_node.as_str(), "", active));
273                }
274            }
275        }
276        values.into_values().collect()
277    }
278
279    pub fn get_tables(&self, context: Context, line: &str) -> Vec<(&str, &str, &str)> {
280        let (database, schema) = context.as_str();
281        let mut split = line.split_terminator('.');
282        if let Some(arg1) = split.next() {
283            if let Some(arg2) = split.next() {
284                if context.database.is_some() && context.schema.is_some() {
285                    self.inner_tables(arg1, arg2, Entity::Default).unwrap_or_default()
286                } else {
287                    Vec::new()
288                }
289            } else {
290                if context.database.is_some() && let Some(values) = self.inner_tables(arg1, schema, Entity::Default) {
291                    values
292                } else if context.schema.is_some() && let Some(values) = self.inner_tables(database, arg1, Entity::Default) {
293                    values
294                } else {
295                    Vec::new()
296                }
297            }
298        } else {
299            self.inner_tables(database, schema, Entity::Default).unwrap_or_default()
300        }
301    }
302
303    pub fn get_columns(
304        &self,
305        context: Context,
306        line: &str,
307    ) -> Vec<(&str, &str, &str, &str, Option<&Payload>)> {
308        let (database, schema) = context.as_str();
309        let mut split = line.split_terminator('.');
310        if let Some(arg1) = split.next() {
311            if let Some(arg2) = split.next() {
312                if let Some(arg3) = split.next() {
313                    if context.database.is_some() && context.schema.is_some() {
314                        self.inner_columns(arg1, arg2, arg3).unwrap_or_default()
315                    } else {
316                        Vec::new()
317                    }
318                } else {
319                    if context.database.is_some() && context.schema.is_some() && let Some(values) = self.inner_columns(arg1, arg2, "") {
320                        values
321                    } else if context.database.is_some() && let Some(values) = self.inner_columns(arg1, schema, arg2) {
322                        values
323                    } else if context.schema.is_some() && let Some(values) = self.inner_columns(database, arg1, arg2) {
324                        values
325                    } else {
326                        Vec::new()
327                    }
328                }
329            } else {
330                if context.database.is_some() && let Some(values) = self.inner_columns(arg1, schema, "") {
331                    values
332                } else if context.schema.is_some() && let Some(values) = self.inner_columns(database, arg1, "") {
333                    values
334                } else if let Some(values) = self.inner_columns(database, schema, arg1) {
335                    values
336                } else {
337                    Vec::new()
338                }
339            }
340        } else {
341            self.inner_columns(database, schema, "").unwrap_or_default()
342        }
343    }
344
345    pub fn get_procedures(&self, context: Context, line: &str) -> Vec<(&str, &str, &str)> {
346        let (database, schema) = context.as_str();
347        let mut split = line.split_terminator('.');
348        if let Some(arg1) = split.next() {
349            if let Some(arg2) = split.next() {
350                if context.database.is_some() && context.schema.is_some() {
351                    self.inner_tables(arg1, arg2, Entity::Procedure).unwrap_or_default()
352                } else {
353                    Vec::new()
354                }
355            } else {
356                if context.database.is_some() && let Some(values) = self.inner_tables(arg1, schema, Entity::Procedure) {
357                    values
358                } else if context.schema.is_some() && let Some(values) = self.inner_tables(database, arg1, Entity::Procedure) {
359                    values
360                } else {
361                    Vec::new()
362                }
363            }
364        } else {
365            self.inner_tables(database, schema, Entity::Procedure).unwrap_or_default()
366        }
367    }
368
369    fn inner_tables(
370        &self,
371        database: &str,
372        schema: &str,
373        entity: Entity,
374    ) -> Option<Vec<(&str, &str, &str)>> {
375        let database = database.to_lowercase();
376        let schema = schema.to_lowercase();
377        if let Some(database_node) = self.objects.get_child(&[&database]) {
378            if let Some(schema_node) = database_node.get_child(&[&schema]) {
379                let mut values = BTreeMap::new();
380                let database_name = if !database.is_empty() { database_node.as_str() } else { "" };
381                let schema_name = if !schema.is_empty() { schema_node.as_str() } else { "" };
382                for (table_key, table_node) in schema_node.get_nodes(entity) {
383                    values.insert(table_key, (database_name, schema_name, table_node.as_str()));
384                }
385                let values = values.into_values().collect();
386                return Some(values);
387            }
388        }
389        None
390    }
391
392    fn inner_columns(
393        &self,
394        database: &str,
395        schema: &str,
396        table: &str,
397    ) -> Option<Vec<(&str, &str, &str, &str, Option<&Payload>)>> {
398        let database = database.to_lowercase();
399        let schema = schema.to_lowercase();
400        if let Some(database_node) = self.objects.get_child(&[&database]) {
401            if let Some(schema_node) = database_node.get_child(&[&schema]) {
402                let mut values = BTreeMap::new();
403                let database_name = if !database.is_empty() { database_node.as_str() } else { "" };
404                let schema_name = if !schema.is_empty() { schema_node.as_str() } else { "" };
405                for (table_key, table_node) in schema_node.get_nodes(Entity::Default) {
406                    if table.is_empty() || table_node.as_str().eq_ignore_ascii_case(table) {
407                        for (column_key, column_node) in table_node.get_nodes(Entity::Default) {
408                            let payload = column_node.get_payload();
409                            let index = payload.map(|p| p.index).unwrap_or_default();
410                            values.insert((table_key, index, column_key), (
411                                database_name,
412                                schema_name,
413                                table_node.as_str(),
414                                column_node.as_str(),
415                                payload,
416                            ));
417                        }
418                    }
419                }
420                let values = values.into_values().collect();
421                return Some(values);
422            }
423        }
424        None
425    }
426
427    pub fn get_keywords(&self) -> Vec<&str> {
428        let mut values = BTreeMap::new();
429        for (key, node) in self.keywords.get_nodes(Entity::Keyword) {
430            values.insert(key, node.as_str());
431        }
432        values.into_values().collect()
433    }
434
435    pub fn get_functions(&self) -> Vec<&str> {
436        let mut values = BTreeMap::new();
437        for (key, node) in self.keywords.get_nodes(Entity::Function) {
438            values.insert(key, node.as_str());
439        }
440        values.into_values().collect()
441    }
442}
443
444fn get_offset(stub: &str, token: &str) -> usize {
445    stub
446        .len()
447        .checked_sub(token.len())
448        .unwrap_or_default()
449}
450
451// noinspection DuplicatedCode
452#[cfg(test)]
453pub mod tests {
454    use crate::core::case::Payload;
455    use crate::core::catalog::Catalog;
456    use crate::db::context::Context;
457    use crate::{cow_str, str_vec};
458    use odbc_api::Nullability;
459    use odbc_api::Nullability::{NoNulls, Nullable};
460    use pretty_assertions::assert_eq;
461    use std::borrow::Cow;
462
463    #[test]
464    fn test_directive_at_start_is_completed() {
465        // Accept a directive verb.
466        let catalog = create_catalog(Model::TSQL);
467        assert_eq!(complete_line(&catalog, (None, None), ":"), (0, str_vec![
468            ":format",
469            ":show",
470        ]));
471        assert_eq!(complete_line(&catalog, (None, None), ":FOr"), (0, str_vec![
472            ":format ",
473        ]));
474        assert_eq!(complete_line(&catalog, (None, None), ":ZZz"), (0, str_vec![]));
475    }
476
477    #[test]
478    fn test_directive_and_noun_is_completed() {
479        // Accept a directive noun according to the verb.
480        let catalog = create_catalog(Model::TSQL);
481        assert_eq!(complete_line(&catalog, (None, None), ":FOrmat "), (8, str_vec![
482            "flatten",
483            "table",
484        ]));
485        assert_eq!(complete_line(&catalog, (None, None), ":FOrmat TAb"), (8, str_vec![
486            "table ",
487        ]));
488        assert_eq!(complete_line(&catalog, (None, None), ":FOrmat ZZz"), (8, str_vec![]));
489    }
490
491    #[test]
492    fn test_directive_in_middle_not_completed() {
493        // Reject a directive verb if not at start of line.
494        let catalog = create_catalog(Model::TSQL);
495        assert_eq!(complete_line(&catalog, (None, None), "SELECT :"), (7, str_vec![]));
496        assert_eq!(complete_line(&catalog, (None, None), "SELECT :FOr"), (8, str_vec![]));
497        assert_eq!(complete_line(&catalog, (None, None), "SELECT :ZZz"), (8, str_vec![]));
498    }
499
500    #[test]
501    fn test_single_keyword_is_completed() {
502        // Accept any database object, SQL keyword or function.
503        let catalog = create_catalog(Model::TSQL);
504        assert_eq!(complete_line(&catalog, (Some("ARchive"), Some("MAin")), "<A"), (1, str_vec![
505            "ABS",
506            "ADD",
507            "ALTER",
508            "AND",
509            "Announce", // Archive.Main.Announce
510            "Appendix", // Archive.Main.Article.Appendix
511            "Archive", // Archive
512            "Article", // Archive.Main.Article
513            "AS",
514            "Author", // Archive.Main.Article.Author
515            "Available", // Archive.Main.Journal.Available
516        ]));
517        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<A"), (1, str_vec![
518            "ABS",
519            "Account", // Business.Main.Account
520            "Active", // Business.Main.Account.Active
521            "ADD",
522            "Address", // Business.Main.Person.Address
523            "Admin", // Business.Admin
524            "Advertise", // Business.Main.Advertise
525            "ALTER",
526            "Amount", // Business.Main.Account.Amount
527            "AND",
528            "Archive", // Archive
529            "AS",
530        ]));
531        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("MAin")), "<A"), (1, str_vec![
532            "ABS",
533            "ADD",
534            "ALTER",
535            "AND",
536            "Archive", // Archive
537            "AS",
538        ]));
539        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<SEl"), (1, str_vec![
540            "SELECT ",
541        ]));
542        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<COu"), (1, str_vec![
543            "COUNT(",
544        ]));
545        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ADv"), (1, str_vec![
546            "Advertise(", // Business.Main.Advertise
547        ]));
548        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ARch"), (1, str_vec![
549            "Archive", // Archive
550        ]));
551        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ADm"), (1, str_vec![
552            "Admin", // Business.Admin
553        ]));
554        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<PEr"), (1, str_vec![
555            "Person", // Business.Main.Person
556        ]));
557        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<FOre"), (1, str_vec![
558            "Forename", // Business.Main.Person.Forename
559        ]));
560        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ZZz"), (1, str_vec![]));
561    }
562
563    #[test]
564    fn test_database_schema_is_completed() {
565        // Accept any schema in the named database.
566        let catalog = create_catalog(Model::TSQL);
567        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness."), (10, str_vec![
568            "Admin", // Business.Admin
569            "Main", // Business.Main
570        ]));
571        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.A"), (10, str_vec![
572            "Admin", // Business.Admin
573        ]));
574        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.SEl"), (10, str_vec![]));
575        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.COu"), (10, str_vec![]));
576        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.ADv"), (10, str_vec![]));
577        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.ARch"), (10, str_vec![]));
578        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.ADm"), (10, str_vec![
579            "Admin", // Business.Admin
580        ]));
581        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.PEr"), (10, str_vec![]));
582        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.FOre"), (10, str_vec![]));
583        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.ZZz"), (10, str_vec![]));
584    }
585
586    #[test]
587    fn test_schema_table_is_completed() {
588        // Accept any table or procedure in the named schema.
589        let catalog = create_catalog(Model::TSQL);
590        assert_eq!(complete_line(&catalog, (Some("ARchive"), Some("UNknown")), "<MAin."), (6, str_vec![
591            "Announce", // Archive.Main.Announce
592            "Article", // Archive.Main.Article
593            "Journal", // Archive.Main.Journal
594        ]));
595        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin."), (6, str_vec![
596            "Account", // Business.Main.Account
597            "Advertise", // Business.Main.Advertise
598            "Person", // Business.Main.Person
599            "Recruit", // Business.Main.Recruit
600        ]));
601        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<MAin."), (6, str_vec![]));
602        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.SEl"), (6, str_vec![]));
603        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.COu"), (6, str_vec![]));
604        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ADv"), (6, str_vec![
605            "Advertise(", // Business.Main.Advertise
606        ]));
607        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ARch"), (6, str_vec![]));
608        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ADm"), (6, str_vec![]));
609        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.PEr"), (6, str_vec![
610            "Person", // Business.Main.Person
611        ]));
612        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.FOre"), (6, str_vec![]));
613        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ZZz"), (6, str_vec![]));
614    }
615
616    #[test]
617    fn test_database_schema_table_is_completed() {
618        // Accept any table or procedure in the named schema.
619        let catalog = create_catalog(Model::TSQL);
620        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin."), (15, str_vec![
621            "Account", // Business.Main.Account
622            "Advertise", // Business.Main.Advertise
623            "Person", // Business.Main.Person
624            "Recruit", // Business.Main.Recruit
625        ]));
626        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.A"), (15, str_vec![
627            "Account", // Business.Main.Account
628            "Advertise", // Business.Main.Advertise
629        ]));
630        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.SEl"), (15, str_vec![]));
631        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.COu"), (15, str_vec![]));
632        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ADv"), (15, str_vec![
633            "Advertise(", // Business.Main.Advertise
634        ]));
635        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ARch"), (15, str_vec![]));
636        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ADm"), (15, str_vec![]));
637        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.PEr"), (15, str_vec![
638            "Person", // Business.Main.Person
639        ]));
640        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.FOre"), (15, str_vec![]));
641        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ZZz"), (15, str_vec![]));
642    }
643
644    #[test]
645    fn test_table_column_is_completed() {
646        // Accept any column in the named table.
647        let catalog = create_catalog(Model::TSQL);
648        assert_eq!(complete_line(&catalog, (Some("ARchive"), Some("MAin")), "<ARticle.A"), (9, str_vec![
649            "Appendix", // Archive.Main.Article.Appendix
650            "Author", // Archive.Main.Article.Author
651        ]));
652        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.A"), (9, str_vec![
653            "Active", // Business.Main.Account.Active
654            "Amount", // Business.Main.Account.Amount
655        ]));
656        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("MAin")), "<ACcount.A"), (9, str_vec![]));
657        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.SEl"), (9, str_vec![]));
658        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.COu"), (9, str_vec![]));
659        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.ADv"), (9, str_vec![]));
660        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.ARch"), (9, str_vec![]));
661        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.ADm"), (9, str_vec![]));
662        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.PEr"), (9, str_vec![]));
663        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.FOre"), (9, str_vec![]));
664        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<ACcount.ZZz"), (9, str_vec![]));
665    }
666
667    #[test]
668    fn test_schema_table_column_is_completed() {
669        // Accept any column in the named schema and table.
670        let catalog = create_catalog(Model::TSQL);
671        assert_eq!(complete_line(&catalog, (Some("ARchive"), Some("UNknown")), "<MAin.ACcount.A"), (14, str_vec![]));
672        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.A"), (14, str_vec![
673            "Active", // Business.Main.Account.Active
674            "Amount", // Business.Main.Account.Amount
675        ]));
676        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<MAin.ACcount.A"), (14, str_vec![]));
677        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.SEl"), (14, str_vec![]));
678        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.COu"), (14, str_vec![]));
679        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.ADv"), (14, str_vec![]));
680        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.ARch"), (14, str_vec![]));
681        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.ADm"), (14, str_vec![]));
682        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.PEr"), (14, str_vec![]));
683        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.FOre"), (14, str_vec![]));
684        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("UNknown")), "<MAin.ACcount.ZZz"), (14, str_vec![]));
685    }
686
687    #[test]
688    fn test_database_schema_table_column_is_completed() {
689        // Accept any column in the named database, schema and table.
690        let catalog = create_catalog(Model::TSQL);
691        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount."), (23, str_vec![
692            "Active", // Business.Main.Account.Active
693            "Amount", // Business.Main.Account.Amount
694            "Branch", // Business.Main.Account.Branch
695            "Company", // Business.Main.Account.Company
696        ]));
697        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.A"), (23, str_vec![
698            "Active", // Business.Main.Account.Active
699            "Amount", // Business.Main.Account.Amount
700        ]));
701        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.SEl"), (23, str_vec![]));
702        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.COu"), (23, str_vec![]));
703        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.ADv"), (23, str_vec![]));
704        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.ARch"), (23, str_vec![]));
705        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.ADm"), (23, str_vec![]));
706        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.PEr"), (23, str_vec![]));
707        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.FOre"), (23, str_vec![]));
708        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("UNknown")), "<BUsiness.MAin.ACcount.ZZz"), (23, str_vec![]));
709    }
710
711    #[test]
712    fn test_unknown_column_is_completed() {
713        // Accept any column in any table in the active database.
714        let catalog = create_catalog(Model::TSQL);
715        assert_eq!(complete_line(&catalog, (Some("ARchive"), Some("MAin")), "<UNknown.A"), (9, str_vec![
716            "Appendix", // Archive.Main.Article.Appendix
717            "Author", // Archive.Main.Article.Author
718            "Available", // Archive.Main.Journal.Available
719        ]));
720        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.A"), (9, str_vec![
721            "Active", // Business.Main.Account.Active
722            "Address", // Business.Main.Person.Address
723            "Amount", // Business.Main.Account.Amount
724        ]));
725        assert_eq!(complete_line(&catalog, (Some("UNknown"), Some("MAin")), "<UNknown.A"), (9, str_vec![]));
726        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.SEl"), (9, str_vec![]));
727        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.COu"), (9, str_vec![]));
728        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.ADv"), (9, str_vec![]));
729        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.ARch"), (9, str_vec![]));
730        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.ADm"), (9, str_vec![]));
731        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.PEr"), (9, str_vec![]));
732        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.FOre"), (9, str_vec![
733            "Forename", // Business.Main.Person.Forename
734        ]));
735        assert_eq!(complete_line(&catalog, (Some("BUsiness"), Some("MAin")), "<UNknown.ZZz"), (9, str_vec![]));
736    }
737
738    #[test]
739    fn test_prefix_is_replaced_on_completion() {
740        let mut catalog = Catalog::new();
741        catalog.insert_keyword(cow_str!("ACTION"));
742        catalog.insert_keyword(cow_str!("Activate"));
743        catalog.insert_keyword(cow_str!("Active"));
744        catalog.insert_keyword(cow_str!("Passive"));
745        assert_eq!(complete_line(&catalog, (None, None), "<AC"), (1, str_vec!(
746            "ACTION",
747            "ACTIvate",
748            "ACTIve",
749        )));
750    }
751
752    #[test]
753    fn test_databases_are_returned_for_show_with_tsql_model() {
754        let catalog = create_catalog(Model::TSQL);
755        assert_eq!(catalog.get_databases(Context::new(Some("ARchive"), Some("UNknown"))), vec![
756            ("Archive", "Main", "Y"),
757            ("Business", "Admin", ""),
758            ("Business", "Main", ""),
759        ]);
760        assert_eq!(catalog.get_databases(Context::new(Some("BUsiness"), Some("UNknown"))), vec![
761            ("Archive", "Main", ""),
762            ("Business", "Admin", "Y"),
763            ("Business", "Main", "Y"),
764        ]);
765        assert_eq!(catalog.get_databases(Context::new(Some("UNknown"), Some("UNknown"))), vec![
766            ("Archive", "Main", ""),
767            ("Business", "Admin", ""),
768            ("Business", "Main", ""),
769        ]);
770    }
771
772    #[test]
773    fn test_databases_are_returned_for_show_with_mysql_model() {
774        let catalog = create_catalog(Model::MySQL);
775        assert_eq!(catalog.get_databases(Context::new(Some("ARchive"), None)), vec![
776            ("Archive", "", "Y"),
777            ("Business", "", ""),
778        ]);
779        assert_eq!(catalog.get_databases(Context::new(Some("BUsiness"), None)), vec![
780            ("Archive", "", ""),
781            ("Business", "", "Y"),
782        ]);
783        assert_eq!(catalog.get_databases(Context::new(Some("UNknown"), None)), vec![
784            ("Archive", "", ""),
785            ("Business", "", ""),
786        ]);
787    }
788
789    #[test]
790    fn test_databases_are_returned_for_show_with_sqlite_model() {
791        let catalog = create_catalog(Model::Sqlite);
792        assert_eq!(catalog.get_databases(Context::default()).is_empty(), true);
793    }
794
795    #[test]
796    fn test_tables_are_returned_for_show_with_tsql_model() {
797        let catalog = create_catalog(Model::TSQL);
798        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), Some("MAin")), ""), vec![
799            ("Business", "Main", "Account"),
800            ("Business", "Main", "Person"),
801        ]);
802        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), Some("MAin")), "ADmin"), vec![
803            ("Business", "Admin", "Server"),
804        ]);
805        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), Some("MAin")), "ARchive"), vec![
806            ("Archive", "Main", "Article"),
807            ("Archive", "Main", "Journal"),
808        ]);
809        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), Some("MAin")), "ARchive.MAin"), vec![
810            ("Archive", "Main", "Article"),
811            ("Archive", "Main", "Journal"),
812        ]);
813        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), Some("MAin")), "UNknown").is_empty(), true);
814    }
815
816    #[test]
817    fn test_tables_are_returned_for_show_with_mysql_model() {
818        let catalog = create_catalog(Model::MySQL);
819        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), None), ""), vec![
820            ("Business", "", "Account"),
821            ("Business", "", "Person"),
822        ]);
823        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), None), "ARchive"), vec![
824            ("Archive", "", "Article"),
825            ("Archive", "", "Journal"),
826        ]);
827        assert_eq!(catalog.get_tables(Context::new(Some("BUsiness"), None), "UNknown").is_empty(), true);
828    }
829
830    #[test]
831    fn test_tables_are_returned_for_show_with_sqlite_model() {
832        let catalog = create_catalog(Model::Sqlite);
833        assert_eq!(catalog.get_tables(Context::default(), ""), vec![
834            ("", "", "Account"),
835            ("", "", "Person"),
836        ]);
837        assert_eq!(catalog.get_tables(Context::default(), "UNknown").is_empty(), true);
838    }
839
840    #[test]
841    fn test_columns_are_returned_for_show_with_tsql_model() {
842        let catalog = create_catalog(Model::TSQL);
843        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), ""), vec![
844            ("Business", "Main", "Account", "Company", "VARCHAR(20)", NoNulls, 1),
845            ("Business", "Main", "Account", "Branch", "VARCHAR(20)", NoNulls, 2),
846            ("Business", "Main", "Account", "Amount", "DECIMAL(10, 2)", NoNulls, 3),
847            ("Business", "Main", "Account", "Active", "BIT", NoNulls, 4),
848            ("Business", "Main", "Person", "Forename", "VARCHAR(20)", NoNulls, 1),
849            ("Business", "Main", "Person", "Surname", "VARCHAR(20)", NoNulls, 2),
850            ("Business", "Main", "Person", "Address", "VARCHAR(20)", Nullable, 3),
851            ("Business", "Main", "Person", "Birthday", "DATE", Nullable, 4),
852        ]);
853        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "PErson"), vec![
854            ("Business", "Main", "Person", "Forename", "VARCHAR(20)", NoNulls, 1),
855            ("Business", "Main", "Person", "Surname", "VARCHAR(20)", NoNulls, 2),
856            ("Business", "Main", "Person", "Address", "VARCHAR(20)", Nullable, 3),
857            ("Business", "Main", "Person", "Birthday", "DATE", Nullable, 4),
858        ]);
859        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "ADmin"), vec![
860            ("Business", "Admin", "Server", "Name", "VARCHAR(20)", NoNulls, 1),
861            ("Business", "Admin", "Server", "Location", "VARCHAR(20)", NoNulls, 2),
862            ("Business", "Admin", "Server", "Hardware", "VARCHAR(20)", NoNulls, 3),
863        ]);
864        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "ADmin.SErver"), vec![
865            ("Business", "Admin", "Server", "Name", "VARCHAR(20)", NoNulls, 1),
866            ("Business", "Admin", "Server", "Location", "VARCHAR(20)", NoNulls, 2),
867            ("Business", "Admin", "Server", "Hardware", "VARCHAR(20)", NoNulls, 3),
868        ]);
869        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "ARchive"), vec![
870            ("Archive", "Main", "Article", "Journal", "INTEGER", NoNulls, 1),
871            ("Archive", "Main", "Article", "Date", "DATE", NoNulls, 2),
872            ("Archive", "Main", "Article", "Author", "VARCHAR(20)", NoNulls, 3),
873            ("Archive", "Main", "Article", "Text", "VARCHAR(4000)", NoNulls, 4),
874            ("Archive", "Main", "Article", "Appendix", "VARCHAR(4000)", Nullable, 5),
875            ("Archive", "Main", "Journal", "Country", "INTEGER", NoNulls, 1),
876            ("Archive", "Main", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2),
877            ("Archive", "Main", "Journal", "Available", "BIT", NoNulls, 3),
878        ]);
879        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "ARchive.JOurnal"), vec![
880            ("Archive", "Main", "Journal", "Country", "INTEGER", NoNulls, 1),
881            ("Archive", "Main", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2),
882            ("Archive", "Main", "Journal", "Available", "BIT", NoNulls, 3),
883        ]);
884        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "ARchive.MAin"), vec![
885            ("Archive", "Main", "Article", "Journal", "INTEGER", NoNulls, 1),
886            ("Archive", "Main", "Article", "Date", "DATE", NoNulls, 2),
887            ("Archive", "Main", "Article", "Author", "VARCHAR(20)", NoNulls, 3),
888            ("Archive", "Main", "Article", "Text", "VARCHAR(4000)", NoNulls, 4),
889            ("Archive", "Main", "Article", "Appendix", "VARCHAR(4000)", Nullable, 5),
890            ("Archive", "Main", "Journal", "Country", "INTEGER", NoNulls, 1),
891            ("Archive", "Main", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2),
892            ("Archive", "Main", "Journal", "Available", "BIT", NoNulls, 3),
893        ]);
894        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "ARchive.MAin.JOurnal"), vec![
895            ("Archive", "Main", "Journal", "Country", "INTEGER", NoNulls, 1),
896            ("Archive", "Main", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2),
897            ("Archive", "Main", "Journal", "Available", "BIT", NoNulls, 3),
898        ]);
899        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), Some("MAin")), "UNknown").is_empty(), true);
900    }
901
902    #[test]
903    fn test_columns_are_returned_for_show_with_mysql_model() {
904        let catalog = create_catalog(Model::MySQL);
905        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), None), ""), vec![
906            ("Business", "", "Account", "Company", "VARCHAR(20)", NoNulls, 1),
907            ("Business", "", "Account", "Branch", "VARCHAR(20)", NoNulls, 2),
908            ("Business", "", "Account", "Amount", "DECIMAL(10, 2)", NoNulls, 3),
909            ("Business", "", "Account", "Active", "BIT", NoNulls, 4),
910            ("Business", "", "Person", "Forename", "VARCHAR(20)", NoNulls, 1),
911            ("Business", "", "Person", "Surname", "VARCHAR(20)", NoNulls, 2),
912            ("Business", "", "Person", "Address", "VARCHAR(20)", Nullable, 3),
913            ("Business", "", "Person", "Birthday", "DATE", Nullable, 4),
914        ]);
915        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), None), "PErson"), vec![
916            ("Business", "", "Person", "Forename", "VARCHAR(20)", NoNulls, 1),
917            ("Business", "", "Person", "Surname", "VARCHAR(20)", NoNulls, 2),
918            ("Business", "", "Person", "Address", "VARCHAR(20)", Nullable, 3),
919            ("Business", "", "Person", "Birthday", "DATE", Nullable, 4),
920        ]);
921        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), None), "ARchive"), vec![
922            ("Archive", "", "Article", "Journal", "INTEGER", NoNulls, 1),
923            ("Archive", "", "Article", "Date", "DATE", NoNulls, 2),
924            ("Archive", "", "Article", "Author", "VARCHAR(20)", NoNulls, 3),
925            ("Archive", "", "Article", "Text", "VARCHAR(4000)", NoNulls, 4),
926            ("Archive", "", "Article", "Appendix", "VARCHAR(4000)", Nullable, 5),
927            ("Archive", "", "Journal", "Country", "INTEGER", NoNulls, 1),
928            ("Archive", "", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2),
929            ("Archive", "", "Journal", "Available", "BIT", NoNulls, 3),
930        ]);
931        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), None), "ARchive.JOurnal"), vec![
932            ("Archive", "", "Journal", "Country", "INTEGER", NoNulls, 1),
933            ("Archive", "", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2),
934            ("Archive", "", "Journal", "Available", "BIT", NoNulls, 3),
935        ]);
936        assert_eq!(get_columns(&catalog, Context::new(Some("BUsiness"), None), "UNknown").is_empty(), true);
937    }
938
939    #[test]
940    fn test_columns_are_returned_for_show_with_sqlite_model() {
941        let catalog = create_catalog(Model::Sqlite);
942        assert_eq!(get_columns(&catalog, Context::default(), ""), vec![
943            ("", "", "Account", "Company", "VARCHAR(20)", NoNulls, 1),
944            ("", "", "Account", "Branch", "VARCHAR(20)", NoNulls, 2),
945            ("", "", "Account", "Amount", "DECIMAL(10, 2)", NoNulls, 3),
946            ("", "", "Account", "Active", "BIT", NoNulls, 4),
947            ("", "", "Person", "Forename", "VARCHAR(20)", NoNulls, 1),
948            ("", "", "Person", "Surname", "VARCHAR(20)", NoNulls, 2),
949            ("", "", "Person", "Address", "VARCHAR(20)", Nullable, 3),
950            ("", "", "Person", "Birthday", "DATE", Nullable, 4),
951        ]);
952        assert_eq!(get_columns(&catalog, Context::default(), "PErson"), vec![
953            ("", "", "Person", "Forename", "VARCHAR(20)", NoNulls, 1),
954            ("", "", "Person", "Surname", "VARCHAR(20)", NoNulls, 2),
955            ("", "", "Person", "Address", "VARCHAR(20)", Nullable, 3),
956            ("", "", "Person", "Birthday", "DATE", Nullable, 4),
957        ]);
958        assert_eq!(get_columns(&catalog, Context::default(), "UNknown").is_empty(), true);
959    }
960
961    #[test]
962    fn test_procedures_are_returned_for_show_with_tsql_model() {
963        let catalog = create_catalog(Model::TSQL);
964        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), Some("MAin")), ""), vec![
965            ("Business", "Main", "Advertise"),
966            ("Business", "Main", "Recruit"),
967        ]);
968        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), Some("MAin")), "ADmin"), vec![
969            ("Business", "Admin", "Activate"),
970        ]);
971        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), Some("MAin")), "ARchive"), vec![
972            ("Archive", "Main", "Announce"),
973        ]);
974        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), Some("MAin")), "ARchive.MAin"), vec![
975            ("Archive", "Main", "Announce"),
976        ]);
977        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), Some("MAin")), "UNknown").is_empty(), true);
978    }
979
980    #[test]
981    fn test_procedures_are_returned_for_show_with_mysql_model() {
982        let catalog = create_catalog(Model::MySQL);
983        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), None), ""), vec![
984            ("Business", "", "Advertise"),
985            ("Business", "", "Recruit"),
986        ]);
987        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), None), "ARchive"), vec![
988            ("Archive", "", "Announce"),
989        ]);
990        assert_eq!(catalog.get_procedures(Context::new(Some("BUsiness"), None), "UNknown").is_empty(), true);
991    }
992
993    #[test]
994    fn test_procedures_are_returned_for_show_with_sqlite_model() {
995        let catalog = create_catalog(Model::Sqlite);
996        assert_eq!(catalog.get_procedures(Context::default(), ""), vec![
997            ("", "", "Advertise"),
998            ("", "", "Recruit"),
999        ]);
1000        assert_eq!(catalog.get_procedures(Context::default(), "UNknown").is_empty(), true);
1001    }
1002
1003    #[test]
1004    fn test_keywords_are_returned_for_show() {
1005        let catalog = create_catalog(Model::TSQL);
1006        assert_eq!(catalog.get_keywords(), vec![
1007            "ADD",
1008            "ALTER",
1009            "AND",
1010            "AS",
1011            "SELECT",
1012            "SET",
1013        ]);
1014    }
1015
1016    #[test]
1017    fn test_functions_are_returned_for_show() {
1018        let catalog = create_catalog(Model::TSQL);
1019        assert_eq!(catalog.get_functions(), vec![
1020            "ABS",
1021            "COUNT",
1022        ]);
1023    }
1024
1025    fn complete_line(
1026        catalog: &Catalog,
1027        context: (Option<&str>, Option<&str>),
1028        line: &str,
1029    ) -> (usize, Vec<String>) {
1030        let (database, schema) = context;
1031        let context = Context::new(database, schema);
1032        catalog.complete_line(context, line, line.len())
1033    }
1034
1035    fn get_columns<'a>(
1036        catalog: &'a Catalog,
1037        context: Context,
1038        line: &str,
1039    ) -> Vec<(&'a str, &'a str, &'a str, &'a str, &'a str, Nullability, usize)> {
1040        let mut transformed = Vec::new();
1041        for (database, schema, table, column, payload) in catalog.get_columns(context, line) {
1042            if let Some(payload) = payload {
1043                let Payload { dtype, null, index } = payload;
1044                transformed.push((database, schema, table, column, dtype.as_str(), *null, *index));
1045            }
1046        }
1047        transformed
1048    }
1049
1050    enum Model {
1051        Sqlite,
1052        MySQL,
1053        TSQL,
1054    }
1055
1056    impl Model {
1057        fn includes_database(&self) -> bool {
1058            match self {
1059                Self::Sqlite => false,
1060                Self::MySQL => true,
1061                Self::TSQL => true,
1062            }
1063        }
1064
1065        fn includes_schema(&self) -> bool {
1066            match self {
1067                Self::Sqlite => false,
1068                Self::MySQL => false,
1069                Self::TSQL => true,
1070            }
1071        }
1072    }
1073
1074    fn create_catalog(model: Model) -> Catalog {
1075        // Define directives.
1076        let mut catalog = Catalog::new();
1077        insert_directive(&mut catalog, ":format", "table");
1078        insert_directive(&mut catalog, ":format", "flatten");
1079        insert_directive(&mut catalog, ":show", "databases");
1080        insert_directive(&mut catalog, ":show", "tables");
1081        insert_directive(&mut catalog, ":show", "columns");
1082        insert_directive(&mut catalog, ":show", "procedures");
1083        insert_directive(&mut catalog, ":show", "keywords");
1084        insert_directive(&mut catalog, ":show", "functions");
1085        if model.includes_database() {
1086            if model.includes_schema() {
1087                // Define archive database.
1088                insert_database(&mut catalog, "Archive", "Main");
1089                insert_table(&mut catalog, "Archive", "Main", "Article");
1090                insert_column(&mut catalog, "Archive", "Main", "Article", "Journal", "INTEGER", NoNulls, 1);
1091                insert_column(&mut catalog, "Archive", "Main", "Article", "Date", "DATE", NoNulls, 2);
1092                insert_column(&mut catalog, "Archive", "Main", "Article", "Author", "VARCHAR(20)", NoNulls, 3);
1093                insert_column(&mut catalog, "Archive", "Main", "Article", "Text", "VARCHAR(4000)", NoNulls, 4);
1094                insert_column(&mut catalog, "Archive", "Main", "Article", "Appendix", "VARCHAR(4000)", Nullable, 5);
1095                insert_table(&mut catalog, "Archive", "Main", "Journal");
1096                insert_column(&mut catalog, "Archive", "Main", "Journal", "Country", "INTEGER", NoNulls, 1);
1097                insert_column(&mut catalog, "Archive", "Main", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2);
1098                insert_column(&mut catalog, "Archive", "Main", "Journal", "Available", "BIT", NoNulls, 3);
1099                insert_procedure(&mut catalog, "Archive", "Main", "Announce");
1100                // Define business database.
1101                insert_database(&mut catalog, "Business", "Admin");
1102                insert_table(&mut catalog, "Business", "Admin", "Server");
1103                insert_column(&mut catalog, "Business", "Admin", "Server", "Name", "VARCHAR(20)", NoNulls, 1);
1104                insert_column(&mut catalog, "Business", "Admin", "Server", "Location", "VARCHAR(20)", NoNulls, 2);
1105                insert_column(&mut catalog, "Business", "Admin", "Server", "Hardware", "VARCHAR(20)", NoNulls, 3);
1106                insert_procedure(&mut catalog, "Business", "Admin", "Activate");
1107                insert_database(&mut catalog, "Business", "Main");
1108                insert_table(&mut catalog, "Business", "Main", "Account");
1109                insert_column(&mut catalog, "Business", "Main", "Account", "Company", "VARCHAR(20)", NoNulls, 1);
1110                insert_column(&mut catalog, "Business", "Main", "Account", "Branch", "VARCHAR(20)", NoNulls, 2);
1111                insert_column(&mut catalog, "Business", "Main", "Account", "Amount", "DECIMAL(10, 2)", NoNulls, 3);
1112                insert_column(&mut catalog, "Business", "Main", "Account", "Active", "BIT", NoNulls, 4);
1113                insert_table(&mut catalog, "Business", "Main", "Person");
1114                insert_column(&mut catalog, "Business", "Main", "Person", "Forename", "VARCHAR(20)", NoNulls, 1);
1115                insert_column(&mut catalog, "Business", "Main", "Person", "Surname", "VARCHAR(20)", NoNulls, 2);
1116                insert_column(&mut catalog, "Business", "Main", "Person", "Address", "VARCHAR(20)", Nullable, 3);
1117                insert_column(&mut catalog, "Business", "Main", "Person", "Birthday", "DATE", Nullable, 4);
1118                insert_procedure(&mut catalog, "Business", "Main", "Advertise");
1119                insert_procedure(&mut catalog, "Business", "Main", "Recruit");
1120            } else {
1121                // Define archive database.
1122                insert_database(&mut catalog, "Archive", "");
1123                insert_table(&mut catalog, "Archive", "", "Article");
1124                insert_column(&mut catalog, "Archive", "", "Article", "Journal", "INTEGER", NoNulls, 1);
1125                insert_column(&mut catalog, "Archive", "", "Article", "Date", "DATE", NoNulls, 2);
1126                insert_column(&mut catalog, "Archive", "", "Article", "Author", "VARCHAR(20)", NoNulls, 3);
1127                insert_column(&mut catalog, "Archive", "", "Article", "Text", "VARCHAR(4000)", NoNulls, 4);
1128                insert_column(&mut catalog, "Archive", "", "Article", "Appendix", "VARCHAR(4000)", Nullable, 5);
1129                insert_table(&mut catalog, "Archive", "", "Journal");
1130                insert_column(&mut catalog, "Archive", "", "Journal", "Country", "INTEGER", NoNulls, 1);
1131                insert_column(&mut catalog, "Archive", "", "Journal", "Price", "DECIMAL(10, 2)", NoNulls, 2);
1132                insert_column(&mut catalog, "Archive", "", "Journal", "Available", "BIT", NoNulls, 3);
1133                insert_procedure(&mut catalog, "Archive", "", "Announce");
1134                // Define business database.
1135                insert_database(&mut catalog, "Business", "");
1136                insert_table(&mut catalog, "Business", "", "Account");
1137                insert_column(&mut catalog, "Business", "", "Account", "Company", "VARCHAR(20)", NoNulls, 1);
1138                insert_column(&mut catalog, "Business", "", "Account", "Branch", "VARCHAR(20)", NoNulls, 2);
1139                insert_column(&mut catalog, "Business", "", "Account", "Amount", "DECIMAL(10, 2)", NoNulls, 3);
1140                insert_column(&mut catalog, "Business", "", "Account", "Active", "BIT", NoNulls, 4);
1141                insert_table(&mut catalog, "Business", "", "Person");
1142                insert_column(&mut catalog, "Business", "", "Person", "Forename", "VARCHAR(20)", NoNulls, 1);
1143                insert_column(&mut catalog, "Business", "", "Person", "Surname", "VARCHAR(20)", NoNulls, 2);
1144                insert_column(&mut catalog, "Business", "", "Person", "Address", "VARCHAR(20)", Nullable, 3);
1145                insert_column(&mut catalog, "Business", "", "Person", "Birthday", "DATE", Nullable, 4);
1146                insert_procedure(&mut catalog, "Business", "", "Advertise");
1147                insert_procedure(&mut catalog, "Business", "", "Recruit");
1148            }
1149        } else {
1150            // Define business database.
1151            insert_table(&mut catalog, "", "", "Account");
1152            insert_column(&mut catalog, "", "", "Account", "Company", "VARCHAR(20)", NoNulls, 1);
1153            insert_column(&mut catalog, "", "", "Account", "Branch", "VARCHAR(20)", NoNulls, 2);
1154            insert_column(&mut catalog, "", "", "Account", "Amount", "DECIMAL(10, 2)", NoNulls, 3);
1155            insert_column(&mut catalog, "", "", "Account", "Active", "BIT", NoNulls, 4);
1156            insert_table(&mut catalog, "", "", "Person");
1157            insert_column(&mut catalog, "", "", "Person", "Forename", "VARCHAR(20)", NoNulls, 1);
1158            insert_column(&mut catalog, "", "", "Person", "Surname", "VARCHAR(20)", NoNulls, 2);
1159            insert_column(&mut catalog, "", "", "Person", "Address", "VARCHAR(20)", Nullable, 3);
1160            insert_column(&mut catalog, "", "", "Person", "Birthday", "DATE", Nullable, 4);
1161            insert_procedure(&mut catalog, "", "", "Advertise");
1162            insert_procedure(&mut catalog, "", "", "Recruit");
1163        }
1164        // Define keywords.
1165        insert_keyword(&mut catalog, "ADD");
1166        insert_keyword(&mut catalog, "ALTER");
1167        insert_keyword(&mut catalog, "AND");
1168        insert_keyword(&mut catalog, "AS");
1169        insert_keyword(&mut catalog, "SELECT");
1170        insert_keyword(&mut catalog, "SET");
1171        // Define functions.
1172        insert_function(&mut catalog, "ABS");
1173        insert_function(&mut catalog, "COUNT");
1174        catalog
1175    }
1176
1177    fn insert_directive(
1178        catalog: &mut Catalog,
1179        verb: &str,
1180        noun: &str,
1181    ) {
1182        catalog.insert_directive(
1183            Cow::Borrowed(verb),
1184            Cow::Borrowed(noun),
1185        );
1186    }
1187
1188    fn insert_database(
1189        catalog: &mut Catalog,
1190        database: &str,
1191        schema: &str,
1192    ) {
1193        catalog.insert_database(
1194            Cow::Borrowed(database),
1195            Cow::Borrowed(schema),
1196        );
1197    }
1198
1199    fn insert_table(
1200        catalog: &mut Catalog,
1201        database: &str,
1202        schema: &str,
1203        table: &str,
1204    ) {
1205        catalog.insert_table(
1206            Cow::Borrowed(database),
1207            Cow::Borrowed(schema),
1208            Cow::Borrowed(table),
1209        );
1210    }
1211
1212    fn insert_column(
1213        catalog: &mut Catalog,
1214        database: &str,
1215        schema: &str,
1216        table: &str,
1217        column: &str,
1218        dtype: &str,
1219        null: Nullability,
1220        index: usize,
1221    ) {
1222        catalog.insert_column(
1223            Cow::Borrowed(database),
1224            Cow::Borrowed(schema),
1225            Cow::Borrowed(table),
1226            Cow::Borrowed(column),
1227            Cow::Borrowed(dtype),
1228            null,
1229            index,
1230        );
1231    }
1232
1233    fn insert_procedure(
1234        catalog: &mut Catalog,
1235        database: &str,
1236        schema: &str,
1237        procedure: &str,
1238    ) {
1239        catalog.insert_procedure(
1240            Cow::Borrowed(database),
1241            Cow::Borrowed(schema),
1242            Cow::Borrowed(procedure),
1243        );
1244    }
1245
1246    fn insert_keyword(catalog: &mut Catalog, keyword: &str) {
1247        catalog.insert_keyword(Cow::Borrowed(keyword));
1248    }
1249
1250    fn insert_function(catalog: &mut Catalog, function: &str) {
1251        catalog.insert_function(Cow::Borrowed(function));
1252    }
1253}