diff --git a/postgres-types/src/lib.rs b/postgres-types/src/lib.rs index e9a5846e1..7e44bc23f 100644 --- a/postgres-types/src/lib.rs +++ b/postgres-types/src/lib.rs @@ -132,6 +132,7 @@ pub use postgres_protocol::Oid; pub use crate::special::{Date, Timestamp}; use bytes::BytesMut; +use std::fmt::{Display, Formatter}; // Number of seconds from 1970-01-01 to 2000-01-01 const TIME_SEC_CONVERSION: u64 = 946_684_800; @@ -542,6 +543,35 @@ impl<'a> FromSql<'a> for &'a str { } } +/// A newtype for String that accepts postgres textual data as +/// opposed to `String` that accepts only a limited set of types (TEXT, VARCHAR...) +/// +pub struct AcceptEverythingString(String); + +impl<'a> FromSql<'a> for AcceptEverythingString { + fn from_sql(_ty: &Type, raw: &'a [u8]) -> Result> { + types::text_from_sql(raw) + .map(ToString::to_string) + .map(Into::into) + } + + fn accepts(_ty: &Type) -> bool { + true + } +} + +impl From for AcceptEverythingString { + fn from(value: String) -> Self { + AcceptEverythingString(value) + } +} + +impl Display for AcceptEverythingString { + fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, formatter) + } +} + macro_rules! simple_from { ($t:ty, $f:ident, $($expected:ident),+) => { impl<'a> FromSql<'a> for $t { diff --git a/postgres/src/client.rs b/postgres/src/client.rs index dcb9c72d4..021ba4927 100644 --- a/postgres/src/client.rs +++ b/postgres/src/client.rs @@ -122,6 +122,23 @@ impl Client { self.connection.block_on(self.client.query(query, params)) } + /// Same as `query` but returns text results instead of binary + /// + /// We introduce an additional method rather than parameters not to break all + /// the conversions and marshalling supplied by `postgres-types` and feature crates and + /// inasmuch as this allows us to maintain API compatibility with upstream + pub fn query_with_text_results( + &mut self, + query: &T, + params: &[&(dyn ToSql + Sync)], + ) -> Result, Error> + where + T: ?Sized + ToStatement, + { + self.connection + .block_on(self.client.query_with_text_results(query, params)) + } + /// Executes a statement which returns a single row, returning it. /// /// Returns an error if the query does not return exactly one row. diff --git a/tokio-postgres/src/client.rs b/tokio-postgres/src/client.rs index e19caae83..95cbd4377 100644 --- a/tokio-postgres/src/client.rs +++ b/tokio-postgres/src/client.rs @@ -226,6 +226,25 @@ impl Client { .await } + /// Same as `query` but returns text results instead of binary + /// + /// We introduce an additional method rather than parameters not to break all + /// the conversions and marshalling supplied by `postgres-types` and feature crates and + /// inasmuch as this allows us to maintain API compatibility with upstream + pub async fn query_with_text_results( + &self, + statement: &T, + params: &[&(dyn ToSql + Sync)], + ) -> Result, Error> + where + T: ?Sized + ToStatement, + { + self.query_raw_common(statement, slice_iter(params), false) + .await? + .try_collect() + .await + } + /// Executes a statement which returns a single row, returning it. /// /// Returns an error if the query does not return exactly one row. @@ -343,13 +362,27 @@ impl Client { /// # } /// ``` pub async fn query_raw<'a, T, I>(&self, statement: &T, params: I) -> Result + where + T: ?Sized + ToStatement, + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + self.query_raw_common(statement, params, true).await + } + + async fn query_raw_common<'a, T, I>( + &self, + statement: &T, + params: I, + binary_results: bool, + ) -> Result where T: ?Sized + ToStatement, I: IntoIterator, I::IntoIter: ExactSizeIterator, { let statement = statement.__convert().into_statement(self).await?; - query::query(&self.inner, statement, params).await + query::query_common(&self.inner, statement, params, binary_results).await } /// Executes a statement, returning the number of rows modified. diff --git a/tokio-postgres/src/query.rs b/tokio-postgres/src/query.rs index 7792f0a8a..94a3a491c 100644 --- a/tokio-postgres/src/query.rs +++ b/tokio-postgres/src/query.rs @@ -18,6 +18,19 @@ pub async fn query<'a, I>( statement: Statement, params: I, ) -> Result +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + query_common(client, statement, params, true).await +} + +pub async fn query_common<'a, I>( + client: &InnerClient, + statement: Statement, + params: I, + binary_results: bool, +) -> Result where I: IntoIterator, I::IntoIter: ExactSizeIterator, @@ -29,9 +42,9 @@ where statement.name(), params, ); - encode(client, &statement, params)? + encode_common(client, &statement, params, binary_results)? } else { - encode(client, &statement, params)? + encode_common(client, &statement, params, binary_results)? }; let responses = start(client, buf).await?; Ok(RowStream { @@ -115,23 +128,37 @@ async fn start(client: &InnerClient, buf: Bytes) -> Result { } pub fn encode<'a, I>(client: &InnerClient, statement: &Statement, params: I) -> Result +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + encode_common(client, statement, params, true) +} + +pub fn encode_common<'a, I>( + client: &InnerClient, + statement: &Statement, + params: I, + binary_results: bool, +) -> Result where I: IntoIterator, I::IntoIter: ExactSizeIterator, { client.with_buf(|buf| { - encode_bind(statement, params, "", buf)?; + encode_bind_common(statement, params, "", buf, binary_results)?; frontend::execute("", 0, buf).map_err(Error::encode)?; frontend::sync(buf); Ok(buf.split().freeze()) }) } -pub fn encode_bind<'a, I>( +pub fn encode_bind_common<'a, I>( statement: &Statement, params: I, portal: &str, buf: &mut BytesMut, + binary_results: bool, ) -> Result<(), Error> where I: IntoIterator, @@ -145,6 +172,7 @@ where statement.params().len(), params.len() ); + let binary_results = if binary_results { 1 } else { 0 }; let mut error_idx = 0; let r = frontend::bind( @@ -160,7 +188,7 @@ where Err(e) } }, - Some(1), + Some(binary_results), buf, ); match r { @@ -170,6 +198,19 @@ where } } +pub fn encode_bind<'a, I>( + statement: &Statement, + params: I, + portal: &str, + buf: &mut BytesMut, +) -> Result<(), Error> +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + encode_bind_common(statement, params, portal, buf, true) +} + pin_project! { /// A stream of table rows. pub struct RowStream {