diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index e9a5846e1..ecda2fbd2 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -108,6 +108,7 @@ #![doc(html_root_url = "https://docs.rs/postgres-types/0.1")] #![warn(clippy::all, rust_2018_idioms, missing_docs)] +use crate::sealed::Sealed; use fallible_iterator::FallibleIterator; use postgres_protocol::types::{self, ArrayDimension}; use std::any::type_name; @@ -951,3 +952,32 @@ fn downcast(len: usize) -> Result> { Ok(len as i32) } } + +mod sealed { + pub trait Sealed {} +} + +/// A helper trait used internally by Rust-Postgres +/// to be able create a parameters iterator from `&dyn ToSql` or `T: ToSql`. +/// +/// This cannot be implemented outside of this crate. +pub trait BorrowToSql: sealed::Sealed { + /// Get a reference to a `ToSql` trait object + fn borrow_to_sql(&self) -> &dyn ToSql; +} + +impl Sealed for &dyn ToSql {} + +impl BorrowToSql for &dyn ToSql { + fn borrow_to_sql(&self) -> &dyn ToSql { + *self + } +} + +impl Sealed for T {} + +impl BorrowToSql for T { + fn borrow_to_sql(&self) -> &dyn ToSql { + self + } +} diff --git a/postgres/src/binary_copy.rs b/postgres/src/binary_copy.rs index 259347195..98ae666b7 100644 --- a/postgres/src/binary_copy.rs +++ b/postgres/src/binary_copy.rs @@ -1,7 +1,7 @@ //! Utilities for working with the PostgreSQL binary copy format. use crate::connection::ConnectionRef; -use crate::types::{ToSql, Type}; +use crate::types::{BorrowToSql, ToSql, Type}; use crate::{CopyInWriter, CopyOutReader, Error}; use fallible_iterator::FallibleIterator; use futures::StreamExt; @@ -46,9 +46,10 @@ impl<'a> BinaryCopyInWriter<'a> { /// # Panics /// /// Panics if the number of values provided does not match the number expected. - pub fn write_raw<'b, I>(&mut self, values: I) -> Result<(), Error> + pub fn write_raw(&mut self, values: I) -> Result<(), Error> where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { self.connection diff --git a/postgres/src/client.rs b/postgres/src/client.rs index dcb9c72d4..f5637cdbd 100644 --- a/postgres/src/client.rs +++ b/postgres/src/client.rs @@ -5,7 +5,7 @@ use crate::{ }; use std::task::Poll; use tokio_postgres::tls::{MakeTlsConnect, TlsConnect}; -use tokio_postgres::types::{ToSql, Type}; +use tokio_postgres::types::{BorrowToSql, ToSql, Type}; use tokio_postgres::{Error, Row, SimpleQueryMessage, Socket}; /// A synchronous PostgreSQL client. @@ -227,7 +227,7 @@ impl Client { /// let mut client = Client::connect("host=localhost user=postgres", NoTls)?; /// /// let baz = true; - /// let mut it = client.query_raw("SELECT foo FROM bar WHERE baz = $1", iter::once(&baz as _))?; + /// let mut it = client.query_raw("SELECT foo FROM bar WHERE baz = $1", iter::once(baz))?; /// /// while let Some(row) = it.next()? { /// let foo: i32 = row.get("foo"); @@ -253,7 +253,7 @@ impl Client { /// ]; /// let mut it = client.query_raw( /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", - /// params.iter().map(|p| p as &dyn ToSql), + /// params, /// )?; /// /// while let Some(row) = it.next()? { @@ -263,10 +263,11 @@ impl Client { /// # Ok(()) /// # } /// ``` - pub fn query_raw<'a, T, I>(&mut self, query: &T, params: I) -> Result, Error> + pub fn query_raw(&mut self, query: &T, params: I) -> Result, Error> where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let stream = self diff --git a/postgres/src/generic_client.rs b/postgres/src/generic_client.rs index 42a466df6..12f07465d 100644 --- a/postgres/src/generic_client.rs +++ b/postgres/src/generic_client.rs @@ -1,4 +1,4 @@ -use crate::types::{ToSql, Type}; +use crate::types::{BorrowToSql, ToSql, Type}; use crate::{ Client, CopyInWriter, CopyOutReader, Error, Row, RowIter, SimpleQueryMessage, Statement, ToStatement, Transaction, @@ -37,10 +37,11 @@ pub trait GenericClient: private::Sealed { T: ?Sized + ToStatement; /// Like `Client::query_raw`. - fn query_raw<'a, T, I>(&mut self, query: &T, params: I) -> Result, Error> + fn query_raw(&mut self, query: &T, params: I) -> Result, Error> where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator; /// Like `Client::prepare`. @@ -104,10 +105,11 @@ impl GenericClient for Client { self.query_opt(query, params) } - fn query_raw<'a, T, I>(&mut self, query: &T, params: I) -> Result, Error> + fn query_raw(&mut self, query: &T, params: I) -> Result, Error> where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { self.query_raw(query, params) @@ -183,10 +185,11 @@ impl GenericClient for Transaction<'_> { self.query_opt(query, params) } - fn query_raw<'a, T, I>(&mut self, query: &T, params: I) -> Result, Error> + fn query_raw(&mut self, query: &T, params: I) -> Result, Error> where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { self.query_raw(query, params) diff --git a/postgres/src/transaction.rs b/postgres/src/transaction.rs index 3213b7c1f..17c49c406 100644 --- a/postgres/src/transaction.rs +++ b/postgres/src/transaction.rs @@ -1,6 +1,6 @@ use crate::connection::ConnectionRef; use crate::{CancelToken, CopyInWriter, CopyOutReader, Portal, RowIter, Statement, ToStatement}; -use tokio_postgres::types::{ToSql, Type}; +use tokio_postgres::types::{BorrowToSql, ToSql, Type}; use tokio_postgres::{Error, Row, SimpleQueryMessage}; /// A representation of a PostgreSQL database transaction. @@ -102,10 +102,11 @@ impl<'a> Transaction<'a> { } /// Like `Client::query_raw`. - pub fn query_raw<'b, T, I>(&mut self, query: &T, params: I) -> Result, Error> + pub fn query_raw(&mut self, query: &T, params: I) -> Result, Error> where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let stream = self diff --git a/tokio-postgres/src/binary_copy.rs b/tokio-postgres/src/binary_copy.rs index 231f202d8..20064c728 100644 --- a/tokio-postgres/src/binary_copy.rs +++ b/tokio-postgres/src/binary_copy.rs @@ -6,6 +6,7 @@ use byteorder::{BigEndian, ByteOrder}; use bytes::{Buf, BufMut, Bytes, BytesMut}; use futures::{ready, SinkExt, Stream}; use pin_project_lite::pin_project; +use postgres_types::BorrowToSql; use std::convert::TryFrom; use std::io; use std::io::Cursor; @@ -58,9 +59,10 @@ impl BinaryCopyInWriter { /// # Panics /// /// Panics if the number of values provided does not match the number expected. - pub async fn write_raw<'a, I>(self: Pin<&mut Self>, values: I) -> Result<(), Error> + pub async fn write_raw(self: Pin<&mut Self>, values: I) -> Result<(), Error> where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let mut this = self.project(); @@ -79,6 +81,7 @@ impl BinaryCopyInWriter { let idx = this.buf.len(); this.buf.put_i32(0); let len = match value + .borrow_to_sql() .to_sql_checked(type_, this.buf) .map_err(|e| Error::to_sql(e, i))? { diff --git a/tokio-postgres/src/bind.rs b/tokio-postgres/src/bind.rs index 69823a9ab..9c5c49218 100644 --- a/tokio-postgres/src/bind.rs +++ b/tokio-postgres/src/bind.rs @@ -1,7 +1,7 @@ use crate::client::InnerClient; use crate::codec::FrontendMessage; use crate::connection::RequestMessages; -use crate::types::ToSql; +use crate::types::BorrowToSql; use crate::{query, Error, Portal, Statement}; use postgres_protocol::message::backend::Message; use postgres_protocol::message::frontend; @@ -10,13 +10,14 @@ use std::sync::Arc; static NEXT_ID: AtomicUsize = AtomicUsize::new(0); -pub async fn bind<'a, I>( +pub async fn bind( client: &Arc, statement: Statement, params: I, ) -> Result where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let name = format!("p{}", NEXT_ID.fetch_add(1, Ordering::SeqCst)); diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index e19caae83..359a7cd16 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -20,6 +20,7 @@ use futures::channel::mpsc; use futures::{future, pin_mut, ready, StreamExt, TryStreamExt}; use parking_lot::Mutex; use postgres_protocol::message::backend::Message; +use postgres_types::BorrowToSql; use std::collections::HashMap; use std::fmt; use std::sync::Arc; @@ -317,9 +318,6 @@ impl Client { /// /// # Examples /// - /// If you have a type like `Vec` where `T: ToSql` Rust will not know how to use it as params. To get around - /// this the type must explicitly be converted to `&dyn ToSql`. - /// /// ```no_run /// # async fn async_main(client: &tokio_postgres::Client) -> Result<(), tokio_postgres::Error> { /// use tokio_postgres::types::ToSql; @@ -331,7 +329,7 @@ impl Client { /// ]; /// let mut it = client.query_raw( /// "SELECT foo FROM bar WHERE biz = $1 AND baz = $2", - /// params.iter().map(|p| p as &dyn ToSql), + /// params, /// ).await?; /// /// pin_mut!(it); @@ -342,10 +340,11 @@ impl Client { /// # Ok(()) /// # } /// ``` - pub async fn query_raw<'a, T, I>(&self, statement: &T, params: I) -> Result + pub async fn query_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let statement = statement.__convert().into_statement(self).await?; @@ -391,10 +390,11 @@ impl Client { /// Panics if the number of parameters provided does not match the number expected. /// /// [`execute`]: #method.execute - pub async fn execute_raw<'a, T, I>(&self, statement: &T, params: I) -> Result + pub async fn execute_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let statement = statement.__convert().into_statement(self).await?; diff --git a/tokio-postgres/src/generic_client.rs b/tokio-postgres/src/generic_client.rs index ad318e864..df2c6b842 100644 --- a/tokio-postgres/src/generic_client.rs +++ b/tokio-postgres/src/generic_client.rs @@ -1,5 +1,5 @@ use crate::query::RowStream; -use crate::types::{ToSql, Type}; +use crate::types::{BorrowToSql, ToSql, Type}; use crate::{Client, Error, Row, Statement, ToStatement, Transaction}; use async_trait::async_trait; @@ -18,10 +18,11 @@ pub trait GenericClient: private::Sealed { T: ?Sized + ToStatement + Sync + Send; /// Like `Client::execute_raw`. - async fn execute_raw<'b, I, T>(&self, statement: &T, params: I) -> Result + async fn execute_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, - I: IntoIterator + Sync + Send, + P: BorrowToSql, + I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator; /// Like `Client::query`. @@ -48,10 +49,11 @@ pub trait GenericClient: private::Sealed { T: ?Sized + ToStatement + Sync + Send; /// Like `Client::query_raw`. - async fn query_raw<'b, T, I>(&self, statement: &T, params: I) -> Result + async fn query_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, - I: IntoIterator + Sync + Send, + P: BorrowToSql, + I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator; /// Like `Client::prepare`. @@ -79,10 +81,11 @@ impl GenericClient for Client { self.execute(query, params).await } - async fn execute_raw<'b, I, T>(&self, statement: &T, params: I) -> Result + async fn execute_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, - I: IntoIterator + Sync + Send, + P: BorrowToSql, + I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator, { self.execute_raw(statement, params).await @@ -117,10 +120,11 @@ impl GenericClient for Client { self.query_opt(statement, params).await } - async fn query_raw<'b, T, I>(&self, statement: &T, params: I) -> Result + async fn query_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, - I: IntoIterator + Sync + Send, + P: BorrowToSql, + I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator, { self.query_raw(statement, params).await @@ -155,10 +159,11 @@ impl GenericClient for Transaction<'_> { self.execute(query, params).await } - async fn execute_raw<'b, I, T>(&self, statement: &T, params: I) -> Result + async fn execute_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, - I: IntoIterator + Sync + Send, + P: BorrowToSql, + I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator, { self.execute_raw(statement, params).await @@ -193,10 +198,11 @@ impl GenericClient for Transaction<'_> { self.query_opt(statement, params).await } - async fn query_raw<'b, T, I>(&self, statement: &T, params: I) -> Result + async fn query_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement + Sync + Send, - I: IntoIterator + Sync + Send, + P: BorrowToSql, + I: IntoIterator + Sync + Send, I::IntoIter: ExactSizeIterator, { self.query_raw(statement, params).await diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index 7792f0a8a..2245b982a 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -1,7 +1,7 @@ use crate::client::{InnerClient, Responses}; use crate::codec::FrontendMessage; use crate::connection::RequestMessages; -use crate::types::{IsNull, ToSql}; +use crate::types::{BorrowToSql, IsNull}; use crate::{Error, Portal, Row, Statement}; use bytes::{Bytes, BytesMut}; use futures::{ready, Stream}; @@ -9,17 +9,28 @@ use log::{debug, log_enabled, Level}; use pin_project_lite::pin_project; use postgres_protocol::message::backend::Message; use postgres_protocol::message::frontend; +use std::fmt; use std::marker::PhantomPinned; use std::pin::Pin; use std::task::{Context, Poll}; -pub async fn query<'a, I>( +struct BorrowToSqlParamsDebug<'a, T: BorrowToSql>(&'a [T]); +impl<'a, T: BorrowToSql> std::fmt::Debug for BorrowToSqlParamsDebug<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_list() + .entries(self.0.iter().map(|x| x.borrow_to_sql())) + .finish() + } +} + +pub async fn query( client: &InnerClient, statement: Statement, params: I, ) -> Result where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let buf = if log_enabled!(Level::Debug) { @@ -27,7 +38,7 @@ where debug!( "executing statement {} with parameters: {:?}", statement.name(), - params, + BorrowToSqlParamsDebug(params.as_slice()), ); encode(client, &statement, params)? } else { @@ -61,13 +72,14 @@ pub async fn query_portal( }) } -pub async fn execute<'a, I>( +pub async fn execute( client: &InnerClient, statement: Statement, params: I, ) -> Result where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let buf = if log_enabled!(Level::Debug) { @@ -75,7 +87,7 @@ where debug!( "executing statement {} with parameters: {:?}", statement.name(), - params, + BorrowToSqlParamsDebug(params.as_slice()), ); encode(client, &statement, params)? } else { @@ -114,9 +126,10 @@ async fn start(client: &InnerClient, buf: Bytes) -> Result { Ok(responses) } -pub fn encode<'a, I>(client: &InnerClient, statement: &Statement, params: I) -> Result +pub fn encode(client: &InnerClient, statement: &Statement, params: I) -> Result where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { client.with_buf(|buf| { @@ -127,14 +140,15 @@ where }) } -pub fn encode_bind<'a, I>( +pub fn encode_bind( statement: &Statement, params: I, portal: &str, buf: &mut BytesMut, ) -> Result<(), Error> where - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let params = params.into_iter(); @@ -152,7 +166,7 @@ where statement.name(), Some(1), params.zip(statement.params()).enumerate(), - |(idx, (param, ty)), buf| match param.to_sql_checked(ty, buf) { + |(idx, (param, ty)), buf| match param.borrow_to_sql().to_sql_checked(ty, buf) { Ok(IsNull::No) => Ok(postgres_protocol::IsNull::No), Ok(IsNull::Yes) => Ok(postgres_protocol::IsNull::Yes), Err(e) => { diff --git a/tokio-postgres/src/transaction.rs b/tokio-postgres/src/transaction.rs index 38fdf7cea..45e9cc3aa 100644 --- a/tokio-postgres/src/transaction.rs +++ b/tokio-postgres/src/transaction.rs @@ -5,7 +5,7 @@ use crate::query::RowStream; #[cfg(feature = "runtime")] use crate::tls::MakeTlsConnect; use crate::tls::TlsConnect; -use crate::types::{ToSql, Type}; +use crate::types::{BorrowToSql, ToSql, Type}; #[cfg(feature = "runtime")] use crate::Socket; use crate::{ @@ -139,10 +139,11 @@ impl<'a> Transaction<'a> { } /// Like `Client::query_raw`. - pub async fn query_raw<'b, T, I>(&self, statement: &T, params: I) -> Result + pub async fn query_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { self.client.query_raw(statement, params).await @@ -161,10 +162,11 @@ impl<'a> Transaction<'a> { } /// Like `Client::execute_iter`. - pub async fn execute_raw<'b, I, T>(&self, statement: &T, params: I) -> Result + pub async fn execute_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { self.client.execute_raw(statement, params).await @@ -192,10 +194,11 @@ impl<'a> Transaction<'a> { /// A maximally flexible version of [`bind`]. /// /// [`bind`]: #method.bind - pub async fn bind_raw<'b, T, I>(&self, statement: &T, params: I) -> Result + pub async fn bind_raw(&self, statement: &T, params: I) -> Result where T: ?Sized + ToStatement, - I: IntoIterator, + P: BorrowToSql, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let statement = statement.__convert().into_statement(&self.client).await?;