1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
use crate::rqlite;
use crate::connection::RqliteConnection;
use crate::options::RqliteConnectOptions;
use futures_core::future::BoxFuture;
use log::LevelFilter;
use sqlx_core::connection::ConnectOptions;
use sqlx_core::error::Error;
//use sqlx_core::executor::Executor;
//use std::fmt::Write;
use percent_encoding::percent_decode_str;
use std::str::FromStr;
use std::time::Duration;
use url::Url;
impl ConnectOptions for RqliteConnectOptions {
type Connection = RqliteConnection;
// borrowed from sqlx-mysql
fn from_url(url: &Url) -> Result<Self, Error> {
let mut options = rqlite::ConnectOptions::default();
if let Some(host) = url.host_str() {
options.host = host.into();
}
if let Some(port) = url.port() {
options.port = port;
}
let username = url.username();
if !username.is_empty() {
options.user = Some(
percent_decode_str(username)
.decode_utf8()
.map_err(Error::config)?
.to_string(),
);
}
if let Some(password) = url.password() {
options.pass = Some(
percent_decode_str(password)
.decode_utf8()
.map_err(Error::config)?
.to_string(),
);
}
/*
let path = url.path().trim_start_matches('/');
if !path.is_empty() {
options = options.database(path);
}
*/
Ok(Self { inner: options })
}
fn to_url_lossy(&self) -> Url {
self.build_url()
}
fn connect(&self) -> BoxFuture<'_, Result<Self::Connection, Error>>
where
Self::Connection: Sized,
{
Box::pin(async move {
let conn = RqliteConnection::establish(self).await?;
/*
// After the connection is established, we initialize by configuring a few
// connection parameters
// https://mariadb.com/kb/en/sql-mode/
// PIPES_AS_CONCAT - Allows using the pipe character (ASCII 124) as string concatenation operator.
// This means that "A" || "B" can be used in place of CONCAT("A", "B").
// NO_ENGINE_SUBSTITUTION - If not set, if the available storage engine specified by a CREATE TABLE is
// not available, a warning is given and the default storage
// engine is used instead.
// NO_ZERO_DATE - Don't allow '0000-00-00'. This is invalid in Rust.
// NO_ZERO_IN_DATE - Don't allow 'YYYY-00-00'. This is invalid in Rust.
// --
// Setting the time zone allows us to assume that the output
// from a TIMESTAMP field is UTC
// --
// https://mathiasbynens.be/notes/mysql-utf8mb4
let mut sql_mode = Vec::new();
if self.pipes_as_concat {
sql_mode.push(r#"PIPES_AS_CONCAT"#);
}
if self.no_engine_subsitution {
sql_mode.push(r#"NO_ENGINE_SUBSTITUTION"#);
}
let mut options = Vec::new();
if !sql_mode.is_empty() {
options.push(format!(
r#"sql_mode=(SELECT CONCAT(@@sql_mode, ',{}'))"#,
sql_mode.join(",")
));
}
if let Some(timezone) = &self.timezone {
options.push(format!(r#"time_zone='{}'"#, timezone));
}
if self.set_names {
options.push(format!(
r#"NAMES {} COLLATE {}"#,
conn.stream.charset.as_str(),
conn.stream.collation.as_str()
))
}
if !options.is_empty() {
conn.execute(&*format!(r#"SET {};"#, options.join(",")))
.await?;
}
*/
Ok(conn)
})
}
fn log_statements(self, _level: LevelFilter) -> Self {
//self.log_settings.log_statements(level);
self
}
fn log_slow_statements(self, _level: LevelFilter, _duration: Duration) -> Self {
//self.log_settings.log_slow_statements(level, duration);
self
}
}
impl FromStr for RqliteConnectOptions {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
let url: Url = s.parse().map_err(Error::config)?;
Self::from_url(&url)
}
}