sea_query/extension/postgres/func/json_table/
builder.rs

1use core::fmt;
2use std::borrow::Cow;
3use std::fmt::Write;
4
5use crate::extension::postgres::json_fn::{
6    write_as_json_path_name, write_json_path_expr, write_passing,
7};
8use crate::extension::postgres::json_table::ExistsColumnBuilder;
9use crate::*;
10
11use super::column::ColumnBuilder;
12use super::nested::NestedPathBuilder;
13use super::types::*;
14
15/// Builder for JSON_TABLE function
16#[derive(Debug, Clone)]
17pub struct Builder {
18    pub(super) context_item: Expr,
19    pub(super) path_expression: Cow<'static, str>,
20    pub(super) as_json_path_name: Option<Cow<'static, str>>,
21    pub(super) passing: Vec<(Value, Cow<'static, str>)>,
22    pub(super) columns: Vec<JsonTableColumn>,
23    pub(super) on_error: Option<OnErrorClause>,
24}
25
26impl From<Builder> for FunctionCall {
27    fn from(builder: Builder) -> Self {
28        builder.build()
29    }
30}
31
32impl From<Builder> for Expr {
33    fn from(value: Builder) -> Self {
34        Expr::FunctionCall(FunctionCall::from(value))
35    }
36}
37
38impl Builder {
39    /// Set the JSON path name (AS clause)
40    pub fn json_path_name<T>(mut self, name: T) -> Self
41    where
42        T: Into<Cow<'static, str>>,
43    {
44        self.as_json_path_name = Some(name.into());
45        self
46    }
47
48    /// Add PASSING parameters
49    pub fn passing<V, A>(mut self, value: V, alias: A) -> Self
50    where
51        V: Into<Value>,
52        A: Into<Cow<'static, str>>,
53    {
54        self.passing.push((value.into(), alias.into()));
55        self
56    }
57
58    /// Add multiple PASSING parameters at once
59    pub fn passing_many<V, A, I>(mut self, passing: I) -> Self
60    where
61        V: Into<Value>,
62        A: Into<Cow<'static, str>>,
63        I: IntoIterator<Item = (V, A)>,
64    {
65        for (value, alias) in passing {
66            self.passing.push((value.into(), alias.into()));
67        }
68        self
69    }
70
71    /// Add a FOR ORDINALITY column
72    pub fn ordinality_column<N>(mut self, name: N) -> Self
73    where
74        N: Into<Cow<'static, str>>,
75    {
76        self.columns
77            .push(JsonTableColumn::Ordinality { name: name.into() });
78        self
79    }
80
81    /// Add a regular column
82    pub fn column<N, T>(self, name: N, column_type: T) -> ColumnBuilder<Self>
83    where
84        N: Into<Cow<'static, str>>,
85        T: Into<TypeRef>,
86    {
87        ColumnBuilder {
88            builder: self,
89            name: name.into(),
90            column_type: column_type.into(),
91            format_json: false,
92            encoding_utf8: false,
93            path: None,
94            wrapper: None,
95            quotes: None,
96            on_empty: None,
97            on_error: None,
98        }
99    }
100
101    /// Add an EXISTS column
102    pub fn exists_column<N, T>(self, name: N, column_type: T) -> ExistsColumnBuilder<Self>
103    where
104        N: Into<Cow<'static, str>>,
105        T: Into<TypeRef>,
106    {
107        ExistsColumnBuilder {
108            builder: self,
109            name: name.into(),
110            column_type: column_type.into(),
111            path: None,
112            on_error: None,
113        }
114    }
115
116    /// Add a NESTED PATH column
117    pub fn nested<P>(self, path: P) -> NestedPathBuilder
118    where
119        P: Into<Cow<'static, str>>,
120    {
121        NestedPathBuilder {
122            builder: self,
123            path: path.into(),
124            explicit: false,
125            json_path_name: None,
126            columns: Vec::new(),
127        }
128    }
129
130    /// Convenience method for `ERROR ON ERROR`
131    pub fn error_on_error(mut self) -> Self {
132        self.on_error = Some(OnErrorClause::Error);
133        self
134    }
135
136    /// Convenience method for `EMPTY ON ERROR`
137    pub fn empty_on_error(mut self) -> Self {
138        self.on_error = Some(OnErrorClause::Empty);
139        self
140    }
141
142    /// Convenience method for `EMPTY ARRAY ON ERROR`
143    pub fn empty_array_on_error(mut self) -> Self {
144        self.on_error = Some(OnErrorClause::EmptyArray);
145        self
146    }
147
148    pub fn build(self) -> FunctionCall {
149        self.build_internal().unwrap()
150    }
151
152    fn build_internal(self) -> Result<FunctionCall, core::fmt::Error> {
153        let mut buf = String::with_capacity(200);
154
155        PostgresQueryBuilder.prepare_expr(&self.context_item, &mut buf);
156        buf.write_str(", ")?;
157        write_json_path_expr(&mut buf, &self.path_expression)?;
158
159        self.as_json_path_name
160            .map(|x| write_as_json_path_name(&mut buf, &x));
161
162        write_passing(&mut buf, self.passing)?;
163
164        Self::write_columns(&self.columns, &mut buf)?;
165
166        if let Some(on_error) = &self.on_error {
167            buf.write_str(" ")?;
168            on_error.write_to(&mut buf)?;
169        }
170
171        Ok(FunctionCall::new(Func::Custom("JSON_TABLE".into())).arg(Expr::Custom(buf.into())))
172    }
173
174    fn write_columns(columns: &[JsonTableColumn], buf: &mut String) -> fmt::Result {
175        buf.write_str(" COLUMNS (")?;
176        let mut citer = columns.iter();
177        join_io!(
178            citer,
179            col,
180            join {
181                buf.write_str(", ")?;
182            },
183            do {
184                Self::write_column_static(col, buf)?;
185            }
186        );
187        buf.write_str(")")?;
188
189        Ok(())
190    }
191
192    fn write_column_static(
193        column: &JsonTableColumn,
194        buf: &mut String,
195    ) -> Result<(), core::fmt::Error> {
196        match column {
197            JsonTableColumn::Ordinality { name } => {
198                buf.write_str(name)?;
199                buf.write_str(" FOR ORDINALITY")?;
200            }
201            JsonTableColumn::Regular {
202                name,
203                column_type,
204                format_json,
205                encoding_utf8,
206                path,
207                wrapper,
208                quotes,
209                on_empty,
210                on_error,
211            } => {
212                buf.write_str(name)?;
213                buf.write_str(" ")?;
214                PostgresQueryBuilder.prepare_type_ref(column_type, buf);
215
216                if *format_json {
217                    buf.write_str(" FORMAT JSON")?;
218                    if *encoding_utf8 {
219                        buf.write_str(" ENCODING UTF8")?;
220                    }
221                }
222
223                if let Some(path) = path {
224                    buf.write_str(" PATH '")?;
225                    buf.write_str(path)?;
226                    buf.write_str("'")?;
227                }
228
229                if let Some(wrapper) = wrapper {
230                    wrapper.write_to(buf)?;
231                }
232
233                if let Some(quotes) = quotes {
234                    quotes.write_to(buf)?;
235                }
236
237                if let Some(on_empty) = on_empty {
238                    buf.write_str(" ")?;
239                    on_empty.write_to(buf)?;
240                    buf.write_str(" ON EMPTY")?;
241                }
242
243                if let Some(on_error) = on_error {
244                    buf.write_str(" ")?;
245                    on_error.write_to(buf)?;
246                    buf.write_str(" ON ERROR")?;
247                }
248            }
249            JsonTableColumn::Exists {
250                name,
251                column_type,
252                path,
253                on_error,
254            } => {
255                buf.write_str(name)?;
256                buf.write_str(" ")?;
257                PostgresQueryBuilder.prepare_type_ref(column_type, buf);
258                buf.write_str(" EXISTS")?;
259
260                if let Some(path) = path {
261                    buf.write_str(" PATH '")?;
262                    buf.write_str(path)?;
263                    buf.write_str("'")?;
264                }
265
266                if let Some(on_error) = on_error {
267                    buf.write_str(" ")?;
268                    on_error.write_to(buf)?;
269                }
270            }
271            JsonTableColumn::Nested {
272                path,
273                as_json_path_name: json_path_name,
274                columns,
275                explicit_path,
276            } => {
277                buf.write_str("NESTED")?;
278                if *explicit_path {
279                    buf.write_str(" PATH")?;
280                }
281                buf.write_str(" ")?;
282                write_json_path_expr(buf, path)?;
283
284                if let Some(name) = json_path_name {
285                    write_as_json_path_name(buf, name)?;
286                }
287
288                Self::write_columns(columns, buf)?;
289            }
290        }
291        Ok(())
292    }
293}