use mysql_common::row::convert::FromRowError;
use mysql_common::{io::ReadMysqlExt, packets::parse_local_infile_packet};
use tokio::prelude::*;
use std::{borrow::Cow, marker::PhantomData, result::Result as StdResult, sync::Arc};
use crate::{
connection_like::Connection,
error::*,
prelude::{FromRow, Protocol},
Column, Row,
};
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ResultSetMeta {
Text(Arc<[Column]>),
Binary(Arc<[Column]>),
Error(ServerError),
}
impl ResultSetMeta {
fn columns(&self) -> StdResult<&Arc<[Column]>, &ServerError> {
match self {
ResultSetMeta::Text(cols) | ResultSetMeta::Binary(cols) => Ok(cols),
ResultSetMeta::Error(err) => Err(err),
}
}
}
#[derive(Debug)]
pub struct QueryResult<'a, 't: 'a, P> {
conn: Connection<'a, 't>,
__phantom: PhantomData<P>,
}
impl<'a, 't: 'a, P> QueryResult<'a, 't, P>
where
P: Protocol,
{
pub(crate) fn new<T: Into<Connection<'a, 't>>>(conn: T) -> Self {
QueryResult {
conn: conn.into(),
__phantom: PhantomData,
}
}
fn has_rows(&self) -> bool {
self.conn
.get_pending_result()
.and_then(|meta| meta.columns().map(|columns| columns.len() > 0).ok())
.unwrap_or(false)
}
pub fn is_empty(&self) -> bool {
!self.has_rows() && !self.conn.more_results_exists()
}
pub async fn next(&mut self) -> Result<Option<Row>> {
loop {
let columns = match self.conn.get_pending_result() {
Some(ResultSetMeta::Text(cols)) | Some(ResultSetMeta::Binary(cols)) => {
Ok(Some(cols.clone()))
}
Some(ResultSetMeta::Error(err)) => Err(Error::from(err.clone())),
None => Ok(None),
};
match columns {
Ok(Some(columns)) => {
if columns.is_empty() {
self.conn.set_pending_result(None);
return Ok(None);
} else {
let packet = match self.conn.read_packet().await {
Ok(packet) => packet,
Err(err) => {
self.conn.set_pending_result(None);
return Err(err);
}
};
if P::is_last_result_set_packet(self.conn.capabilities(), &packet) {
self.conn.set_pending_result(None);
return Ok(None);
} else {
return Ok(Some(P::read_result_set_row(&packet, columns)?));
}
}
}
Ok(None) => {
if self.conn.more_results_exists() {
self.conn.sync_seq_id();
self.conn.read_result_set::<P>().await?;
continue;
} else {
return Ok(None);
}
}
Err(err) => {
self.conn.set_pending_result(None);
return Err(err);
}
}
}
}
pub fn last_insert_id(&self) -> Option<u64> {
self.conn.last_insert_id()
}
pub fn affected_rows(&self) -> u64 {
self.conn.affected_rows()
}
pub fn info(&self) -> Cow<'_, str> {
self.conn.info()
}
pub fn warnings(&self) -> u16 {
self.conn.get_warnings()
}
pub async fn collect<R>(&mut self) -> Result<Vec<R>>
where
R: FromRow + Send + 'static,
{
self.reduce(Vec::new(), |mut acc, row| {
acc.push(FromRow::from_row(row));
acc
})
.await
}
pub async fn try_collect<R>(&mut self) -> Result<Vec<StdResult<R, FromRowError>>>
where
R: FromRow + Send + 'static,
{
self.reduce(Vec::new(), |mut acc, row| {
acc.push(FromRow::from_row_opt(row));
acc
})
.await
}
pub async fn collect_and_drop<R>(mut self) -> Result<Vec<R>>
where
R: FromRow + Send + 'static,
{
let output = self.collect::<R>().await?;
self.drop_result().await?;
Ok(output)
}
pub async fn try_collect_and_drop<R>(mut self) -> Result<Vec<StdResult<R, FromRowError>>>
where
R: FromRow + Send + 'static,
{
let output = self.try_collect().await?;
self.drop_result().await?;
Ok(output)
}
pub async fn for_each<F>(&mut self, mut fun: F) -> Result<()>
where
F: FnMut(Row),
{
if self.is_empty() {
Ok(())
} else {
while let Some(row) = self.next().await? {
fun(row);
}
Ok(())
}
}
pub async fn for_each_and_drop<F>(mut self, fun: F) -> Result<()>
where
F: FnMut(Row),
{
self.for_each(fun).await?;
self.drop_result().await?;
Ok(())
}
pub async fn map<F, U>(&mut self, mut fun: F) -> Result<Vec<U>>
where
F: FnMut(Row) -> U,
{
let mut acc = Vec::new();
while let Some(row) = self.next().await? {
acc.push(fun(crate::from_row(row)));
}
Ok(acc)
}
pub async fn map_and_drop<F, U>(mut self, fun: F) -> Result<Vec<U>>
where
F: FnMut(Row) -> U,
{
let rows = self.map(fun).await?;
self.drop_result().await?;
Ok(rows)
}
pub async fn reduce<T, F, U>(&mut self, mut init: U, mut fun: F) -> Result<U>
where
F: FnMut(U, T) -> U,
T: FromRow + Send + 'static,
{
while let Some(row) = self.next().await? {
init = fun(init, crate::from_row(row));
}
Ok(init)
}
pub async fn reduce_and_drop<T, F, U>(mut self, init: U, fun: F) -> Result<U>
where
F: FnMut(U, T) -> U,
T: FromRow + Send + 'static,
{
let acc = self.reduce(init, fun).await?;
self.drop_result().await?;
Ok(acc)
}
pub async fn drop_result(mut self) -> Result<()> {
loop {
while let Some(_) = self.next().await? {}
if !self.conn.more_results_exists() {
break Ok(());
}
}
}
pub fn columns_ref(&self) -> &[Column] {
self.conn
.get_pending_result()
.and_then(|meta| meta.columns().map(|cols| &cols[..]).ok())
.unwrap_or_default()
}
pub fn columns(&self) -> Option<Arc<[Column]>> {
self.conn
.get_pending_result()
.and_then(|meta| meta.columns().map(|columns| columns.clone()).ok())
}
}
impl crate::Conn {
pub(crate) async fn read_result_set<P>(&mut self) -> Result<()>
where
P: Protocol,
{
let packet = self.read_packet().await?;
match packet.get(0) {
Some(0x00) => self.set_pending_result(Some(P::result_set_meta(Arc::from(
Vec::new().into_boxed_slice(),
)))),
Some(0xFB) => self.handle_local_infile::<P>(&*packet).await?,
_ => self.handle_result_set::<P>(&*packet).await?,
}
Ok(())
}
pub(crate) async fn handle_local_infile<P>(&mut self, packet: &[u8]) -> Result<()>
where
P: Protocol,
{
let local_infile = parse_local_infile_packet(&*packet)?;
let (local_infile, handler) = match self.opts().local_infile_handler() {
Some(handler) => ((local_infile.into_owned(), handler)),
None => return Err(DriverError::NoLocalInfileHandler.into()),
};
let mut reader = handler.handle(local_infile.file_name_ref()).await?;
let mut buf = [0; 4096];
loop {
let read = reader.read(&mut buf[..]).await?;
self.write_packet(&buf[..read]).await?;
if read == 0 {
break;
}
}
self.read_packet().await?;
self.set_pending_result(Some(P::result_set_meta(Arc::from(
Vec::new().into_boxed_slice(),
))));
Ok(())
}
pub(crate) async fn handle_result_set<P>(&mut self, mut packet: &[u8]) -> Result<()>
where
P: Protocol,
{
let column_count = packet.read_lenenc_int()?;
let columns = self.read_column_defs(column_count as usize).await?;
let meta = P::result_set_meta(Arc::from(columns.into_boxed_slice()));
self.set_pending_result(Some(meta));
Ok(())
}
}