Skip to main content

drizzle_postgres/expr/
array_ops.rs

1//! `PostgreSQL` array operators.
2//!
3//! This module provides PostgreSQL-specific array operators:
4//! - `@>` (contains)
5//! - `<@` (contained by)
6//! - `&&` (overlaps)
7//!
8//! # Example
9//!
10//! ```
11//! # use drizzle_postgres::expr::array_contains;
12//! # use drizzle_core::{SQL, ToSQL};
13//! # use drizzle_postgres::values::PostgresValue;
14//! let tags = SQL::<PostgresValue>::raw("tags");
15//! let condition = array_contains(tags, "test");
16//! assert!(condition.to_sql().sql().contains("@>"));
17//! ```
18
19#[cfg(not(feature = "std"))]
20use crate::prelude::*;
21use crate::values::PostgresValue;
22use drizzle_core::ToSQL;
23use drizzle_core::expr::{Expr, NonNull, SQLExpr, Scalar};
24use drizzle_core::sql::{SQL, SQLChunk};
25use drizzle_types::postgres::types::Boolean;
26
27/// Wrapper for passing a `Vec<T>` as a single `PostgreSQL` array parameter.
28///
29/// Without this wrapper, `Vec<T>` implements `ToSQL` by joining elements
30/// with commas (`$1, $2, $3`), which is correct for `IN (...)` clauses
31/// but wrong for array operators like `@>`, `<@`, and `&&` which expect
32/// a single array parameter.
33///
34/// # Example
35///
36/// ```
37/// # use drizzle_postgres::expr::{array_contains, PgArray};
38/// # use drizzle_core::{SQL, ToSQL};
39/// # use drizzle_postgres::values::PostgresValue;
40/// let tags = SQL::<PostgresValue>::raw("tags");
41/// // Correct: passes as a single array parameter
42/// let condition = array_contains(tags, PgArray(vec!["rust", "python"]));
43/// ```
44pub struct PgArray<T>(pub Vec<T>);
45
46impl<'a, T> ToSQL<'a, PostgresValue<'a>> for PgArray<T>
47where
48    T: Into<PostgresValue<'a>> + Clone,
49{
50    fn to_sql(&self) -> SQL<'a, PostgresValue<'a>> {
51        let array: Vec<PostgresValue<'a>> = self.0.iter().map(|v| v.clone().into()).collect();
52        SQL::param(PostgresValue::Array(array))
53    }
54}
55
56/// `PostgreSQL` `@>` operator - array contains.
57///
58/// Returns true if the left array contains all elements of the right array.
59///
60/// # Example
61///
62/// ```
63/// # use drizzle_postgres::expr::array_contains;
64/// # use drizzle_core::{SQL, ToSQL};
65/// # use drizzle_postgres::values::PostgresValue;
66/// let tags = SQL::<PostgresValue>::raw("tags");
67/// let condition = array_contains(tags, "rust");
68/// assert!(condition.to_sql().sql().contains("@>"));
69/// // Generates: tags @> $1
70/// ```
71pub fn array_contains<'a, L, R>(
72    left: L,
73    right: R,
74) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
75where
76    L: Expr<'a, PostgresValue<'a>>,
77    R: ToSQL<'a, PostgresValue<'a>>,
78{
79    SQLExpr::new(
80        left.to_sql()
81            .push(SQLChunk::Raw("@>".into()))
82            .append(right.to_sql()),
83    )
84}
85
86/// `PostgreSQL` `<@` operator - array is contained by.
87///
88/// Returns true if the left array is contained by the right array
89/// (i.e., all elements of left are in right).
90///
91/// # Example
92///
93/// ```
94/// # use drizzle_postgres::expr::array_contained;
95/// # use drizzle_core::{SQL, ToSQL};
96/// # use drizzle_postgres::values::PostgresValue;
97/// let tags = SQL::<PostgresValue>::raw("tags");
98/// let condition = array_contained(tags, "rust");
99/// assert!(condition.to_sql().sql().contains("<@"));
100/// // Generates: tags <@ $1
101/// ```
102pub fn array_contained<'a, L, R>(
103    left: L,
104    right: R,
105) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
106where
107    L: Expr<'a, PostgresValue<'a>>,
108    R: ToSQL<'a, PostgresValue<'a>>,
109{
110    SQLExpr::new(
111        left.to_sql()
112            .push(SQLChunk::Raw("<@".into()))
113            .append(right.to_sql()),
114    )
115}
116
117/// `PostgreSQL` `&&` operator - arrays overlap.
118///
119/// Returns true if the arrays have any elements in common.
120///
121/// # Example
122///
123/// ```
124/// # use drizzle_postgres::expr::array_overlaps;
125/// # use drizzle_core::{SQL, ToSQL};
126/// # use drizzle_postgres::values::PostgresValue;
127/// let tags = SQL::<PostgresValue>::raw("tags");
128/// let condition = array_overlaps(tags, "rust");
129/// assert!(condition.to_sql().sql().contains("&&"));
130/// // Generates: tags && $1
131/// ```
132pub fn array_overlaps<'a, L, R>(
133    left: L,
134    right: R,
135) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
136where
137    L: Expr<'a, PostgresValue<'a>>,
138    R: ToSQL<'a, PostgresValue<'a>>,
139{
140    SQLExpr::new(
141        left.to_sql()
142            .push(SQLChunk::Raw("&&".into()))
143            .append(right.to_sql()),
144    )
145}
146
147/// Extension trait providing method-based array operators for `PostgreSQL` expressions.
148///
149/// This trait provides `.array_contains()`, `.array_contained()`, and `.array_overlaps()`
150/// methods on any expression type.
151///
152/// # Example
153///
154/// ```
155/// # use drizzle_postgres::expr::ArrayExprExt;
156/// # use drizzle_core::{SQL, ToSQL};
157/// # use drizzle_postgres::values::PostgresValue;
158/// let tags = SQL::<PostgresValue>::raw("tags");
159/// let condition = tags.array_contains("rust");
160/// assert!(condition.to_sql().sql().contains("@>"));
161/// ```
162pub trait ArrayExprExt<'a>: Expr<'a, PostgresValue<'a>> + Sized {
163    /// `PostgreSQL` `@>` operator - array contains.
164    ///
165    /// Returns true if self contains all elements of the other array.
166    fn array_contains<R>(self, other: R) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
167    where
168        R: ToSQL<'a, PostgresValue<'a>>,
169    {
170        array_contains(self, other)
171    }
172
173    /// `PostgreSQL` `<@` operator - array is contained by.
174    ///
175    /// Returns true if self is contained by the other array.
176    fn array_contained<R>(
177        self,
178        other: R,
179    ) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
180    where
181        R: ToSQL<'a, PostgresValue<'a>>,
182    {
183        array_contained(self, other)
184    }
185
186    /// `PostgreSQL` `&&` operator - arrays overlap.
187    ///
188    /// Returns true if self and the other array have any elements in common.
189    fn array_overlaps<R>(self, other: R) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, Scalar>
190    where
191        R: ToSQL<'a, PostgresValue<'a>>,
192    {
193        array_overlaps(self, other)
194    }
195}
196
197/// Blanket implementation for all `PostgreSQL` `Expr` types.
198impl<'a, E: Expr<'a, PostgresValue<'a>>> ArrayExprExt<'a> for E {}