feat(mysql): expand MySqlRow

This commit is contained in:
Ryan Leckey 2021-02-22 21:22:30 -08:00
parent 3470195839
commit 9a31baa15a
No known key found for this signature in database
GPG Key ID: F8AA68C235AB08C9
5 changed files with 102 additions and 32 deletions

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use sqlx_core::{Result, Runtime};
use crate::connection::command::QueryCommand;
@ -31,7 +33,7 @@ macro_rules! impl_recv_columns {
// STATE: remember that we are now expecting a row or the end
*$cmd = QueryCommand::QueryStep;
Ok(columns)
Ok(columns.into())
}};
}
@ -42,7 +44,7 @@ impl<Rt: Runtime> MySqlStream<Rt> {
store: bool,
columns: u16,
cmd: &mut QueryCommand,
) -> Result<Vec<MySqlColumn>>
) -> Result<Arc<[MySqlColumn]>>
where
Rt: sqlx_core::Async,
{
@ -55,7 +57,7 @@ impl<Rt: Runtime> MySqlStream<Rt> {
store: bool,
columns: u16,
cmd: &mut QueryCommand,
) -> Result<Vec<MySqlColumn>>
) -> Result<Arc<[MySqlColumn]>>
where
Rt: sqlx_core::blocking::Runtime,
{

View File

@ -31,7 +31,7 @@ macro_rules! impl_raw_prepare {
// extract the type only from the column definition
// most other fields are useless
stmt.parameters.push(MySqlTypeInfo::new(&def));
stmt.parameters_mut().push(MySqlTypeInfo::new(&def));
}
// TODO: handle EOF for old MySQL
@ -42,7 +42,7 @@ macro_rules! impl_raw_prepare {
let def = read_packet!($(@$blocking)? stream).deserialize()?;
stmt.columns.push(MySqlColumn::new(ordinal, def));
stmt.columns_mut().push(MySqlColumn::new(ordinal, def));
}
// TODO: handle EOF for old MySQL

View File

@ -12,7 +12,7 @@ macro_rules! impl_raw_query {
// execute the prepared statement
$self.stream.write_packet(&protocol::Execute {
statement: statement.id(),
parameters: &statement.parameters,
parameters: statement.parameters(),
arguments: &arguments,
})?;

View File

@ -1,11 +1,11 @@
use crate::protocol::PrepareOk;
use crate::{MySqlColumn, MySqlTypeInfo};
#[derive(Debug)]
#[derive(Debug, Clone)]
pub(crate) struct RawStatement {
id: u32,
pub(crate) columns: Vec<MySqlColumn>,
pub(crate) parameters: Vec<MySqlTypeInfo>,
columns: Vec<MySqlColumn>,
parameters: Vec<MySqlTypeInfo>,
}
impl RawStatement {
@ -17,7 +17,23 @@ impl RawStatement {
}
}
pub(crate) fn id(&self) -> u32 {
pub(crate) const fn id(&self) -> u32 {
self.id
}
pub(crate) fn columns(&self) -> &[MySqlColumn] {
&self.columns
}
pub(crate) fn columns_mut(&mut self) -> &mut Vec<MySqlColumn> {
&mut self.columns
}
pub(crate) fn parameters(&self) -> &[MySqlTypeInfo] {
&self.parameters
}
pub(crate) fn parameters_mut(&mut self) -> &mut Vec<MySqlTypeInfo> {
&mut self.parameters
}
}

View File

@ -1,46 +1,97 @@
use std::sync::Arc;
use bytes::Bytes;
use sqlx_core::{Decode, Error, Row};
use sqlx_core::{ColumnIndex, Decode, Error, Result, Row};
use crate::{protocol, MySql, MySqlColumn, MySqlRawValue, MySqlRawValueFormat};
/// A single row from a result set generated from MySQL.
#[allow(clippy::module_name_repetitions)]
pub struct MySqlRow {
format: MySqlRawValueFormat,
columns: Vec<MySqlColumn>,
columns: Arc<[MySqlColumn]>,
values: Vec<Option<Bytes>>,
}
impl MySqlRow {
// FIXME: Use Arc or some other way of sharing columns between rows
pub(crate) fn new(row: protocol::Row, columns: &Vec<MySqlColumn>) -> Self {
Self { values: row.values, columns: columns.clone(), format: row.format }
pub(crate) fn new(row: protocol::Row, columns: &Arc<[MySqlColumn]>) -> Self {
Self { values: row.values, columns: Arc::clone(columns), format: row.format }
}
/// Returns `true` if the row contains only `NULL` values.
fn is_null(&self) -> bool {
self.values.iter().all(Option::is_some)
}
/// Returns the number of columns in the row.
#[must_use]
pub fn len(&self) -> usize {
self.values.len()
}
/// Returns `true` if there are no columns in the row.
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn try_get<'r, T>(&'r self, index: usize) -> sqlx_core::Result<T>
/// Returns a reference to the columns in the row.
#[must_use]
pub fn columns(&self) -> &[MySqlColumn] {
&self.columns
}
/// Returns the column name, given the ordinal (also known as index) of the column.
#[must_use]
pub fn column_name_of(&self, ordinal: usize) -> &str {
self.try_column_name_of(ordinal).unwrap()
}
/// Returns the column name, given the ordinal (also known as index) of the column.
pub fn try_column_name_of(&self, ordinal: usize) -> Result<&str> {
self.columns
.get(ordinal)
.map(MySqlColumn::name)
.ok_or_else(|| Error::ColumnIndexOutOfBounds { index: ordinal, len: self.len() })
}
/// Returns the column ordinal, given the name of the column.
#[must_use]
pub fn ordinal_of(&self, name: &str) -> usize {
self.try_ordinal_of(name).unwrap()
}
/// Returns the column ordinal, given the name of the column.
pub fn try_ordinal_of(&self, name: &str) -> Result<usize> {
self.columns
.iter()
.position(|col| col.name() == name)
.ok_or_else(|| Error::ColumnNotFound { name: name.to_owned().into_boxed_str() })
}
/// Returns the decoded value at the index.
pub fn try_get<'r, T, I>(&'r self, index: I) -> Result<T>
where
I: ColumnIndex<Self>,
T: Decode<'r, MySql>,
{
Ok(self.try_get_raw(index)?.decode()?)
}
// noinspection RsNeedlessLifetimes
pub fn try_get_raw<'r>(&'r self, index: usize) -> sqlx_core::Result<MySqlRawValue<'r>> {
/// Returns the raw representation of the value at the index.
#[allow(clippy::needless_lifetimes)]
pub fn try_get_raw<'r, I>(&'r self, index: I) -> Result<MySqlRawValue<'r>>
where
I: ColumnIndex<Self>,
{
let ordinal = index.get(self)?;
let value = self
.values
.get(index)
.ok_or_else(|| Error::ColumnIndexOutOfBounds { len: self.len(), index })?;
.get(ordinal)
.ok_or_else(|| Error::ColumnIndexOutOfBounds { len: self.len(), index: ordinal })?;
let column = &self.columns[index];
let column = &self.columns[ordinal];
Ok(MySqlRawValue::new(value, self.format, column.type_info()))
}
@ -50,7 +101,7 @@ impl Row for MySqlRow {
type Database = MySql;
fn is_null(&self) -> bool {
todo!()
self.is_null()
}
fn len(&self) -> usize {
@ -58,34 +109,35 @@ impl Row for MySqlRow {
}
fn columns(&self) -> &[MySqlColumn] {
todo!()
self.columns()
}
fn column_name_of(&self, ordinal: usize) -> &str {
todo!()
self.column_name_of(ordinal)
}
fn try_column_name_of(&self, ordinal: usize) -> sqlx_core::Result<&str> {
todo!()
fn try_column_name_of(&self, ordinal: usize) -> Result<&str> {
self.try_column_name_of(ordinal)
}
fn ordinal_of(&self, name: &str) -> usize {
todo!()
self.ordinal_of(name)
}
fn try_ordinal_of(&self, name: &str) -> sqlx_core::Result<usize> {
todo!()
fn try_ordinal_of(&self, name: &str) -> Result<usize> {
self.try_ordinal_of(name)
}
fn try_get<'r, T>(&'r self, index: usize) -> sqlx_core::Result<T>
fn try_get<'r, T, I>(&'r self, index: I) -> Result<T>
where
I: ColumnIndex<Self>,
T: Decode<'r, MySql>,
{
self.try_get(index)
}
// noinspection RsNeedlessLifetimes
fn try_get_raw<'r>(&'r self, index: usize) -> sqlx_core::Result<MySqlRawValue<'r>> {
#[allow(clippy::needless_lifetimes)]
fn try_get_raw<'r, I: ColumnIndex<Self>>(&'r self, index: I) -> Result<MySqlRawValue<'r>> {
self.try_get_raw(index)
}
}