Skip to content

Commit 8fc1ab7

Browse files
committed
Switch query_raw_txt() to text format
That allows to grab postgres-provided serializations for types.
1 parent c473f35 commit 8fc1ab7

File tree

5 files changed

+76
-18
lines changed

5 files changed

+76
-18
lines changed

postgres-types/src/lib.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,22 @@ impl WrongType {
395395
}
396396
}
397397

398+
/// An error indicating that a as_text conversion was attempted on a binary
399+
/// result.
400+
#[derive(Debug)]
401+
pub struct WrongFormat {}
402+
403+
impl Error for WrongFormat {}
404+
405+
impl fmt::Display for WrongFormat {
406+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
407+
write!(
408+
fmt,
409+
"cannot read column as text while it is in binary format"
410+
)
411+
}
412+
}
413+
398414
/// A trait for types that can be created from a Postgres value.
399415
///
400416
/// # Types
@@ -846,7 +862,7 @@ pub trait ToSql: fmt::Debug {
846862
/// Supported Postgres message format types
847863
///
848864
/// Using Text format in a message assumes a Postgres `SERVER_ENCODING` of `UTF8`
849-
#[derive(Clone, Copy, Debug)]
865+
#[derive(Clone, Copy, Debug, PartialEq)]
850866
pub enum Format {
851867
/// Text format (UTF-8)
852868
Text,

tokio-postgres/src/client.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::keepalive::KeepaliveConfig;
1010
use crate::prepare::get_type;
1111
use crate::query::RowStream;
1212
use crate::simple_query::SimpleQueryStream;
13+
use crate::statement::Column;
1314
#[cfg(feature = "runtime")]
1415
use crate::tls::MakeTlsConnect;
1516
use crate::tls::TlsConnect;
@@ -21,12 +22,11 @@ use crate::{
2122
CopyInSink, Error, Row, SimpleQueryMessage, Statement, ToStatement, Transaction,
2223
TransactionBuilder,
2324
};
24-
use bytes::{Buf, BytesMut, BufMut};
25+
use bytes::{Buf, BufMut, BytesMut};
2526
use fallible_iterator::FallibleIterator;
2627
use futures_channel::mpsc;
2728
use futures_util::{future, pin_mut, ready, StreamExt, TryStreamExt};
2829
use parking_lot::Mutex;
29-
use crate::statement::Column;
3030
use postgres_protocol::message::{backend::Message, frontend};
3131
use postgres_types::BorrowToSql;
3232
use std::collections::HashMap;
@@ -392,15 +392,15 @@ impl Client {
392392
frontend::parse("", query.as_ref(), std::iter::empty(), buf).map_err(Error::encode)?;
393393
// Bind, pass params as text, retrieve as binary
394394
match frontend::bind(
395-
"", // empty string selects the unnamed portal
396-
"", // empty string selects the unnamed prepared statement
395+
"", // empty string selects the unnamed portal
396+
"", // empty string selects the unnamed prepared statement
397397
std::iter::empty(), // all parameters use the default format (text)
398398
params,
399399
|param, buf| {
400400
buf.put_slice(param.as_ref().as_bytes());
401401
Ok(postgres_protocol::IsNull::No)
402402
},
403-
Some(1), // all binary
403+
Some(0), // all text
404404
buf,
405405
) {
406406
Ok(()) => Ok(()),
@@ -418,7 +418,9 @@ impl Client {
418418
Ok(buf.split().freeze())
419419
})?;
420420

421-
let mut responses = self.inner.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
421+
let mut responses = self
422+
.inner
423+
.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
422424

423425
// now read the responses
424426

@@ -450,12 +452,7 @@ impl Client {
450452
}
451453
}
452454

453-
let statement = Statement::new(
454-
&self.inner,
455-
"".to_owned(),
456-
parameters,
457-
columns
458-
);
455+
let statement = Statement::new_text(&self.inner, "".to_owned(), parameters, columns);
459456

460457
Ok(RowStream::new(statement, responses))
461458
}

tokio-postgres/src/row.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::types::{FromSql, Type, WrongType};
77
use crate::{Error, Statement};
88
use fallible_iterator::FallibleIterator;
99
use postgres_protocol::message::backend::DataRowBody;
10+
use postgres_types::{Format, WrongFormat};
1011
use std::fmt;
1112
use std::ops::Range;
1213
use std::str;
@@ -187,6 +188,22 @@ impl Row {
187188
let range = self.ranges[idx].to_owned()?;
188189
Some(&self.body.buffer()[range])
189190
}
191+
192+
/// Interpret the column at the given index as text
193+
///
194+
/// Useful when using query_raw_txt() which sets text transfer mode
195+
pub fn as_text(&self, idx: usize) -> Result<Option<&str>, Error> {
196+
if self.statement.output_format() == Format::Text {
197+
match self.col_buffer(idx) {
198+
Some(raw) => {
199+
FromSql::from_sql(&Type::TEXT, raw).map_err(|e| Error::from_sql(e, idx))
200+
}
201+
None => Ok(None),
202+
}
203+
} else {
204+
Err(Error::from_sql(Box::new(WrongFormat {}), idx))
205+
}
206+
}
190207
}
191208

