From b33bc3c017daef8ac917f747f010f1beda27a4da Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Tue, 20 Aug 2019 21:01:19 -0700 Subject: [PATCH] Clean up SQL type (de)serialize --- src/connection.rs | 8 +- src/deserialize.rs | 9 +-- src/executor.rs | 16 ++-- src/pg/protocol/backend_key_data.rs | 6 +- src/pg/query.rs | 8 +- src/pg/types/boolean.rs | 17 ++-- src/pg/types/character.rs | 26 +++---- src/pg/types/mod.rs | 2 - src/pg/types/numeric.rs | 60 +++++--------- src/pool.rs | 8 +- src/query.rs | 36 +++------ src/row.rs | 116 ++++++++++++++-------------- src/serialize.rs | 8 +- src/types.rs | 64 ++++++++------- 14 files changed, 173 insertions(+), 211 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 5ea6d8a0..a140938e 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -95,10 +95,10 @@ where }) } - fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> + fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow + Send + Unpin, + T: FromRow + Send + Unpin, { Box::pin(async_stream::try_stream! { let mut conn = self.get().await; @@ -110,13 +110,13 @@ where }) } - fn fetch_optional<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>( + fn fetch_optional<'c, 'q, T: 'c, Q: 'q + 'c>( &'c self, query: Q, ) -> BoxFuture<'c, io::Result>> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow, + T: FromRow, { Box::pin(async move { let mut conn = self.get().await; diff --git a/src/deserialize.rs b/src/deserialize.rs index ec7227f8..6d9eb106 100644 --- a/src/deserialize.rs +++ b/src/deserialize.rs @@ -1,16 +1,15 @@ use crate::{backend::Backend, types::HasSqlType}; // TODO: Allow from_sql to return an error (that can be unified) -// TODO: Consider using a RawValue wrapper type inUead of exposing raw bytes (as different back-ends may want to expose different data here.. maybe?) -pub trait FromSql { +pub trait FromSql { fn from_sql(raw: Option<&[u8]>) -> Self; } -impl FromSql for Option +impl FromSql for Option where - DB: Backend + HasSqlType, - T: FromSql, + DB: Backend + HasSqlType, + T: FromSql, { #[inline] fn from_sql(raw: Option<&[u8]>) -> Self { diff --git a/src/executor.rs b/src/executor.rs index db3c5deb..60b1f22a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -9,18 +9,18 @@ pub trait Executor: Send { where Q: RawQuery<'q, Backend = Self::Backend>; - fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> + fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow + Send + Unpin; + T: FromRow + Send + Unpin; - fn fetch_optional<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>( + fn fetch_optional<'c, 'q, T: 'c, Q: 'q + 'c>( &'c self, query: Q, ) -> BoxFuture<'c, io::Result>> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow; + T: FromRow; } impl<'e, E> Executor for &'e E @@ -37,21 +37,21 @@ where (*self).execute(query) } - fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> + fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow + Send + Unpin, + T: FromRow + Send + Unpin, { (*self).fetch(query) } - fn fetch_optional<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>( + fn fetch_optional<'c, 'q, T: 'c, Q: 'q + 'c>( &'c self, query: Q, ) -> BoxFuture<'c, io::Result>> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow, + T: FromRow, { (*self).fetch_optional(query) } diff --git a/src/pg/protocol/backend_key_data.rs b/src/pg/protocol/backend_key_data.rs index e0dbd0cc..903de973 100644 --- a/src/pg/protocol/backend_key_data.rs +++ b/src/pg/protocol/backend_key_data.rs @@ -24,8 +24,10 @@ impl BackendKeyData { impl Decode for BackendKeyData { fn decode(src: &[u8]) -> Self { - let process_id = u32::from_be_bytes(src[..4].try_into().unwrap()); - let secret_key = u32::from_be_bytes(src[4..].try_into().unwrap()); + debug_assert_eq!(src.len(), 8); + + let process_id = u32::from_be_bytes(src[0..4].try_into().unwrap()); + let secret_key = u32::from_be_bytes(src[4..8].try_into().unwrap()); Self { process_id, diff --git a/src/pg/query.rs b/src/pg/query.rs index 12b63488..d3e9e12b 100644 --- a/src/pg/query.rs +++ b/src/pg/query.rs @@ -32,16 +32,16 @@ impl<'q> RawQuery<'q> for PgRawQuery<'q> { } } - fn bind_as(mut self, value: T) -> Self + fn bind(mut self, value: T) -> Self where Self: Sized, - Self::Backend: HasSqlType, - T: ToSql, + Self::Backend: HasSqlType, + T: ToSql, { // TODO: When/if we receive types that do _not_ support BINARY, we need to check here // TODO: There is no need to be explicit unless we are expecting mixed BINARY / TEXT - self.types.push(>::metadata().oid); + self.types.push(>::metadata().oid); let pos = self.buf.len(); self.buf.put_int_32(0); diff --git a/src/pg/types/boolean.rs b/src/pg/types/boolean.rs index 25c54dfa..e268e2f9 100644 --- a/src/pg/types/boolean.rs +++ b/src/pg/types/boolean.rs @@ -2,12 +2,10 @@ use super::{Pg, PgTypeMetadata}; use crate::{ deserialize::FromSql, serialize::{IsNull, ToSql}, - types::{AsSqlType, HasSqlType}, + types::HasSqlType, }; -pub struct Bool; - -impl HasSqlType for Pg { +impl HasSqlType for Pg { fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 16, @@ -16,11 +14,7 @@ impl HasSqlType for Pg { } } -impl AsSqlType for bool { - type SqlType = Bool; -} - -impl ToSql for bool { +impl ToSql for bool { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { buf.push(self as u8); @@ -29,10 +23,13 @@ impl ToSql for bool { } } -impl FromSql for bool { +impl FromSql for bool { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { // TODO: Handle optionals buf.unwrap()[0] != 0 } } + +// TODO: #[derive(SqlType)] +// pub struct Bool(pub bool); diff --git a/src/pg/types/character.rs b/src/pg/types/character.rs index 4321889a..77ec56ba 100644 --- a/src/pg/types/character.rs +++ b/src/pg/types/character.rs @@ -2,11 +2,12 @@ use super::{Pg, PgTypeMetadata}; use crate::{ deserialize::FromSql, serialize::{IsNull, ToSql}, - types::{AsSqlType, HasSqlType, Text}, + types::HasSqlType, }; use std::str; -impl HasSqlType for Pg { +impl HasSqlType<&'_ str> for Pg { + #[inline] fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 25, @@ -15,11 +16,14 @@ impl HasSqlType for Pg { } } -impl AsSqlType for &'_ str { - type SqlType = Text; +impl HasSqlType for Pg { + #[inline] + fn metadata() -> PgTypeMetadata { + >::metadata() + } } -impl ToSql for &'_ str { +impl ToSql for &'_ str { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { buf.extend_from_slice(self.as_bytes()); @@ -28,20 +32,14 @@ impl ToSql for &'_ str { } } -impl AsSqlType for String { - type SqlType = Text; -} - -impl ToSql for String { +impl ToSql for String { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { - buf.extend_from_slice(self.as_bytes()); - - IsNull::No + self.as_str().to_sql(buf) } } -impl FromSql for String { +impl FromSql for String { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { // TODO: Handle nulls diff --git a/src/pg/types/mod.rs b/src/pg/types/mod.rs index c410f201..aa67f246 100644 --- a/src/pg/types/mod.rs +++ b/src/pg/types/mod.rs @@ -5,8 +5,6 @@ mod boolean; mod character; mod numeric; -pub use self::boolean::Bool; - pub struct PgTypeMetadata { pub oid: u32, pub array_oid: u32, diff --git a/src/pg/types/numeric.rs b/src/pg/types/numeric.rs index 07087680..483b7114 100644 --- a/src/pg/types/numeric.rs +++ b/src/pg/types/numeric.rs @@ -2,11 +2,12 @@ use super::{Pg, PgTypeMetadata}; use crate::{ deserialize::FromSql, serialize::{IsNull, ToSql}, - types::{AsSqlType, BigInt, Double, HasSqlType, Int, Real, SmallInt}, + types::HasSqlType, }; use byteorder::{BigEndian, ByteOrder}; -impl HasSqlType for Pg { +impl HasSqlType for Pg { + #[inline] fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 21, @@ -15,11 +16,7 @@ impl HasSqlType for Pg { } } -impl AsSqlType for i16 { - type SqlType = SmallInt; -} - -impl ToSql for i16 { +impl ToSql for i16 { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { buf.extend_from_slice(&self.to_be_bytes()); @@ -28,15 +25,15 @@ impl ToSql for i16 { } } -impl FromSql for i16 { +impl FromSql for i16 { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { - // TODO: Handle optionals BigEndian::read_i16(buf.unwrap()) } } -impl HasSqlType for Pg { +impl HasSqlType for Pg { + #[inline] fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 23, @@ -45,11 +42,7 @@ impl HasSqlType for Pg { } } -impl AsSqlType for i32 { - type SqlType = Int; -} - -impl ToSql for i32 { +impl ToSql for i32 { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { buf.extend_from_slice(&self.to_be_bytes()); @@ -58,15 +51,15 @@ impl ToSql for i32 { } } -impl FromSql for i32 { +impl FromSql for i32 { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { - // TODO: Handle optionals BigEndian::read_i32(buf.unwrap()) } } -impl HasSqlType for Pg { +impl HasSqlType for Pg { + #[inline] fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 20, @@ -75,11 +68,7 @@ impl HasSqlType for Pg { } } -impl AsSqlType for i64 { - type SqlType = BigInt; -} - -impl ToSql for i64 { +impl ToSql for i64 { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { buf.extend_from_slice(&self.to_be_bytes()); @@ -88,15 +77,15 @@ impl ToSql for i64 { } } -impl FromSql for i64 { +impl FromSql for i64 { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { - // TODO: Handle optionals BigEndian::read_i64(buf.unwrap()) } } -impl HasSqlType for Pg { +impl HasSqlType for Pg { + #[inline] fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 700, @@ -105,25 +94,22 @@ impl HasSqlType for Pg { } } -impl AsSqlType for f32 { - type SqlType = Real; -} - -impl ToSql for f32 { +impl ToSql for f32 { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { (self.to_bits() as i32).to_sql(buf) } } -impl FromSql for f32 { +impl FromSql for f32 { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { f32::from_bits(i32::from_sql(buf) as u32) } } -impl HasSqlType for Pg { +impl HasSqlType for Pg { + #[inline] fn metadata() -> PgTypeMetadata { PgTypeMetadata { oid: 701, @@ -132,18 +118,14 @@ impl HasSqlType for Pg { } } -impl AsSqlType for f64 { - type SqlType = Double; -} - -impl ToSql for f64 { +impl ToSql for f64 { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { (self.to_bits() as i64).to_sql(buf) } } -impl FromSql for f64 { +impl FromSql for f64 { #[inline] fn from_sql(buf: Option<&[u8]>) -> Self { f64::from_bits(i64::from_sql(buf) as u64) diff --git a/src/pool.rs b/src/pool.rs index e357cc83..3ea31526 100644 --- a/src/pool.rs +++ b/src/pool.rs @@ -140,10 +140,10 @@ where }) } - fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> + fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow + Send + Unpin, + T: FromRow + Send + Unpin, { Box::pin(async_stream::try_stream! { let live = self.0.acquire().await?; @@ -156,13 +156,13 @@ where }) } - fn fetch_optional<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>( + fn fetch_optional<'c, 'q, T: 'c, Q: 'q + 'c>( &'c self, query: Q, ) -> BoxFuture<'c, io::Result>> where Q: RawQuery<'q, Backend = Self::Backend>, - T: FromRow, + T: FromRow, { Box::pin(async move { let live = self.0.acquire().await?; diff --git a/src/query.rs b/src/query.rs index 11b0f200..0e6d8bbe 100644 --- a/src/query.rs +++ b/src/query.rs @@ -3,7 +3,7 @@ use crate::{ executor::Executor, row::FromRow, serialize::ToSql, - types::{AsSqlType, HasSqlType}, + types::HasSqlType, }; use futures_core::{future::BoxFuture, stream::BoxStream}; use std::io; @@ -13,10 +13,10 @@ pub trait RawQuery<'q>: Sized + Send + Sync { fn new(query: &'q str) -> Self; - fn bind_as(self, value: T) -> Self + fn bind(self, value: T) -> Self where - Self::Backend: HasSqlType, - T: ToSql; + Self::Backend: HasSqlType, + T: ToSql; fn finish(self, conn: &mut ::RawConnection); } @@ -40,21 +40,12 @@ where } #[inline] - pub fn bind(self, value: T) -> Self + pub fn bind(mut self, value: T) -> Self where - DB: HasSqlType<>::SqlType>, - T: AsSqlType + ToSql<>::SqlType, DB>, + DB: HasSqlType, + T: ToSql, { - self.bind_as::(value) - } - - #[inline] - pub fn bind_as(mut self, value: T) -> Self - where - DB: HasSqlType, - T: ToSql, - { - self.inner = self.inner.bind_as::(value); + self.inner = self.inner.bind(value); self } @@ -70,23 +61,20 @@ where } #[inline] - pub fn fetch(self, executor: &'q E) -> BoxStream<'q, io::Result> + pub fn fetch(self, executor: &'q E) -> BoxStream<'q, io::Result> where E: Executor, - T: FromRow + Send + Unpin, + T: FromRow + Send + Unpin, >::RawQuery: 'q, { executor.fetch(self.inner) } #[inline] - pub fn fetch_optional( - self, - executor: &'q E, - ) -> BoxFuture<'q, io::Result>> + pub fn fetch_optional(self, executor: &'q E) -> BoxFuture<'q, io::Result>> where E: Executor, - T: FromRow, + T: FromRow, >::RawQuery: 'q, { executor.fetch_optional(self.inner) diff --git a/src/row.rs b/src/row.rs index 8d6a4988..0c59adbf 100644 --- a/src/row.rs +++ b/src/row.rs @@ -10,41 +10,41 @@ pub trait Row: Send { fn get_raw(&self, index: usize) -> Option<&[u8]>; #[inline] - fn get(&self, index: usize) -> T + fn get(&self, index: usize) -> T where - Self::Backend: HasSqlType, - T: FromSql, + Self::Backend: HasSqlType, + T: FromSql, { T::from_sql(self.get_raw(index)) } } -pub trait FromRow { +pub trait FromRow { fn from_row>(row: R) -> Self; } -impl FromRow for T +impl FromRow for T where - DB: Backend + HasSqlType, - T: FromSql, + DB: Backend + HasSqlType, + T: FromSql, { #[inline] fn from_row>(row: R) -> Self { - row.get::(0) + row.get::(0) } } #[allow(unused)] macro_rules! impl_from_row_tuple { - ($B:ident: $( ($idx:tt) -> $T:ident, $ST:ident );+;) => { - impl<$($ST,)+ $($T,)+> crate::row::FromRow<($($ST,)+), $B> for ($($T,)+) + ($B:ident: $( ($idx:tt) -> $T:ident );+;) => { + impl<$($T,)+> crate::row::FromRow<$B> for ($($T,)+) where - $($B: crate::types::HasSqlType<$ST>,)+ - $($T: crate::deserialize::FromSql<$ST, $B>,)+ + $($B: crate::types::HasSqlType<$T>,)+ + $($T: crate::deserialize::FromSql<$B>,)+ { #[inline] fn from_row>(row: R) -> Self { - ($(row.get::<$ST, $T>($idx),)+) + ($(row.get($idx),)+) } } }; @@ -54,75 +54,75 @@ macro_rules! impl_from_row_tuple { macro_rules! impl_from_row_tuples_for_backend { ($B:ident) => { impl_from_row_tuple!($B: - (0) -> ST1, T1; + (0) -> T1; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; + (0) -> T1; + (1) -> T2; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; + (0) -> T1; + (1) -> T2; + (2) -> T3; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; - (3) -> ST4, T4; + (0) -> T1; + (1) -> T2; + (2) -> T3; + (3) -> T4; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; - (3) -> ST4, T4; - (4) -> ST5, T5; + (0) -> T1; + (1) -> T2; + (2) -> T3; + (3) -> T4; + (4) -> T5; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; - (3) -> ST4, T4; - (4) -> ST5, T5; - (5) -> ST6, T6; + (0) -> T1; + (1) -> T2; + (2) -> T3; + (3) -> T4; + (4) -> T5; + (5) -> T6; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; - (3) -> ST4, T4; - (4) -> ST5, T5; - (5) -> ST6, T6; - (6) -> ST7, T7; + (0) -> T1; + (1) -> T2; + (2) -> T3; + (3) -> T4; + (4) -> T5; + (5) -> T6; + (6) -> T7; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; - (3) -> ST4, T4; - (4) -> ST5, T5; - (5) -> ST6, T6; - (6) -> ST7, T7; - (7) -> ST8, T8; + (0) -> T1; + (1) -> T2; + (2) -> T3; + (3) -> T4; + (4) -> T5; + (5) -> T6; + (6) -> T7; + (7) -> T8; ); impl_from_row_tuple!($B: - (0) -> ST1, T1; - (1) -> ST2, T2; - (2) -> ST3, T3; - (3) -> ST4, T4; - (4) -> ST5, T5; - (5) -> ST6, T6; - (6) -> ST7, T7; - (7) -> ST8, T8; - (8) -> ST9, T9; + (0) -> T1; + (1) -> T2; + (2) -> T3; + (3) -> T4; + (4) -> T5; + (5) -> T6; + (6) -> T7; + (7) -> T8; + (8) -> T9; ); } } diff --git a/src/serialize.rs b/src/serialize.rs index cf78d0b9..99dd0dd7 100644 --- a/src/serialize.rs +++ b/src/serialize.rs @@ -12,14 +12,14 @@ pub enum IsNull { } /// Serializes a single value to be sent to the database. -pub trait ToSql { +pub trait ToSql { fn to_sql(self, buf: &mut Vec) -> IsNull; } -impl ToSql for Option +impl ToSql for Option where - DB: Backend + HasSqlType, - T: ToSql, + DB: Backend + HasSqlType, + T: ToSql, { #[inline] fn to_sql(self, buf: &mut Vec) -> IsNull { diff --git a/src/types.rs b/src/types.rs index c8592095..0186b5af 100644 --- a/src/types.rs +++ b/src/types.rs @@ -13,45 +13,43 @@ pub trait HasSqlType: TypeMetadata { fn metadata() -> Self::TypeMetadata; } -/// Defines the canonical SQL that the implementing Rust type represents. -/// This trait is used to map Rust types to SQL types when the explicit mapping is missing. -pub trait AsSqlType -where - DB: HasSqlType, -{ - type SqlType; -} +// TODO: #[derive(SqlType)] +// pub struct Text<'a>(Cow<'a, str>); -impl AsSqlType for Option -where - DB: Backend + HasSqlType<>::SqlType>, - T: AsSqlType, -{ - type SqlType = T::SqlType; -} +// TODO: #[derive(SqlType)] +// pub struct SmallInt(i16); -// Character types -// All character types (VARCHAR, CHAR, TEXT, etc.) are represented equivalently in binary and all fold -// to this `Text` type. +// TODO: #[derive(SqlType)] +// pub struct Int(i32); -pub struct Text; +// TODO: #[derive(SqlType)] +// pub struct BigInt(i64); -// Numeric types +// TODO: #[derive(SqlType)] +// pub struct Real(f32); -// i16 -pub struct SmallInt; +// TODO: #[derive(SqlType)] +// pub struct Double(f64); -// i32 -pub struct Int; +// Example of what that derive should generate -// i64 -pub struct BigInt; +// impl HasSqlType for Pg { +// #[inline] +// fn metadata() -> PgTypeMetadata { +// >::metadata() +// } +// } -// decimal? -// TODO pub struct Decimal; +// impl ToSql for Bool { +// #[inline] +// fn to_sql(self, buf: &mut Vec) -> IsNull { +// self.0.to_sql(buf) +// } +// } -// f32 -pub struct Real; - -// f64 -pub struct Double; +// impl FromSql for bool { +// #[inline] +// fn from_sql(buf: Option<&[u8]>) -> Self { +// Self(bool::from_sql(buf)) +// } +// }