diff --git a/Cargo.toml b/Cargo.toml index 0e83482..84237a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,6 @@ regex = "1.5" rust_decimal = { version = "1.0", optional = true } sha1 = "0.10" sha2 = "0.10" -smallvec = { version = "1.6.1", features = ["union", "write"] } thiserror = "1.0" time02 = { package = "time", version = "0.2", default-features = false, features = [ "std", diff --git a/derive/src/from_value/enums/mod.rs b/derive/src/from_value/enums/mod.rs index 066def4..bc342a9 100644 --- a/derive/src/from_value/enums/mod.rs +++ b/derive/src/from_value/enums/mod.rs @@ -146,6 +146,7 @@ pub fn impl_from_value_for_enum( } else if min_discriminant >= BigInt::default() && max_discriminant <= BigInt::from(u16::MAX) && !matches!(repr.0, EnumRepr::U8(_)) + && !item_attrs.allow_suboptimal_repr { crate::warn::print_warning( "enum representation is suboptimal. Consider the following annotation:", diff --git a/src/misc/raw/mod.rs b/src/misc/raw/mod.rs index 8859c72..5271863 100644 --- a/src/misc/raw/mod.rs +++ b/src/misc/raw/mod.rs @@ -11,14 +11,12 @@ use std::io; use ::bytes::BufMut; -use smallvec::{Array, SmallVec}; use crate::{ io::ParseBuf, proto::{MyDeserialize, MySerialize}, }; -use self::bytes::LenEnc; pub use self::{ _const::{Const, RawConst}, bytes::RawBytes, @@ -96,31 +94,6 @@ impl<'de> MyDeserialize<'de> for ParseBuf<'de> { } } -/// This ad-hock impl parses length-encoded string into a `SmallVec`. -impl<'de, const LEN: usize> MyDeserialize<'de> for SmallVec<[u8; LEN]> -where - [u8; LEN]: Array, -{ - const SIZE: Option = None; - type Ctx = (); - - fn deserialize((): Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result { - let mut small_vec = SmallVec::new(); - let s: RawBytes<'de, LenEnc> = buf.parse(())?; - small_vec.extend_from_slice(s.as_bytes()); - Ok(small_vec) - } -} - -impl MySerialize for SmallVec<[u8; LEN]> -where - [u8; LEN]: Array, -{ - fn serialize(&self, buf: &mut Vec) { - buf.put_slice(self) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Either { Left(T), diff --git a/src/packets/mod.rs b/src/packets/mod.rs index 3d7c002..6ed529e 100644 --- a/src/packets/mod.rs +++ b/src/packets/mod.rs @@ -9,10 +9,10 @@ use btoi::btoi; use bytes::BufMut; use regex::bytes::Regex; -use smallvec::SmallVec; use uuid::Uuid; use std::str::FromStr; +use std::sync::Arc; use std::{ borrow::Cow, cmp::max, collections::HashMap, convert::TryFrom, fmt, io, marker::PhantomData, }; @@ -110,15 +110,112 @@ define_const!( 0x0c ); +/// Dynamically-sized column metadata — a part of the [`Column`] packet. +#[derive(Debug, Default, Clone, Eq, PartialEq)] +struct ColumnMeta<'a> { + schema: RawBytes<'a, LenEnc>, + table: RawBytes<'a, LenEnc>, + org_table: RawBytes<'a, LenEnc>, + name: RawBytes<'a, LenEnc>, + org_name: RawBytes<'a, LenEnc>, +} + +impl<'a> ColumnMeta<'a> { + pub fn into_owned(self) -> ColumnMeta<'static> { + ColumnMeta { + schema: self.schema.into_owned(), + table: self.table.into_owned(), + org_table: self.org_table.into_owned(), + name: self.name.into_owned(), + org_name: self.org_name.into_owned(), + } + } + + /// Returns the value of the [`ColumnMeta::schema`] field as a byte slice. + pub fn schema_ref(&self) -> &[u8] { + self.schema.as_bytes() + } + + /// Returns the value of the [`ColumnMeta::schema`] field as a string (lossy converted). + pub fn schema_str(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self.schema_ref()) + } + + /// Returns the value of the [`ColumnMeta::table`] field as a byte slice. + pub fn table_ref(&self) -> &[u8] { + self.table.as_bytes() + } + + /// Returns the value of the [`ColumnMeta::table`] field as a string (lossy converted). + pub fn table_str(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self.table_ref()) + } + + /// Returns the value of the [`ColumnMeta::org_table`] field as a byte slice. + /// + /// "org_table" is for original table name. + pub fn org_table_ref(&self) -> &[u8] { + self.org_table.as_bytes() + } + + /// Returns the value of the [`ColumnMeta::org_table`] field as a string (lossy converted). + pub fn org_table_str(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self.org_table_ref()) + } + + /// Returns the value of the [`ColumnMeta::name`] field as a byte slice. + pub fn name_ref(&self) -> &[u8] { + self.name.as_bytes() + } + + /// Returns the value of the [`ColumnMeta::name`] field as a string (lossy converted). + pub fn name_str(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self.name_ref()) + } + + /// Returns the value of the [`ColumnMeta::org_name`] field as a byte slice. + /// + /// "org_name" is for original column name. + pub fn org_name_ref(&self) -> &[u8] { + self.org_name.as_bytes() + } + + /// Returns value of the [`ColumnMeta::org_name`] field as a string (lossy converted). + pub fn org_name_str(&self) -> Cow<'_, str> { + String::from_utf8_lossy(self.org_name_ref()) + } +} + +impl<'de> MyDeserialize<'de> for ColumnMeta<'de> { + const SIZE: Option = None; + type Ctx = (); + + fn deserialize(_ctx: Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result { + Ok(Self { + schema: buf.parse_unchecked(())?, + table: buf.parse_unchecked(())?, + org_table: buf.parse_unchecked(())?, + name: buf.parse_unchecked(())?, + org_name: buf.parse_unchecked(())?, + }) + } +} + +impl MySerialize for ColumnMeta<'_> { + fn serialize(&self, buf: &mut Vec) { + self.schema.serialize(&mut *buf); + self.table.serialize(&mut *buf); + self.org_table.serialize(&mut *buf); + self.name.serialize(&mut *buf); + self.org_name.serialize(&mut *buf); + } +} + /// Represents MySql Column (column packet). #[derive(Debug, Clone, Eq, PartialEq)] pub struct Column { catalog: ColumnDefinitionCatalog, - schema: SmallVec<[u8; 16]>, - table: SmallVec<[u8; 16]>, - org_table: SmallVec<[u8; 16]>, - name: SmallVec<[u8; 16]>, - org_name: SmallVec<[u8; 16]>, + meta: Arc>, fixed_length_fields_len: FixedLengthFieldsLen, column_length: RawInt, character_set: RawInt, @@ -135,20 +232,12 @@ impl<'de> MyDeserialize<'de> for Column { fn deserialize((): Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result { let catalog = buf.parse(())?; - let schema = buf.parse_unchecked(())?; - let table = buf.parse_unchecked(())?; - let org_table = buf.parse_unchecked(())?; - let name = buf.parse_unchecked(())?; - let org_name = buf.parse_unchecked(())?; + let meta = Arc::new(buf.parse::(())?.into_owned()); let mut buf: ParseBuf = buf.parse(13)?; Ok(Column { catalog, - schema, - table, - org_table, - name, - org_name, + meta, fixed_length_fields_len: buf.parse_unchecked(())?, character_set: buf.parse_unchecked(())?, column_length: buf.parse_unchecked(())?, @@ -163,11 +252,7 @@ impl<'de> MyDeserialize<'de> for Column { impl MySerialize for Column { fn serialize(&self, buf: &mut Vec) { self.catalog.serialize(&mut *buf); - self.schema.serialize(&mut *buf); - self.table.serialize(&mut *buf); - self.org_table.serialize(&mut *buf); - self.name.serialize(&mut *buf); - self.org_name.serialize(&mut *buf); + self.meta.serialize(&mut *buf); self.fixed_length_fields_len.serialize(&mut *buf); self.column_length.serialize(&mut *buf); self.character_set.serialize(&mut *buf); @@ -182,11 +267,7 @@ impl Column { pub fn new(column_type: ColumnType) -> Self { Self { catalog: Default::default(), - schema: Default::default(), - table: Default::default(), - org_table: Default::default(), - name: Default::default(), - org_name: Default::default(), + meta: Default::default(), fixed_length_fields_len: Default::default(), column_length: Default::default(), character_set: Default::default(), @@ -198,27 +279,27 @@ impl Column { } pub fn with_schema(mut self, schema: &[u8]) -> Self { - self.schema = schema.into(); + Arc::make_mut(&mut self.meta).schema = RawBytes::new(schema).into_owned(); self } pub fn with_table(mut self, table: &[u8]) -> Self { - self.table = table.into(); + Arc::make_mut(&mut self.meta).table = RawBytes::new(table).into_owned(); self } pub fn with_org_table(mut self, org_table: &[u8]) -> Self { - self.org_table = org_table.into(); + Arc::make_mut(&mut self.meta).org_table = RawBytes::new(org_table).into_owned(); self } pub fn with_name(mut self, name: &[u8]) -> Self { - self.name = name.into(); + Arc::make_mut(&mut self.meta).name = RawBytes::new(name).into_owned(); self } pub fn with_org_name(mut self, org_name: &[u8]) -> Self { - self.org_name = org_name.into(); + Arc::make_mut(&mut self.meta).org_name = RawBytes::new(org_name).into_owned(); self } @@ -276,57 +357,67 @@ impl Column { } /// Returns value of the schema field of a column packet as a byte slice. + #[inline(always)] pub fn schema_ref(&self) -> &[u8] { - &self.schema + self.meta.schema_ref() } /// Returns value of the schema field of a column packet as a string (lossy converted). + #[inline(always)] pub fn schema_str(&self) -> Cow<'_, str> { - String::from_utf8_lossy(self.schema_ref()) + self.meta.schema_str() } /// Returns value of the table field of a column packet as a byte slice. + #[inline(always)] pub fn table_ref(&self) -> &[u8] { - &self.table + self.meta.table_ref() } /// Returns value of the table field of a column packet as a string (lossy converted). + #[inline(always)] pub fn table_str(&self) -> Cow<'_, str> { - String::from_utf8_lossy(self.table_ref()) + self.meta.table_str() } /// Returns value of the org_table field of a column packet as a byte slice. /// /// "org_table" is for original table name. + #[inline(always)] pub fn org_table_ref(&self) -> &[u8] { - &self.org_table + self.meta.org_table_ref() } /// Returns value of the org_table field of a column packet as a string (lossy converted). + #[inline(always)] pub fn org_table_str(&self) -> Cow<'_, str> { - String::from_utf8_lossy(self.org_table_ref()) + self.meta.org_table_str() } /// Returns value of the name field of a column packet as a byte slice. + #[inline(always)] pub fn name_ref(&self) -> &[u8] { - &self.name + self.meta.name_ref() } /// Returns value of the name field of a column packet as a string (lossy converted). + #[inline(always)] pub fn name_str(&self) -> Cow<'_, str> { - String::from_utf8_lossy(self.name_ref()) + self.meta.name_str() } /// Returns value of the org_name field of a column packet as a byte slice. /// /// "org_name" is for original column name. + #[inline(always)] pub fn org_name_ref(&self) -> &[u8] { - &self.org_name + self.meta.org_name_ref() } /// Returns value of the org_name field of a column packet as a string (lossy converted). + #[inline(always)] pub fn org_name_str(&self) -> Cow<'_, str> { - String::from_utf8_lossy(self.org_name_ref()) + self.meta.org_name_str() } } diff --git a/src/row/convert/frunk.rs b/src/row/convert/frunk.rs index 4f168c1..713d86a 100644 --- a/src/row/convert/frunk.rs +++ b/src/row/convert/frunk.rs @@ -10,11 +10,14 @@ #![cfg(feature = "frunk")] +use std::sync::Arc; + use frunk::{hlist::h_cons, prelude::HList}; pub use frunk::{HCons, HNil}; use super::{FromRow, FromRowError}; use crate::{ + packets::Column, row::{new_row_raw, Row}, value::{ convert::{FromValue, FromValueError}, @@ -40,79 +43,49 @@ impl FromRow for HCons where H: FromValue, H::Intermediate: Into, - T: FromRow + sealed::HlistFromRow, + T: FromRow, T: HList, { fn from_row_opt(row: Row) -> Result where Self: Sized, { - use sealed::HlistFromRow; - if row.len() != Self::LEN { return Err(FromRowError(row)); } let columns = row.columns(); - let values = row.unwrap_raw(); + let mut values = row.unwrap_raw(); - Self::hlist_from_row_opt(values) - .map_err(|values| FromRowError(new_row_raw(values, columns))) - } -} + debug_assert_eq!(values.len(), Self::LEN); -mod sealed { - use super::*; + let Some(head) = values[0].take() else { + return Err(FromRowError(new_row_raw(values, columns))); + }; - /// Helper trait for `FromRow for HList`. - pub trait HlistFromRow: Sized { - fn hlist_from_row_opt(values: Vec>) -> Result>>; - } - - impl HlistFromRow for HNil { - fn hlist_from_row_opt(values: Vec>) -> Result>> { - debug_assert_eq!(values.len(), Self::LEN); - Ok(HNil) - } - } - - impl HlistFromRow for HCons - where - H: FromValue, - H::Intermediate: Into, - T: HlistFromRow, - T: HList, - { - fn hlist_from_row_opt(mut values: Vec>) -> Result>> - where - Self: Sized, - { - debug_assert_eq!(values.len(), Self::LEN); - - if values[0].is_none() { - return Err(values); + let ir = match H::get_intermediate(head) { + Ok(ir) => ir, + Err(FromValueError(value)) => { + values[0] = Some(value); + return Err(FromRowError(new_row_raw(values, columns))); + } + }; + + // TODO: Avoid cloning columns here by using something like `owning_ref::ArcRef` + let columns_tail: Arc<[Column]> = + columns.iter().skip(1).cloned().collect::>().into(); + values.remove(0); + + let tail = match T::from_row_opt(new_row_raw(values, columns_tail)) { + Ok(x) => x, + Err(FromRowError(row)) => { + let mut values = row.unwrap_raw(); + values.insert(0, Some(ir.into())); + return Err(FromRowError(new_row_raw(values, columns))); } + }; - let head = values.remove(0).expect("must be here"); - - let ir = match H::get_intermediate(head) { - Ok(ir) => ir, - Err(FromValueError(value)) => { - values.insert(0, Some(value)); - return Err(values); - } - }; - - let tail = match T::hlist_from_row_opt(values) { - Ok(t) => t, - Err(mut values) => { - values.insert(0, Some(Into::::into(ir))); - return Err(values); - } - }; - - Ok(h_cons(Into::::into(ir), tail)) - } + Ok(h_cons(Into::::into(ir), tail)) } }