Skip to content

Commit 89f5198

Browse files
committed
Add query_portal
1 parent 349f376 commit 89f5198

File tree

8 files changed

+122
-19
lines changed

8 files changed

+122
-19
lines changed

tokio-postgres/src/lib.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ impl Client {
9292
Bind(self.0.bind(&statement.0, next_portal(), params))
9393
}
9494

95+
pub fn query_portal(&mut self, portal: &Portal, max_rows: i32) -> QueryPortal {
96+
QueryPortal(self.0.query_portal(&portal.0, max_rows))
97+
}
98+
9599
pub fn copy_in<S>(&mut self, statement: &Statement, params: &[&ToSql], stream: S) -> CopyIn<S>
96100
where
97101
S: Stream,
@@ -218,7 +222,7 @@ impl Future for Execute {
218222
}
219223

220224
#[must_use = "streams do nothing unless polled"]
221-
pub struct Query(proto::QueryStream);
225+
pub struct Query(proto::QueryStream<proto::Statement>);
222226

223227
impl Stream for Query {
224228
type Item = Row;
@@ -250,6 +254,23 @@ impl Future for Bind {
250254
}
251255
}
252256

257+
#[must_use = "streams do nothing unless polled"]
258+
pub struct QueryPortal(proto::QueryStream<proto::Portal>);
259+
260+
impl Stream for QueryPortal {
261+
type Item = Row;
262+
type Error = Error;
263+
264+
fn poll(&mut self) -> Poll<Option<Row>, Error> {
265+
match self.0.poll() {
266+
Ok(Async::Ready(Some(row))) => Ok(Async::Ready(Some(Row(row)))),
267+
Ok(Async::Ready(None)) => Ok(Async::Ready(None)),
268+
Ok(Async::NotReady) => Ok(Async::NotReady),
269+
Err(e) => Err(e),
270+
}
271+
}
272+
}
273+
253274
pub struct Portal(proto::Portal);
254275

255276
#[must_use = "futures do nothing unless polled"]

tokio-postgres/src/proto/client.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use proto::connection::{Request, RequestMessages};
1313
use proto::copy_in::{CopyInFuture, CopyInReceiver, CopyMessage};
1414
use proto::copy_out::CopyOutStream;
1515
use proto::execute::ExecuteFuture;
16+
use proto::portal::Portal;
1617
use proto::prepare::PrepareFuture;
1718
use proto::query::QueryStream;
1819
use proto::simple_query::SimpleQueryFuture;
@@ -133,7 +134,7 @@ impl Client {
133134
ExecuteFuture::new(self.clone(), pending, statement.clone())
134135
}
135136

