Skip to main content

zql_cli/db/plugin/
mysql.rs

1use crate::core::catalog::Catalog;
2use crate::cow_str;
3use crate::db::context::Context;
4use crate::db::database::{Database, Plugin};
5use crate::error::MyResult;
6use crate::util::convert::{parse_null, parse_type};
7use itertools::Itertools;
8use std::borrow::Cow;
9
10pub struct MySQLPlugin;
11
12impl MySQLPlugin {
13    pub fn new() -> Self {
14        Self { }
15    }
16}
17
18impl Plugin for MySQLPlugin {
19    fn populate_catalog(&self, database: &Database, catalog: &mut Catalog) -> MyResult<()> {
20        fetch_databases(database, catalog)?;
21        fetch_columns(database, catalog)?;
22        fetch_procedures(database, catalog)?;
23        fetch_keywords(database, catalog)?;
24        fetch_functions(catalog);
25        Ok(())
26    }
27
28    fn query_context(&self, database: &Database) -> Context {
29        Context::new(Some(fetch_context(database)), None)
30    }
31
32    fn get_batch(&self) -> &str {
33        ";"
34    }
35}
36
37// [https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html]
38const MYSQL_FUNCTIONS: &[&str] = &[
39    "AVG", "BIT_AND", "BIT_OR", "BIT_XOR", "COUNT", "GROUP_CONCAT",
40    "JSON_ARRAYAGG", "JSON_OBJECTAGG", "MAX", "MIN", "STD", "STDDEV",
41    "STDDEV_POP", "STDDEV_SAMP", "SUM", "VARIANCE", "VAR_POP", "VAR_SAMP",
42];
43const MYSQL_COLUMNS: &str = "\
44SELECT table_schema, table_name, column_name, ordinal_position, is_nullable,
45data_type, character_maximum_length, numeric_precision, numeric_scale
46FROM information_schema.columns";
47const MYSQL_ROUTINES: &str = "\
48SELECT routine_schema, routine_name
49FROM information_schema.routines";
50const MYSQL_KEYWORDS: &str = "\
51SELECT word
52FROM information_schema.keywords";
53
54fn fetch_databases(database: &Database, catalog: &mut Catalog) -> MyResult<()> {
55    database.fetch_one("SHOW DATABASES", |database| {
56        catalog.insert_database(database, cow_str!(""));
57        Ok(())
58    })?;
59    Ok(())
60}
61
62fn fetch_columns(database: &Database, catalog: &mut Catalog) -> MyResult<()> {
63    database.fetch_records(MYSQL_COLUMNS, |record| {
64        if let Some((database, table, column, index, null, dtype, dsize, nsize, nprec)) = record.into_iter().next_tuple() {
65            let dsize = nsize.parse().unwrap_or_else(|_| dsize.parse().unwrap_or_default());
66            let dprec = nprec.parse().unwrap_or_default();
67            let dtype = parse_type(dtype, dsize, dprec);
68            let null = parse_null(null);
69            let index = index.parse().unwrap_or_default();
70            catalog.insert_column(
71                database,
72                cow_str!(""),
73                table,
74                column,
75                dtype,
76                null,
77                index,
78            );
79        }
80        Ok(())
81    })?;
82    Ok(())
83}
84
85fn fetch_procedures(database: &Database, catalog: &mut Catalog) -> MyResult<()> {
86    database.fetch_two(MYSQL_ROUTINES, |database, routine| {
87        catalog.insert_procedure(database, cow_str!(""), routine);
88        Ok(())
89    })?;
90    Ok(())
91}
92
93fn fetch_keywords(database: &Database, catalog: &mut Catalog) -> MyResult<()> {
94    database.fetch_one(MYSQL_KEYWORDS, |keyword| {
95        catalog.insert_keyword(keyword);
96        Ok(())
97    })?;
98    Ok(())
99}
100
101fn fetch_functions(catalog: &mut Catalog) {
102    for function in MYSQL_FUNCTIONS {
103        catalog.insert_function(Cow::Borrowed(function));
104    }
105}
106
107fn fetch_context(database: &Database) -> String {
108    let mut name = String::new();
109    let _ = database.fetch_one("SELECT DATABASE()", |value| {
110        name = value.to_string();
111        Ok(())
112    });
113    name
114}