1use crate::client::{InnerClient, Responses};
2use crate::codec::FrontendMessage;
3use crate::connection::RequestMessages;
4use crate::prepare::get_type;
5use crate::types::{BorrowToSql, IsNull};
6use crate::{Column, Error, Portal, Row, Statement};
7use bytes::{Bytes, BytesMut};
8use fallible_iterator::FallibleIterator;
9use futures_util::{ready, Stream};
10use log::{debug, log_enabled, Level};
11use pin_project_lite::pin_project;
12use postgres_protocol::message::backend::{CommandCompleteBody, Message};
13use postgres_protocol::message::frontend;
14use postgres_types::Type;
15use std::fmt;
16use std::marker::PhantomPinned;
17use std::pin::Pin;
18use std::sync::Arc;
19use std::task::{Context, Poll};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ResultFormat {
24 Text,
27 Binary,
30}
31
32impl ResultFormat {
33 fn format_code(self) -> i16 {
34 match self {
35 ResultFormat::Text => 0,
36 ResultFormat::Binary => 1,
37 }
38 }
39}
40
41struct BorrowToSqlParamsDebug<'a, T>(&'a [T]);
42
43impl<T> fmt::Debug for BorrowToSqlParamsDebug<'_, T>
44where
45 T: BorrowToSql,
46{
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 f.debug_list()
49 .entries(self.0.iter().map(|x| x.borrow_to_sql()))
50 .finish()
51 }
52}
53
54pub async fn query<P, I>(
55 client: &InnerClient,
56 statement: Statement,
57 params: I,
58) -> Result<RowStream, Error>
59where
60 P: BorrowToSql,
61 I: IntoIterator<Item = P>,
62 I::IntoIter: ExactSizeIterator,
63{
64 query_with_format(client, statement, params, ResultFormat::Binary).await
65}
66
67pub async fn query_with_format<P, I>(
68 client: &InnerClient,
69 statement: Statement,
70 params: I,
71 result_format: ResultFormat,
72) -> Result<RowStream, Error>
73where
74 P: BorrowToSql,
75 I: IntoIterator<Item = P>,
76 I::IntoIter: ExactSizeIterator,
77{
78 let buf = if log_enabled!(Level::Debug) {
79 let params = params.into_iter().collect::<Vec<_>>();
80 debug!(
81 "executing statement {} with parameters: {:?}",
82 statement.name(),
83 BorrowToSqlParamsDebug(params.as_slice()),
84 );
85 encode(client, &statement, params, result_format)?
86 } else {
87 encode(client, &statement, params, result_format)?
88 };
89 let responses = start(client, buf).await?;
90 Ok(RowStream {
91 statement,
92 responses,
93 rows_affected: None,
94 command_tag: None,
95 _p: PhantomPinned,
96 })
97}
98
99pub async fn query_typed<P, I>(
100 client: &Arc<InnerClient>,
101 query: &str,
102 params: I,
103) -> Result<RowStream, Error>
104where
105 P: BorrowToSql,
106 I: IntoIterator<Item = (P, Type)>,
107{
108 let buf = {
109 let params = params.into_iter().collect::<Vec<_>>();
110 let param_oids = params.iter().map(|(_, t)| t.oid()).collect::<Vec<_>>();
111
112 client.with_buf(|buf| {
113 frontend::parse("", query, param_oids, buf).map_err(Error::parse)?;
114 encode_bind_raw("", params, "", ResultFormat::Binary, buf)?;
115 frontend::describe(b'S', "", buf).map_err(Error::encode)?;
116 frontend::execute("", 0, buf).map_err(Error::encode)?;
117 frontend::sync(buf);
118
119 Ok(buf.split().freeze())
120 })?
121 };
122
123 let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
124
125 loop {
126 match responses.next().await? {
127 Message::ParseComplete | Message::BindComplete | Message::ParameterDescription(_) => {}
128 Message::NoData => {
129 return Ok(RowStream {
130 statement: Statement::unnamed(vec![], vec![]),
131 responses,
132 rows_affected: None,
133 command_tag: None,
134 _p: PhantomPinned,
135 });
136 }
137 Message::RowDescription(row_description) => {
138 let mut columns: Vec<Column> = vec![];
139 let mut it = row_description.fields();
140 while let Some(field) = it.next().map_err(Error::parse)? {
141 let type_ = get_type(client, field.type_oid()).await?;
142 let column = Column {
143 name: field.name().to_string(),
144 table_oid: Some(field.table_oid()).filter(|n| *n != 0),
145 column_id: Some(field.column_id()).filter(|n| *n != 0),
146 r#type: type_,
147 };
148 columns.push(column);
149 }
150 return Ok(RowStream {
151 statement: Statement::unnamed(vec![], columns),
152 responses,
153 rows_affected: None,
154 command_tag: None,
155 _p: PhantomPinned,
156 });
157 }
158 _ => return Err(Error::unexpected_message()),
159 }
160 }
161}
162
163pub async fn query_portal(
164 client: &InnerClient,
165 portal: &Portal,
166 max_rows: i32,
167) -> Result<RowStream, Error> {
168 let buf = client.with_buf(|buf| {
169 frontend::execute(portal.name(), max_rows, buf).map_err(Error::encode)?;
170 frontend::sync(buf);
171 Ok(buf.split().freeze())
172 })?;
173
174 let responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
175
176 Ok(RowStream {
177 statement: portal.statement().clone(),
178 responses,
179 rows_affected: None,
180 command_tag: None,
181 _p: PhantomPinned,
182 })
183}
184
185pub fn extract_row_affected(body: &CommandCompleteBody) -> Result<u64, Error> {
187 let rows = body
188 .tag()
189 .map_err(Error::parse)?
190 .rsplit(' ')
191 .next()
192 .unwrap()
193 .parse()
194 .unwrap_or(0);
195 Ok(rows)
196}
197
198pub async fn execute<P, I>(
199 client: &InnerClient,
200 statement: Statement,
201 params: I,
202) -> Result<u64, Error>
203where
204 P: BorrowToSql,
205 I: IntoIterator<Item = P>,
206 I::IntoIter: ExactSizeIterator,
207{
208 let buf = if log_enabled!(Level::Debug) {
209 let params = params.into_iter().collect::<Vec<_>>();
210 debug!(
211 "executing statement {} with parameters: {:?}",
212 statement.name(),
213 BorrowToSqlParamsDebug(params.as_slice()),
214 );
215 encode(client, &statement, params, ResultFormat::Binary)?
216 } else {
217 encode(client, &statement, params, ResultFormat::Binary)?
218 };
219 let mut responses = start(client, buf).await?;
220
221 let mut rows = 0;
222 loop {
223 match responses.next().await? {
224 Message::DataRow(_) => {}
225 Message::CommandComplete(body) => {
226 rows = extract_row_affected(&body)?;
227 }
228 Message::EmptyQueryResponse => rows = 0,
229 Message::ReadyForQuery(_) => return Ok(rows),
230 _ => return Err(Error::unexpected_message()),
231 }
232 }
233}
234
235async fn start(client: &InnerClient, buf: Bytes) -> Result<Responses, Error> {
236 let mut responses = client.send(RequestMessages::Single(FrontendMessage::Raw(buf)))?;
237
238 match responses.next().await? {
239 Message::BindComplete => {}
240 _ => return Err(Error::unexpected_message()),
241 }
242
243 Ok(responses)
244}
245
246pub fn encode<P, I>(
247 client: &InnerClient,
248 statement: &Statement,
249 params: I,
250 result_format: ResultFormat,
251) -> Result<Bytes, Error>
252where
253 P: BorrowToSql,
254 I: IntoIterator<Item = P>,
255 I::IntoIter: ExactSizeIterator,
256{
257 client.with_buf(|buf| {
258 encode_bind(statement, params, "", result_format, buf)?;
259 frontend::execute("", 0, buf).map_err(Error::encode)?;
260 frontend::sync(buf);
261 Ok(buf.split().freeze())
262 })
263}
264
265pub fn encode_bind<P, I>(
266 statement: &Statement,
267 params: I,
268 portal: &str,
269 result_format: ResultFormat,
270 buf: &mut BytesMut,
271) -> Result<(), Error>
272where
273 P: BorrowToSql,
274 I: IntoIterator<Item = P>,
275 I::IntoIter: ExactSizeIterator,
276{
277 let params = params.into_iter();
278 if params.len() != statement.params().len() {
279 return Err(Error::parameters(params.len(), statement.params().len()));
280 }
281
282 encode_bind_raw(
283 statement.name(),
284 params.zip(statement.params().iter().cloned()),
285 portal,
286 result_format,
287 buf,
288 )
289}
290
291fn encode_bind_raw<P, I>(
292 statement_name: &str,
293 params: I,
294 portal: &str,
295 result_format: ResultFormat,
296 buf: &mut BytesMut,
297) -> Result<(), Error>
298where
299 P: BorrowToSql,
300 I: IntoIterator<Item = (P, Type)>,
301 I::IntoIter: ExactSizeIterator,
302{
303 let (param_formats, params): (Vec<_>, Vec<_>) = params
304 .into_iter()
305 .map(|(p, ty)| (p.borrow_to_sql().encode_format(&ty) as i16, (p, ty)))
306 .unzip();
307
308 let mut error_idx = 0;
309 let r = frontend::bind(
310 portal,
311 statement_name,
312 param_formats,
313 params.into_iter().enumerate(),
314 |(idx, (param, ty)), buf| match param.borrow_to_sql().to_sql_checked(&ty, buf) {
315 Ok(IsNull::No) => Ok(postgres_protocol::IsNull::No),
316 Ok(IsNull::Yes) => Ok(postgres_protocol::IsNull::Yes),
317 Err(e) => {
318 error_idx = idx;
319 Err(e)
320 }
321 },
322 Some(result_format.format_code()),
323 buf,
324 );
325 match r {
326 Ok(()) => Ok(()),
327 Err(frontend::BindError::Conversion(e)) => Err(Error::to_sql(e, error_idx)),
328 Err(frontend::BindError::Serialization(e)) => Err(Error::encode(e)),
329 }
330}
331
332pin_project! {
333 pub struct RowStream {
335 statement: Statement,
336 responses: Responses,
337 rows_affected: Option<u64>,
338 command_tag: Option<String>,
339 #[pin]
340 _p: PhantomPinned,
341 }
342}
343
344impl Stream for RowStream {
345 type Item = Result<Row, Error>;
346
347 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
348 let this = self.project();
349 loop {
350 match ready!(this.responses.poll_next(cx)?) {
351 Message::DataRow(body) => {
352 return Poll::Ready(Some(Ok(Row::new(this.statement.clone(), body)?)))
353 }
354 Message::CommandComplete(body) => {
355 *this.rows_affected = Some(extract_row_affected(&body)?);
356 *this.command_tag = extract_command_tag(&body).ok();
357 }
358 Message::EmptyQueryResponse | Message::PortalSuspended => {}
359 Message::ReadyForQuery(_) => return Poll::Ready(None),
360 _ => return Poll::Ready(Some(Err(Error::unexpected_message()))),
361 }
362 }
363 }
364}
365
366fn extract_command_tag(body: &CommandCompleteBody) -> Result<String, Error> {
368 let tag = body.tag().map_err(Error::parse)?;
369 Ok(tag
372 .split(' ')
373 .take_while(|part| part.parse::<u64>().is_err())
374 .collect::<Vec<_>>()
375 .join(" "))
376}
377
378impl RowStream {
379 pub fn columns(&self) -> &[Column] {
381 self.statement.columns()
382 }
383
384 pub fn rows_affected(&self) -> Option<u64> {
388 self.rows_affected
389 }
390
391 pub fn command_tag(&self) -> Option<&str> {
395 self.command_tag.as_deref()
396 }
397}