Clean up SQL type (de)serialize

This commit is contained in:
Ryan Leckey 2019-08-20 21:01:19 -07:00
parent 2d5170d742
commit b33bc3c017
14 changed files with 173 additions and 211 deletions

View file

@ -95,10 +95,10 @@ where
})
}
fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + 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<Option<T>>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
T: FromRow<Self::Backend>,
{
Box::pin(async move {
let mut conn = self.get().await;

View file

@ -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<A, DB: Backend> {
pub trait FromSql<DB: Backend> {
fn from_sql(raw: Option<&[u8]>) -> Self;
}
impl<T, ST, DB> FromSql<ST, DB> for Option<T>
impl<T, DB> FromSql<DB> for Option<T>
where
DB: Backend + HasSqlType<ST>,
T: FromSql<ST, DB>,
DB: Backend + HasSqlType<T>,
T: FromSql<DB>,
{
#[inline]
fn from_sql(raw: Option<&[u8]>) -> Self {

View file

@ -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<T>>
fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin;
T: FromRow<Self::Backend> + 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<Option<T>>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>;
T: FromRow<Self::Backend>;
}
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<T>>
fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + 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<Option<T>>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
T: FromRow<Self::Backend>,
{
(*self).fetch_optional(query)
}

View file

@ -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,

View file

@ -32,16 +32,16 @@ impl<'q> RawQuery<'q> for PgRawQuery<'q> {
}
}
fn bind_as<ST, T>(mut self, value: T) -> Self
fn bind<T>(mut self, value: T) -> Self
where
Self: Sized,
Self::Backend: HasSqlType<ST>,
T: ToSql<ST, Self::Backend>,
Self::Backend: HasSqlType<T>,
T: ToSql<Self::Backend>,
{
// 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(<Pg as HasSqlType<ST>>::metadata().oid);
self.types.push(<Pg as HasSqlType<T>>::metadata().oid);
let pos = self.buf.len();
self.buf.put_int_32(0);

View file

@ -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<Bool> for Pg {
impl HasSqlType<bool> for Pg {
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 16,
@ -16,11 +14,7 @@ impl HasSqlType<Bool> for Pg {
}
}
impl AsSqlType<Pg> for bool {
type SqlType = Bool;
}
impl ToSql<Bool, Pg> for bool {
impl ToSql<Pg> for bool {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
buf.push(self as u8);
@ -29,10 +23,13 @@ impl ToSql<Bool, Pg> for bool {
}
}
impl FromSql<Bool, Pg> for bool {
impl FromSql<Pg> 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);

View file

@ -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<Text> for Pg {
impl HasSqlType<&'_ str> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 25,
@ -15,11 +16,14 @@ impl HasSqlType<Text> for Pg {
}
}
impl AsSqlType<Pg> for &'_ str {
type SqlType = Text;
impl HasSqlType<String> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
<Pg as HasSqlType<&str>>::metadata()
}
}
impl ToSql<Text, Pg> for &'_ str {
impl ToSql<Pg> for &'_ str {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
buf.extend_from_slice(self.as_bytes());
@ -28,20 +32,14 @@ impl ToSql<Text, Pg> for &'_ str {
}
}
impl AsSqlType<Pg> for String {
type SqlType = Text;
}
impl ToSql<Text, Pg> for String {
impl ToSql<Pg> for String {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
buf.extend_from_slice(self.as_bytes());
IsNull::No
self.as_str().to_sql(buf)
}
}
impl FromSql<Text, Pg> for String {
impl FromSql<Pg> for String {
#[inline]
fn from_sql(buf: Option<&[u8]>) -> Self {
// TODO: Handle nulls

View file

@ -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,

View file

@ -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<SmallInt> for Pg {
impl HasSqlType<i16> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 21,
@ -15,11 +16,7 @@ impl HasSqlType<SmallInt> for Pg {
}
}
impl AsSqlType<Pg> for i16 {
type SqlType = SmallInt;
}
impl ToSql<SmallInt, Pg> for i16 {
impl ToSql<Pg> for i16 {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
buf.extend_from_slice(&self.to_be_bytes());
@ -28,15 +25,15 @@ impl ToSql<SmallInt, Pg> for i16 {
}
}
impl FromSql<SmallInt, Pg> for i16 {
impl FromSql<Pg> for i16 {
#[inline]
fn from_sql(buf: Option<&[u8]>) -> Self {
// TODO: Handle optionals
BigEndian::read_i16(buf.unwrap())
}
}
impl HasSqlType<Int> for Pg {
impl HasSqlType<i32> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 23,
@ -45,11 +42,7 @@ impl HasSqlType<Int> for Pg {
}
}
impl AsSqlType<Pg> for i32 {
type SqlType = Int;
}
impl ToSql<Int, Pg> for i32 {
impl ToSql<Pg> for i32 {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
buf.extend_from_slice(&self.to_be_bytes());
@ -58,15 +51,15 @@ impl ToSql<Int, Pg> for i32 {
}
}
impl FromSql<Int, Pg> for i32 {
impl FromSql<Pg> for i32 {
#[inline]
fn from_sql(buf: Option<&[u8]>) -> Self {
// TODO: Handle optionals
BigEndian::read_i32(buf.unwrap())
}
}
impl HasSqlType<BigInt> for Pg {
impl HasSqlType<i64> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 20,
@ -75,11 +68,7 @@ impl HasSqlType<BigInt> for Pg {
}
}
impl AsSqlType<Pg> for i64 {
type SqlType = BigInt;
}
impl ToSql<BigInt, Pg> for i64 {
impl ToSql<Pg> for i64 {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
buf.extend_from_slice(&self.to_be_bytes());
@ -88,15 +77,15 @@ impl ToSql<BigInt, Pg> for i64 {
}
}
impl FromSql<BigInt, Pg> for i64 {
impl FromSql<Pg> for i64 {
#[inline]
fn from_sql(buf: Option<&[u8]>) -> Self {
// TODO: Handle optionals
BigEndian::read_i64(buf.unwrap())
}
}
impl HasSqlType<Real> for Pg {
impl HasSqlType<f32> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 700,
@ -105,25 +94,22 @@ impl HasSqlType<Real> for Pg {
}
}
impl AsSqlType<Pg> for f32 {
type SqlType = Real;
}
impl ToSql<Real, Pg> for f32 {
impl ToSql<Pg> for f32 {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
(self.to_bits() as i32).to_sql(buf)
}
}
impl FromSql<BigInt, Pg> for f32 {
impl FromSql<Pg> for f32 {
#[inline]
fn from_sql(buf: Option<&[u8]>) -> Self {
f32::from_bits(i32::from_sql(buf) as u32)
}
}
impl HasSqlType<Double> for Pg {
impl HasSqlType<f64> for Pg {
#[inline]
fn metadata() -> PgTypeMetadata {
PgTypeMetadata {
oid: 701,
@ -132,18 +118,14 @@ impl HasSqlType<Double> for Pg {
}
}
impl AsSqlType<Pg> for f64 {
type SqlType = Double;
}
impl ToSql<Double, Pg> for f64 {
impl ToSql<Pg> for f64 {
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
(self.to_bits() as i64).to_sql(buf)
}
}
impl FromSql<Double, Pg> for f64 {
impl FromSql<Pg> for f64 {
#[inline]
fn from_sql(buf: Option<&[u8]>) -> Self {
f64::from_bits(i64::from_sql(buf) as u64)

View file

@ -140,10 +140,10 @@ where
})
}
fn fetch<'c, 'q, A: 'c, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
fn fetch<'c, 'q, T: 'c, Q: 'q + 'c>(&'c self, query: Q) -> BoxStream<'c, io::Result<T>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend> + Send + Unpin,
T: FromRow<Self::Backend> + 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<Option<T>>>
where
Q: RawQuery<'q, Backend = Self::Backend>,
T: FromRow<A, Self::Backend>,
T: FromRow<Self::Backend>,
{
Box::pin(async move {
let live = self.0.acquire().await?;

View file

@ -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<ST, T>(self, value: T) -> Self
fn bind<T>(self, value: T) -> Self
where
Self::Backend: HasSqlType<ST>,
T: ToSql<ST, Self::Backend>;
Self::Backend: HasSqlType<T>,
T: ToSql<Self::Backend>;
fn finish(self, conn: &mut <Self::Backend as Backend>::RawConnection);
}
@ -40,21 +40,12 @@ where
}
#[inline]
pub fn bind<T>(self, value: T) -> Self
pub fn bind<T>(mut self, value: T) -> Self
where
DB: HasSqlType<<T as AsSqlType<DB>>::SqlType>,
T: AsSqlType<DB> + ToSql<<T as AsSqlType<DB>>::SqlType, DB>,
DB: HasSqlType<T>,
T: ToSql<DB>,
{
self.bind_as::<T::SqlType, T>(value)
}
#[inline]
pub fn bind_as<ST, T>(mut self, value: T) -> Self
where
DB: HasSqlType<ST>,
T: ToSql<ST, DB>,
{
self.inner = self.inner.bind_as::<ST, T>(value);
self.inner = self.inner.bind(value);
self
}
@ -70,23 +61,20 @@ where
}
#[inline]
pub fn fetch<E, A: 'q, T: 'q>(self, executor: &'q E) -> BoxStream<'q, io::Result<T>>
pub fn fetch<E, T: 'q>(self, executor: &'q E) -> BoxStream<'q, io::Result<T>>
where
E: Executor<Backend = DB>,
T: FromRow<A, DB> + Send + Unpin,
T: FromRow<DB> + Send + Unpin,
<DB as BackendAssocRawQuery<'q, DB>>::RawQuery: 'q,
{
executor.fetch(self.inner)
}
#[inline]
pub fn fetch_optional<E, A: 'q, T: 'q>(
self,
executor: &'q E,
) -> BoxFuture<'q, io::Result<Option<T>>>
pub fn fetch_optional<E, T: 'q>(self, executor: &'q E) -> BoxFuture<'q, io::Result<Option<T>>>
where
E: Executor<Backend = DB>,
T: FromRow<A, DB>,
T: FromRow<DB>,
<DB as BackendAssocRawQuery<'q, DB>>::RawQuery: 'q,
{
executor.fetch_optional(self.inner)

View file

@ -10,41 +10,41 @@ pub trait Row: Send {
fn get_raw(&self, index: usize) -> Option<&[u8]>;
#[inline]
fn get<ST, T>(&self, index: usize) -> T
fn get<T>(&self, index: usize) -> T
where
Self::Backend: HasSqlType<ST>,
T: FromSql<ST, Self::Backend>,
Self::Backend: HasSqlType<T>,
T: FromSql<Self::Backend>,
{
T::from_sql(self.get_raw(index))
}
}
pub trait FromRow<A, DB: Backend> {
pub trait FromRow<DB: Backend> {
fn from_row<R: Row<Backend = DB>>(row: R) -> Self;
}
impl<T, ST, DB> FromRow<ST, DB> for T
impl<T, DB> FromRow<DB> for T
where
DB: Backend + HasSqlType<ST>,
T: FromSql<ST, DB>,
DB: Backend + HasSqlType<T>,
T: FromSql<DB>,
{
#[inline]
fn from_row<R: Row<Backend = DB>>(row: R) -> Self {
row.get::<ST, T>(0)
row.get::<T>(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<R: crate::row::Row<Backend = $B>>(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;
);
}
}

View file

@ -12,14 +12,14 @@ pub enum IsNull {
}
/// Serializes a single value to be sent to the database.
pub trait ToSql<A, DB: Backend> {
pub trait ToSql<DB: Backend> {
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull;
}
impl<T, ST, DB> ToSql<ST, DB> for Option<T>
impl<T, DB> ToSql<DB> for Option<T>
where
DB: Backend + HasSqlType<ST>,
T: ToSql<ST, DB>,
DB: Backend + HasSqlType<T>,
T: ToSql<DB>,
{
#[inline]
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {

View file

@ -13,45 +13,43 @@ pub trait HasSqlType<A>: 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<DB: Backend>
where
DB: HasSqlType<Self::SqlType>,
{
type SqlType;
}
// TODO: #[derive(SqlType)]
// pub struct Text<'a>(Cow<'a, str>);
impl<T, DB> AsSqlType<DB> for Option<T>
where
DB: Backend + HasSqlType<<T as AsSqlType<DB>>::SqlType>,
T: AsSqlType<DB>,
{
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<Bool> for Pg {
// #[inline]
// fn metadata() -> PgTypeMetadata {
// <Pg as HasSqlType<bool>>::metadata()
// }
// }
// decimal?
// TODO pub struct Decimal;
// impl ToSql<Pg> for Bool {
// #[inline]
// fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
// self.0.to_sql(buf)
// }
// }
// f32
pub struct Real;
// f64
pub struct Double;
// impl FromSql<Pg> for bool {
// #[inline]
// fn from_sql(buf: Option<&[u8]>) -> Self {
// Self(bool::from_sql(buf))
// }
// }