drizzle_postgres/expr/json_ops.rs
1//! `PostgreSQL` JSON and JSONB operators. Documented in [`crate::expr`].
2
3#[cfg(not(feature = "std"))]
4use crate::prelude::*;
5use crate::values::PostgresValue;
6use drizzle_core::expr::{AggregateKind, Expr, NonNull, Null, SQLExpr};
7use drizzle_core::scope::Arg;
8use drizzle_core::sql::{SQL, SQLChunk, Token};
9
10/// Wraps a containment operand in `CAST(operand AS JSONB)`.
11///
12/// A bound `serde_json::Value` is declared as `json` by the drivers and a text
13/// literal as `text`; neither resolves `jsonb @> ...` without the cast.
14fn jsonb_operand<'a>(operand: SQL<'a, PostgresValue<'a>>) -> SQL<'a, PostgresValue<'a>> {
15 SQL::func("CAST", operand.push(Token::AS).append(SQL::raw("JSONB")))
16}
17
18/// Binds `value` as `CAST($n AS type_name)`.
19///
20/// PostgreSQL infers untyped parameters at prepare time and picks the `text`
21/// overload of `->` / `->>` / `#>`; without the cast the driver binds an
22/// integer or array where the server expects text and the query fails.
23fn typed_param<'a>(
24 value: PostgresValue<'a>,
25 type_name: &'static str,
26) -> SQL<'a, PostgresValue<'a>> {
27 SQL::func(
28 "CAST",
29 SQL::param(value)
30 .push(Token::AS)
31 .append(SQL::raw(type_name)),
32 )
33}
34
35use drizzle_types::postgres::types::{Any, Boolean, Json, Jsonb, Text, Varchar};
36
37/// SQL types the JSON access operators (`->`, `->>`, `#>`, `#>>`) accept.
38///
39/// Implemented for `json`, `jsonb`, and untyped SQL ([`Any`], treated as
40/// `json`). Text columns are rejected even when they hold JSON; cast them first.
41///
42/// # Type safety
43///
44/// ```compile_fail
45/// use drizzle_core::expr::raw_non_null;
46/// use drizzle_postgres::expr::json_get_text;
47/// use drizzle_postgres::values::PostgresValue;
48/// use drizzle_types::postgres::types::Text;
49///
50/// let raw = raw_non_null::<PostgresValue, Text>("raw");
51/// let _ = json_get_text(raw, "name"); // `text` is not a JSON type
52/// ```
53#[diagnostic::on_unimplemented(
54 message = "`{Self}` is not a PostgreSQL JSON type",
55 label = "JSON operators need a `json` or `jsonb` operand"
56)]
57pub trait JsonType {
58 /// The SQL type `->` and `#>` return: `json` for `json`, `jsonb` for `jsonb`,
59 /// and `json` for untyped SQL.
60 type Field: drizzle_types::DataType;
61}
62
63impl JsonType for Json {
64 type Field = Json;
65}
66impl JsonType for Jsonb {
67 type Field = Jsonb;
68}
69impl JsonType for Any {
70 type Field = Json;
71}
72
73/// SQL types the JSONB-only operators (`@>`, `<@`, `?`, `?|`, `?&`) accept
74/// as their left operand.
75///
76/// Implemented for `jsonb` and untyped SQL ([`Any`]). PostgreSQL has none of
77/// these operators for `json`, so a `json` column is rejected.
78///
79/// # Type safety
80///
81/// ```compile_fail
82/// use drizzle_core::expr::raw_non_null;
83/// use drizzle_postgres::expr::jsonb_exists_key;
84/// use drizzle_postgres::values::PostgresValue;
85/// use drizzle_types::postgres::types::Json;
86///
87/// let data = raw_non_null::<PostgresValue, Json>("data");
88/// let _ = jsonb_exists_key(data, "name"); // `json`, not `jsonb`
89/// ```
90#[diagnostic::on_unimplemented(
91 message = "`{Self}` is not `jsonb`",
92 label = "this operator exists only for `jsonb`",
93 note = "PostgreSQL has no `@>`, `<@`, `?`, `?|` or `?&` for `json`; declare the column as `jsonb`"
94)]
95pub trait JsonbType {}
96
97impl JsonbType for Jsonb {}
98impl JsonbType for Any {}
99
100/// SQL types accepted as the right operand of [`jsonb_contains`] (`@>`) and
101/// [`jsonb_contained`] (`<@`).
102///
103/// The operand is rendered as `CAST(operand AS JSONB)`, so it may be `json`,
104/// `jsonb`, JSON text (`text`, `varchar`, such as a `&str` literal), or
105/// untyped SQL ([`Any`]). Other types, such as integers, are rejected.
106///
107/// # Type safety
108///
109/// ```compile_fail
110/// use drizzle_core::expr::raw_non_null;
111/// use drizzle_postgres::expr::jsonb_contains;
112/// use drizzle_postgres::values::PostgresValue;
113/// use drizzle_types::postgres::types::Jsonb;
114///
115/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
116/// let _ = jsonb_contains(data, 42_i32); // an integer is not JSON
117/// ```
118#[diagnostic::on_unimplemented(
119 message = "`{Self}` cannot be cast to `jsonb` for a containment check",
120 label = "expected a JSON value, JSON text, or untyped SQL"
121)]
122pub trait JsonbOperand {}
123
124impl JsonbOperand for Json {}
125impl JsonbOperand for Jsonb {}
126impl JsonbOperand for Text {}
127impl JsonbOperand for Varchar {}
128impl JsonbOperand for Any {}
129
130/// Gets an object field by key (`->`), keeping the JSON type.
131///
132/// The operand must be `json` or `jsonb` ([`JsonType`]); the result has the
133/// same type, so calls can be chained. The result is NULL when the key is
134/// missing.
135///
136/// # Examples
137///
138/// ```
139/// use drizzle_postgres::expr::json_get;
140/// use drizzle_core::{ToSQL, expr::raw_non_null};
141/// use drizzle_postgres::values::PostgresValue;
142/// use drizzle_types::postgres::types::Jsonb;
143///
144/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
145/// let address = json_get(data, "address"); // a `jsonb` expression
146/// assert_eq!(address.to_sql().sql(), "data -> CAST ($1 AS TEXT)");
147/// ```
148pub fn json_get<'a, E>(
149 expr: E,
150 key: &'a str,
151) -> SQLExpr<'a, PostgresValue<'a>, <E::SQLType as JsonType>::Field, Null, E::Aggregate, E::Sources>
152where
153 E: Expr<'a, PostgresValue<'a>>,
154 E::SQLType: JsonType,
155{
156 SQLExpr::new(
157 expr.to_sql()
158 .push(SQLChunk::Raw("->".into()))
159 .append(typed_param(PostgresValue::Text(key.into()), "TEXT")),
160 )
161}
162
163/// Gets an array element by zero-based index (`->`), keeping the JSON type.
164///
165/// Negative indexes count from the end. The operand must be `json` or `jsonb`
166/// ([`JsonType`]). The result is NULL when the index is out of range.
167///
168/// # Examples
169///
170/// ```
171/// use drizzle_postgres::expr::json_get_idx;
172/// use drizzle_core::{ToSQL, expr::raw_non_null};
173/// use drizzle_postgres::values::PostgresValue;
174/// use drizzle_types::postgres::types::Jsonb;
175///
176/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
177/// let first = json_get_idx(data, 0);
178/// assert_eq!(first.to_sql().sql(), "data -> CAST ($1 AS INTEGER)");
179/// ```
180pub fn json_get_idx<'a, E>(
181 expr: E,
182 index: i32,
183) -> SQLExpr<'a, PostgresValue<'a>, <E::SQLType as JsonType>::Field, Null, E::Aggregate, E::Sources>
184where
185 E: Expr<'a, PostgresValue<'a>>,
186 E::SQLType: JsonType,
187{
188 SQLExpr::new(
189 expr.to_sql()
190 .push(SQLChunk::Raw("->".into()))
191 .append(typed_param(PostgresValue::Integer(index), "INTEGER")),
192 )
193}
194
195/// Gets an object field by key as `text` (`->>`).
196///
197/// The operand must be `json` or `jsonb` ([`JsonType`]). The result is NULL
198/// when the key is missing or the value is JSON `null`.
199///
200/// # Examples
201///
202/// ```
203/// use drizzle_postgres::expr::json_get_text;
204/// use drizzle_core::{ToSQL, expr::raw_non_null};
205/// use drizzle_postgres::values::PostgresValue;
206/// use drizzle_types::postgres::types::Jsonb;
207///
208/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
209/// let name = json_get_text(data, "name"); // a `text` expression
210/// assert_eq!(name.to_sql().sql(), "data ->> CAST ($1 AS TEXT)");
211/// ```
212pub fn json_get_text<'a, E>(
213 expr: E,
214 key: &'a str,
215) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, E::Aggregate, E::Sources>
216where
217 E: Expr<'a, PostgresValue<'a>>,
218 E::SQLType: JsonType,
219{
220 SQLExpr::new(
221 expr.to_sql()
222 .push(SQLChunk::Raw("->>".into()))
223 .append(typed_param(PostgresValue::Text(key.into()), "TEXT")),
224 )
225}
226
227/// Gets an array element by zero-based index as `text` (`->>`).
228///
229/// The operand must be `json` or `jsonb` ([`JsonType`]). The result is NULL
230/// when the index is out of range.
231///
232/// # Examples
233///
234/// ```
235/// use drizzle_postgres::expr::json_get_text_idx;
236/// use drizzle_core::{ToSQL, expr::raw_non_null};
237/// use drizzle_postgres::values::PostgresValue;
238/// use drizzle_types::postgres::types::Jsonb;
239///
240/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
241/// let first = json_get_text_idx(data, 0);
242/// assert_eq!(first.to_sql().sql(), "data ->> CAST ($1 AS INTEGER)");
243/// ```
244pub fn json_get_text_idx<'a, E>(
245 expr: E,
246 index: i32,
247) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, E::Aggregate, E::Sources>
248where
249 E: Expr<'a, PostgresValue<'a>>,
250 E::SQLType: JsonType,
251{
252 SQLExpr::new(
253 expr.to_sql()
254 .push(SQLChunk::Raw("->>".into()))
255 .append(typed_param(PostgresValue::Integer(index), "INTEGER")),
256 )
257}
258
259/// Gets the value at a path (`#>`), keeping the JSON type.
260///
261/// `path` is a `PostgreSQL` text-array literal such as `"{address,city}"`;
262/// array indexes go in the path as numbers (`"{tags,0}"`). The operand must be
263/// `json` or `jsonb` ([`JsonType`]). The result is NULL when the path does not exist.
264///
265/// # Examples
266///
267/// ```
268/// use drizzle_postgres::expr::json_get_path;
269/// use drizzle_core::{ToSQL, expr::raw_non_null};
270/// use drizzle_postgres::values::PostgresValue;
271/// use drizzle_types::postgres::types::Jsonb;
272///
273/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
274/// let city = json_get_path(data, "{address,city}");
275/// assert_eq!(city.to_sql().sql(), "data #> CAST ($1 AS TEXT[])");
276/// ```
277pub fn json_get_path<'a, E>(
278 expr: E,
279 path: &'a str,
280) -> SQLExpr<'a, PostgresValue<'a>, <E::SQLType as JsonType>::Field, Null, E::Aggregate, E::Sources>
281where
282 E: Expr<'a, PostgresValue<'a>>,
283 E::SQLType: JsonType,
284{
285 SQLExpr::new(
286 expr.to_sql()
287 .push(SQLChunk::Raw("#>".into()))
288 .append(typed_param(PostgresValue::Text(path.into()), "TEXT[]")),
289 )
290}
291
292/// Gets the value at a path as `text` (`#>>`).
293///
294/// `path` is a `PostgreSQL` text-array literal such as `"{address,city}"`.
295/// The operand must be `json` or `jsonb` ([`JsonType`]). The result is NULL
296/// when the path does not exist.
297///
298/// # Examples
299///
300/// ```
301/// use drizzle_postgres::expr::json_get_path_text;
302/// use drizzle_core::{ToSQL, expr::raw_non_null};
303/// use drizzle_postgres::values::PostgresValue;
304/// use drizzle_types::postgres::types::Jsonb;
305///
306/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
307/// let city = json_get_path_text(data, "{address,city}");
308/// assert_eq!(city.to_sql().sql(), "data #>> CAST ($1 AS TEXT[])");
309/// ```
310pub fn json_get_path_text<'a, E>(
311 expr: E,
312 path: &'a str,
313) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, E::Aggregate, E::Sources>
314where
315 E: Expr<'a, PostgresValue<'a>>,
316 E::SQLType: JsonType,
317{
318 SQLExpr::new(
319 expr.to_sql()
320 .push(SQLChunk::Raw("#>>".into()))
321 .append(typed_param(PostgresValue::Text(path.into()), "TEXT[]")),
322 )
323}
324
325/// Tests whether the left `jsonb` value contains the right one (`@>`).
326///
327/// The left operand must be `jsonb` ([`JsonbType`]). The right operand may be
328/// `json`, `jsonb`, or JSON text ([`JsonbOperand`]); it is cast to `jsonb`.
329/// The result is NULL when either operand is NULL.
330///
331/// # Examples
332///
333/// ```
334/// use drizzle_postgres::expr::jsonb_contains;
335/// use drizzle_core::{ToSQL, expr::raw_non_null};
336/// use drizzle_postgres::values::PostgresValue;
337/// use drizzle_types::postgres::types::Jsonb;
338///
339/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
340/// let cond = jsonb_contains(data, r#"{"role": "admin"}"#);
341/// assert_eq!(cond.to_sql().sql(), "data @> CAST ($1 AS JSONB)");
342/// ```
343#[allow(clippy::type_complexity)]
344pub fn jsonb_contains<'a, L, R>(
345 left: L,
346 right: R,
347) -> SQLExpr<
348 'a,
349 PostgresValue<'a>,
350 Boolean,
351 NonNull,
352 <L::Aggregate as AggregateKind>::Or<R::Aggregate>,
353 (Arg<L::Nullable, L::Sources>, Arg<R::Nullable, R::Sources>),
354>
355where
356 L: Expr<'a, PostgresValue<'a>>,
357 L::SQLType: JsonbType,
358 R: Expr<'a, PostgresValue<'a>>,
359 R::SQLType: JsonbOperand,
360{
361 SQLExpr::new(
362 left.to_sql()
363 .push(SQLChunk::Raw("@>".into()))
364 .append(jsonb_operand(right.to_sql())),
365 )
366}
367
368/// Tests whether the left `jsonb` value is contained in the right one (`<@`).
369///
370/// The left operand must be `jsonb` ([`JsonbType`]). The right operand may be
371/// `json`, `jsonb`, or JSON text ([`JsonbOperand`]); it is cast to `jsonb`.
372/// The result is NULL when either operand is NULL.
373///
374/// # Examples
375///
376/// ```
377/// use drizzle_postgres::expr::jsonb_contained;
378/// use drizzle_core::{ToSQL, expr::raw_non_null};
379/// use drizzle_postgres::values::PostgresValue;
380/// use drizzle_types::postgres::types::Jsonb;
381///
382/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
383/// let cond = jsonb_contained(data, r#"{"role": "admin", "active": true}"#);
384/// assert_eq!(cond.to_sql().sql(), "data <@ CAST ($1 AS JSONB)");
385/// ```
386#[allow(clippy::type_complexity)]
387pub fn jsonb_contained<'a, L, R>(
388 left: L,
389 right: R,
390) -> SQLExpr<
391 'a,
392 PostgresValue<'a>,
393 Boolean,
394 NonNull,
395 <L::Aggregate as AggregateKind>::Or<R::Aggregate>,
396 (Arg<L::Nullable, L::Sources>, Arg<R::Nullable, R::Sources>),
397>
398where
399 L: Expr<'a, PostgresValue<'a>>,
400 L::SQLType: JsonbType,
401 R: Expr<'a, PostgresValue<'a>>,
402 R::SQLType: JsonbOperand,
403{
404 SQLExpr::new(
405 left.to_sql()
406 .push(SQLChunk::Raw("<@".into()))
407 .append(jsonb_operand(right.to_sql())),
408 )
409}
410
411/// Tests whether a key exists at the top level of a `jsonb` value (`?`).
412///
413/// The operand must be `jsonb` ([`JsonbType`]). For a `jsonb` array, the
414/// test matches string elements instead. The result is NULL when the operand is NULL.
415///
416/// # Examples
417///
418/// ```
419/// use drizzle_postgres::expr::jsonb_exists_key;
420/// use drizzle_core::{ToSQL, expr::raw_non_null};
421/// use drizzle_postgres::values::PostgresValue;
422/// use drizzle_types::postgres::types::Jsonb;
423///
424/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
425/// let cond = jsonb_exists_key(data, "email");
426/// assert_eq!(cond.to_sql().sql(), "data ? CAST ($1 AS TEXT)");
427/// ```
428#[allow(clippy::type_complexity)]
429pub fn jsonb_exists_key<'a, E>(
430 expr: E,
431 key: &'a str,
432) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
433where
434 E: Expr<'a, PostgresValue<'a>>,
435 E::SQLType: JsonbType,
436{
437 SQLExpr::new(
438 expr.to_sql()
439 .push(SQLChunk::Raw("?".into()))
440 .append(typed_param(PostgresValue::Text(key.into()), "TEXT")),
441 )
442}
443
444/// Tests whether any of the keys exists at the top level of a `jsonb` value (`?|`).
445///
446/// The operand must be `jsonb` ([`JsonbType`]). The keys are bound as one
447/// `text[]` parameter. The result is NULL when the operand is NULL.
448///
449/// # Examples
450///
451/// ```
452/// use drizzle_postgres::expr::jsonb_exists_any;
453/// use drizzle_core::{ToSQL, expr::raw_non_null};
454/// use drizzle_postgres::values::PostgresValue;
455/// use drizzle_types::postgres::types::Jsonb;
456///
457/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
458/// let cond = jsonb_exists_any(data, &["email", "phone"]);
459/// assert_eq!(cond.to_sql().sql(), "data ?| $1");
460/// ```
461#[allow(clippy::type_complexity)]
462pub fn jsonb_exists_any<'a, E>(
463 expr: E,
464 keys: &[&'a str],
465) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
466where
467 E: Expr<'a, PostgresValue<'a>>,
468 E::SQLType: JsonbType,
469{
470 let arr: Vec<PostgresValue<'a>> = keys
471 .iter()
472 .map(|k| PostgresValue::Text((*k).into()))
473 .collect();
474 SQLExpr::new(
475 expr.to_sql()
476 .push(SQLChunk::Raw("?|".into()))
477 .append(SQL::param(PostgresValue::Array(arr))),
478 )
479}
480
481/// Tests whether all of the keys exist at the top level of a `jsonb` value (`?&`).
482///
483/// The operand must be `jsonb` ([`JsonbType`]). The keys are bound as one
484/// `text[]` parameter. The result is NULL when the operand is NULL.
485///
486/// # Examples
487///
488/// ```
489/// use drizzle_postgres::expr::jsonb_exists_all;
490/// use drizzle_core::{ToSQL, expr::raw_non_null};
491/// use drizzle_postgres::values::PostgresValue;
492/// use drizzle_types::postgres::types::Jsonb;
493///
494/// let data = raw_non_null::<PostgresValue, Jsonb>("data");
495/// let cond = jsonb_exists_all(data, &["name", "email"]);
496/// assert_eq!(cond.to_sql().sql(), "data ?& $1");
497/// ```
498#[allow(clippy::type_complexity)]
499pub fn jsonb_exists_all<'a, E>(
500 expr: E,
501 keys: &[&'a str],
502) -> SQLExpr<'a, PostgresValue<'a>, Boolean, NonNull, E::Aggregate, Arg<E::Nullable, E::Sources>>
503where
504 E: Expr<'a, PostgresValue<'a>>,
505 E::SQLType: JsonbType,
506{
507 let arr: Vec<PostgresValue<'a>> = keys
508 .iter()
509 .map(|k| PostgresValue::Text((*k).into()))
510 .collect();
511 SQLExpr::new(
512 expr.to_sql()
513 .push(SQLChunk::Raw("?&".into()))
514 .append(SQL::param(PostgresValue::Array(arr))),
515 )
516}
517
518/// Method forms of the JSON operators, available on every `PostgreSQL` expression.
519///
520/// Each method calls the free function of the same name and has the same
521/// operand rules ([`JsonType`], [`JsonbType`], [`JsonbOperand`]).
522/// `?|` and `?&` have no method form; use [`jsonb_exists_any`] and
523/// [`jsonb_exists_all`].
524///
525/// # Examples
526///
527/// ```
528/// use drizzle_core::{ToSQL, expr::raw_non_null};
529/// use drizzle_postgres::expr::JsonExprExt;
530/// use drizzle_postgres::values::PostgresValue;
531/// use drizzle_types::postgres::types::Jsonb;
532///
533/// let settings = raw_non_null::<PostgresValue, Jsonb>("settings");
534/// let theme = settings.json_get_path_text("{ui,theme}");
535/// assert_eq!(theme.to_sql().sql(), "settings #>> CAST ($1 AS TEXT[])");
536/// ```
537pub trait JsonExprExt<'a>: Expr<'a, PostgresValue<'a>> + Sized {
538 /// Gets an object field by key (`->`), keeping the JSON type. See [`json_get`].
539 fn json_get(
540 self,
541 key: &'a str,
542 ) -> SQLExpr<
543 'a,
544 PostgresValue<'a>,
545 <Self::SQLType as JsonType>::Field,
546 Null,
547 Self::Aggregate,
548 Self::Sources,
549 >
550 where
551 Self::SQLType: JsonType,
552 {
553 json_get(self, key)
554 }
555
556 /// Gets an array element by index (`->`), keeping the JSON type. See [`json_get_idx`].
557 fn json_get_idx(
558 self,
559 index: i32,
560 ) -> SQLExpr<
561 'a,
562 PostgresValue<'a>,
563 <Self::SQLType as JsonType>::Field,
564 Null,
565 Self::Aggregate,
566 Self::Sources,
567 >
568 where
569 Self::SQLType: JsonType,
570 {
571 json_get_idx(self, index)
572 }
573
574 /// Gets an object field by key as `text` (`->>`). See [`json_get_text`].
575 fn json_get_text(
576 self,
577 key: &'a str,
578 ) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Self::Aggregate, Self::Sources>
579 where
580 Self::SQLType: JsonType,
581 {
582 json_get_text(self, key)
583 }
584
585 /// Gets an array element by index as `text` (`->>`). See [`json_get_text_idx`].
586 fn json_get_text_idx(
587 self,
588 index: i32,
589 ) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Self::Aggregate, Self::Sources>
590 where
591 Self::SQLType: JsonType,
592 {
593 json_get_text_idx(self, index)
594 }
595
596 /// Gets the value at a path (`#>`), keeping the JSON type. See [`json_get_path`].
597 fn json_get_path(
598 self,
599 path: &'a str,
600 ) -> SQLExpr<
601 'a,
602 PostgresValue<'a>,
603 <Self::SQLType as JsonType>::Field,
604 Null,
605 Self::Aggregate,
606 Self::Sources,
607 >
608 where
609 Self::SQLType: JsonType,
610 {
611 json_get_path(self, path)
612 }
613
614 /// Gets the value at a path as `text` (`#>>`). See [`json_get_path_text`].
615 fn json_get_path_text(
616 self,
617 path: &'a str,
618 ) -> SQLExpr<'a, PostgresValue<'a>, Text, Null, Self::Aggregate, Self::Sources>
619 where
620 Self::SQLType: JsonType,
621 {
622 json_get_path_text(self, path)
623 }
624
625 /// Tests whether `self` contains `other` (`@>`, `jsonb` only). See [`jsonb_contains`].
626 #[allow(clippy::type_complexity)]
627 fn jsonb_contains<R>(
628 self,
629 other: R,
630 ) -> SQLExpr<
631 'a,
632 PostgresValue<'a>,
633 Boolean,
634 NonNull,
635 <Self::Aggregate as AggregateKind>::Or<R::Aggregate>,
636 (
637 Arg<Self::Nullable, Self::Sources>,
638 Arg<R::Nullable, R::Sources>,
639 ),
640 >
641 where
642 Self::SQLType: JsonbType,
643 R: Expr<'a, PostgresValue<'a>>,
644 R::SQLType: JsonbOperand,
645 {
646 jsonb_contains(self, other)
647 }
648
649 /// Tests whether `self` is contained in `other` (`<@`, `jsonb` only). See [`jsonb_contained`].
650 #[allow(clippy::type_complexity)]
651 fn jsonb_contained<R>(
652 self,
653 other: R,
654 ) -> SQLExpr<
655 'a,
656 PostgresValue<'a>,
657 Boolean,
658 NonNull,
659 <Self::Aggregate as AggregateKind>::Or<R::Aggregate>,
660 (
661 Arg<Self::Nullable, Self::Sources>,
662 Arg<R::Nullable, R::Sources>,
663 ),
664 >
665 where
666 Self::SQLType: JsonbType,
667 R: Expr<'a, PostgresValue<'a>>,
668 R::SQLType: JsonbOperand,
669 {
670 jsonb_contained(self, other)
671 }
672
673 /// Tests whether a top-level key exists (`?`, `jsonb` only). See [`jsonb_exists_key`].
674 #[allow(clippy::type_complexity)]
675 fn jsonb_exists_key(
676 self,
677 key: &'a str,
678 ) -> SQLExpr<
679 'a,
680 PostgresValue<'a>,
681 Boolean,
682 NonNull,
683 Self::Aggregate,
684 Arg<Self::Nullable, Self::Sources>,
685 >
686 where
687 Self::SQLType: JsonbType,
688 {
689 jsonb_exists_key(self, key)
690 }
691}
692
693impl<'a, E: Expr<'a, PostgresValue<'a>>> JsonExprExt<'a> for E {}