use std::borrow::Borrow;
use std::collections::HashMap;
use std::io::{self, Write};
use byteorder::WriteBytesExt;
use mysql_common::constants::{CapabilityFlags, ColumnFlags, StatusFlags};
use tokio::io::AsyncWrite;
use crate::packet_writer::PacketWriter;
use crate::value::ToMysqlValue;
use crate::{writers, OkResponse};
use crate::{Column, ErrorKind, StatementData};
pub struct InitWriter<'a, W> {
pub(crate) client_capabilities: CapabilityFlags,
pub(crate) writer: &'a mut PacketWriter<W>,
}
impl<'a, W: 'a + AsyncWrite + Unpin> InitWriter<'a, W> {
pub async fn ok(self) -> io::Result<()> {
writers::write_ok_packet(self.writer, self.client_capabilities, OkResponse::default()).await
}
pub async fn error<E>(self, kind: ErrorKind, msg: &E) -> io::Result<()>
where
E: Borrow<[u8]> + ?Sized,
{
writers::write_err(kind, msg.borrow(), self.writer).await
}
}
#[must_use]
pub struct StatementMetaWriter<'a, W> {
pub(crate) writer: &'a mut PacketWriter<W>,
pub(crate) stmts: &'a mut HashMap<u32, StatementData>,
pub(crate) client_capabilities: CapabilityFlags,
}
impl<'a, W: AsyncWrite + Unpin + 'a> StatementMetaWriter<'a, W> {
pub async fn reply<PI, CI>(self, id: u32, params: PI, columns: CI) -> io::Result<()>
where
PI: IntoIterator<Item = &'a Column>,
CI: IntoIterator<Item = &'a Column>,
<PI as IntoIterator>::IntoIter: ExactSizeIterator,
<CI as IntoIterator>::IntoIter: ExactSizeIterator,
{
let params = params.into_iter();
self.stmts.insert(
id,
StatementData {
params: params.len() as u16,
..Default::default()
},
);
writers::write_prepare_ok(id, params, columns, self.writer, self.client_capabilities).await
}
pub async fn error<E>(self, kind: ErrorKind, msg: &E) -> io::Result<()>
where
E: Borrow<[u8]> + ?Sized,
{
writers::write_err(kind, msg.borrow(), self.writer).await
}
}
enum Finalizer {
Ok(OkResponse),
Eof,
}
#[must_use]
pub struct QueryResultWriter<'a, W> {
pub(crate) is_bin: bool,
pub(crate) client_capabilities: CapabilityFlags,
pub(crate) writer: &'a mut PacketWriter<W>,
last_end: Option<Finalizer>,
}
impl<'a, W: AsyncWrite + Unpin> QueryResultWriter<'a, W> {
pub(crate) fn new(
writer: &'a mut PacketWriter<W>,
is_bin: bool,
client_capabilities: CapabilityFlags,
) -> Self {
QueryResultWriter {
is_bin,
client_capabilities,
writer,
last_end: None,
}
}
async fn finalize(&mut self, more_exists: bool) -> io::Result<()> {
let mut status = StatusFlags::empty();
if more_exists {
status.set(StatusFlags::SERVER_MORE_RESULTS_EXISTS, true);
}
match self.last_end.take() {
None => Ok(()),
Some(Finalizer::Ok(ok_packet)) => {
writers::write_ok_packet(self.writer, self.client_capabilities, ok_packet).await
}
Some(Finalizer::Eof) => writers::write_eof_packet(self.writer, status).await,
}
}
pub async fn start(mut self, columns: &'a [Column]) -> io::Result<RowWriter<'a, W>> {
self.finalize(true).await?;
RowWriter::new(self, columns).await
}
pub async fn complete_one(
mut self,
ok_packet: OkResponse,
) -> io::Result<QueryResultWriter<'a, W>> {
self.finalize(true).await?;
self.last_end = Some(Finalizer::Ok(ok_packet));
Ok(self)
}
pub async fn completed(self, ok_packet: OkResponse) -> io::Result<()> {
self.complete_one(ok_packet).await?.no_more_results().await
}
pub async fn error<E>(mut self, kind: ErrorKind, msg: &E) -> io::Result<()>
where
E: Borrow<[u8]> + ?Sized,
{
self.finalize(true).await?;
writers::write_err(kind, msg.borrow(), self.writer).await
}
pub async fn no_more_results(mut self) -> io::Result<()> {
self.finalize(false).await
}
}
#[must_use]
pub struct RowWriter<'a, W: AsyncWrite + Unpin> {
client_capabilities: CapabilityFlags,
result: Option<QueryResultWriter<'a, W>>,
bitmap_len: usize,
data: Vec<u8>,
columns: &'a [Column],
col: usize,
finished: bool,
}
impl<'a, W> RowWriter<'a, W>
where
W: 'a + AsyncWrite + Unpin,
{
async fn new(
result: QueryResultWriter<'a, W>,
columns: &'a [Column],
) -> io::Result<RowWriter<'a, W>> {
let bitmap_len = (columns.len() + 7 + 2) / 8;
let client_capabilities = result.client_capabilities;
let mut rw = RowWriter {
client_capabilities,
result: Some(result),
columns,
bitmap_len,
data: Vec::new(),
col: 0,
finished: false,
};
rw.start().await?;
Ok(rw)
}
#[inline]
async fn start(&mut self) -> io::Result<()> {
if !self.columns.is_empty() {
writers::column_definitions(
self.columns,
self.result.as_mut().unwrap().writer,
self.client_capabilities,
)
.await?;
}
Ok(())
}
pub fn write_col<T>(&mut self, v: T) -> io::Result<()>
where
T: ToMysqlValue,
{
if self.columns.is_empty() {
return Ok(());
}
if self.result.as_mut().unwrap().is_bin {
if self.col == 0 {
self.result.as_mut().unwrap().writer.write_u8(0x00)?;
self.data.resize(self.bitmap_len, 0);
}
let c = self.columns.get(self.col).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
"row has more columns than specification",
)
})?;
if v.is_null() {
if c.colflags.contains(ColumnFlags::NOT_NULL_FLAG) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"given NULL value for NOT NULL column",
));
} else {
self.data[(self.col + 2) / 8] |= 1u8 << ((self.col + 2) % 8);
}
} else {
v.to_mysql_bin(&mut self.data, c)?;
}
} else {
v.to_mysql_text(self.result.as_mut().unwrap().writer)?;
}
self.col += 1;
Ok(())
}
pub async fn end_row(&mut self) -> io::Result<()> {
if self.columns.is_empty() {
self.col += 1;
return Ok(());
}
if self.col != self.columns.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"row has fewer columns than specification",
));
}
if self.result.as_mut().unwrap().is_bin {
self.result
.as_mut()
.unwrap()
.writer
.write_all(&self.data[..])?;
self.data.clear();
}
self.result.as_mut().unwrap().writer.end_packet().await?;
self.col = 0;
Ok(())
}
pub async fn write_row<I, E>(&mut self, row: I) -> io::Result<()>
where
I: IntoIterator<Item = E>,
E: ToMysqlValue,
{
if !self.columns.is_empty() {
for v in row {
self.write_col(v)?;
}
}
self.end_row().await
}
}
impl<'a, W: AsyncWrite + Unpin + 'a> RowWriter<'a, W> {
async fn finish_inner(&mut self, extra_info: &str, complete: bool) -> io::Result<()> {
if self.finished {
return Ok(());
}
self.finished = true;
if !self.columns.is_empty() && self.col != 0 {
self.end_row().await?;
}
if complete {
if self.columns.is_empty() {
let resp = OkResponse {
info: extra_info.to_string(),
..Default::default()
};
self.result.as_mut().unwrap().last_end = Some(Finalizer::Ok(resp));
} else if self
.client_capabilities
.contains(CapabilityFlags::CLIENT_DEPRECATE_EOF)
{
let resp = OkResponse {
info: extra_info.to_string(),
header: 0xfe,
..Default::default()
};
self.result.as_mut().unwrap().last_end = Some(Finalizer::Ok(resp));
} else {
self.result.as_mut().unwrap().last_end = Some(Finalizer::Eof);
}
}
Ok(())
}
pub async fn finish(self) -> io::Result<()> {
self.finish_with_info("").await
}
pub async fn finish_one(self) -> io::Result<QueryResultWriter<'a, W>> {
self.finish_one_with_info("").await
}
pub async fn finish_with_info(self, extra_info: &str) -> io::Result<()> {
self.finish_one_with_info(extra_info)
.await?
.no_more_results()
.await
}
pub async fn finish_one_with_info(
mut self,
extra_info: &str,
) -> io::Result<QueryResultWriter<'a, W>> {
self.finish_inner(extra_info, true).await?;
Ok(self.result.take().unwrap())
}
pub async fn finish_error<E>(mut self, kind: ErrorKind, msg: &E) -> io::Result<()>
where
E: Borrow<[u8]>,
{
self.finish_inner("", false).await?;
self.result.take().unwrap().error(kind, msg).await
}
}