136-
pub fn query(&self, statement: &Statement, params: &[&ToSql]) -> QueryStream {
137+
pub fn query(&self, statement: &Statement, params: &[&ToSql]) -> QueryStream<Statement> {
137138
let pending = PendingRequest(
138139
self.excecute_message(statement, params)
139140
.map(RequestMessages::Single),
@@ -150,6 +151,15 @@ impl Client {
150151
BindFuture::new(self.clone(), pending, name, statement.clone())
151152
}
152153

154+
pub fn query_portal(&self, portal: &Portal, rows: i32) -> QueryStream<Portal> {
155+
let pending = self.pending(|buf| {
156+
frontend::execute(portal.name(), rows, buf).map_err(Error::parse)?;
157+
frontend::sync(buf);
158+
Ok(())
159+
});
160+
QueryStream::new(self.clone(), pending, portal.clone())
161+
}
162+
153163
pub fn copy_in<S>(&self, statement: &Statement, params: &[&ToSql], stream: S) -> CopyInFuture<S>
154164
where
155165
S: Stream,

tokio-postgres/src/proto/portal.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ struct Inner {
99
statement: Statement,
1010
}
1111

12-
pub struct Portal(Arc<Inner>);
13-
14-
impl Drop for Portal {
12+
impl Drop for Inner {
1513
fn drop(&mut self) {
16-
if let Some(client) = self.0.client.upgrade() {
17-
client.close_portal(&self.0.name);
14+
if let Some(client) = self.client.upgrade() {
15+
client.close_portal(&self.name);
1816
}
1917
}
2018
}
2119

20+
#[derive(Clone)]
21+
pub struct Portal(Arc<Inner>);
22+
2223
impl Portal {
2324
pub fn new(client: WeakClient, name: String, statement: Statement) -> Portal {
2425
Portal(Arc::new(Inner {

tokio-postgres/src/proto/query.rs

+34-9
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,46 @@ use postgres_protocol::message::backend::Message;
44
use std::mem;
55

66
use proto::client::{Client, PendingRequest};
7+
use proto::portal::Portal;
78
use proto::row::Row;
89
use proto::statement::Statement;
910
use Error;
1011

11-
enum State {
12+
pub trait StatementHolder {
13+
fn statement(&self) -> &Statement;
14+
}
15+
16+
impl StatementHolder for Statement {
17+
fn statement(&self) -> &Statement {
18+
self
19+
}
20+
}
21+
22+
impl StatementHolder for Portal {
23+
fn statement(&self) -> &Statement {
24+
self.statement()
25+
}
26+
}
27+
28+
enum State<T> {
1229
Start {
1330
client: Client,
1431
request: PendingRequest,
15-
statement: Statement,
32+
statement: T,
1633
},
1734
ReadingResponse {
1835
receiver: mpsc::Receiver<Message>,
19-
statement: Statement,
36+
statement: T,
2037
},
2138
Done,
2239
}
2340

24-
pub struct QueryStream(State);
41+
pub struct QueryStream<T>(State<T>);
2542

26-
impl Stream for QueryStream {
43+
impl<T> Stream for QueryStream<T>
44+
where
45+
T: StatementHolder,
46+
{
2747
type Item = Row;
2848
type Error = Error;
2949

@@ -66,14 +86,16 @@ impl Stream for QueryStream {
6686
}
6787
Some(Message::ErrorResponse(body)) => break Err(Error::db(body)),
6888
Some(Message::DataRow(body)) => {
69-
let row = Row::new(statement.clone(), body)?;
89+
let row = Row::new(statement.statement().clone(), body)?;
7090
self.0 = State::ReadingResponse {
7191
receiver,
7292
statement,
7393
};
7494
break Ok(Async::Ready(Some(row)));
7595
}
76-
Some(Message::EmptyQueryResponse) | Some(Message::CommandComplete(_)) => {
96+
Some(Message::EmptyQueryResponse)
97+
| Some(Message::PortalSuspended)
98+
| Some(Message::CommandComplete(_)) => {
7799
break Ok(Async::Ready(None));
78100
}
79101
Some(_) => break Err(Error::unexpected_message()),
@@ -86,8 +108,11 @@ impl Stream for QueryStream {
86108
}
87109
}
88110

89-
impl QueryStream {
90-
pub fn new(client: Client, request: PendingRequest, statement: Statement) -> QueryStream {
111+
impl<T> QueryStream<T>
112+
where
113+
T: StatementHolder,
114+
{
115+
pub fn new(client: Client, request: PendingRequest, statement: T) -> QueryStream<T> {
91116
QueryStream(State::Start {
92117
client,
93118
request,

tokio-postgres/src/proto/typeinfo.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use next_statement;
77
use proto::client::Client;
88
use proto::prepare::PrepareFuture;
99
use proto::query::QueryStream;
10+
use proto::statement::Statement;
1011
use proto::typeinfo_composite::TypeinfoCompositeFuture;
1112
use proto::typeinfo_enum::TypeinfoEnumFuture;
1213
use types::{Kind, Oid, Type};
@@ -55,7 +56,7 @@ pub enum Typeinfo {
5556
QueryingRangeSubtype
5657
))]
5758
QueryingTypeinfo {
58-
future: stream::Collect<QueryStream>,
59+
future: stream::Collect<QueryStream<Statement>>,
5960
oid: Oid,
6061
client: Client,
6162
},

tokio-postgres/src/proto/typeinfo_composite.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use next_statement;
99
use proto::client::Client;
1010
use proto::prepare::PrepareFuture;
1111
use proto::query::QueryStream;
12+
use proto::statement::Statement;
1213
use proto::typeinfo::TypeinfoFuture;
1314
use types::{Field, Oid};
1415

@@ -36,7 +37,7 @@ pub enum TypeinfoComposite {
3637
},
3738
#[state_machine_future(transitions(QueryingCompositeFieldTypes, Finished))]
3839
QueryingCompositeFields {
39-
future: stream::Collect<QueryStream>,
40+
future: stream::Collect<QueryStream<Statement>>,
4041
client: Client,
4142
},
4243
#[state_machine_future(transitions(Finished))]

tokio-postgres/src/proto/typeinfo_enum.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use next_statement;
77
use proto::client::Client;
88
use proto::prepare::PrepareFuture;
99
use proto::query::QueryStream;
10+
use proto::statement::Statement;
1011
use types::Oid;
1112

1213
const TYPEINFO_ENUM_QUERY: &'static str = "
@@ -45,7 +46,7 @@ pub enum TypeinfoEnum {
4546
},
4647
#[state_machine_future(transitions(Finished))]
4748
QueryingEnumVariants {
48-
future: stream::Collect<QueryStream>,
49+
future: stream::Collect<QueryStream<Statement>>,
4950
client: Client,
5051
},
5152
#[state_machine_future(ready)]

tokio-postgres/tests/test.rs

+43
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,49 @@ fn insert_select() {
198198
runtime.block_on(tests).unwrap();
199199
}
200200

201+
#[test]
202+
fn query_portal() {
203+
let _ = env_logger::try_init();
204+
let mut runtime = Runtime::new().unwrap();
205+
206+
let handshake = tokio_postgres::connect(
207+
"postgres://postgres@localhost:5433".parse().unwrap(),
208+
TlsMode::None,
209+
);
210+
let (mut client, connection) = runtime.block_on(handshake).unwrap();
211+
let connection = connection.map_err(|e| panic!("{}", e));
212+
runtime.handle().spawn(connection).unwrap();
213+
214+
runtime
215+
.block_on(client.batch_execute(
216+
"CREATE TEMPORARY TABLE foo (id SERIAL, name TEXT);
217+
INSERT INTO foo (name) VALUES ('alice'), ('bob'), ('charlie');
218+
BEGIN;",
219+
)).unwrap();
220+
221+
let statement = runtime
222+
.block_on(client.prepare("SELECT id, name FROM foo ORDER BY id"))
223+
.unwrap();
224+
let portal = runtime.block_on(client.bind(&statement, &[])).unwrap();
225+
226+
let f1 = client.query_portal(&portal, 2).collect();
227+
let f2 = client.query_portal(&portal, 2).collect();
228+
let f3 = client.query_portal(&portal, 2).collect();
229+
let (r1, r2, r3) = runtime.block_on(f1.join3(f2, f3)).unwrap();
230+
231+
assert_eq!(r1.len(), 2);
232+
assert_eq!(r1[0].get::<_, i32>(0), 1);
233+
assert_eq!(r1[0].get::<_, &str>(1), "alice");
234+
assert_eq!(r1[1].get::<_, i32>(0), 2);
235+
assert_eq!(r1[1].get::<_, &str>(1), "bob");
236+
237+
assert_eq!(r2.len(), 1);
238+
assert_eq!(r2[0].get::<_, i32>(0), 3);
239+
assert_eq!(r2[0].get::<_, &str>(1), "charlie");
240+
241+
assert_eq!(r3.len(), 0);
242+
}
243+
201244
#[test]
202245
fn cancel_query() {
203246
let _ = env_logger::try_init();

0 commit comments

Comments
 (0)