Skip to main content

elefant_client/postgres_client/
query.rs

1use crate::pool::ConnectionFactory;
2use crate::postgres_client::statements::{PreparedQuery, Statement};
3use crate::postgres_client::PostgresClient;
4use crate::protocol::{
5    BackendMessage, FieldDescription, FrontendMessage, RowDescription, ValueFormat,
6};
7use crate::types::EnumTypeRegistry;
8use crate::{
9    protocol, ElefantClientError, FromSql, FromSqlBinary, FromSqlBinaryOwned, FromSqlRowOwned,
10    FromSqlText, FromSqlTextOwned, ToSql,
11};
12use std::borrow::Cow;
13use std::marker::PhantomData;
14use std::rc::Rc;
15use std::sync::Arc;
16use tracing::{debug, trace};
17
18#[macro_export]
19macro_rules! reborrow_until_polonius {
20    ($e:expr) => {
21        unsafe {
22            // This gets around the borrow checker not supporting releasing the borrow because
23            // it is only kept alive in the return statement. This should all be solved when polonius is a thing
24            // properly, but for now this is the best way to go.
25            &mut *(($e) as *mut _)
26        }
27    };
28}
29
30impl<F: ConnectionFactory> PostgresClient<F> {
31    /// Execute a query in binary mode - always uses prepared statements
32    pub async fn query(
33        &mut self,
34        query: &(impl Statement + ?Sized),
35        parameters: &[&dyn ToSql],
36    ) -> Result<QueryResult<'_, F>, ElefantClientError> {
37        let prepared = query.prepare(self).await?;
38        prepared.execute(self, parameters).await
39    }
40
41    /// Execute a simple query in text mode - only accepts &str, no parameters
42    pub async fn query_simple(
43        &mut self,
44        query: &str,
45    ) -> Result<SimpleQueryResult<'_, F>, ElefantClientError> {
46        self.start_new_query().await?;
47        self.connection
48            .write_frontend_message(&FrontendMessage::Query(protocol::Query {
49                query: Cow::Borrowed(query),
50            }))
51            .await?;
52        self.connection.flush().await?;
53
54        Ok(SimpleQueryResult::new(self, None))
55    }
56
57    pub async fn prepare_query(
58        &mut self,
59        query: &str,
60    ) -> Result<PreparedQuery, ElefantClientError> {
61        self.prepared_query_counter += 1;
62
63        let name = format!("elefant_prepared_query_{}", self.prepared_query_counter);
64
65        self.prepare_with_name(query, Some(name)).await
66    }
67
68    pub(crate) async fn prepare_with_name(
69        &mut self,
70        query: &str,
71        name: Option<String>,
72    ) -> Result<PreparedQuery, ElefantClientError> {
73        self.start_new_query().await?;
74
75        let destination = name
76            .as_ref()
77            .map(|n| Cow::Borrowed(n.as_ref()))
78            .unwrap_or(Cow::Borrowed(""));
79
80        self.connection
81            .write_frontend_message(&FrontendMessage::Parse(protocol::Parse {
82                destination: destination.clone(),
83                query: Cow::Borrowed(query),
84                parameter_types: vec![],
85            }))
86            .await?;
87
88        self.connection
89            .write_frontend_message(&FrontendMessage::Describe(protocol::Describe {
90                name: destination,
91                target: protocol::DescribeTarget::Statement,
92            }))
93            .await?;
94        self.connection
95            .write_frontend_message(&FrontendMessage::Flush)
96            .await?;
97        self.connection.flush().await?;
98
99        let msg = self.read_next_backend_message().await?;
100
101        match msg {
102            BackendMessage::ParseComplete => {
103                trace!("Parse complete");
104            }
105            BackendMessage::ErrorResponse(er) => {
106                return Err(ElefantClientError::PostgresError(format!("{er:?}")));
107            }
108            _ => {
109                return Err(ElefantClientError::UnexpectedBackendMessage(format!(
110                    "{msg:?}"
111                )));
112            }
113        }
114
115        let parameter_description = {
116            let msg = self.read_next_backend_message().await?;
117
118            match msg {
119                BackendMessage::ParameterDescription(pd) => pd,
120                BackendMessage::ErrorResponse(er) => {
121                    return Err(ElefantClientError::PostgresError(format!("{er:?}")));
122                }
123                _ => {
124                    return Err(ElefantClientError::UnexpectedBackendMessage(format!(
125                        "{msg:?}"
126                    )));
127                }
128            }
129        };
130
131        let row_description = {
132            let msg = self.read_next_backend_message().await?;
133
134            match msg {
135                BackendMessage::RowDescription(rd) => {
136                    PreparedQueryResult::RowDescription(RowDescription {
137                        fields: rd
138                            .fields
139                            .iter()
140                            .map(|f| FieldDescription {
141                                name: f.name.clone(),
142                                format: ValueFormat::Binary,
143                                data_type_oid: f.data_type_oid,
144                                data_type_size: f.data_type_size,
145                                type_modifier: f.type_modifier,
146                                table_oid: f.table_oid,
147                                column_attribute_number: f.column_attribute_number,
148                            })
149                            .collect(),
150                    })
151                }
152                BackendMessage::NoData => PreparedQueryResult::NoData,
153                BackendMessage::ErrorResponse(er) => {
154                    return Err(ElefantClientError::PostgresError(format!("{er:?}")));
155                }
156                _ => {
157                    return Err(ElefantClientError::UnexpectedBackendMessage(format!(
158                        "{msg:?}"
159                    )));
160                }
161            }
162        };
163
164        self.ready_for_query = true;
165
166        Ok(PreparedQuery::new(
167            name,
168            self.client_id,
169            parameter_description,
170            row_description,
171        ))
172    }
173}
174
175pub(crate) enum PreparedQueryResult {
176    RowDescription(protocol::RowDescription),
177    NoData,
178}
179
180// Shared base structure for common query result functionality
181pub struct QueryResultBase<'postgres_client, F: ConnectionFactory> {
182    client: &'postgres_client mut PostgresClient<F>,
183    prepared_query_result: Option<Rc<PreparedQueryResult>>,
184}
185
186// Binary mode query result - enforces FromSqlBinary constraint
187pub struct QueryResult<'postgres_client, F: ConnectionFactory> {
188    base: QueryResultBase<'postgres_client, F>,
189}
190
191// Simple mode query result - enforces FromSqlText constraint
192pub struct SimpleQueryResult<'postgres_client, F: ConnectionFactory> {
193    base: QueryResultBase<'postgres_client, F>,
194}
195
196impl<'postgres_client, F: ConnectionFactory> QueryResultBase<'postgres_client, F> {
197    pub(crate) fn new(
198        client: &'postgres_client mut PostgresClient<F>,
199        prepared_query_result: Option<Rc<PreparedQueryResult>>,
200    ) -> Self {
201        Self {
202            client,
203            prepared_query_result,
204        }
205    }
206
207    pub async fn next_result_set<'query_result>(
208        &'query_result mut self,
209    ) -> Result<QueryResultSet<'postgres_client, 'query_result, F>, ElefantClientError> {
210        if let Some(prepared) = self.prepared_query_result.take() {
211            self.prepared_query_result = Some(Rc::new(PreparedQueryResult::NoData));
212            return match prepared.as_ref() {
213                PreparedQueryResult::RowDescription(rd) => {
214                    let client: &mut PostgresClient<F> = reborrow_until_polonius!(self.client);
215                    let registry = client.enum_registry.clone();
216                    Ok(QueryResultSet::RowDescriptionReceived(RowResultReader {
217                        client,
218                        row_description: rd.clone(),
219                        enum_registry: registry,
220                        query_result_res: PhantomData,
221                    }))
222                }
223                PreparedQueryResult::NoData => Ok(QueryResultSet::QueryProcessingComplete),
224            };
225        }
226
227        loop {
228            let client: &mut PostgresClient<F> = reborrow_until_polonius!(self.client);
229            let msg = client.read_next_backend_message().await?;
230
231            match msg {
232                BackendMessage::CommandComplete(cc) => {
233                    debug!("Command complete: {:?}", cc);
234                }
235                BackendMessage::RowDescription(rd) => {
236                    let registry = client.enum_registry.clone();
237                    return Ok(QueryResultSet::RowDescriptionReceived(RowResultReader {
238                        client,
239                        row_description: rd,
240                        enum_registry: registry,
241                        query_result_res: PhantomData,
242                    }));
243                }
244                BackendMessage::DataRow(dr) => {
245                    return Err(ElefantClientError::UnexpectedBackendMessage(format!(
246                        "Received DataRow without receiving a RowDescription: {dr:?}"
247                    )));
248                }
249                BackendMessage::EmptyQueryResponse => {
250                    debug!("Empty query response");
251                }
252                BackendMessage::ErrorResponse(er) => {
253                    return Err(ElefantClientError::PostgresError(format!("{er:?}")));
254                }
255                BackendMessage::ReadyForQuery(rfq) => {
256                    self.client.ready_for_query = true;
257                    self.client.current_transaction_status = rfq.current_transaction_status;
258                    return Ok(QueryResultSet::QueryProcessingComplete);
259                }
260                _ => {
261                    return Err(ElefantClientError::UnexpectedBackendMessage(format!(
262                        "{msg:?}"
263                    )));
264                }
265            }
266        }
267    }
268}
269
270// QueryResult implementations (binary mode)
271impl<'postgres_client, F: ConnectionFactory> QueryResult<'postgres_client, F> {
272    pub(crate) fn new(
273        client: &'postgres_client mut PostgresClient<F>,
274        prepared_query_result: Option<Rc<PreparedQueryResult>>,
275    ) -> Self {
276        Self {
277            base: QueryResultBase::new(client, prepared_query_result),
278        }
279    }
280
281    pub async fn next_result_set<'query_result>(
282        &'query_result mut self,
283    ) -> Result<QueryResultSet<'postgres_client, 'query_result, F>, ElefantClientError> {
284        self.base.next_result_set().await
285    }
286
287    pub async fn collect_to_vec<T>(mut self) -> Result<Vec<T>, ElefantClientError>
288    where
289        T: FromSqlRowOwned,
290    {
291        let mut results = Vec::new();
292        loop {
293            match self.next_result_set().await? {
294                QueryResultSet::QueryProcessingComplete => return Ok(results),
295                QueryResultSet::RowDescriptionReceived(reader) => {
296                    results.extend(reader.collect_to_vec::<T>().await?);
297                }
298            }
299        }
300    }
301
302    pub async fn collect_single_column_to_vec<T>(mut self) -> Result<Vec<T>, ElefantClientError>
303    where
304        T: FromSqlBinaryOwned,
305    {
306        let mut results = Vec::new();
307        loop {
308            match self.next_result_set().await? {
309                QueryResultSet::QueryProcessingComplete => return Ok(results),
310                QueryResultSet::RowDescriptionReceived(mut row_result_reader) => {
311                    while let Some(row) = row_result_reader.next_row().await? {
312                        results.push(row.get_binary(0)?);
313                    }
314                }
315            }
316        }
317    }
318}
319
320// SimpleQueryResult implementations (text mode)
321impl<'postgres_client, F: ConnectionFactory> SimpleQueryResult<'postgres_client, F> {
322    pub(crate) fn new(
323        client: &'postgres_client mut PostgresClient<F>,
324        prepared_query_result: Option<Rc<PreparedQueryResult>>,
325    ) -> Self {
326        Self {
327            base: QueryResultBase::new(client, prepared_query_result),
328        }
329    }
330
331    pub async fn next_result_set<'query_result>(
332        &'query_result mut self,
333    ) -> Result<QueryResultSet<'postgres_client, 'query_result, F>, ElefantClientError> {
334        self.base.next_result_set().await
335    }
336
337    pub async fn collect_to_vec<T>(mut self) -> Result<Vec<T>, ElefantClientError>
338    where
339        T: FromSqlRowOwned,
340    {
341        let mut results = Vec::new();
342        loop {
343            match self.next_result_set().await? {
344                QueryResultSet::QueryProcessingComplete => return Ok(results),
345                QueryResultSet::RowDescriptionReceived(reader) => {
346                    results.extend(reader.collect_to_vec::<T>().await?);
347                }
348            }
349        }
350    }
351
352    /// Advance to the next result set and collect all rows into a `Vec<T>`.
353    ///
354    /// Returns `BatchQueryUnexpectedEnd` if the query has already completed
355    /// (no more result sets available).
356    pub async fn collect_next_to_vec<T: FromSqlRowOwned>(
357        &mut self,
358    ) -> Result<Vec<T>, ElefantClientError> {
359        match self.next_result_set().await? {
360            QueryResultSet::RowDescriptionReceived(reader) => Ok(reader.collect_to_vec().await?),
361            QueryResultSet::QueryProcessingComplete => {
362                Err(ElefantClientError::BatchQueryUnexpectedEnd)
363            }
364        }
365    }
366
367    pub async fn collect_single_column_to_vec<T>(mut self) -> Result<Vec<T>, ElefantClientError>
368    where
369        T: FromSqlTextOwned,
370    {
371        let mut results = Vec::new();
372        loop {
373            match self.next_result_set().await? {
374                QueryResultSet::QueryProcessingComplete => return Ok(results),
375                QueryResultSet::RowDescriptionReceived(mut row_result_reader) => {
376                    while let Some(row) = row_result_reader.next_row().await? {
377                        results.push(row.get_text(0)?);
378                    }
379                }
380            }
381        }
382    }
383}
384
385pub enum QueryResultSet<'postgres_client, 'query_result_set, F: ConnectionFactory> {
386    QueryProcessingComplete,
387    RowDescriptionReceived(RowResultReader<'postgres_client, 'query_result_set, F>),
388}
389
390pub struct RowResultReader<'postgres_client, 'query_result_set, F: ConnectionFactory> {
391    client: &'postgres_client mut PostgresClient<F>,
392    row_description: RowDescription,
393    enum_registry: Arc<EnumTypeRegistry>,
394    // Ensures that the QueryResult cannot be used while we are processing rows.
395    query_result_res: PhantomData<&'query_result_set QueryResult<'postgres_client, F>>,
396}
397
398impl<'postgres_client, 'query_result_set, F: ConnectionFactory>
399    RowResultReader<'postgres_client, 'query_result_set, F>
400{
401    pub async fn next_row<'row_result_reader>(
402        &'row_result_reader mut self,
403    ) -> Result<Option<PostgresDataRow<'postgres_client, 'row_result_reader>>, ElefantClientError>
404    {
405        let client: &mut PostgresClient<F> = reborrow_until_polonius!(self.client);
406        let msg = client.read_next_backend_message().await?;
407
408        match msg {
409            BackendMessage::DataRow(dr) => Ok(Some(PostgresDataRow {
410                row_description: &self.row_description,
411                data_row: dr,
412                enum_registry: &self.enum_registry,
413            })),
414            BackendMessage::CommandComplete(cc) => {
415                debug!("Command complete: {:?}", cc);
416                Ok(None)
417            }
418            BackendMessage::ReadyForQuery(rfq) => {
419                self.client.ready_for_query = true;
420                self.client.current_transaction_status = rfq.current_transaction_status;
421                Ok(None)
422            }
423            BackendMessage::ErrorResponse(er) => {
424                Err(ElefantClientError::PostgresError(format!("{er:?}")))
425            }
426            _ => Err(ElefantClientError::UnexpectedBackendMessage(format!(
427                "{msg:?}"
428            ))),
429        }
430    }
431
432    pub async fn collect_to_vec<T>(mut self) -> Result<Vec<T>, ElefantClientError>
433    where
434        T: FromSqlRowOwned,
435    {
436        let mut results = Vec::new();
437        while let Some(row) = self.next_row().await? {
438            results.push(T::from_sql_row(&row)?);
439        }
440        Ok(results)
441    }
442}
443
444pub struct PostgresDataRow<'postgres_client, 'row_result_reader> {
445    row_description: &'row_result_reader RowDescription,
446    data_row: protocol::DataRow<'postgres_client>,
447    enum_registry: &'row_result_reader EnumTypeRegistry,
448}
449
450impl<'postgres_client> PostgresDataRow<'postgres_client, '_> {
451    pub fn get_some_bytes(&self) -> &[Option<&[u8]>] {
452        &self.data_row.values
453    }
454
455    pub fn get<T>(&self, index: usize) -> Result<T, ElefantClientError>
456    where
457        T: FromSql<'postgres_client>,
458    {
459        let field = &self.row_description.fields[index];
460
461        if !T::accepts_with_registry(field, self.enum_registry) {
462            return Err(ElefantClientError::UnsupportedFieldType {
463                postgres_field: field.clone(),
464                desired_rust_type: std::any::type_name::<T>(),
465            });
466        }
467
468        if let Some(raw) = self.data_row.values[index] {
469            let value = match field.format {
470                ValueFormat::Text => {
471                    let raw_str = std::str::from_utf8(raw).map_err(|e| {
472                        ElefantClientError::IoError(std::io::Error::new(
473                            std::io::ErrorKind::InvalidData,
474                            e,
475                        ))
476                    })?;
477                    T::from_sql_text(raw_str, field).map_err(|e| {
478                        ElefantClientError::DataTypeParseError {
479                            original_error: e,
480                            column_index: index,
481                        }
482                    })?
483                }
484                ValueFormat::Binary => T::from_sql_binary(raw, field).map_err(|e| {
485                    ElefantClientError::DataTypeParseError {
486                        original_error: e,
487                        column_index: index,
488                    }
489                })?,
490            };
491
492            Ok(value)
493        } else {
494            T::from_null(field)
495        }
496    }
497
498    /// Get a value from binary format data - enforces compile-time constraint that T supports binary parsing
499    pub fn get_binary<T>(&self, index: usize) -> Result<T, ElefantClientError>
500    where
501        T: FromSqlBinary<'postgres_client>,
502    {
503        let field = &self.row_description.fields[index];
504
505        if !T::accepts_with_registry(field, self.enum_registry) {
506            return Err(ElefantClientError::UnsupportedFieldType {
507                postgres_field: field.clone(),
508                desired_rust_type: std::any::type_name::<T>(),
509            });
510        }
511
512        if let Some(raw) = self.data_row.values[index] {
513            let value = T::from_sql_binary(raw, field).map_err(|e| {
514                ElefantClientError::DataTypeParseError {
515                    original_error: e,
516                    column_index: index,
517                }
518            })?;
519            Ok(value)
520        } else {
521            T::from_null(field)
522        }
523    }
524
525    /// Get a value from text format data - enforces compile-time constraint that T supports text parsing
526    pub fn get_text<T>(&self, index: usize) -> Result<T, ElefantClientError>
527    where
528        T: FromSqlText<'postgres_client>,
529    {
530        let field = &self.row_description.fields[index];
531
532        if !T::accepts_with_registry(field, self.enum_registry) {
533            return Err(ElefantClientError::UnsupportedFieldType {
534                postgres_field: field.clone(),
535                desired_rust_type: std::any::type_name::<T>(),
536            });
537        }
538
539        if let Some(raw) = self.data_row.values[index] {
540            let raw_str = std::str::from_utf8(raw).map_err(|e| {
541                ElefantClientError::IoError(std::io::Error::new(std::io::ErrorKind::InvalidData, e))
542            })?;
543            let value = T::from_sql_text(raw_str, field).map_err(|e| {
544                ElefantClientError::DataTypeParseError {
545                    original_error: e,
546                    column_index: index,
547                }
548            })?;
549            Ok(value)
550        } else {
551            T::from_null(field)
552        }
553    }
554
555    pub fn column_count(&self) -> usize {
556        self.row_description.fields.len()
557    }
558
559    pub fn require_columns(&self, count: usize) -> Result<(), ElefantClientError> {
560        if self.column_count() < count {
561            return Err(ElefantClientError::NotEnoughColumns {
562                desired: count,
563                actual: self.column_count(),
564            });
565        }
566        Ok(())
567    }
568}