diesel/pg/expression/
array.rs

1use crate::dsl;
2use crate::expression::array_comparison::{AsInExpression, InExpression};
3use crate::expression::subselect::Subselect;
4use crate::expression::{
5    AppearsOnTable, AsExpression, Expression, SelectableExpression, TypedExpressionType,
6    ValidGrouping,
7};
8use crate::pg::Pg;
9use crate::query_builder::combination_clause::CombinationClause;
10use crate::query_builder::{
11    AstPass, BoxedSelectStatement, QueryFragment, QueryId, SelectQuery, SelectStatement,
12};
13use crate::sql_types::{self, SqlType};
14use std::marker::PhantomData;
15
16/// Creates an `ARRAY[e1, e2, ...]` or `ARRAY(subselect)` expression.
17///
18/// The argument should be a tuple of expressions which can be represented by the
19/// same SQL type, or a subquery.
20///
21/// # Examples
22///
23/// ```rust
24/// # include!("../../doctest_setup.rs");
25/// #
26/// # fn main() {
27/// #     run_test().unwrap();
28/// # }
29/// #
30/// # fn run_test() -> QueryResult<()> {
31/// #     use schema::users::dsl::*;
32/// #     use diesel::dsl::array;
33/// #     use diesel::sql_types::Integer;
34/// #     let connection = &mut establish_connection();
35/// let ints = diesel::select(array::<Integer, _>((1, 2)))
36///     .get_result::<Vec<i32>>(connection)?;
37/// assert_eq!(vec![1, 2], ints);
38///
39/// let ids = users.select(array((id, id * 2)))
40///     .get_results::<Vec<i32>>(connection)?;
41/// let expected = vec![
42///     vec![1, 2],
43///     vec![2, 4],
44/// ];
45/// assert_eq!(expected, ids);
46///
47/// let ids = diesel::select(array(users.select(id)))
48///     .first::<Vec<i32>>(connection)?;
49/// assert_eq!(vec![1, 2], ids);
50/// #     Ok(())
51/// # }
52/// ```
53pub fn array<ST, T>(elements: T) -> dsl::array<ST, T>
54where
55    T: IntoArrayExpression<ST>,
56    ST: SqlType + TypedExpressionType,
57{
58    elements.into_array_expression()
59}
60
61/// Return type of [`array(tuple_or_subselect)`](super::dsl::array())
62#[allow(non_camel_case_types)]
63#[cfg(feature = "postgres_backend")]
64pub type array<ST, T> = <T as IntoArrayExpression<ST>>::ArrayExpression;
65
66/// Trait for types which can be converted into an expression of type `Array`
67///
68/// This includes tuples of expressions with the same SQL type, and subselects with a single column.
69#[diagnostic::on_unimplemented(
70    message = "Cannot convert `{Self}` into an expression of type `Array<{ST}>`",
71    note = "`the trait bound `{Self}: IntoArrayExpression<{ST}>` is not satisfied. \
72        (`AsExpressionList` is a deprecated trait alias for `IntoArrayExpression`)"
73)]
74pub trait IntoArrayExpression<ST: SqlType + TypedExpressionType> {
75    /// Type of the expression returned by [IntoArrayExpression::into_array_expression]
76    type ArrayExpression: Expression<SqlType = sql_types::Array<ST>>;
77
78    /// Construct the diesel query dsl representation of
79    /// the `ARRAY (values)` clause for the given type
80    fn into_array_expression(self) -> Self::ArrayExpression;
81}
82
83/// Implement as a no-op for expressions that were already arrays (that is, don't wrap in ARRAY[]).
84impl<ST, T> IntoArrayExpression<ST> for T
85where
86    T: AsExpression<sql_types::Array<ST>>,
87    ST: SqlType + TypedExpressionType + 'static,
88{
89    type ArrayExpression = <T as AsExpression<sql_types::Array<ST>>>::Expression;
90
91    fn into_array_expression(self) -> Self::ArrayExpression {
92        <T as AsExpression<sql_types::Array<ST>>>::as_expression(self)
93    }
94}
95
96// This has to be implemented for each tuple directly because an intermediate trait would cause
97// conflicting impls. (Compiler says people could implement AsExpression<CustomSqlType> for
98// SelectStatement<CustomType, ...>)
99// This is not implemented with other tuple impls because this is feature-flagged by
100// `postgres-backend`
101macro_rules! tuple_impls {
102    ($(
103        $Tuple:tt {
104            $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+
105        }
106    )+) => {
107        $(
108            impl<$($T,)+ ST> IntoArrayExpression<ST> for ($($T,)+) where
109                $($T: AsExpression<ST>,)+
110                ST: SqlType + TypedExpressionType,
111            {
112                type ArrayExpression = ArrayLiteral<($($T::Expression,)+), ST>;
113
114                fn into_array_expression(self) -> Self::ArrayExpression {
115                    ArrayLiteral {
116                        elements: ($(self.$idx.as_expression(),)+),
117                        _marker: PhantomData,
118                    }
119                }
120            }
121        )+
122    }
123}
124
125diesel_derives::__diesel_for_each_tuple!(tuple_impls);
126
127/// An ARRAY[...] literal.
128#[derive(Debug, Clone, Copy, QueryId)]
129pub struct ArrayLiteral<T, ST> {
130    elements: T,
131    _marker: PhantomData<ST>,
132}
133
134impl<T, ST> Expression for ArrayLiteral<T, ST>
135where
136    ST: 'static,
137    T: Expression,
138{
139    type SqlType = sql_types::Array<ST>;
140}
141
142impl<T, ST> QueryFragment<Pg> for ArrayLiteral<T, ST>
143where
144    T: QueryFragment<Pg>,
145{
146    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> crate::result::QueryResult<()> {
147        out.push_sql("ARRAY[");
148        QueryFragment::walk_ast(&self.elements, out.reborrow())?;
149        out.push_sql("]");
150        Ok(())
151    }
152}
153
154impl<T, ST, QS> SelectableExpression<QS> for ArrayLiteral<T, ST>
155where
156    T: SelectableExpression<QS>,
157    ArrayLiteral<T, ST>: AppearsOnTable<QS>,
158{
159}
160
161impl<T, ST, QS> AppearsOnTable<QS> for ArrayLiteral<T, ST>
162where
163    T: AppearsOnTable<QS>,
164    ArrayLiteral<T, ST>: Expression,
165{
166}
167
168impl<T, ST, GB> ValidGrouping<GB> for ArrayLiteral<T, ST>
169where
170    T: ValidGrouping<GB>,
171{
172    type IsAggregate = T::IsAggregate;
173}
174
175impl<T, ST> InExpression for ArrayLiteral<T, ST>
176where
177    Self: Expression<SqlType = sql_types::Array<ST>>,
178    ST: SqlType,
179{
180    type SqlType = ST;
181
182    fn is_empty(&self) -> bool {
183        false
184    }
185
186    fn is_array(&self) -> bool {
187        // we want to use the `= ANY(_)` syntax
188        false
189    }
190}
191
192impl<T, ST> AsInExpression<ST> for ArrayLiteral<T, ST>
193where
194    Self: Expression<SqlType = sql_types::Array<ST>>,
195    ST: SqlType,
196{
197    type InExpression = Self;
198
199    fn as_in_expression(self) -> Self::InExpression {
200        self
201    }
202}
203
204impl<ST, F, S, D, W, O, LOf, G, H, LC> IntoArrayExpression<ST>
205    for SelectStatement<F, S, D, W, O, LOf, G, H, LC>
206where
207    ST: SqlType + TypedExpressionType,
208    ArraySubselect<Self, ST>: Expression<SqlType = sql_types::Array<ST>>,
209    Self: SelectQuery<SqlType = ST>,
210{
211    type ArrayExpression = ArraySubselect<Self, ST>;
212
213    fn into_array_expression(self) -> Self::ArrayExpression {
214        ArraySubselect::new(self)
215    }
216}
217
218impl<'a, ST, QS, DB, GB> IntoArrayExpression<ST> for BoxedSelectStatement<'a, ST, QS, DB, GB>
219where
220    ST: SqlType + TypedExpressionType,
221    ArraySubselect<BoxedSelectStatement<'a, ST, QS, DB, GB>, ST>:
222        Expression<SqlType = sql_types::Array<ST>>,
223{
224    type ArrayExpression = ArraySubselect<Self, ST>;
225
226    fn into_array_expression(self) -> Self::ArrayExpression {
227        ArraySubselect::new(self)
228    }
229}
230
231impl<ST, Combinator, Rule, Source, Rhs> IntoArrayExpression<ST>
232    for CombinationClause<Combinator, Rule, Source, Rhs>
233where
234    ST: SqlType + TypedExpressionType,
235    Self: SelectQuery<SqlType = ST>,
236    ArraySubselect<Self, ST>: Expression<SqlType = sql_types::Array<ST>>,
237{
238    type ArrayExpression = ArraySubselect<Self, ST>;
239
240    fn into_array_expression(self) -> Self::ArrayExpression {
241        ArraySubselect::new(self)
242    }
243}
244
245/// An ARRAY(...) subselect.
246#[derive(Debug, Clone, Copy, QueryId)]
247pub struct ArraySubselect<T, ST> {
248    subquery: Subselect<T, ST>,
249}
250
251impl<T, ST> ArraySubselect<T, ST> {
252    pub(crate) fn new(elements: T) -> Self {
253        Self {
254            subquery: Subselect::new(elements),
255        }
256    }
257}
258
259impl<T, ST> Expression for ArraySubselect<T, ST>
260where
261    ST: 'static,
262    Subselect<T, ST>: Expression<SqlType = ST>,
263{
264    type SqlType = sql_types::Array<ST>;
265}
266
267impl<T, ST> QueryFragment<Pg> for ArraySubselect<T, ST>
268where
269    Subselect<T, ST>: QueryFragment<Pg>,
270{
271    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> crate::result::QueryResult<()> {
272        out.push_sql("ARRAY(");
273        QueryFragment::walk_ast(&self.subquery, out.reborrow())?;
274        out.push_sql(")");
275        Ok(())
276    }
277}
278
279impl<T, ST, QS> SelectableExpression<QS> for ArraySubselect<T, ST>
280where
281    Subselect<T, ST>: SelectableExpression<QS>,
282    ArraySubselect<T, ST>: AppearsOnTable<QS>,
283{
284}
285
286impl<T, ST, QS> AppearsOnTable<QS> for ArraySubselect<T, ST>
287where
288    Subselect<T, ST>: AppearsOnTable<QS>,
289    ArraySubselect<T, ST>: Expression,
290{
291}
292
293impl<T, ST, GB> ValidGrouping<GB> for ArraySubselect<T, ST>
294where
295    Subselect<T, ST>: ValidGrouping<GB>,
296{
297    type IsAggregate = <Subselect<T, ST> as ValidGrouping<GB>>::IsAggregate;
298}
299
300impl<T, ST> InExpression for ArraySubselect<T, ST>
301where
302    Self: Expression<SqlType = sql_types::Array<ST>>,
303    ST: SqlType,
304{
305    type SqlType = ST;
306
307    fn is_empty(&self) -> bool {
308        false
309    }
310
311    fn is_array(&self) -> bool {
312        // we want to use the `= ANY(_)` syntax
313        false
314    }
315}
316
317impl<T, ST> AsInExpression<ST> for ArraySubselect<T, ST>
318where
319    Self: Expression<SqlType = sql_types::Array<ST>>,
320    ST: SqlType,
321{
322    type InExpression = Self;
323
324    fn as_in_expression(self) -> Self::InExpression {
325        self
326    }
327}