Skip to main content

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 {}