mirror of
https://github.com/launchbadge/sqlx.git
synced 2025-12-29 21:00:54 +00:00
[postgres] Initial experiment with FromSql
This commit is contained in:
parent
ebb3588d30
commit
c67f751968
@ -58,15 +58,15 @@ async fn main() -> io::Result<()> {
|
||||
// println!(" :: insert");
|
||||
|
||||
let row = conn
|
||||
.prepare("SELECT pg_typeof($1), pg_typeof($2)")
|
||||
.bind(20)
|
||||
.bind_as::<sqlx::postgres::types::BigInt, _>(10)
|
||||
.prepare("SELECT $1")
|
||||
.bind("We're cooking with fire now")
|
||||
.get()
|
||||
.await?;
|
||||
.await?
|
||||
.unwrap();
|
||||
|
||||
println!("{:?}", row);
|
||||
let value: String = row.get(0);
|
||||
|
||||
// println!(" :: select");
|
||||
println!(" - {}", value);
|
||||
|
||||
// conn.prepare("SELECT id FROM users")
|
||||
// .select()
|
||||
|
||||
@ -22,4 +22,5 @@ pub mod postgres;
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
|
||||
pub mod row;
|
||||
pub mod types;
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
use super::prepare::Prepare;
|
||||
use crate::postgres::protocol::{self, DataRow, Message};
|
||||
use crate::{
|
||||
postgres::protocol::{self, DataRow, Message},
|
||||
row::Row,
|
||||
};
|
||||
use std::io;
|
||||
|
||||
impl<'a, 'b> Prepare<'a, 'b> {
|
||||
pub async fn get(self) -> io::Result<Option<DataRow>> {
|
||||
pub async fn get(self) -> io::Result<Option<Row>> {
|
||||
let conn = self.finish();
|
||||
|
||||
conn.flush().await?;
|
||||
|
||||
let mut row: Option<DataRow> = None;
|
||||
let mut raw: Option<DataRow> = None;
|
||||
|
||||
while let Some(message) = conn.receive().await? {
|
||||
match message {
|
||||
@ -21,13 +24,13 @@ impl<'a, 'b> Prepare<'a, 'b> {
|
||||
|
||||
Message::DataRow(data_row) => {
|
||||
// note: because we used `EXECUTE 1` this will only execute once
|
||||
row = Some(data_row);
|
||||
raw = Some(data_row);
|
||||
}
|
||||
|
||||
Message::CommandComplete(_) => {}
|
||||
|
||||
Message::ReadyForQuery(_) => {
|
||||
return Ok(row);
|
||||
return Ok(raw.map(Row));
|
||||
}
|
||||
|
||||
message => {
|
||||
|
||||
@ -50,7 +50,7 @@ impl<'a, 'b> Prepare<'a, 'b> {
|
||||
formats: self.bind.formats(),
|
||||
values_len: self.bind.values_len(),
|
||||
values: self.bind.values(),
|
||||
result_formats: &[],
|
||||
result_formats: &[1],
|
||||
});
|
||||
|
||||
self.connection.write(protocol::Execute {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
mod connection;
|
||||
pub use connection::Connection;
|
||||
|
||||
mod protocol;
|
||||
pub(crate) mod protocol;
|
||||
pub mod types;
|
||||
|
||||
25
src/postgres/types/boolean.rs
Normal file
25
src/postgres/types/boolean.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use crate::types::{SqlType, ToSql, ToSqlAs, FromSql};
|
||||
|
||||
pub struct Bool;
|
||||
|
||||
impl SqlType for Bool {
|
||||
const OID: u32 = 16;
|
||||
}
|
||||
|
||||
impl ToSql for bool {
|
||||
type Type = Bool;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Bool> for bool {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.push(self as u8);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Bool> for bool {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
buf[0] != 0
|
||||
}
|
||||
}
|
||||
31
src/postgres/types/character.rs
Normal file
31
src/postgres/types/character.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use crate::types::{ToSql, ToSqlAs, FromSql, 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 ToSql for String {
|
||||
type Type = Text;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Text> for String {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(self.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Text> for String {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
// Using lossy here as it should be impossible to get non UTF8 data here
|
||||
String::from_utf8_lossy(buf).into()
|
||||
}
|
||||
}
|
||||
@ -1,119 +1,5 @@
|
||||
use crate::types::{SqlType, ToSql, ToSqlAs};
|
||||
mod character;
|
||||
mod numeric;
|
||||
mod boolean;
|
||||
|
||||
// TODO: Generalize by Backend and move common types to crate [sqlx::types]
|
||||
|
||||
// Character
|
||||
// https://www.postgresql.org/docs/devel/datatype-character.html
|
||||
|
||||
pub struct Text;
|
||||
|
||||
impl SqlType for Text {
|
||||
const OID: u32 = 25;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// Numeric
|
||||
// https://www.postgresql.org/docs/devel/datatype-numeric.html
|
||||
|
||||
// i16
|
||||
pub struct SmallInt;
|
||||
|
||||
impl SqlType for SmallInt {
|
||||
const OID: u32 = 21;
|
||||
}
|
||||
|
||||
impl ToSql for i16 {
|
||||
type Type = SmallInt;
|
||||
}
|
||||
|
||||
impl ToSqlAs<SmallInt> for i16 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// i32
|
||||
pub struct Int;
|
||||
|
||||
impl SqlType for Int {
|
||||
const OID: u32 = 23;
|
||||
}
|
||||
|
||||
impl ToSql for i32 {
|
||||
type Type = Int;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Int> for i32 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// i64
|
||||
pub struct BigInt;
|
||||
|
||||
impl SqlType for BigInt {
|
||||
const OID: u32 = 20;
|
||||
}
|
||||
|
||||
impl ToSql for i64 {
|
||||
type Type = BigInt;
|
||||
}
|
||||
|
||||
impl ToSqlAs<BigInt> for i64 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
// decimal?
|
||||
// TODO pub struct Decimal;
|
||||
|
||||
// f32
|
||||
pub struct Real;
|
||||
|
||||
impl SqlType for Real {
|
||||
const OID: u32 = 700;
|
||||
}
|
||||
|
||||
impl ToSql for f32 {
|
||||
type Type = Real;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Real> for f32 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
(self.to_bits() as i32).to_sql(buf);
|
||||
}
|
||||
}
|
||||
|
||||
// f64
|
||||
pub struct Double;
|
||||
|
||||
impl SqlType for Double {
|
||||
const OID: u32 = 701;
|
||||
}
|
||||
|
||||
impl ToSql for f64 {
|
||||
type Type = Double;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Double> for f64 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
(self.to_bits() as i64).to_sql(buf);
|
||||
}
|
||||
}
|
||||
pub use self::boolean::Bool;
|
||||
|
||||
92
src/postgres/types/numeric.rs
Normal file
92
src/postgres/types/numeric.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use crate::types::{ToSql, ToSqlAs, FromSql, SmallInt, Int, BigInt, Real, Double};
|
||||
use byteorder::{BigEndian, ByteOrder};
|
||||
|
||||
impl ToSql for i16 {
|
||||
type Type = SmallInt;
|
||||
}
|
||||
|
||||
impl ToSqlAs<SmallInt> for i16 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<SmallInt> for i16 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
BigEndian::read_i16(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for i32 {
|
||||
type Type = Int;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Int> for i32 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Int> for i32 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
BigEndian::read_i32(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for i64 {
|
||||
type Type = BigInt;
|
||||
}
|
||||
|
||||
impl ToSqlAs<BigInt> for i64 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
buf.extend_from_slice(&self.to_be_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<BigInt> for i64 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
BigEndian::read_i64(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for f32 {
|
||||
type Type = Real;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Real> for f32 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
(self.to_bits() as i32).to_sql(buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<BigInt> for f32 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
f32::from_bits(i32::from_sql(buf) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for f64 {
|
||||
type Type = Double;
|
||||
}
|
||||
|
||||
impl ToSqlAs<Double> for f64 {
|
||||
#[inline]
|
||||
fn to_sql(self, buf: &mut Vec<u8>) {
|
||||
(self.to_bits() as i64).to_sql(buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql<Double> for f64 {
|
||||
#[inline]
|
||||
fn from_sql(buf: &[u8]) -> Self {
|
||||
f64::from_bits(i64::from_sql(buf) as u64)
|
||||
}
|
||||
}
|
||||
17
src/row.rs
Normal file
17
src/row.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use crate::{
|
||||
postgres::protocol::DataRow,
|
||||
types::{FromSql, SqlType},
|
||||
};
|
||||
|
||||
// TODO: Make this generic over backend
|
||||
pub struct Row(pub(crate) DataRow);
|
||||
|
||||
impl Row {
|
||||
pub fn get<ST, T>(&self, index: usize) -> T
|
||||
where
|
||||
ST: SqlType,
|
||||
T: FromSql<ST>,
|
||||
{
|
||||
T::from_sql(self.0.get(index).unwrap())
|
||||
}
|
||||
}
|
||||
60
src/types.rs
60
src/types.rs
@ -1,6 +1,10 @@
|
||||
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: Make generic over backend
|
||||
|
||||
pub trait SqlType {
|
||||
// FIXME: This is a postgres thing
|
||||
const OID: u32;
|
||||
@ -14,3 +18,59 @@ pub trait ToSql {
|
||||
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;
|
||||
}
|
||||
|
||||
// Character types
|
||||
// All character types (VARCHAR, CHAR, TEXT, etc.) are represented equivalently in binary and all fold
|
||||
// to this `Text` type.
|
||||
|
||||
pub struct Text;
|
||||
|
||||
impl SqlType for Text {
|
||||
// FIXME: This is postgres-specific
|
||||
const OID: u32 = 25;
|
||||
}
|
||||
|
||||
// Numeric types
|
||||
|
||||
// i16
|
||||
pub struct SmallInt;
|
||||
|
||||
impl SqlType for SmallInt {
|
||||
const OID: u32 = 21;
|
||||
}
|
||||
|
||||
// i32
|
||||
pub struct Int;
|
||||
|
||||
impl SqlType for Int {
|
||||
const OID: u32 = 23;
|
||||
}
|
||||
|
||||
// i64
|
||||
pub struct BigInt;
|
||||
|
||||
impl SqlType for BigInt {
|
||||
const OID: u32 = 20;
|
||||
}
|
||||
|
||||
// decimal?
|
||||
// TODO pub struct Decimal;
|
||||
|
||||
// f32
|
||||
pub struct Real;
|
||||
|
||||
impl SqlType for Real {
|
||||
const OID: u32 = 700;
|
||||
}
|
||||
|
||||
// f64
|
||||
pub struct Double;
|
||||
|
||||
impl SqlType for Double {
|
||||
const OID: u32 = 701;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user