use std::path::PathBuf;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use crate::cancel::CancelHandle;
use crate::capabilities::Capabilities;
use crate::error::Result;
use crate::schema::{QueryResult, Schema, Table, TableSchema};
use crate::stream::RowStream;
use crate::value::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum SslMode {
Disable,
#[default]
Prefer,
Require,
VerifyCa,
VerifyFull,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionConfig {
pub id: uuid::Uuid,
pub name: String,
pub driver: String,
pub params: ConnectionParams,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConnectionParams {
pub host: Option<String>,
pub port: Option<u16>,
pub database: Option<String>,
pub username: Option<String>,
pub path: Option<String>,
#[serde(default)]
pub options: std::collections::BTreeMap<String, String>,
#[serde(default)]
pub ssl_mode: SslMode,
#[serde(default)]
pub ssl_root_cert: Option<PathBuf>,
#[serde(default)]
pub ssl_cert: Option<PathBuf>,
#[serde(default)]
pub ssl_key: Option<PathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh: Option<SshConfig>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pre_connect: Vec<PreConnectStep>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct PreConnectStep {
pub command: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub save_output_to: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_secs: Option<u32>,
#[serde(default = "default_required")]
pub required: bool,
}
const fn default_required() -> bool {
true
}
impl PreConnectStep {
#[must_use]
pub fn new(command: impl Into<String>) -> Self {
Self {
command: command.into(),
save_output_to: None,
timeout_secs: None,
required: true,
}
}
#[must_use]
pub fn with_save_output_to(mut self, key: impl Into<String>) -> Self {
self.save_output_to = Some(key.into());
self
}
#[must_use]
pub const fn with_timeout_secs(mut self, secs: u32) -> Self {
self.timeout_secs = Some(secs);
self
}
#[must_use]
pub const fn with_required(mut self, required: bool) -> Self {
self.required = required;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[non_exhaustive]
pub struct SshConfig {
pub host: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub port: Option<u16>,
pub user: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key_path: Option<PathBuf>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub jump_host: Option<String>,
}
impl SshConfig {
pub fn new(host: impl Into<String>, user: impl Into<String>) -> Self {
Self {
host: host.into(),
port: None,
user: user.into(),
key_path: None,
jump_host: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum IsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
#[async_trait]
pub trait Connection: Send + Sync {
async fn execute(&mut self, sql: &str, params: &[Value]) -> Result<QueryResult>;
async fn stream(&mut self, sql: &str, params: &[Value]) -> Result<Box<dyn RowStream>>;
async fn begin(&mut self) -> Result<()>;
async fn begin_with(&mut self, isolation: IsolationLevel) -> Result<()>;
async fn commit(&mut self) -> Result<()>;
async fn rollback(&mut self) -> Result<()>;
async fn savepoint(&mut self, name: &str) -> Result<()> {
let _ = name;
Err(crate::Error::unsupported("savepoints"))
}
async fn release_savepoint(&mut self, name: &str) -> Result<()> {
let _ = name;
Err(crate::Error::unsupported("savepoints"))
}
async fn rollback_to_savepoint(&mut self, name: &str) -> Result<()> {
let _ = name;
Err(crate::Error::unsupported("savepoints"))
}
async fn list_schemas(&mut self) -> Result<Vec<Schema>>;
async fn list_tables(&mut self, schema: &str) -> Result<Vec<Table>>;
async fn list_all_tables(&mut self) -> Result<Vec<(Schema, Vec<Table>)>> {
let schemas = self.list_schemas().await?;
let mut out = Vec::with_capacity(schemas.len());
for schema in schemas {
let tables = self.list_tables(&schema.name).await?;
out.push((schema, tables));
}
Ok(out)
}
async fn describe_table(&mut self, schema: &str, name: &str) -> Result<TableSchema>;
async fn ping(&mut self) -> Result<()>;
fn cancel_handle(&self) -> Option<Box<dyn CancelHandle>>;
fn capabilities(&self) -> Capabilities;
async fn fetch_ddl(&mut self, _schema: &str, _table: &str) -> Result<String> {
Err(crate::Error::unsupported("fetch_ddl"))
}
async fn set_read_only(&mut self, read_only: bool) -> Result<()> {
let _ = read_only;
Err(crate::Error::unsupported("set_read_only"))
}
async fn close(self: Box<Self>) -> Result<()>;
}