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