sea_query/extension/postgres/func/
json_exists.rs

1use std::{borrow::Cow, fmt::Write};
2
3use crate::{
4    Expr, Func, FunctionCall, PgFunc, PostgresQueryBuilder, QueryBuilder, Value,
5    extension::postgres::json_fn::{write_json_path_expr, write_passing},
6};
7
8#[derive(Debug, Clone)]
9pub struct Builder {
10    context_item: Expr,
11    path_expression: Cow<'static, str>,
12    passing: Vec<(Value, Cow<'static, str>)>,
13    on_error: Option<OnClause>,
14}
15
16impl From<Builder> for FunctionCall {
17    fn from(builder: Builder) -> Self {
18        builder.build()
19    }
20}
21
22impl From<Builder> for Expr {
23    fn from(builder: Builder) -> Self {
24        Expr::FunctionCall(builder.build())
25    }
26}
27
28#[derive(Debug, Clone)]
29pub(super) enum OnClause {
30    True,
31    False,
32    Unknown,
33    Error,
34}
35
36impl From<bool> for OnClause {
37    fn from(value: bool) -> Self {
38        if value {
39            OnClause::True
40        } else {
41            OnClause::False
42        }
43    }
44}
45
46impl Builder {
47    /// Add PASSING parameters
48    pub fn passing<V, A>(mut self, value: V, alias: A) -> Self
49    where
50        V: Into<Value>,
51        A: Into<Cow<'static, str>>,
52    {
53        self.passing.push((value.into(), alias.into()));
54        self
55    }
56
57    /// Add multiple PASSING parameters at once
58    pub fn passing_many<V, A, I>(mut self, passing: I) -> Self
59    where
60        V: Into<Value>,
61        A: Into<Cow<'static, str>>,
62        I: IntoIterator<Item = (V, A)>,
63    {
64        for (value, alias) in passing {
65            self.passing.push((value.into(), alias.into()));
66        }
67        self
68    }
69
70    /// Convenience method for `TRUE ON ERROR`
71    pub fn true_on_error(mut self) -> Self {
72        self.on_error = Some(OnClause::True);
73        self
74    }
75
76    /// Convenience method for `FALSE ON ERROR`
77    pub fn false_on_error(mut self) -> Self {
78        self.on_error = Some(OnClause::False);
79        self
80    }
81
82    /// Convenience method for `UNKNOWN ON ERROR`
83    pub fn unknown_on_error(mut self) -> Self {
84        self.on_error = Some(OnClause::Unknown);
85        self
86    }
87
88    /// Convenience method for `ERROR ON ERROR`
89    pub fn error_on_error(mut self) -> Self {
90        self.on_error = Some(OnClause::Error);
91        self
92    }
93
94    pub fn build(self) -> FunctionCall {
95        self.build_internal().unwrap()
96    }
97
98    fn build_internal(self) -> Result<FunctionCall, core::fmt::Error> {
99        let mut buf = String::with_capacity(20);
100
101        PostgresQueryBuilder.prepare_expr(&self.context_item, &mut buf);
102        buf.write_str(" ")?;
103        write_json_path_expr(&mut buf, &self.path_expression)?;
104
105        write_passing(&mut buf, self.passing)?;
106
107        if let Some(on_error) = self.on_error {
108            buf.write_str(match on_error {
109                OnClause::True => " TRUE",
110                OnClause::False => " FALSE",
111                OnClause::Unknown => " UNKNOWN",
112                OnClause::Error => " ERROR",
113            })?;
114            buf.write_str(" ON ERROR")?;
115        }
116
117        Ok(FunctionCall::new(Func::Custom("JSON_EXISTS".into())).arg(Expr::Custom(buf.into())))
118    }
119}
120
121impl PgFunc {
122    /// Create a `JSON_EXISTS` function builder. Postgres only.
123    ///
124    /// # Examples
125    ///
126    /// Basic usage with PASSING parameters:
127    /// ```
128    /// use sea_query::extension::postgres::*;
129    /// use sea_query::{tests_cfg::*, *};
130    ///
131    /// let query = Query::select()
132    ///     .expr(
133    ///         PgFunc::json_exists(
134    ///            Expr::cust(r#"jsonb '{"key1": [1,2,3]}'"#),
135    ///             "strict $.key1[*] ? (@ > $x)",
136    ///         )
137    ///         .passing(2, "x"),
138    ///     )
139    ///     .to_owned();
140    ///
141    /// assert_eq!(
142    ///     query.to_string(PostgresQueryBuilder),
143    ///     r#"SELECT JSON_EXISTS(jsonb '{"key1": [1,2,3]}' 'strict $.key1[*] ? (@ > $x)' PASSING 2 AS x)"#
144    /// );
145    /// ```
146    ///
147    /// With ERROR ON ERROR clause:
148    /// ```
149    /// use sea_query::extension::postgres::*;
150    /// use sea_query::{tests_cfg::*, *};
151    ///
152    /// let query = Query::select()
153    ///     .expr(
154    ///         PgFunc::json_exists(Expr::cust(r#"jsonb '{"a": [1,2,3]}'"#), "lax $.a[5]")
155    ///             .error_on_error(),
156    ///     )
157    ///     .to_owned();
158    ///
159    /// assert_eq!(
160    ///     query.to_string(PostgresQueryBuilder),
161    ///     r#"SELECT JSON_EXISTS(jsonb '{"a": [1,2,3]}' 'lax $.a[5]' ERROR ON ERROR)"#
162    /// );
163    /// ```
164    ///
165    /// With strict mode and ERROR ON ERROR:
166    /// ```
167    /// use sea_query::extension::postgres::*;
168    /// use sea_query::{tests_cfg::*, *};
169    ///
170    /// let query = Query::select()
171    ///     .expr(
172    ///         PgFunc::json_exists(Expr::cust(r#"jsonb '{"a": [1,2,3]}'"#), "strict $.a[5]")
173    ///             .error_on_error(),
174    ///     )
175    ///     .to_owned();
176    ///
177    /// assert_eq!(
178    ///     query.to_string(PostgresQueryBuilder),
179    ///     r#"SELECT JSON_EXISTS(jsonb '{"a": [1,2,3]}' 'strict $.a[5]' ERROR ON ERROR)"#
180    /// );
181    /// ```
182    pub fn json_exists<C, P>(context_item: C, path_expression: P) -> Builder
183    where
184        C: Into<Expr>,
185        P: Into<Cow<'static, str>>,
186    {
187        Builder {
188            context_item: context_item.into(),
189            path_expression: path_expression.into(),
190            passing: Vec::new(),
191            on_error: None,
192        }
193    }
194}