Skip to content

Commit 01fa1b8

Browse files
author
Dan Wilbanks
committed
Add Row Description Fields to SimpleQuery response
Adds the field information from the Row Description message to SimpleQueryStream and propagates this information to SimpleQueryRow. Previously, we took the column name from the fields and stored this as a SimpleColumn. This change was designed to limit the impact to the current SimpleQuery interface. The field information is stored in a new OwnedField struct, which is identical to the Field struct except that the name is stored as a String. This allows the SimpleQueryMessage to outlive the RowDescriptionBody. We could instead store the RowDescriptionBody and attempt to parse it when SimpleQueryRow::columns() is called. This is analogous to how the row body is handled by SimpleQueryRow::get()/try_get(). However, this approach was not taken to limit the changes to the current interface. SimpleQueryRow::columns() now requires an allocation to return each column as a SimpleColumn. This can be avoided by using the new SimpleQueryRow::fields() method.
1 parent 278f240 commit 01fa1b8

File tree

4 files changed

+110
-17
lines changed

4 files changed

+110
-17
lines changed

postgres-protocol/src/message/backend.rs

+65
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ impl CopyBothResponseBody {
557557
}
558558
}
559559

560+
#[derive(PartialEq, Eq)]
560561
pub struct DataRowBody {
561562
storage: Bytes,
562563
len: u16,
@@ -901,6 +902,70 @@ impl<'a> Field<'a> {
901902
}
902903
}
903904

