use byteorder::WriteBytesExt;
use myc::constants::{ColumnFlags, StatusFlags};
use packet::PacketWriter;
use std::borrow::Borrow;
use std::collections::HashMap;
use std::io::{self, Write};
use value::ToMysqlValue;
use writers;
use {Column, ErrorKind, StatementData};
#[must_use]
pub struct StatementMetaWriter<'a, W: Write + 'a> {
pub(crate) writer: &'a mut PacketWriter<W>,
pub(crate) stmts: &'a mut HashMap<u32, StatementData>,
}
impl<'a, W: Write + 'a> StatementMetaWriter<'a, W> {
pub 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)
}
pub fn error<E>(self, kind: ErrorKind, msg: &E) -> io::Result<()>
where
E: Borrow<[u8]> + ?Sized,
{
writers::write_err(kind, msg.borrow(), self.writer)
}
}
enum Finalizer {
Ok { rows: u64, last_insert_id: u64 },
EOF,
}
#[must_use]
pub struct QueryResultWriter<'a, W: Write + 'a> {
pub(crate) is_bin: bool,
pub(crate) writer: &'a mut PacketWriter<W>,
last_end: Option<Finalizer>,
}
impl<'a, W: Write> QueryResultWriter<'a, W> {
pub(crate) fn new(writer: &'a mut PacketWriter<W>, is_bin: bool) -> Self {
QueryResultWriter {
is_bin: is_bin,
writer: writer,
last_end: None,
}
}
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 {
rows,
last_insert_id,
}) => writers::write_ok_packet(self.writer, rows, last_insert_id, status),
Some(Finalizer::EOF) => writers::write_eof_packet(self.writer, status),
}
}
pub fn start(mut self, columns: &'a [Column]) -> io::Result<RowWriter<'a, W>> {
self.finalize(true)?;
RowWriter::new(self, columns)
}
pub fn complete_one(mut self, rows: u64, last_insert_id: u64) -> io::Result<Self> {
self.finalize(true)?;
self.last_end = Some(Finalizer::Ok {
rows,
last_insert_id,
});
Ok(self)
}
pub fn completed(self, rows: u64, last_insert_id: u64) -> io::Result<()> {
self.complete_one(rows, last_insert_id)?.no_more_results()
}
pub fn error<E>(mut self, kind: ErrorKind, msg: &E) -> io::Result<()>
where
E: Borrow<[u8]> + ?Sized,
{
self.finalize(true)?;
writers::write_err(kind, msg.borrow(), self.writer)
}
pub fn no_more_results(mut self) -> io::Result<()> {
self.finalize(false)
}
}
impl<'a, W: Write> Drop for QueryResultWriter<'a, W> {
fn drop(&mut self) {
self.finalize(false).unwrap();
}
}
#[must_use]
pub struct RowWriter<'a, W: Write + 'a> {
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: Write + 'a,
{
fn new(
result: QueryResultWriter<'a, W>,
columns: &'a [Column],
) -> io::Result<RowWriter<'a, W>> {
let bitmap_len = (columns.len() + 7 + 2) / 8;
let mut rw = RowWriter {
result: Some(result),
columns: columns,
bitmap_len,
data: Vec::new(),
col: 0,
finished: false,
};
rw.start()?;
Ok(rw)
}
#[inline]
fn start(&mut self) -> io::Result<()> {
if !self.columns.is_empty() {
writers::column_definitions(self.columns, self.result.as_mut().unwrap().writer)?;
}
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",
)
})?
.borrow();
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 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()?;
self.col = 0;
Ok(())
}
pub 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()
}
}
impl<'a, W: Write + 'a> RowWriter<'a, W> {
fn finish_inner(&mut self) -> io::Result<()> {
if self.finished {
return Ok(());
}
self.finished = true;
if !self.columns.is_empty() && self.col != 0 {
self.end_row()?;
}
if self.columns.is_empty() {
self.result.as_mut().unwrap().last_end = Some(Finalizer::Ok {
rows: self.col as u64,
last_insert_id: 0,
});
} else {
self.result.as_mut().unwrap().last_end = Some(Finalizer::EOF);
}
Ok(())
}
pub fn finish(self) -> io::Result<()> {
self.finish_one()?.no_more_results()
}
pub fn finish_one(mut self) -> io::Result<QueryResultWriter<'a, W>> {
self.finish_inner()?;
Ok(self.result.take().unwrap())
}
}
impl<'a, W: Write + 'a> Drop for RowWriter<'a, W> {
fn drop(&mut self) {
self.finish_inner().unwrap();
}
}