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}