905+
/// A struct representing the fields of a RowDescription message. Clones the fields to avoid keeping
906+
/// the `RowDescriptionMessage`.
907+
#[derive(Debug, PartialEq, Eq, Clone)]
908+
pub struct OwnedField {
909+
name: String,
910+
table_oid: Oid,
911+
column_id: i16,
912+
type_oid: Oid,
913+
type_size: i16,
914+
type_modifier: i32,
915+
format: i16,
916+
}
917+
918+
impl OwnedField {
919+
#[inline]
920+
pub fn name(&self) -> &str {
921+
&self.name
922+
}
923+
924+
#[inline]
925+
pub fn table_oid(&self) -> Oid {
926+
self.table_oid
927+
}
928+
929+
#[inline]
930+
pub fn column_id(&self) -> i16 {
931+
self.column_id
932+
}
933+
934+
#[inline]
935+
pub fn type_oid(&self) -> Oid {
936+
self.type_oid
937+
}
938+
939+
#[inline]
940+
pub fn type_size(&self) -> i16 {
941+
self.type_size
942+
}
943+
944+
#[inline]
945+
pub fn type_modifier(&self) -> i32 {
946+
self.type_modifier
947+
}
948+
949+
#[inline]
950+
pub fn format(&self) -> i16 {
951+
self.format
952+
}
953+
}
954+
955+
impl From<Field<'_>> for OwnedField {
956+
fn from(f: Field<'_>) -> Self {
957+
OwnedField {
958+
name: f.name().to_string(),
959+
table_oid: f.table_oid(),
960+
column_id: f.column_id(),
961+
type_oid: f.type_oid(),
962+
type_size: f.type_size(),
963+
type_modifier: f.type_modifier(),
964+
format: f.format(),
965+
}
966+
}
967+
}
968+
904969
#[inline]
905970
fn find_null(buf: &[u8], start: usize) -> io::Result<usize> {
906971
match memchr(0, &buf[start..]) {

tokio-postgres/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ pub use crate::to_statement::ToStatement;
141141
pub use crate::transaction::Transaction;
142142
pub use crate::transaction_builder::{IsolationLevel, TransactionBuilder};
143143
use crate::types::ToSql;
144+
pub use postgres_protocol::message::backend::OwnedField;
144145
pub use postgres_protocol::message::backend::Message;
145146

146147
pub mod binary_copy;
@@ -240,6 +241,7 @@ pub enum AsyncMessage {
240241

241242
/// Message returned by the `SimpleQuery` stream.
242243
#[non_exhaustive]
244+
#[derive(Debug)]
243245
pub enum SimpleQueryMessage {
244246
/// A row of data.
245247
Row(SimpleQueryRow),

tokio-postgres/src/row.rs

+35-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::statement::Column;
66
use crate::types::{FromSql, Type, WrongType};
77
use crate::{Error, Statement};
88
use fallible_iterator::FallibleIterator;
9-
use postgres_protocol::message::backend::DataRowBody;
9+
use postgres_protocol::message::backend::{OwnedField, DataRowBody};
1010
use std::fmt;
1111
use std::ops::Range;
1212
use std::str;
@@ -32,6 +32,12 @@ impl AsName for String {
3232
}
3333
}
3434

35+
impl AsName for OwnedField {
36+
fn as_name(&self) -> &str {
37+
self.name()
38+
}
39+
}
40+
3541
/// A trait implemented by types that can index into columns of a row.
3642
///
3743
/// This cannot be implemented outside of this crate.
@@ -196,29 +202,39 @@ impl AsName for SimpleColumn {
196202
}
197203

198204
/// A row of data returned from the database by a simple query.
205+
#[derive(Eq, PartialEq)]
199206
pub struct SimpleQueryRow {
200-
columns: Arc<[SimpleColumn]>,
207+
fields: Arc<[OwnedField]>,
201208
body: DataRowBody,
202209
ranges: Vec<Option<Range<usize>>>,
203210
}
204211

205212
impl SimpleQueryRow {
206213
#[allow(clippy::new_ret_no_self)]
207214
pub(crate) fn new(
208-
columns: Arc<[SimpleColumn]>,
215+
fields: Arc<[OwnedField]>,
209216
body: DataRowBody,
210217
) -> Result<SimpleQueryRow, Error> {
211218
let ranges = body.ranges().collect().map_err(Error::parse)?;
212219
Ok(SimpleQueryRow {
213-
columns,
220+
fields,
214221
body,
215222
ranges,
216223
})
217224
}
218225

219-
/// Returns information about the columns of data in the row.
220-
pub fn columns(&self) -> &[SimpleColumn] {
221-
&self.columns
226+
/// Returns information about the columns of data in the row. Performs an allocation to return
227+
/// each name as a `SimpleColumn`. To avoid the allocation, use `SimpleQueryRow::fields()`.
228+
pub fn columns(&self) -> Vec<SimpleColumn> {
229+
self.fields
230+
.iter()
231+
.map(|f| SimpleColumn::new(f.name().to_string()))
232+
.collect()
233+
}
234+
235+
/// Returns the fields found in the RowDescription message with information on each column.
236+
pub fn fields(&self) -> &[OwnedField] {
237+
&self.fields
222238
}
223239

224240
/// Determines if the row contains no values.
@@ -228,7 +244,7 @@ impl SimpleQueryRow {
228244

229245
/// Returns the number of values in the row.
230246
pub fn len(&self) -> usize {
231-
self.columns.len()
247+
self.fields.len()
232248
}
233249

234250
/// Returns a value from the row.
@@ -260,7 +276,7 @@ impl SimpleQueryRow {
260276
where
261277
I: RowIndex + fmt::Display,
262278
{
263-
let idx = match idx.__idx(&self.columns) {
279+
let idx = match idx.__idx(&self.fields) {
264280
Some(idx) => idx,
265281
None => return Err(Error::column(idx.to_string())),
266282
};
@@ -269,3 +285,13 @@ impl SimpleQueryRow {
269285
FromSql::from_sql_nullable(&Type::TEXT, buf).map_err(|e| Error::from_sql(e, idx))
270286
}
271287
}
288+
289+
impl std::fmt::Debug for SimpleQueryRow {
290+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
291+
let mut values = Vec::with_capacity(self.len());
292+
for i in 0..self.len() {
293+
values.push(self.get(i).unwrap_or(""))
294+
}
295+
write!(f, "{:?}", values)
296+
}
297+
}

tokio-postgres/src/simple_query.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use fallible_iterator::FallibleIterator;
77
use futures::{ready, Stream};
88
use log::debug;
99
use pin_project_lite::pin_project;
10-
use postgres_protocol::message::backend::Message;
10+
use postgres_protocol::message::backend::{OwnedField, Message};
1111
use postgres_protocol::message::frontend;
1212
use std::marker::PhantomPinned;
1313
use std::pin::Pin;
@@ -38,7 +38,7 @@ pub async fn simple_query(client: &InnerClient, query: &str) -> Result<SimpleQue
3838

3939
Ok(SimpleQueryStream {
4040
responses,
41-
columns: None,
41+
fields: None,
4242
_p: PhantomPinned,
4343
})
4444
}
@@ -72,7 +72,7 @@ pin_project! {
7272
/// A stream of simple query results.
7373
pub struct SimpleQueryStream {
7474
responses: Responses,
75-
columns: Option<Arc<[SimpleColumn]>>,
75+
fields: Option<Arc<[OwnedField]>>,
7676
#[pin]
7777
_p: PhantomPinned,
7878
}
@@ -100,18 +100,18 @@ impl Stream for SimpleQueryStream {
100100
return Poll::Ready(Some(Ok(SimpleQueryMessage::CommandComplete(0))));
101101
}
102102
Message::RowDescription(body) => {
103-
let columns = body
103+
let fields = body
104104
.fields()
105-
.map(|f| Ok(SimpleColumn::new(f.name().to_string())))
105+
.map(|f| Ok(f.into()))
106106
.collect::<Vec<_>>()
107107
.map_err(Error::parse)?
108108
.into();
109109

110-
*this.columns = Some(columns);
110+
*this.fields = Some(fields);
111111
}
112112
Message::DataRow(body) => {
113-
let row = match &this.columns {
114-
Some(columns) => SimpleQueryRow::new(columns.clone(), body)?,
113+
let row = match &this.fields {
114+
Some(fields) => SimpleQueryRow::new(fields.clone(), body)?,
115115
None => return Poll::Ready(Some(Err(Error::unexpected_message()))),
116116
};
117117
return Poll::Ready(Some(Ok(SimpleQueryMessage::Row(row))));

0 commit comments

Comments
 (0)