pgx/iter.rs
1use std::iter::once;
2
3use crate::IntoHeapTuple;
4use pgx_sql_entity_graph::metadata::{
5 ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable,
6};
7
8/// Support for returning a `SETOF T` from an SQL function.
9///
10/// [`SetOfIterator`] is typically used as a return type on `#[pg_extern]`-style functions
11/// and indicates that the SQL function should return a `SETOF` over the generic argument `T`.
12///
13/// It is a lightweight wrapper around an iterator, which you provide during construction. The
14/// iterator *can* borrow from its environment, following Rust's normal borrowing rules. If no
15/// borrowing is necessary, the `'static` lifetime should be used.
16///
17/// # Examples
18///
19/// This example simply returns a set of integers in the range `1..=5`.
20///
21/// ```rust,no_run
22/// use pgx::prelude::*;
23/// #[pg_extern]
24/// fn return_ints() -> SetOfIterator<'static, i32> {
25/// SetOfIterator::new(1..=5)
26/// }
27/// ```
28///
29/// Here we return a set of `&str`s, borrowed from an argument:
30///
31/// ```rust,no_run
32/// use pgx::prelude::*;
33/// #[pg_extern]
34/// fn split_string<'a>(input: &'a str) -> SetOfIterator<'a, &'a str> {
35/// SetOfIterator::new(input.split_whitespace())
36/// }
37/// ```
38pub struct SetOfIterator<'a, T> {
39 iter: Box<dyn Iterator<Item = T> + 'a>,
40}
41
42impl<'a, T> SetOfIterator<'a, T> {
43 pub fn new<I>(iter: I) -> Self
44 where
45 I: IntoIterator<Item = T> + 'a,
46 {
47 Self { iter: Box::new(iter.into_iter()) }
48 }
49}
50
51impl<'a, T> Iterator for SetOfIterator<'a, T> {
52 type Item = T;
53
54 #[inline]
55 fn next(&mut self) -> Option<Self::Item> {
56 self.iter.next()
57 }
58}
59
60unsafe impl<'a, T> SqlTranslatable for SetOfIterator<'a, T>
61where
62 T: SqlTranslatable,
63{
64 fn argument_sql() -> Result<SqlMapping, ArgumentError> {
65 T::argument_sql()
66 }
67 fn return_sql() -> Result<Returns, ReturnsError> {
68 match T::return_sql() {
69 Ok(Returns::One(sql)) => Ok(Returns::SetOf(sql)),
70 Ok(Returns::SetOf(_)) => Err(ReturnsError::NestedSetOf),
71 Ok(Returns::Table(_)) => Err(ReturnsError::SetOfContainingTable),
72 err @ Err(_) => err,
73 }
74 }
75}
76
77/// Support for a `TABLE (...)` from an SQL function.
78///
79/// [`TableIterator`] is typically used as the return type of a `#[pg_extern]`-style function,
80/// indicating that the function returns a table of named columns. [`TableIterator`] is
81/// generic over `T`, but that `T` must be a Rust tuple containing one or more elements. They
82/// must also be "named" using pgx' [`name!`] macro. See the examples below.
83///
84/// It is a lightweight wrapper around an iterator, which you provide during construction. The
85/// iterator *can* borrow from its environment, following Rust's normal borrowing rules. If no
86/// borrowing is necessary, the `'static` lifetime should be used.
87///
88/// # Examples
89///
90/// This example returns a table of employee information.
91///
92/// ```rust,no_run
93/// use pgx::prelude::*;
94/// #[pg_extern]
95/// fn employees() -> TableIterator<'static,
96/// (
97/// name!(id, i64),
98/// name!(dept_code, String),
99/// name!(full_text, &'static str)
100/// )
101/// > {
102/// TableIterator::new(vec![
103/// (42, "ARQ".into(), "John Hammond"),
104/// (87, "EGA".into(), "Mary Kinson"),
105/// (3, "BLA".into(), "Perry Johnson"),
106/// ])
107/// }
108/// ```
109///
110/// And here we return a simple numbered list of words, borrowed from the input `&str`.
111///
112/// ```rust,no_run
113/// use pgx::prelude::*;
114/// #[pg_extern]
115/// fn split_string<'a>(input: &'a str) -> TableIterator<'a, ( name!(num, i32), name!(word, &'a str) )> {
116/// TableIterator::new(input.split_whitespace().enumerate().map(|(n, w)| (n as i32, w)))
117/// }
118/// ```
119pub struct TableIterator<'a, T> {
120 iter: Box<dyn Iterator<Item = T> + 'a>,
121}
122
123impl<'a, T> TableIterator<'a, T>
124where
125 T: IntoHeapTuple + 'a,
126{
127 pub fn new<I>(iter: I) -> Self
128 where
129 I: IntoIterator<Item = T> + 'a,
130 {
131 Self { iter: Box::new(iter.into_iter()) }
132 }
133
134 pub fn once(value: T) -> Self {
135 Self::new(once(value))
136 }
137}
138
139impl<'a, T> Iterator for TableIterator<'a, T> {
140 type Item = T;
141
142 #[inline]
143 fn next(&mut self) -> Option<Self::Item> {
144 self.iter.next()
145 }
146}
147
148seq_macro::seq!(I in 0..=32 {
149 #(
150 seq_macro::seq!(N in 0..=I {
151 unsafe impl<'a, #(Input~N,)*> SqlTranslatable for TableIterator<'a, (#(Input~N,)*)>
152 where
153 #(
154 Input~N: SqlTranslatable + 'a,
155 )*
156 {
157 fn argument_sql() -> Result<SqlMapping, ArgumentError> {
158 Err(ArgumentError::Table)
159 }
160 fn return_sql() -> Result<Returns, ReturnsError> {
161 let mut vec = Vec::new();
162 #(
163 vec.push(match Input~N::return_sql() {
164 Ok(Returns::One(sql)) => sql,
165 Ok(Returns::SetOf(_)) => return Err(ReturnsError::TableContainingSetOf),
166 Ok(Returns::Table(_)) => return Err(ReturnsError::NestedTable),
167 Err(err) => return Err(err),
168 });
169 )*
170 Ok(Returns::Table(vec))
171 }
172 }
173 });
174 )*
175});