sqlpage/webserver/database/
mod.rs

1pub mod blob_to_data_url;
2mod connect;
3mod csv_import;
4pub mod execute_queries;
5pub mod migrations;
6mod sql;
7mod sqlpage_functions;
8mod syntax_tree;
9
10mod error_highlighting;
11mod sql_to_json;
12
13pub use sql::ParsedSqlFile;
14use sql::{DbPlaceHolder, DB_PLACEHOLDERS};
15use sqlx::any::AnyKind;
16// SupportedDatabase is defined in this module
17
18/// Supported database types in `SQLPage`. Represents an actual DBMS, not a sqlx backend kind (like "Odbc")
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
20pub enum SupportedDatabase {
21    Sqlite,
22    Postgres,
23    MySql,
24    Mssql,
25    Snowflake,
26    Generic,
27}
28
29impl SupportedDatabase {
30    /// Detect the database type from a connection's `dbms_name`
31    #[must_use]
32    pub fn from_dbms_name(dbms_name: &str) -> Self {
33        match dbms_name.to_lowercase().as_str() {
34            "sqlite" | "sqlite3" => Self::Sqlite,
35            "postgres" | "postgresql" => Self::Postgres,
36            "mysql" | "mariadb" => Self::MySql,
37            "mssql" | "sql server" | "microsoft sql server" => Self::Mssql,
38            "snowflake" => Self::Snowflake,
39            _ => Self::Generic,
40        }
41    }
42
43    /// Get the display name for the database
44    #[must_use]
45    pub fn display_name(self) -> &'static str {
46        match self {
47            Self::Sqlite => "SQLite",
48            Self::Postgres => "PostgreSQL",
49            Self::MySql => "MySQL",
50            Self::Mssql => "Microsoft SQL Server",
51            Self::Snowflake => "Snowflake",
52            Self::Generic => "Generic",
53        }
54    }
55}
56
57pub struct Database {
58    pub connection: sqlx::AnyPool,
59    pub info: DbInfo,
60}
61
62#[derive(Debug, Clone)]
63pub struct DbInfo {
64    pub dbms_name: String,
65    /// The actual database we are connected to. Can be "Generic" when using an unknown ODBC driver
66    pub database_type: SupportedDatabase,
67    /// The sqlx database backend we are using. Can be "Odbc", in which case we need to use `database_type` to know what database we are actually using.
68    pub kind: AnyKind,
69}
70
71impl Database {
72    pub async fn close(&self) -> anyhow::Result<()> {
73        log::info!("Closing all database connections...");
74        self.connection.close().await;
75        Ok(())
76    }
77}
78
79#[derive(Debug)]
80pub enum DbItem {
81    Row(serde_json::Value),
82    FinishedQuery,
83    Error(anyhow::Error),
84}
85
86impl std::fmt::Display for Database {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        write!(f, "{:?}", self.connection.any_kind())
89    }
90}
91
92#[inline]
93#[must_use]
94pub fn make_placeholder(dbms: AnyKind, arg_number: usize) -> String {
95    if let Some((_, placeholder)) = DB_PLACEHOLDERS.iter().find(|(kind, _)| *kind == dbms) {
96        match *placeholder {
97            DbPlaceHolder::PrefixedNumber { prefix } => format!("{prefix}{arg_number}"),
98            DbPlaceHolder::Positional { placeholder } => placeholder.to_string(),
99        }
100    } else {
101        unreachable!("missing dbms: {dbms:?} in DB_PLACEHOLDERS ({DB_PLACEHOLDERS:?})")
102    }
103}