pub use mysql_common::proto::{Binary, Text};
use mysql_common::{io::ParseBuf, packets::OkPacket, row::RowDeserializer, value::ServerSide};
use std::{borrow::Cow, marker::PhantomData, sync::Arc};
use crate::{conn::ConnMut, Column, Conn, Error, Result, Row};
pub trait Protocol: 'static + Send + Sync {
fn next(conn: &mut Conn, columns: Arc<[Column]>) -> Result<Option<Row>>;
}
impl Protocol for Text {
fn next(conn: &mut Conn, columns: Arc<[Column]>) -> Result<Option<Row>> {
match conn.next_row_packet()? {
Some(pld) => {
let row = ParseBuf(&pld).parse::<RowDeserializer<(), Text>>(columns)?;
Ok(Some(row.into()))
}
None => Ok(None),
}
}
}
impl Protocol for Binary {
fn next(conn: &mut Conn, columns: Arc<[Column]>) -> Result<Option<Row>> {
match conn.next_row_packet()? {
Some(pld) => {
let row = ParseBuf(&pld).parse::<RowDeserializer<ServerSide, Binary>>(columns)?;
Ok(Some(row.into()))
}
None => Ok(None),
}
}
}
#[derive(Debug)]
enum SetIteratorState {
InSet(Arc<[Column]>),
InEmptySet(OkPacket<'static>),
Errored(Error),
OnBoundary,
Done,
}
impl SetIteratorState {
fn ok_packet(&self) -> Option<&OkPacket<'_>> {
if let Self::InEmptySet(ref ok) = self {
Some(ok)
} else {
None
}
}
fn columns(&self) -> Option<&Arc<[Column]>> {
if let Self::InSet(ref cols) = self {
Some(cols)
} else {
None
}
}
}
impl From<Arc<[Column]>> for SetIteratorState {
fn from(columns: Arc<[Column]>) -> Self {
Self::InSet(columns)
}
}
impl From<OkPacket<'static>> for SetIteratorState {
fn from(ok_packet: OkPacket<'static>) -> Self {
Self::InEmptySet(ok_packet)
}
}
impl From<Error> for SetIteratorState {
fn from(err: Error) -> Self {
Self::Errored(err)
}
}
impl From<ResultSetMeta> for SetIteratorState {
fn from(value: ResultSetMeta) -> Self {
match value {
ResultSetMeta::Empty(ok_packet) => Self::from(ok_packet),
ResultSetMeta::NonEmptyWithMeta(column) => Self::from(column),
}
}
}
#[derive(Debug)]
pub(crate) enum ResultSetMeta {
Empty(OkPacket<'static>),
NonEmptyWithMeta(Arc<[Column]>),
}
#[derive(Debug)]
pub struct QueryResult<'c, 't, 'tc, T: crate::prelude::Protocol> {
conn: ConnMut<'c, 't, 'tc>,
state: SetIteratorState,
set_index: usize,
protocol: PhantomData<T>,
}
impl<'c, 't, 'tc, T: crate::prelude::Protocol> QueryResult<'c, 't, 'tc, T> {
fn from_state(
conn: ConnMut<'c, 't, 'tc>,
state: SetIteratorState,
) -> QueryResult<'c, 't, 'tc, T> {
QueryResult {
conn,
state,
set_index: 0,
protocol: PhantomData,
}
}
pub(crate) fn new(
conn: ConnMut<'c, 't, 'tc>,
meta: ResultSetMeta,
) -> QueryResult<'c, 't, 'tc, T> {
Self::from_state(conn, meta.into())
}
fn handle_next(&mut self) {
debug_assert!(
matches!(self.state, SetIteratorState::OnBoundary),
"self.state != OnBoundary"
);
if self.conn.more_results_exists() {
match self.conn.handle_result_set() {
Ok(info) => self.state = info.into_query_meta().into(),
Err(err) => self.state = err.into(),
}
self.set_index += 1;
} else {
self.state = SetIteratorState::Done;
}
}
#[deprecated = "Please use QueryResult::iter"]
pub fn next_set<'d>(&'d mut self) -> Option<ResultSet<'c, 't, 'tc, 'd, T>> {
self.iter()
}
pub fn iter<'d>(&'d mut self) -> Option<ResultSet<'c, 't, 'tc, 'd, T>> {
use SetIteratorState::*;
if let OnBoundary | Done = &self.state {
debug_assert!(
!self.conn.more_results_exists(),
"the next state must be handled by the Iterator::next"
);
None
} else {
Some(ResultSet {
set_index: self.set_index,
inner: self,
})
}
}
pub fn affected_rows(&self) -> u64 {
self.state
.ok_packet()
.map(|ok| ok.affected_rows())
.unwrap_or_default()
}
pub fn last_insert_id(&self) -> Option<u64> {
self.state
.ok_packet()
.map(|ok| ok.last_insert_id())
.unwrap_or_default()
}
pub fn warnings(&self) -> u16 {
self.state
.ok_packet()
.map(|ok| ok.warnings())
.unwrap_or_default()
}
pub fn info_ref(&self) -> &[u8] {
self.state
.ok_packet()
.and_then(|ok| ok.info_ref())
.unwrap_or_default()
}
pub fn info_str(&self) -> Cow<'_, str> {
self.state
.ok_packet()
.and_then(|ok| ok.info_str())
.unwrap_or_else(|| "".into())
}
pub fn columns(&self) -> SetColumns<'_> {
SetColumns {
inner: self.state.columns(),
}
}
}
impl<'c, 't, 'tc, T: crate::prelude::Protocol> Drop for QueryResult<'c, 't, 'tc, T> {
fn drop(&mut self) {
while self.iter().is_some() {}
}
}
#[derive(Debug)]
pub struct ResultSet<'a, 'b, 'c, 'd, T: crate::prelude::Protocol> {
set_index: usize,
inner: &'d mut QueryResult<'a, 'b, 'c, T>,
}
impl<'a, 'b, 'c, T: crate::prelude::Protocol> std::ops::Deref for ResultSet<'a, 'b, 'c, '_, T> {
type Target = QueryResult<'a, 'b, 'c, T>;
fn deref(&self) -> &Self::Target {
&*self.inner
}
}
impl<T: crate::prelude::Protocol> Iterator for ResultSet<'_, '_, '_, '_, T> {
type Item = Result<Row>;
fn next(&mut self) -> Option<Self::Item> {
if self.set_index == self.inner.set_index {
self.inner.next()
} else {
None
}
}
}
impl<T: crate::prelude::Protocol> Iterator for QueryResult<'_, '_, '_, T> {
type Item = Result<Row>;
fn next(&mut self) -> Option<Self::Item> {
use SetIteratorState::*;
let state = std::mem::replace(&mut self.state, OnBoundary);
match state {
InSet(cols) => match T::next(&mut self.conn, cols.clone()) {
Ok(Some(row)) => {
self.state = InSet(cols);
Some(Ok(row))
}
Ok(None) => {
self.handle_next();
None
}
Err(e) => {
self.handle_next();
Some(Err(e))
}
},
InEmptySet(_) => {
self.handle_next();
None
}
Errored(err) => {
self.handle_next();
Some(Err(err))
}
OnBoundary => None,
Done => {
self.state = Done;
None
}
}
}
}
impl<T: crate::prelude::Protocol> Drop for ResultSet<'_, '_, '_, '_, T> {
fn drop(&mut self) {
while self.next().is_some() {}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SetColumns<'a> {
inner: Option<&'a Arc<[Column]>>,
}
impl<'a> SetColumns<'a> {
pub fn column_index<U: AsRef<str>>(&self, name: U) -> Option<usize> {
let name = name.as_ref().as_bytes();
self.inner
.as_ref()
.and_then(|cols| cols.iter().position(|col| col.name_ref() == name))
}
}
impl AsRef<[Column]> for SetColumns<'_> {
fn as_ref(&self) -> &[Column] {
self.inner
.as_ref()
.map(|cols| &(*cols)[..])
.unwrap_or(&[][..])
}
}