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}