192209
impl AsName for SimpleColumn {

tokio-postgres/src/statement.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::codec::FrontendMessage;
33
use crate::connection::RequestMessages;
44
use crate::types::Type;
55
use postgres_protocol::message::frontend;
6+
use postgres_types::Format;
67
use std::{
78
fmt,
89
sync::{Arc, Weak},
@@ -13,6 +14,7 @@ struct StatementInner {
1314
name: String,
1415
params: Vec<Type>,
1516
columns: Vec<Column>,
17+
output_format: Format,
1618
}
1719

1820
impl Drop for StatementInner {
@@ -46,6 +48,22 @@ impl Statement {
4648
name,
4749
params,
4850
columns,
51+
output_format: Format::Binary,
52+
}))
53+
}
54+
55+
pub(crate) fn new_text(
56+
inner: &Arc<InnerClient>,
57+
name: String,
58+
params: Vec<Type>,
59+
columns: Vec<Column>,
60+
) -> Statement {
61+
Statement(Arc::new(StatementInner {
62+
client: Arc::downgrade(inner),
63+
name,
64+
params,
65+
columns,
66+
output_format: Format::Text,
4967
}))
5068
}
5169

@@ -62,6 +80,11 @@ impl Statement {
6280
pub fn columns(&self) -> &[Column] {
6381
&self.0.columns
6482
}
83+
84+
/// Returns output format for the statement.
85+
pub fn output_format(&self) -> Format {
86+
self.0.output_format
87+
}
6588
}
6689

6790
/// Information about a column of a query.

tokio-postgres/tests/test/main.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,18 +257,23 @@ async fn query_raw_txt() {
257257

258258
let rows: Vec<tokio_postgres::Row> = client
259259
.query_raw_txt("SELECT 55 * $1", ["42"])
260-
.await.unwrap()
260+
.await
261+
.unwrap()
261262
.try_collect()
262-
.await.unwrap();
263+
.await
264+
.unwrap();
263265

264266
assert_eq!(rows.len(), 1);
265-
assert_eq!(rows[0].get::<_, i32>(0), 55 * 42);
267+
let res: i32 = rows[0].as_text(0).unwrap().parse::<i32>().unwrap();
268+
assert_eq!(res, 55 * 42);
266269

267270
let rows: Vec<tokio_postgres::Row> = client
268271
.query_raw_txt("SELECT $1", ["42"])
269-
.await.unwrap()
272+
.await
273+
.unwrap()
270274
.try_collect()
271-
.await.unwrap();
275+
.await
276+
.unwrap();
272277

273278
assert_eq!(rows.len(), 1);
274279
assert_eq!(rows[0].get::<_, &str>(0), "42");

0 commit comments

Comments
 (0)