Skip to main content

zql_cli/db/plugin/
sqlite.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 std::borrow::Cow;
8
9pub struct SQLitePlugin;
10
11impl SQLitePlugin {
12    pub fn new() -> Self {
13        Self { }
14    }
15}
16
17impl Plugin for SQLitePlugin {
18    fn populate_catalog(&self, database: &Database, catalog: &mut Catalog) -> MyResult<()> {
19        fetch_tables(database, catalog)?;
20        fetch_keywords(catalog);
21        fetch_functions(database, catalog)?;
22        Ok(())
23    }
24
25    fn query_context(&self, _database: &Database) -> Context {
26        Context::default()
27    }
28
29    fn get_batch(&self) -> &str {
30        ";"
31    }
32}
33
34// [https://www.sqlite.org/lang_keywords.html]
35const SQLITE_KEYWORDS: &[&str] = &[
36    "ABORT", "ACTION", "ADD", "AFTER", "ALL", "ALTER", "ALWAYS", "ANALYZE",
37    "AND", "AS", "ASC", "ATTACH", "AUTOINCREMENT", "BEFORE", "BEGIN", "BETWEEN",
38    "BY", "CASCADE", "CASE", "CAST", "CHECK", "COLLATE", "COLUMN", "COMMIT",
39    "CONFLICT", "CONSTRAINT", "CREATE", "CROSS", "CURRENT", "CURRENT_DATE",
40    "CURRENT_TIME", "CURRENT_TIMESTAMP", "DATABASE", "DEFAULT", "DEFERRABLE",
41    "DEFERRED", "DELETE", "DESC", "DETACH", "DISTINCT", "DO", "DROP", "EACH",
42    "ELSE", "END", "ESCAPE", "EXCEPT", "EXCLUDE", "EXCLUSIVE", "EXISTS",
43    "EXPLAIN", "FAIL", "FILTER", "FIRST", "FOLLOWING", "FOR", "FOREIGN", "FROM",
44    "FULL", "GENERATED", "GLOB", "GROUP", "GROUPS", "HAVING", "IF", "IGNORE",
45    "IMMEDIATE", "IN", "INDEX", "INDEXED", "INITIALLY", "INNER", "INSERT",
46    "INSTEAD", "INTERSECT", "INTO", "IS", "ISNULL", "JOIN", "KEY", "LAST",
47    "LEFT", "LIKE", "LIMIT", "MATCH", "MATERIALIZED", "NATURAL", "NO", "NOT",
48    "NOTHING", "NOTNULL", "NULL", "NULLS", "OF", "OFFSET", "ON", "OR", "ORDER",
49    "OTHERS", "OUTER", "OVER", "PARTITION", "PLAN", "PRAGMA", "PRECEDING",
50    "PRIMARY", "QUERY", "RAISE", "RANGE", "RECURSIVE", "REFERENCES", "REGEXP",
51    "REINDEX", "RELEASE", "RENAME", "REPLACE", "RESTRICT", "RETURNING", "RIGHT",
52    "ROLLBACK", "ROW", "ROWS", "SAVEPOINT", "SELECT", "SET", "TABLE", "TEMP",
53    "TEMPORARY", "THEN", "TIES", "TO", "TRANSACTION", "TRIGGER", "UNBOUNDED",
54    "UNION", "UNIQUE", "UPDATE", "USING", "VACUUM", "VALUES", "VIEW", "VIRTUAL",
55    "WHEN", "WHERE", "WINDOW", "WITH", "WITHOUT",
56];
57
58fn fetch_tables(database: &Database, catalog: &mut Catalog) -> MyResult<()> {
59    database.fetch_two("PRAGMA main.table_list", |_, table| {
60        if !table.starts_with("sqlite_") {
61            fetch_columns(database, catalog, table)?;
62        }
63        Ok(())
64    })?;
65    Ok(())
66}
67
68fn fetch_columns(database: &Database, catalog: &mut Catalog, table: Cow<str>) -> MyResult<()> {
69    let query = format!("PRAGMA main.table_info ({})", table);
70    database.fetch_four(&query, |index, column, dtype, null| {
71        let dtype = parse_type(dtype, 0, 0);
72        let null = parse_null(null);
73        let index = index.parse::<usize>().unwrap_or_default() + 1;
74        catalog.insert_column(
75            cow_str!(""),
76            cow_str!(""),
77            table.clone(),
78            column,
79            dtype,
80            null,
81            index,
82        );
83        Ok(())
84    })?;
85    Ok(())
86}
87
88fn fetch_keywords(catalog: &mut Catalog) {
89    for keyword in SQLITE_KEYWORDS {
90        catalog.insert_keyword(Cow::Borrowed(keyword));
91    }
92}
93
94fn fetch_functions(database: &Database, catalog: &mut Catalog) -> MyResult<()> {
95    database.fetch_one("PRAGMA main.function_list", |function| {
96        if function.chars().all(is_word_character) {
97            catalog.insert_function(function);
98        }
99        Ok(())
100    })?;
101    Ok(())
102}
103
104fn is_word_character(c: char) -> bool {
105    c.is_alphanumeric() || c == '_'
106}