use lazy_regex::regex;
use regex::Captures;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::net::TcpStream;
use std::rc::Rc;
use rsa::{pkcs1::FromRsaPublicKey, RsaPublicKey};
use serde::Serialize;
use serde_json::{json, Value};
use tungstenite::{stream::MaybeTlsStream, Message, WebSocket};
use url::Url;
use crate::con_opts::{ConOpts, ProtocolVersion};
use crate::error::{ConnectionError, DriverError, RequestError, Result};
use crate::query::{PreparedStatement, QueryResult};
use crate::response::{Attributes, Response, ResponseData};
#[cfg(feature = "flate2")]
use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
#[cfg(feature = "flate2")]
use std::io::Write;
type ReqResult<T> = std::result::Result<T, RequestError>;
type ConResult = std::result::Result<WebSocket<MaybeTlsStream<TcpStream>>, ConnectionError>;
pub fn connect(dsn: &str, schema: &str, user: &str, password: &str) -> Result<Connection> {
let mut opts = ConOpts::new();
opts.set_dsn(dsn);
opts.set_user(user);
opts.set_password(password);
opts.set_schema(schema);
Connection::new(opts)
}
pub struct Connection {
con: Rc<RefCell<ConnectionImpl>>,
}
impl Debug for Connection {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.con.borrow())
}
}
impl Connection {
#[inline]
pub fn new(opts: ConOpts) -> Result<Connection> {
Ok(Connection {
con: Rc::new(RefCell::new(ConnectionImpl::new(opts)?)),
})
}
#[inline]
pub fn execute<T>(&mut self, query: T) -> Result<QueryResult>
where
T: AsRef<str> + Serialize,
{
(*self.con).borrow_mut().execute(&self.con, &query)
}
#[inline]
pub fn execute_batch<T>(&mut self, queries: &[T]) -> Result<Vec<QueryResult>>
where
T: AsRef<str> + Serialize,
{
(*self.con).borrow_mut().execute_batch(&self.con, queries)
}
#[inline]
pub fn prepare<'a, T>(&mut self, query: T) -> Result<PreparedStatement>
where
T: Serialize + Into<Cow<'a, str>>,
{
(*self.con).borrow_mut().prepare(&self.con, query)
}
#[inline]
pub fn ping(&mut self) -> Result<()> {
Ok((*self.con)
.borrow_mut()
.ping()
.map_err(DriverError::RequestError)?)
}
#[inline]
pub fn set_fetch_size(&mut self, val: u32) {
(*self.con).borrow_mut().driver_attr.fetch_size = val;
}
#[inline]
pub fn set_lowercase_columns(&mut self, flag: bool) {
(*self.con).borrow_mut().driver_attr.lowercase_columns = flag;
}
#[inline]
pub fn set_autocommit(&mut self, val: bool) -> Result<()> {
let payload = json!({ "autocommit": val });
self.set_attributes(payload)
}
#[inline]
pub fn set_query_timeout(&mut self, val: usize) -> Result<()> {
let payload = json!({ "queryTimeout": val });
self.set_attributes(payload)
}
#[inline]
pub fn set_schema(&mut self, schema: &str) -> Result<()> {
let payload = json!({ "currentSchema": schema });
self.set_attributes(payload)
}
#[inline]
fn set_attributes(&mut self, attr: Value) -> Result<()> {
(*self.con).borrow_mut().set_attributes(attr)
}
}
#[doc(hidden)]
pub(crate) struct ConnectionImpl {
pub(crate) driver_attr: DriverAttributes,
exa_attr: HashMap<String, Value>,
ws: WebSocket<MaybeTlsStream<TcpStream>>,
send: fn(&mut WebSocket<MaybeTlsStream<TcpStream>>, Value) -> ReqResult<()>,
recv: fn(&mut WebSocket<MaybeTlsStream<TcpStream>>) -> ReqResult<Response>,
}
impl Drop for ConnectionImpl {
#[allow(unused)]
fn drop(&mut self) {
self.do_request(json!({"command": "disconnect"}));
self.ws.close(None);
while self.ws.read_message().is_ok() {}
}
}
impl Debug for ConnectionImpl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let str_attr = self
.exa_attr
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.collect::<Vec<String>>()
.join("\n");
write!(f, "active: {}\n{}", self.ws.can_write(), str_attr)
}
}
impl ConnectionImpl {
pub(crate) fn new(opts: ConOpts) -> Result<ConnectionImpl> {
let ws = Self::try_websocket_from_opts(&opts).map_err(DriverError::ConnectionError)?;
let exa_attr = HashMap::new();
let driver_attr = DriverAttributes {
fetch_size: opts.get_fetch_size(),
lowercase_columns: opts.get_lowercase_columns(),
};
let mut con = Self {
driver_attr,
exa_attr,
ws,
send,
recv,
};
con.login(opts)?;
con.get_attributes()?;
Ok(con)
}
pub(crate) fn set_attributes(&mut self, attrs: Value) -> Result<()> {
let payload = json!({"command": "setAttributes", "attributes": attrs});
self.do_request(payload)?;
self.get_attributes()?;
Ok(())
}
pub(crate) fn execute<T>(
&mut self,
con_impl: &Rc<RefCell<ConnectionImpl>>,
query: &T,
) -> Result<QueryResult>
where
T: AsRef<str> + Serialize,
{
let payload = json!({"command": "execute", "sqlText": query});
self.exec_and_get_first(con_impl, payload)
}
pub(crate) fn execute_batch<T>(
&mut self,
con_impl: &Rc<RefCell<ConnectionImpl>>,
queries: &[T],
) -> Result<Vec<QueryResult>>
where
T: AsRef<str> + Serialize,
{
let payload = json!({"command": "executeBatch", "sqlTexts": queries});
self.exec_with_results(con_impl, payload)
}
pub(crate) fn exec_and_get_first(
&mut self,
con_impl: &Rc<RefCell<ConnectionImpl>>,
payload: Value,
) -> Result<QueryResult> {
self.exec_with_results(con_impl, payload)
.and_then(|mut v: Vec<QueryResult>| {
if v.is_empty() {
Err(
DriverError::RequestError(RequestError::InvalidResponse("result sets"))
.into(),
)
} else {
Ok(v.swap_remove(0))
}
})
}
pub(crate) fn prepare<'a, T>(
&mut self,
con_impl: &Rc<RefCell<ConnectionImpl>>,
query: T,
) -> Result<PreparedStatement>
where
T: Serialize + Into<Cow<'a, str>>,
{
let re = regex!(r"\\(\?\w*)|[?\w]\?\w*|\?\w*\?|\?(\w*)");
let mut col_names = Vec::new();
let cow_q = query.into();
let x = cow_q.as_ref();
let q = re.replace_all(x, |cap: &Captures| {
cap.get(2)
.map(|m| {
col_names.push(m.as_str().to_owned());
"?"
})
.or_else(|| cap.get(1).map(|m| &x[m.range()]))
.unwrap_or(&x[cap.get(0).unwrap().range()])
});
let payload = json!({"command": "createPreparedStatement", "sqlText": q});
self.get_resp_data(payload)
.and_then(|r| r.try_to_prepared_stmt(con_impl, col_names))
}
#[inline]
pub(crate) fn close_result_set(&mut self, handle: u16) -> Result<()> {
let payload = json!({"command": "closeResultSet", "resultSetHandles": [handle]});
self.do_request(payload)?;
Ok(())
}
#[inline]
pub(crate) fn close_prepared_stmt(&mut self, handle: usize) -> Result<()> {
let payload = json!({"command": "closePreparedStatement", "statementHandle": handle});
self.do_request(payload)?;
Ok(())
}
pub(crate) fn ping(&mut self) -> ReqResult<()> {
self.ws.write_message(Message::Ping(vec![]))?;
match self.ws.read_message()? {
Message::Pong(_) => Ok(()),
_ => Err(RequestError::InvalidResponse("pong frame")),
}
}
pub(crate) fn get_resp_data(&mut self, payload: Value) -> Result<ResponseData> {
self.do_request(payload)?.ok_or_else(|| {
DriverError::RequestError(RequestError::InvalidResponse("response data")).into()
})
}
pub(crate) fn do_request(&mut self, payload: Value) -> Result<Option<ResponseData>> {
let resp = self
.send(payload)
.and_then(|_| self.recv())
.map_err(DriverError::RequestError)?;
let (data, attr): (Option<ResponseData>, Option<Attributes>) = resp.try_into()?;
if let Some(attributes) = attr {
self.exa_attr.extend(attributes.map)
}
Ok(data)
}
pub(crate) fn exec_with_results(
&mut self,
con_impl: &Rc<RefCell<ConnectionImpl>>,
payload: Value,
) -> Result<Vec<QueryResult>> {
let lc = self.driver_attr.lowercase_columns;
self.get_resp_data(payload)
.and_then(|r| r.try_to_query_results(con_impl, lc))
}
#[inline]
fn send(&mut self, payload: Value) -> ReqResult<()> {
(self.send)(&mut self.ws, payload)
}
#[inline]
fn recv(&mut self) -> ReqResult<Response> {
(self.recv)(&mut self.ws)
}
fn try_websocket_from_opts(opts: &ConOpts) -> ConResult {
let addresses = opts.parse_dsn()?;
let ws_prefix = opts.get_ws_prefix();
let mut try_count = addresses.len();
let mut addr_iter = addresses.into_iter();
loop {
try_count -= 1;
if let Some(addr) = addr_iter.next() {
let url = format!("{}://{}", ws_prefix, addr);
let res = Self::try_websocket_from_url(&url);
match res {
Ok(ws) => break Ok(ws),
Err(e) => {
if try_count == 0 {
break Err(e);
}
}
}
} else {
break Err(ConnectionError::InvalidDSN);
}
}
}
fn try_websocket_from_url(url: &str) -> ConResult {
let url = Url::parse(url)?;
let (ws, _) = tungstenite::connect(url)?;
Ok(ws)
}
fn get_attributes(&mut self) -> Result<()> {
let payload = json!({"command": "getAttributes"});
self.do_request(payload)?;
Ok(())
}
fn get_public_key(&mut self, protocol_version: ProtocolVersion) -> Result<RsaPublicKey> {
let payload = json!({"command": "login", "protocolVersion": protocol_version});
let pem = self
.get_resp_data(payload)
.and_then(|p| p.try_to_public_key_string())?;
Ok(RsaPublicKey::from_pkcs1_pem(&pem)
.map_err(ConnectionError::PKCSError)
.map_err(DriverError::ConnectionError)?)
}
fn login(&mut self, opts: ConOpts) -> Result<()> {
#[cfg(feature = "flate2")]
let compress = opts.get_compression();
let key = self.get_public_key(opts.get_protocol_version())?;
let payload = opts.into_value(key).map_err(DriverError::ConnectionError)?;
self.do_request(payload)?;
#[cfg(feature = "flate2")]
if compress {
self.send = compressed_send;
self.recv = compressed_recv;
}
Ok(())
}
}
pub(crate) struct DriverAttributes {
pub(crate) fetch_size: u32,
pub(crate) lowercase_columns: bool,
}
#[inline]
fn send(ws: &mut WebSocket<MaybeTlsStream<TcpStream>>, payload: Value) -> ReqResult<()> {
Ok(ws.write_message(Message::Text(payload.to_string()))?)
}
#[inline]
fn recv(ws: &mut WebSocket<MaybeTlsStream<TcpStream>>) -> ReqResult<Response> {
loop {
break match ws.read_message()? {
Message::Text(resp) => Ok(serde_json::from_str::<Response>(&resp)?),
Message::Binary(resp) => Ok(serde_json::from_slice::<Response>(&resp)?),
_ => continue,
};
}
}
#[inline]
#[cfg(feature = "flate2")]
fn compressed_send(ws: &mut WebSocket<MaybeTlsStream<TcpStream>>, payload: Value) -> ReqResult<()> {
let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
enc.write_all(payload.to_string().as_bytes())?;
Ok(ws.write_message(Message::Binary(enc.finish()?))?)
}
#[inline]
#[cfg(feature = "flate2")]
fn compressed_recv(ws: &mut WebSocket<MaybeTlsStream<TcpStream>>) -> ReqResult<Response> {
loop {
break match ws.read_message()? {
Message::Text(resp) => Ok(serde_json::from_reader(ZlibDecoder::new(resp.as_bytes()))?),
Message::Binary(resp) => {
Ok(serde_json::from_reader(ZlibDecoder::new(resp.as_slice()))?)
}
_ => continue,
};
}
}