use std::fmt;
use sqlx::{MySql, Row, Type, ValueRef, mysql::MySqlRow};
use crate::{
Result,
error::{invalid_column, result_limit_exceeded, unsupported_column_type},
};
pub const MAX_FIELD_BYTES: usize = 1_048_576;
pub const MAX_RESULT_BYTES: usize = 8_388_608;
pub struct DbRow(pub(crate) MySqlRow);
impl fmt::Debug for DbRow {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("DbRow")
.field("columns", &self.0.len())
.field("values", &"<redacted>")
.finish()
}
}
impl DbRow {
pub fn bool(&self, column: &str) -> Result<bool> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn i64(&self, column: &str) -> Result<i64> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn u64(&self, column: &str) -> Result<u64> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn f64(&self, column: &str) -> Result<f64> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn string(&self, column: &str) -> Result<String> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn bytes(&self, column: &str) -> Result<Vec<u8>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn optional_i64(&self, column: &str) -> Result<Option<i64>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn optional_bool(&self, column: &str) -> Result<Option<bool>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn optional_u64(&self, column: &str) -> Result<Option<u64>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn optional_f64(&self, column: &str) -> Result<Option<f64>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn optional_string(&self, column: &str) -> Result<Option<String>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
pub fn optional_bytes(&self, column: &str) -> Result<Option<Vec<u8>>> {
self.0.try_get(column).map_err(|_| invalid_column())
}
}
pub(crate) fn row_payload_bytes(row: &MySqlRow) -> Result<usize> {
let mut total = 0_usize;
for index in 0..row.len() {
let raw = row.try_get_raw(index).map_err(|_| invalid_column())?;
if raw.is_null() {
continue;
}
let type_info = raw.type_info();
let bytes = if <&[u8] as Type<MySql>>::compatible(type_info.as_ref()) {
row.try_get::<&[u8], _>(index)
.map_err(|_| invalid_column())?
.len()
} else if <i64 as Type<MySql>>::compatible(type_info.as_ref())
|| <u64 as Type<MySql>>::compatible(type_info.as_ref())
|| <f64 as Type<MySql>>::compatible(type_info.as_ref())
|| <bool as Type<MySql>>::compatible(type_info.as_ref())
{
8
} else {
return Err(unsupported_column_type());
};
if bytes > MAX_FIELD_BYTES {
return Err(result_limit_exceeded());
}
total = total.saturating_add(bytes);
if total > MAX_RESULT_BYTES {
return Err(result_limit_exceeded());
}
}
Ok(total)
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct WriteResult {
rows_affected: u64,
last_insert_id: u64,
}
impl WriteResult {
pub(crate) const fn new(rows_affected: u64, last_insert_id: u64) -> Self {
Self {
rows_affected,
last_insert_id,
}
}
pub const fn rows_affected(self) -> u64 {
self.rows_affected
}
pub const fn last_insert_id(self) -> u64 {
self.last_insert_id
}
}