tokio_postgres_extractor/
lib.rs

1//! High-performance extractors for [tokio_postgres].
2//!
3//! This crate contains traits and proc macros for creating high-performance extractors
4//! for Rust types from [`Row`]s.
5//!
6//! # Examples
7//!
8//! ## Extracting a single row
9//!
10//! ```
11//! # use tokio_postgres::{Client, Error};
12//! # use tokio_postgres_extractor::RowExtractExt;
13//! # use tokio_postgres_extractor::Columns;
14//! # use tokio_postgres_extractor::Extract;
15//! #[derive(Columns, Extract)]
16//! struct User {
17//!     id: i32,
18//!     name: String,
19//! }
20//!
21//! async fn get_user(client: &Client, id: i32) -> Result<User, Error> {
22//!     client
23//!         .query_one("select * from user where id = $1", &[&id])
24//!         .await
25//!         .map(|r| r.extract_once())
26//! }
27//! ```
28//!
29//! ## Extracting a stream of rows
30//!
31//! ```
32//! # use futures_util::TryStreamExt;
33//! # use tokio_postgres::{Client, Error};
34//! # use tokio_postgres_extractor::stream::RowStreamExtractExt;
35//! # use tokio_postgres_extractor::Columns;
36//! # use tokio_postgres_extractor::Extract;
37//! #[derive(Columns, Extract)]
38//! struct User {
39//!     id: i32,
40//!     name: String,
41//! }
42//!
43//! async fn get_users(client: &Client) -> Result<Vec<User>, Error> {
44//!     client
45//!         .query_raw("select * from user", None::<i32>)
46//!         .await?
47//!         .extract()
48//!         .try_collect()
49//!         .await
50//! }
51//! ```
52//!
53//! # Generic types
54//!
55//! Generic types are fully supported.
56//!
57//! ```
58//! # use tokio_postgres_extractor::Columns;
59//! # use tokio_postgres_extractor::Extract;
60//! #[derive(Columns, Extract)]
61//! struct User<'a, T: ?Sized> {
62//!     id: i32,
63//!     name: &'a T,
64//! }
65//!
66//! fn assert_is_extract<'a, T: Extract<'a>>() { }
67//!
68//! assert_is_extract::<User<str>>();
69//! ```
70//!
71//! # Custom column names
72//!
73//! You can specify column names that are different from field names. See the
74//! documentation of the [`Columns`][macro@Columns] proc macro.
75//!
76//! # Design
77//!
78//! A naive mapping function such as
79//!
80//! ```rust,ignore
81//! User {
82//!     id: row.get("id"),
83//!     name: row.get("name"),
84//! }
85//! ```
86//!
87//! has `O(N^2)` runtime where `N` is the number of columns. Each invocation of `row.get`
88//! must walk all columns and compare their name to the name of the requested column. This
89//! crate solves this by
90//!
91//! 1. Constructing an efficient data structure at compile time that reduces lookup time
92//!    to `O(N)`.
93//!
94//!    This data structure is similar to a perfect hash function but more efficient.
95//!
96//! 2. Memorizing the mapping from fields to columns whenever possible.
97
98extern crate self as tokio_postgres_extractor;
99
100/// Proc macro for deriving the [`Columns`] trait.
101///
102/// # Custom column names
103///
104/// If the column name is different from the name of the field, you can use
105///
106/// ```rust,ignore
107/// #[column(name = "Type")]`
108/// ty: i32,
109/// ```
110///
111/// to explicitly specify a name. The name must be a string literal.
112///
113/// # Explicit indices
114///
115/// If you already know the index a field maps to, you can use
116///
117/// ```rust,ignore
118/// #[column(idx = 123)]`
119/// ty: i32,
120/// ```
121///
122/// to specify it.
123///
124/// # Implementation
125///
126/// The critical section in the expansion of
127///
128/// ```
129/// # use tokio_postgres_extractor_macros::Columns;
130/// #[derive(Columns)]
131/// struct Account {
132///     account_id: i32,
133///     account_name: String,
134///     account_role: i64,
135/// }
136/// ```
137///
138/// is
139///
140/// ```rust,ignore
141/// for column in row.columns().iter() {
142///     let name = column.name();
143///     let idx = match name.len() {
144///         10 => match name {
145///             "account_id" => 0,
146///             _ => continue,
147///         },
148///         12 => {
149///             let b = name.as_bytes();
150///             let disc = b[8];
151///             match disc {
152///                 110 => match name {
153///                     "account_name" => 1,
154///                     _ => continue,
155///                 },
156///                 114 => match name {
157///                     "account_role" => 2,
158///                     _ => continue,
159///                 },
160///                 _ => continue,
161///             }
162///         }
163///         _ => continue,
164///     };
165///     // ...
166/// }
167/// ```
168///
169/// meaning that for each column the code
170///
171/// 1. uses a jump table indexed with the length of the column name,
172/// 2. extracts an integer of size 1, 2, 4, or 8 from the column name,
173/// 3. compares this integer to a number of candidates,
174/// 4. compares the column name to the candidate.
175///
176/// This is very fast on current hardware. If the name of the candidate is no more than 16
177/// bytes, then the final comparison will compile to a number of assembly instructions on
178/// x86_64. Otherwise it compiles to a call to `bcmp`. If you compile with `-C
179/// target-cpu=native`, then even these calls to `bcmp` will be inlined.
180///
181/// In practice, this means that the implementation of `Columns` is usually `O(N)` in the
182/// number of columns with a "small" constant. In some degenerate cases the construction
183/// above is not possible and the implementation will have to perform multiple string
184/// comparisons. Even this is still much faster than using the phf crate or similar.
185pub use tokio_postgres_extractor_macros::Columns;
186/// Proc macro for deriving the [`Extract`] trait.
187pub use tokio_postgres_extractor_macros::Extract;
188use {crate::sealed::Sealed, std::ops::Index, tokio_postgres::Row};
189
190pub mod iter;
191pub mod stream;
192
193#[cfg(test)]
194mod tests;
195
196#[doc(hidden)]
197pub mod private {
198    pub use tokio_postgres;
199}
200
201/// A type whose fields map to Postgres columns.
202///
203/// This trait is almost always derived with the [Columns](macro@Columns) proc macro:
204///
205/// ```
206/// # use tokio_postgres_extractor::Columns;
207/// #[derive(Columns)]
208/// struct User<'a> {
209///     id: i32,
210///     name: &'a str,
211/// }
212/// ```
213///
214/// In this case the associated `Columns` type is `[usize; N]` where `N` is the number of
215/// fields.
216///
217/// Assume that a [`Row`] was created from the following query:
218///
219/// ```sql
220/// select 'john' user, 123 balance, 1 id;
221/// ```
222///
223/// Then the implementation of `Columns` for the type `User` above should return `[2, 0]`
224/// because the `id` field maps to the third column and the `name` field maps to the
225/// first column.
226pub trait Columns {
227    /// The type identifying the columns.
228    ///
229    /// This should always be `[usize; N]` where `N` is the number of fields. In a future
230    /// version of this crate, this field will be replaced by
231    ///
232    /// ```
233    /// pub trait Columns {
234    ///     const NUM_COLUMNS: usize;
235    /// }
236    /// ```
237    type Columns: Unpin + Index<usize, Output = usize>;
238
239    /// Returns the mapping from the type's fields to the columns in a [`Row`].
240    fn columns(row: &Row) -> Self::Columns;
241}
242
243/// A type that can be extracted from a [`Row`].
244///
245/// This trait is usually derived:
246///
247/// ```
248/// # use tokio_postgres_extractor_macros::{Columns, Extract};
249/// #[derive(Columns, Extract)]
250/// struct User<'a> {
251///     id: i32,
252///     name: &'a str,
253/// }
254/// ```
255///
256/// Most of the time you will not use this trait directly but one of the utility functions:
257///
258/// - [`IterExtractExt::extract`][iter::IterExtractExt::extract]
259/// - [`IterExtractRefExt::extract_ref`][iter::IterExtractRefExt::extract_ref]
260/// - [`RowStreamExtractExt::extract`][stream::RowStreamExtractExt::extract]
261/// - [`RowStreamExtractExt::extract_mut`][stream::RowStreamExtractExt::extract_mut]
262///
263/// # Examples
264///
265/// ```
266/// # use tokio_postgres::{Client, Error};
267/// # use tokio_postgres_extractor::{Columns, Extract};
268/// #[derive(Columns, Extract)]
269/// struct User {
270///     id: i32,
271///     name: String,
272/// }
273///
274/// async fn get_user(client: &Client, id: u32) -> Result<User, Error> {
275///     client
276///         .query_one("select * from user where id = $1", &[&id])
277///         .await
278///         .map(|r| User::extract_once(&r))
279/// }
280/// ```
281pub trait Extract<'row>: Columns + Sized {
282    /// Extracts an instance of the type from a [`Row`].
283    ///
284    /// If the implementation of [`Columns`] was derived, then this function is almost
285    /// always faster than manually calling [`Row::get`] with the column names. Usually it
286    /// is much faster.
287    ///
288    /// However, if you are extracting more than one row at at time, then you will want to
289    /// instead call
290    ///
291    /// - [`Extract::extract`],
292    /// - [`IterExtractExt::extract`][iter::IterExtractExt::extract],
293    /// - [`IterExtractRefExt::extract_ref`][iter::IterExtractRefExt::extract_ref],
294    /// - [`RowStreamExtractExt::extract`][stream::RowStreamExtractExt::extract], or
295    /// - [`RowStreamExtractExt::extract_mut`][stream::RowStreamExtractExt::extract_mut].
296    ///
297    /// These function memorize the output of [`Columns::columns`] and are often 2x as fast
298    /// per row as this function.
299    ///
300    /// # Panics
301    ///
302    /// Panics if [`Columns::columns`] or [`Row::get`] panics.
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// # use tokio_postgres::Row;
308    /// # use tokio_postgres_extractor::{Columns, Extract};
309    /// #[derive(Columns, Extract)]
310    /// struct User<'a> {
311    ///     id: i32,
312    ///     name: &'a str,
313    /// }
314    ///
315    /// fn map_user(row: &Row) -> User<'_> {
316    ///     User::extract_once(row)
317    /// }
318    /// ```
319    fn extract_once(row: &'row Row) -> Self {
320        Self::extract(&mut None, row)
321    }
322
323    /// Extracts an instance of the type from a [`Row`], memorizing the mapping between
324    /// fields and columns.
325    ///
326    /// If you call this function multiple times with a memorized mapping, then you should
327    /// make sure that the columns in the rows are in the same order. This is always the
328    /// case if the rows were produced by a single SQL statement. Otherwise this function
329    /// might panic or the mapping might be incorrect.
330    ///
331    /// Often you will want to call
332    ///
333    /// - [`IterExtractExt::extract`][iter::IterExtractExt::extract],
334    /// - [`IterExtractRefExt::extract_ref`][iter::IterExtractRefExt::extract_ref],
335    /// - [`RowStreamExtractExt::extract`][stream::RowStreamExtractExt::extract], or
336    /// - [`RowStreamExtractExt::extract_mut`][stream::RowStreamExtractExt::extract_mut].
337    ///
338    /// instead, which hide the memorization behind and abstraction.
339    ///
340    /// # Panics
341    ///
342    /// Panics if [`Columns::columns`] or [`Row::get`] panics.
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// # use futures_util::StreamExt;
348    /// # use tokio_postgres::Row;
349    /// # use tokio_postgres_extractor::{Columns, Extract};
350    /// #[derive(Columns, Extract)]
351    /// struct User<'a> {
352    ///     id: i32,
353    ///     name: &'a str,
354    /// }
355    ///
356    /// fn map_users(rows: &[Row]) -> Vec<User<'_>> {
357    ///     let mut columns = None;
358    ///     rows.iter().map(|row| User::extract(&mut columns, row)).collect()
359    /// }
360    /// ```
361    fn extract(columns: &mut Option<<Self as Columns>::Columns>, row: &'row Row) -> Self {
362        Self::extract_with_columns(
363            columns.get_or_insert_with(|| <Self as Columns>::columns(row)),
364            row,
365        )
366    }
367
368    /// Extracts an instance of the type from a [`Row`] and a mapping between the
369    /// fields and columns.
370    ///
371    /// This function is usually derived with the [`Extract`](macro@Extract) proc macro.
372    /// In this case the implementation looks as follows:
373    ///
374    /// ```
375    /// # use futures_util::StreamExt;
376    /// # use tokio_postgres::Row;
377    /// # use tokio_postgres::types::FromSql;
378    /// # use tokio_postgres_extractor::{Columns, Extract};
379    /// #[derive(Columns)]
380    /// struct User<'a> {
381    ///     id: i32,
382    ///     name: &'a str,
383    /// }
384    ///
385    /// impl<'a, 'row> Extract<'row> for User<'a>
386    /// where
387    ///     i32: FromSql<'row>,
388    ///     &'a str: FromSql<'row>,
389    /// {
390    ///     fn extract_with_columns(columns: &Self::Columns, row: &'row Row) -> Self {
391    ///         Self {
392    ///             id: row.get(columns[0]),
393    ///             name: row.get(columns[1]),
394    ///         }
395    ///     }
396    /// }
397    /// ```
398    ///
399    /// # Panics
400    ///
401    /// Panics if [`Row::get`] panics.
402    fn extract_with_columns(columns: &<Self as Columns>::Columns, row: &'row Row) -> Self;
403}
404
405/// A type that can be extracted from a [`Row`] without borrowing the [`Row`].
406///
407/// This is primarily useful for trait bounds on functions. For example
408///
409/// ```
410/// # use tokio_postgres::Row;
411/// # use tokio_postgres_extractor::ExtractOwned;
412/// # fn get_row() -> Row {
413/// #     unimplemented!()
414/// # }
415/// fn extract<T: ExtractOwned>() -> T {
416///     let row: Row = get_row();
417///     T::extract_once(&row)
418/// }
419/// ```
420pub trait ExtractOwned: for<'a> Extract<'a> {}
421
422impl<T> ExtractOwned for T where T: for<'a> Extract<'a> {}
423
424/// Extension trait for extracting from a [`Row`].
425pub trait RowExtractExt: Sealed {
426    /// Extracts an instance of `T` from this [`Row`].
427    ///
428    /// This is equivalent to [`T::extract_once(self)`][Extract::extract_once].
429    ///
430    /// # Panics
431    ///
432    /// Panics if [`Extract::extract_once`] panics.
433    ///
434    /// # Examples
435    ///
436    /// ```
437    /// # use tokio_postgres::Row;
438    /// # use tokio_postgres_extractor::RowExtractExt;
439    /// # use tokio_postgres_extractor_macros::{Columns, Extract};
440    /// #[derive(Columns, Extract)]
441    /// struct User<'a> {
442    ///     id: i32,
443    ///     name: &'a str,
444    /// }
445    ///
446    /// fn map_user(row: &Row) -> User<'_> {
447    ///     row.extract_once()
448    /// }
449    /// ```
450    fn extract_once<'row, T>(&'row self) -> T
451    where
452        T: Extract<'row>;
453
454    /// Extracts an instance of `T` from this [`Row`], memorizing the mapping between
455    /// fields and columns.
456    ///
457    /// This is equivalent to [`T::extract(columns, self)`][Extract::extract].
458    ///
459    /// # Panics
460    ///
461    /// Panics if [`Extract::extract`] panics.
462    ///
463    /// # Examples
464    ///
465    /// ```
466    /// # use futures_util::StreamExt;
467    /// # use tokio_postgres::Row;
468    /// # use tokio_postgres_extractor::{Columns, Extract, RowExtractExt};
469    /// #[derive(Columns, Extract)]
470    /// struct User<'a> {
471    ///     id: i32,
472    ///     name: &'a str,
473    /// }
474    ///
475    /// fn map_users(rows: &[Row]) -> Vec<User<'_>> {
476    ///     let mut columns = None;
477    ///     rows.iter().map(|row| row.extract(&mut columns)).collect()
478    /// }
479    /// ```
480    fn extract<'row, T>(&'row self, columns: &mut Option<<T as Columns>::Columns>) -> T
481    where
482        T: Extract<'row>;
483
484    /// Extracts an instance of `T` from this [`Row`] and a mapping between the
485    /// fields and columns.
486    ///
487    /// This is equivalent to [`T::extract_with_columns(columns, self)`][Extract::extract_with_columns].
488    ///
489    /// # Panics
490    ///
491    /// Panics if [`Extract::extract_with_columns`] panics.
492    fn extract_with_columns<'row, T>(&'row self, columns: &<T as Columns>::Columns) -> T
493    where
494        T: Extract<'row>;
495}
496
497impl Sealed for Row {}
498
499impl RowExtractExt for Row {
500    fn extract_once<'row, T: Extract<'row>>(&'row self) -> T {
501        T::extract_once(self)
502    }
503
504    fn extract<'row, T>(&'row self, columns: &mut Option<<T as Columns>::Columns>) -> T
505    where
506        T: Extract<'row>,
507    {
508        T::extract(columns, self)
509    }
510    fn extract_with_columns<'row, T>(&'row self, columns: &<T as Columns>::Columns) -> T
511    where
512        T: Extract<'row>,
513    {
514        T::extract_with_columns(columns, self)
515    }
516}
517
518mod sealed {
519    pub trait Sealed {}
520}