mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-30 05:11:13 +00:00
Generalize ToSql, AsSql, FromSql, and FromSql over Backend
This commit is contained in:
parent
0b98527550
commit
1db85d1069
8
src/backend.rs
Normal file
8
src/backend.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::row::RawRow;
|
||||
|
||||
pub trait Backend {
|
||||
type RawRow: RawRow;
|
||||
|
||||
/// The type used to represent metadata associated with a SQL type.
|
||||
type TypeMetadata;
|
||||
}
|
||||
27
src/deserialize.rs
Normal file
27
src/deserialize.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
types::{AsSql, SqlType},
|
||||
};
|
||||
|
||||
// TODO: Allow from_sql to return an error (that can be unified)
|
||||
// TODO: Consider using a RawValue wrapper type instead of exposing raw bytes (as different back-ends may want to expose different data here.. maybe?)
|
||||
|
||||
pub trait FromSql<B, ST>: AsSql<B>
|
||||
where
|
||||
B: Backend,
|
||||
ST: SqlType<B>,
|
||||
{
|
||||
fn from_sql(raw: Option<&[u8]>) -> Self;
|
||||
}
|
||||
|
||||
impl<B, ST, T> FromSql<B, ST> for Option<T>
|
||||
where
|
||||
B: Backend,
|
||||
ST: SqlType<B>,
|
||||
T: FromSql<B, ST>,
|
||||
{
|
||||
#[inline]
|
||||
fn from_sql(raw: Option<&[u8]>) -> Self {
|
||||
Some(T::from_sql(Some(raw?)))
|
||||
}
|
||||
}
|
||||
@ -22,5 +22,8 @@ pub mod postgres;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod backend;
|
||||
pub mod deserialize;
|
||||
pub mod row;
|
||||
pub mod serialize;
|
||||
pub mod types;
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
use super::prepare::Prepare;
|
||||
use crate::{
|
||||
postgres::protocol::{self, DataRow, Message},
|
||||
postgres::{
|
||||
protocol::{self, DataRow, Message},
|
||||
Postgres,
|
||||
},
|
||||
row::{FromRow, Row},
|
||||
types::SqlType,
|
||||
};
|
||||
@ -12,19 +15,19 @@ impl<'a, 'b> Prepare<'a, 'b> {
|
||||
#[inline]
|
||||
pub async fn get<Record, T>(self) -> io::Result<T>
|
||||
where
|
||||
T: FromRow<Record>,
|
||||
T: FromRow<Postgres, Record>,
|
||||
{
|
||||
Ok(T::from_row(self.get_raw().await?.unwrap()))
|
||||
}
|
||||
|
||||
// TODO: Better name?
|
||||
// TODO: Should this be public?
|
||||
async fn get_raw(self) -> io::Result<Option<Row>> {
|
||||
async fn get_raw(self) -> io::Result<Option<Row<Postgres>>> {
|
||||
let conn = self.finish();
|
||||
|
||||
conn.flush().await?;
|
||||
|
||||
let mut row: Option<Row> = None;
|
||||
let mut row: Option<Row<Postgres>> = None;
|
||||
|
||||
while let Some(message) = conn.receive().await? {
|
||||
match message {
|
||||
@ -37,7 +40,7 @@ impl<'a, 'b> Prepare<'a, 'b> {
|
||||
|
||||
Message::DataRow(body) => {
|
||||
// note: because we used `EXECUTE 1` this will only execute once
|
||||
row = Some(Row(body));
|
||||
row = Some(Row::<Postgres>(body));
|
||||
}
|
||||
|
||||
Message::CommandComplete(_) => {}
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
use super::Connection;
|
||||
use crate::{
|
||||
postgres::protocol::{self, BindValues},
|
||||
types::{SqlType, ToSql, ToSqlAs},
|
||||
postgres::{
|
||||
protocol::{self, BindValues},
|
||||
Postgres,
|
||||
},
|
||||
serialize::ToSql,
|
||||
types::{AsSql, SqlType},
|
||||
};
|
||||
|
||||
pub struct Prepare<'a, 'b> {
|
||||
@ -23,16 +27,16 @@ pub fn prepare<'a, 'b>(connection: &'a mut Connection, query: &'b str) -> Prepar
|
||||
|
||||
impl<'a, 'b> Prepare<'a, 'b> {
|
||||
#[inline]
|
||||
pub fn bind<T: ToSql>(mut self, value: T) -> Self
|
||||
pub fn bind<T: AsSql<Postgres>>(mut self, value: T) -> Self
|
||||
where
|
||||
T: ToSqlAs<<T as ToSql>::Type>,
|
||||
T: ToSql<Postgres, <T as AsSql<Postgres>>::Type>,
|
||||
{
|
||||
self.bind.add(value);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn bind_as<ST: SqlType, T: ToSqlAs<ST>>(mut self, value: T) -> Self {
|
||||
pub fn bind_as<ST: SqlType<Postgres>, T: ToSql<Postgres, ST>>(mut self, value: T) -> Self {
|
||||
self.bind.add_as::<ST, T>(value);
|
||||
self
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
use super::prepare::Prepare;
|
||||
use crate::{
|
||||
postgres::protocol::{self, DataRow, Message},
|
||||
postgres::{
|
||||
protocol::{self, DataRow, Message},
|
||||
Postgres,
|
||||
},
|
||||
row::{FromRow, Row},
|
||||
};
|
||||
use futures::{stream, Stream, TryStreamExt};
|
||||
@ -12,14 +15,14 @@ impl<'a, 'b> Prepare<'a, 'b> {
|
||||
self,
|
||||
) -> impl Stream<Item = Result<T, io::Error>> + 'a + Unpin
|
||||
where
|
||||
T: FromRow<Record>,
|
||||
T: FromRow<Postgres, Record>,
|
||||
{
|
||||
self.select_raw().map_ok(T::from_row)
|
||||
}
|
||||
|
||||
// TODO: Better name?
|
||||
// TODO: Should this be public?
|
||||
fn select_raw(self) -> impl Stream<Item = Result<Row, io::Error>> + 'a + Unpin {
|
||||
fn select_raw(self) -> impl Stream<Item = Result<Row<Postgres>, io::Error>> + 'a + Unpin {
|
||||
// FIXME: Manually implement Stream on a new type to avoid the unfold adapter
|
||||
stream::unfold(self.finish(), |conn| {
|
||||
Box::pin(async {
|
||||
@ -43,7 +46,7 @@ impl<'a, 'b> Prepare<'a, 'b> {
|
||||
}
|
||||
|
||||
Message::DataRow(row) => {
|
||||
break Some((Ok(Row(row)), conn));
|
||||
break Some((Ok(Row::<Postgres>(row)), conn));
|
||||
}
|
||||
|
||||
Message::CommandComplete(_) => {}
|
||||
|
||||
@ -1,5 +1,16 @@
|
||||
use crate::backend::Backend;
|
||||
|
||||
mod connection;
|
||||
|
||||
pub use connection::Connection;
|
||||
|
||||
pub(crate) mod protocol;
|
||||
|
||||
pub mod types;
|
||||
|
||||
pub struct Postgres;
|
||||
|
||||
impl Backend for Postgres {
|
||||
type RawRow = protocol::DataRow;
|
||||
type TypeMetadata = types::TypeMetadata;
|
||||
}
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
use super::{BufMut, Encode};
|
||||
use crate::types::{SqlType, ToSql, ToSqlAs};
|
||||
use crate::{
|
||||
postgres::Postgres,
|
||||
serialize::ToSql,
|
||||
types::{AsSql, SqlType},
|
||||
};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
const TEXT: i16 = 0;
|
||||
@ -24,18 +28,18 @@ impl BindValues {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn add<T: ToSql>(&mut self, value: T)
|
||||
pub fn add<T: AsSql<Postgres>>(&mut self, value: T)
|
||||
where
|
||||
T: ToSqlAs<<T as ToSql>::Type>,
|
||||
T: ToSql<Postgres, <T as AsSql<Postgres>>::Type>,
|
||||
{
|
||||
self.add_as::<T::Type, T>(value);
|
||||
}
|
||||
|
||||
pub fn add_as<ST: SqlType, T: ToSqlAs<ST>>(&mut self, value: T) {
|
||||
pub fn add_as<ST: SqlType<Postgres>, T: ToSql<Postgres, ST>>(&mut self, value: T) {
|
||||
// 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(ST::OID as i32);
|
||||
self.types.push(ST::metadata().oid as i32);
|
||||
|
||||
let pos = self.values.len();
|
||||
self.values.put_int_32(0); // skip over len
|
||||
@ -120,7 +124,7 @@ impl Encode for Bind<'_> {
|
||||
mod tests {
|
||||
use super::{Bind, BindValues, BufMut, Encode};
|
||||
|
||||
const BIND: &[u8] = b"B\0\0\0\x16\0\0\0\0\0\x02\0\0\0\x011\0\0\0\x012\0\0";
|
||||
const BIND: &[u8] = b"B\0\0\0\x18\0\0\0\x01\0\x01\0\x02\0\0\0\x011\0\0\0\x012\0\0";
|
||||
|
||||
#[test]
|
||||
fn it_encodes_bind_for_two() {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use super::Decode;
|
||||
use crate::row::RawRow;
|
||||
use bytes::Bytes;
|
||||
use std::{
|
||||
convert::TryInto,
|
||||
@ -46,19 +47,19 @@ impl Decode for DataRow {
|
||||
}
|
||||
}
|
||||
|
||||
impl DataRow {
|
||||
impl RawRow for DataRow {
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.ranges.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
fn len(&self) -> usize {
|
||||
self.ranges.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, index: usize) -> Option<&[u8]> {
|
||||
fn get(&self, index: usize) -> Option<&[u8]> {
|
||||
Some(&self.buf[self.ranges[index].clone()?])
|
||||
}
|
||||
}
|
||||
@ -81,6 +82,7 @@ impl Debug for DataRow {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{DataRow, Decode};
|
||||
use crate::row::RawRow;
|
||||
use bytes::Bytes;
|
||||
use std::io;
|
||||
|
||||
|
||||
@ -1,25 +1,39 @@
|
||||
use crate::types::{FromSql, SqlType, ToSql, ToSqlAs};
|
||||
use super::TypeMetadata;
|
||||
use crate::{
|
||||
deserialize::FromSql,
|
||||
postgres::Postgres,
|
||||
serialize::{IsNull, ToSql},
|
||||
types::{AsSql, SqlType},
|
||||
};
|
||||
|
||||
pub struct Bool;
|
||||
|
||||
impl SqlType for Bool {
|
||||
const OID: u32 = 16;
|
||||
impl SqlType<Postgres> for Bool {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 16,
|
||||
array_oid: 1000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for bool {
|
||||
impl AsSql<Postgres> for bool {
|
||||
type Type = Bool;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Bool> for bool {
|
||||
impl ToSql<Postgres, Bool> for bool {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.push(self as u8);
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Bool> for bool {
|
||||
impl FromSql<Postgres, Bool> for bool {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
buf[0] != 0
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
// TODO: Handle optionals
|
||||
buf.unwrap()[0] != 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,51 @@
|
||||
use crate::types::{FromSql, Text, ToSql, ToSqlAs};
|
||||
use super::TypeMetadata;
|
||||
use crate::{
|
||||
deserialize::FromSql,
|
||||
postgres::Postgres,
|
||||
serialize::{IsNull, ToSql},
|
||||
types::{AsSql, SqlType, Text},
|
||||
};
|
||||
|
||||
impl ToSql for &'_ str {
|
||||
type Type = Text;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Text> for &'_ str {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(self.as_bytes());
|
||||
impl SqlType<Postgres> for Text {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 25,
|
||||
array_oid: 1009,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for String {
|
||||
impl AsSql<Postgres> for &'_ str {
|
||||
type Type = Text;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Text> for String {
|
||||
impl ToSql<Postgres, Text> for &'_ str {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.extend_from_slice(self.as_bytes());
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Text> for String {
|
||||
impl AsSql<Postgres> for String {
|
||||
type Type = Text;
|
||||
}
|
||||
|
||||
impl ToSql<Postgres, Text> for String {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.extend_from_slice(self.as_bytes());
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Postgres, Text> for String {
|
||||
#[inline]
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
// TODO: Handle optionals
|
||||
// Using lossy here as it should be impossible to get non UTF8 data here
|
||||
String::from_utf8_lossy(buf).into()
|
||||
String::from_utf8_lossy(buf.unwrap()).into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,3 +3,8 @@ mod character;
|
||||
mod numeric;
|
||||
|
||||
pub use self::boolean::Bool;
|
||||
|
||||
pub struct TypeMetadata {
|
||||
pub oid: u32,
|
||||
pub array_oid: u32,
|
||||
}
|
||||
|
||||
@ -1,92 +1,152 @@
|
||||
use crate::types::{BigInt, Double, FromSql, Int, Real, SmallInt, ToSql, ToSqlAs};
|
||||
use super::TypeMetadata;
|
||||
use crate::{
|
||||
deserialize::FromSql,
|
||||
postgres::Postgres,
|
||||
serialize::{IsNull, ToSql},
|
||||
types::{AsSql, BigInt, Double, Int, Real, SmallInt, SqlType},
|
||||
};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
impl ToSql for i16 {
|
||||
impl SqlType<Postgres> for SmallInt {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 21,
|
||||
array_oid: 1005,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSql<Postgres> for i16 {
|
||||
type Type = SmallInt;
|
||||
}
|
||||
|
||||
impl ToSqlAs<SmallInt> for i16 {
|
||||
impl ToSql<Postgres, SmallInt> for i16 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<SmallInt> for i16 {
|
||||
impl FromSql<Postgres, SmallInt> for i16 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
BigEndian::read_i16(buf)
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
// TODO: Handle optionals
|
||||
BigEndian::read_i16(buf.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for i32 {
|
||||
impl SqlType<Postgres> for Int {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 23,
|
||||
array_oid: 1007,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSql<Postgres> for i32 {
|
||||
type Type = Int;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Int> for i32 {
|
||||
impl ToSql<Postgres, Int> for i32 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Int> for i32 {
|
||||
impl FromSql<Postgres, Int> for i32 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
BigEndian::read_i32(buf)
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
// TODO: Handle optionals
|
||||
BigEndian::read_i32(buf.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for i64 {
|
||||
impl SqlType<Postgres> for BigInt {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 20,
|
||||
array_oid: 1016,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSql<Postgres> for i64 {
|
||||
type Type = BigInt;
|
||||
}
|
||||
|
||||
impl ToSqlAs<BigInt> for i64 {
|
||||
impl ToSql<Postgres, BigInt> for i64 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
|
||||
IsNull::No
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<BigInt> for i64 {
|
||||
impl FromSql<Postgres, BigInt> for i64 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
BigEndian::read_i64(buf)
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
// TODO: Handle optionals
|
||||
BigEndian::read_i64(buf.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for f32 {
|
||||
impl SqlType<Postgres> for Real {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 700,
|
||||
array_oid: 1021,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSql<Postgres> for f32 {
|
||||
type Type = Real;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Real> for f32 {
|
||||
impl ToSql<Postgres, Real> for f32 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
(self.to_bits() as i32).to_sql(buf);
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
(self.to_bits() as i32).to_sql(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<BigInt> for f32 {
|
||||
impl FromSql<Postgres, BigInt> for f32 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
f32::from_bits(i32::from_sql(buf) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for f64 {
|
||||
impl SqlType<Postgres> for Double {
|
||||
fn metadata() -> TypeMetadata {
|
||||
TypeMetadata {
|
||||
oid: 701,
|
||||
array_oid: 1022,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSql<Postgres> for f64 {
|
||||
type Type = Double;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Double> for f64 {
|
||||
impl ToSql<Postgres, Double> for f64 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
(self.to_bits() as i64).to_sql(buf);
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull {
|
||||
(self.to_bits() as i64).to_sql(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Double> for f64 {
|
||||
impl FromSql<Postgres, Double> for f64 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
fn from_sql(buf: Option<&[u8]>) -> Self {
|
||||
f64::from_bits(i64::from_sql(buf) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
153
src/row.rs
153
src/row.rs
@ -1,57 +1,140 @@
|
||||
use crate::{
|
||||
postgres::protocol::DataRow,
|
||||
types::{FromSql, SqlType},
|
||||
backend::Backend,
|
||||
deserialize::FromSql,
|
||||
postgres::{protocol::DataRow, Postgres},
|
||||
types::SqlType,
|
||||
};
|
||||
|
||||
// TODO: Make this generic over backend
|
||||
pub struct Row(pub(crate) DataRow);
|
||||
pub trait RawRow {
|
||||
fn is_empty(&self) -> bool;
|
||||
|
||||
impl Row {
|
||||
fn len(&self) -> usize;
|
||||
|
||||
fn get(&self, index: usize) -> Option<&[u8]>;
|
||||
}
|
||||
|
||||
pub struct Row<B>(pub(crate) B::RawRow)
|
||||
where
|
||||
B: Backend;
|
||||
|
||||
impl<B> Row<B>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
#[inline]
|
||||
pub fn get<ST, T>(&self, index: usize) -> T
|
||||
where
|
||||
ST: SqlType,
|
||||
T: FromSql<ST>,
|
||||
ST: SqlType<B>,
|
||||
T: FromSql<B, ST>,
|
||||
{
|
||||
T::from_sql(self.0.get(index).unwrap())
|
||||
T::from_sql(self.0.get(index))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FromRow<Record> {
|
||||
fn from_row(row: Row) -> Self;
|
||||
pub trait FromRow<B, Record>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
fn from_row(row: Row<B>) -> Self;
|
||||
}
|
||||
|
||||
impl<ST, T> FromRow<ST> for T
|
||||
impl<B, ST, T> FromRow<B, ST> for T
|
||||
where
|
||||
ST: SqlType,
|
||||
T: FromSql<ST>,
|
||||
B: Backend,
|
||||
ST: SqlType<B>,
|
||||
T: FromSql<B, ST>,
|
||||
{
|
||||
#[inline]
|
||||
fn from_row(row: Row) -> Self {
|
||||
fn from_row(row: Row<B>) -> Self {
|
||||
row.get::<ST, T>(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<ST1, T1> FromRow<(ST1,)> for (T1,)
|
||||
where
|
||||
ST1: SqlType,
|
||||
T1: FromSql<ST1>,
|
||||
{
|
||||
#[inline]
|
||||
fn from_row(row: Row) -> Self {
|
||||
(row.get::<ST1, T1>(0),)
|
||||
}
|
||||
// TODO: Think of a better way to generate these tuple implementations
|
||||
|
||||
macro_rules! impl_from_row_tuple {
|
||||
($B:ident: $( ($idx:tt) -> $T:ident, $ST:ident );+;) => {
|
||||
impl<$($ST,)+ $($T,)+> FromRow<Postgres, ($($ST,)+)> for ($($T,)+)
|
||||
where
|
||||
$($ST: SqlType<Postgres>,)+
|
||||
$($T: FromSql<Postgres, $ST>,)+
|
||||
{
|
||||
#[inline]
|
||||
fn from_row(row: Row<$B>) -> Self {
|
||||
($(row.get::<$ST, $T>($idx),)+)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<ST1, ST2, T1, T2> FromRow<(ST1, ST2)> for (T1, T2)
|
||||
where
|
||||
ST1: SqlType,
|
||||
ST2: SqlType,
|
||||
T1: FromSql<ST1>,
|
||||
T2: FromSql<ST2>,
|
||||
{
|
||||
#[inline]
|
||||
fn from_row(row: Row) -> Self {
|
||||
(row.get::<ST1, T1>(0), row.get::<ST2, T2>(1))
|
||||
}
|
||||
}
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
(2) -> ST3, T3;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
(2) -> ST3, T3;
|
||||
(3) -> ST4, T4;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
(2) -> ST3, T3;
|
||||
(3) -> ST4, T4;
|
||||
(4) -> ST5, T5;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
(2) -> ST3, T3;
|
||||
(3) -> ST4, T4;
|
||||
(4) -> ST5, T5;
|
||||
(5) -> ST6, T6;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
(2) -> ST3, T3;
|
||||
(3) -> ST4, T4;
|
||||
(4) -> ST5, T5;
|
||||
(5) -> ST6, T6;
|
||||
(6) -> ST7, T7;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(0) -> ST1, T1;
|
||||
(1) -> ST2, T2;
|
||||
(2) -> ST3, T3;
|
||||
(3) -> ST4, T4;
|
||||
(4) -> ST5, T5;
|
||||
(5) -> ST6, T6;
|
||||
(6) -> ST7, T7;
|
||||
(7) -> ST8, T8;
|
||||
);
|
||||
|
||||
impl_from_row_tuple!(Postgres:
|
||||
(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;
|
||||
);
|
||||
|
||||
36
src/serialize.rs
Normal file
36
src/serialize.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use crate::{
|
||||
backend::Backend,
|
||||
types::{AsSql, SqlType},
|
||||
};
|
||||
|
||||
/// Annotates the result of [ToSql] to differentiate between an empty value and a null value.
|
||||
pub enum IsNull {
|
||||
/// The value was null (and no data was written to the buffer).
|
||||
Yes,
|
||||
|
||||
/// The value was not null.
|
||||
///
|
||||
/// This does not necessarily mean that any data was written to the buffer.
|
||||
No,
|
||||
}
|
||||
|
||||
/// Serializes a single value to be sent to the database.
|
||||
pub trait ToSql<B, ST>: AsSql<B>
|
||||
where
|
||||
B: Backend,
|
||||
ST: SqlType<B>,
|
||||
{
|
||||
fn to_sql(self, buf: &mut Vec<u8>) -> IsNull;
|
||||
}
|
||||
|
||||
impl<B, ST, T> ToSql<B, ST> for Option<T>
|
||||
where
|
||||
B: Backend,
|
||||
ST: SqlType<B>,
|
||||
T: ToSql<B, ST>,
|
||||
{
|
||||
#[inline]
|
||||
fn to_sql(self, _buf: &mut Vec<u8>) -> IsNull {
|
||||
IsNull::Yes
|
||||
}
|
||||
}
|
||||
75
src/types.rs
75
src/types.rs
@ -1,27 +1,33 @@
|
||||
use crate::backend::Backend;
|
||||
|
||||
pub use crate::postgres::types::*;
|
||||
|
||||
// TODO: Better name for ToSql/ToSqlAs. ToSqlAs is the _conversion_ trait.
|
||||
// ToSql is type fallback for Rust/SQL (e.g., what is the probable SQL type for this Rust type)
|
||||
// TODO: Does [AsSql] need to be generic over back-end ?
|
||||
|
||||
// TODO: Make generic over backend
|
||||
|
||||
pub trait SqlType {
|
||||
// FIXME: This is a postgres thing
|
||||
const OID: u32;
|
||||
pub trait SqlType<B>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
// FIXME: This should be a const fn
|
||||
fn metadata() -> B::TypeMetadata;
|
||||
}
|
||||
|
||||
pub trait ToSql {
|
||||
/// 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 AsSql<B>
|
||||
where
|
||||
B: Backend,
|
||||
{
|
||||
/// SQL type that should be inferred from the implementing Rust type.
|
||||
type Type: SqlType;
|
||||
type Type: SqlType<B>;
|
||||
}
|
||||
|
||||
pub trait ToSqlAs<T: SqlType>: ToSql {
|
||||
fn to_sql(self, buf: &mut Vec<u8>);
|
||||
}
|
||||
|
||||
pub trait FromSql<T: SqlType>: ToSql {
|
||||
// TODO: Errors?
|
||||
fn from_sql(buf: &[u8]) -> Self;
|
||||
impl<B, T> AsSql<B> for Option<T>
|
||||
where
|
||||
B: Backend,
|
||||
T: AsSql<B>,
|
||||
{
|
||||
type Type = T::Type;
|
||||
}
|
||||
|
||||
// Character types
|
||||
@ -30,33 +36,32 @@ pub trait FromSql<T: SqlType>: ToSql {
|
||||
|
||||
pub struct Text;
|
||||
|
||||
impl SqlType for Text {
|
||||
// FIXME: This is postgres-specific
|
||||
const OID: u32 = 25;
|
||||
}
|
||||
// impl SqlType for Text {
|
||||
// const OID: u32 = 25;
|
||||
// }
|
||||
|
||||
// Numeric types
|
||||
|
||||
// i16
|
||||
pub struct SmallInt;
|
||||
|
||||
impl SqlType for SmallInt {
|
||||
const OID: u32 = 21;
|
||||
}
|
||||
// impl SqlType for SmallInt {
|
||||
// const OID: u32 = 21;
|
||||
// }
|
||||
|
||||
// i32
|
||||
pub struct Int;
|
||||
|
||||
impl SqlType for Int {
|
||||
const OID: u32 = 23;
|
||||
}
|
||||
// impl SqlType for Int {
|
||||
// const OID: u32 = 23;
|
||||
// }
|
||||
|
||||
// i64
|
||||
pub struct BigInt;
|
||||
|
||||
impl SqlType for BigInt {
|
||||
const OID: u32 = 20;
|
||||
}
|
||||
// impl SqlType for BigInt {
|
||||
// const OID: u32 = 20;
|
||||
// }
|
||||
|
||||
// decimal?
|
||||
// TODO pub struct Decimal;
|
||||
@ -64,13 +69,13 @@ impl SqlType for BigInt {
|
||||
// f32
|
||||
pub struct Real;
|
||||
|
||||
impl SqlType for Real {
|
||||
const OID: u32 = 700;
|
||||
}
|
||||
// impl SqlType for Real {
|
||||
// const OID: u32 = 700;
|
||||
// }
|
||||
|
||||
// f64
|
||||
pub struct Double;
|
||||
|
||||
impl SqlType for Double {
|
||||
const OID: u32 = 701;
|
||||
}
|
||||
// impl SqlType for Double {
|
||||
// const OID: u32 = 701;
|
||||
// }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user