Skip to main content

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::*;
9
10use super::column::Column;
11use super::exists_column::ExistsColumn;
12use super::nested::NestedPath;
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 path_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
41        self.as_json_path_name = Some(name.into());
42        self
43    }
44
45    /// Add PASSING parameters
46    pub fn passing(mut self, value: impl Into<Value>, alias: impl Into<Cow<'static, str>>) -> Self {
47        self.passing.push((value.into(), alias.into()));
48        self
49    }
50
51    /// Add multiple PASSING parameters at once
52    pub fn passing_many(
53        mut self,
54        passing: impl IntoIterator<Item = (impl Into<Value>, impl Into<Cow<'static, str>>)>,
55    ) -> Self {
56        for (value, alias) in passing {
57            self.passing.push((value.into(), alias.into()));
58        }
59        self
60    }
61
62    /// Add a `FOR ORDINALITY` column.
63    pub fn for_ordinality(mut self, name: impl Into<Cow<'static, str>>) -> Self {
64        self.columns
65            .push(JsonTableColumn::Ordinality { name: name.into() });
66        self
67    }
68
69    pub fn column(mut self, column: Column) -> Self {
70        self.columns.push(column.into_column());
71        self
72    }
73
74    pub fn exists(mut self, column: ExistsColumn) -> Self {
75        self.columns.push(column.into_column());
76        self
77    }
78
79    pub fn nested(mut self, nested: NestedPath) -> Self {
80        self.columns.push(nested.into_column());
81        self
82    }
83
84    /// Convenience method for `ERROR ON ERROR`
85    pub fn error_on_error(mut self) -> Self {
86        self.on_error = Some(OnErrorClause::Error);
87        self
88    }
89
90    /// Convenience method for `EMPTY ON ERROR`
91    pub fn empty_on_error(mut self) -> Self {
92        self.on_error = Some(OnErrorClause::Empty);
93        self
94    }
95
96    /// Convenience method for `EMPTY ARRAY ON ERROR`
97    pub fn empty_array_on_error(mut self) -> Self {
98        self.on_error = Some(OnErrorClause::EmptyArray);
99        self
100    }
101
102    pub fn build(self) -> FunctionCall {
103        self.build_internal().unwrap()
104    }
105
106    fn build_internal(self) -> Result<FunctionCall, core::fmt::Error> {
107        let mut buf = String::with_capacity(200);
108
109        PostgresQueryBuilder.prepare_expr(&self.context_item, &mut buf);
110        buf.write_str(", ")?;
111        write_json_path_expr(&mut buf, &self.path_expression)?;
112
113        if let Some(name) = &self.as_json_path_name {
114            write_as_json_path_name(&mut buf, name)?;
115        }
116
117        write_passing(&mut buf, self.passing)?;
118
119        Self::write_columns(&self.columns, &mut buf)?;
120
121        if let Some(on_error) = &self.on_error {
122            buf.write_str(" ")?;
123            on_error.write_to(&mut buf)?;
124        }
125
126        Ok(FunctionCall::new(Func::Custom("JSON_TABLE".into())).arg(Expr::Custom(buf.into())))
127    }
128
129    fn write_columns(columns: &[JsonTableColumn], buf: &mut String) -> fmt::Result {
130        buf.write_str(" COLUMNS (")?;
131        let mut citer = columns.iter();
132        join_io!(
133            citer,
134            col,
135            join {
136                buf.write_str(", ")?;
137            },
138            do {
139                Self::write_column_static(col, buf)?;
140            }
141        );
142        buf.write_str(")")?;
143
144        Ok(())
145    }
146
147    fn write_column_static(
148        column: &JsonTableColumn,
149        buf: &mut String,
150    ) -> Result<(), core::fmt::Error> {
151        match column {
152            JsonTableColumn::Ordinality { name } => {
153                buf.write_str(name)?;
154                buf.write_str(" FOR ORDINALITY")?;
155            }
156            JsonTableColumn::Regular {
157                name,
158                column_type,
159                format_json,
160                encoding_utf8,
161                path,
162                wrapper,
163                quotes,
164                on_empty,
165                on_error,
166            } => {
167                buf.write_str(name)?;
168                buf.write_str(" ")?;
169                PostgresQueryBuilder.prepare_type_ref(column_type, buf);
170
171                if *format_json {
172                    buf.write_str(" FORMAT JSON")?;
173                    if *encoding_utf8 {
174                        buf.write_str(" ENCODING UTF8")?;
175                    }
176                }
177
178                if let Some(path) = path {
179                    buf.write_str(" PATH '")?;
180                    buf.write_str(path)?;
181                    buf.write_str("'")?;
182                }
183
184                if let Some(wrapper) = wrapper {
185                    wrapper.write_to(buf)?;
186                }
187
188                if let Some(quotes) = quotes {
189                    quotes.write_to(buf)?;
190                }
191
192                if let Some(on_empty) = on_empty {
193                    buf.write_str(" ")?;
194                    on_empty.write_to(buf)?;
195                    buf.write_str(" ON EMPTY")?;
196                }
197
198                if let Some(on_error) = on_error {
199                    buf.write_str(" ")?;
200                    on_error.write_to(buf)?;
201                    buf.write_str(" ON ERROR")?;
202                }
203            }
204            JsonTableColumn::Exists {
205                name,
206                column_type,
207                path,
208                on_error,
209            } => {
210                buf.write_str(name)?;
211                buf.write_str(" ")?;
212                PostgresQueryBuilder.prepare_type_ref(column_type, buf);
213                buf.write_str(" EXISTS")?;
214
215                if let Some(path) = path {
216                    buf.write_str(" PATH '")?;
217                    buf.write_str(path)?;
218                    buf.write_str("'")?;
219                }
220
221                if let Some(on_error) = on_error {
222                    buf.write_str(" ")?;
223                    on_error.write_to(buf)?;
224                }
225            }
226            JsonTableColumn::Nested {
227                path,
228                as_json_path_name: json_path_name,
229                columns,
230                // explicit_path,
231            } => {
232                buf.write_str("NESTED PATH")?;
233                buf.write_str(" ")?;
234                write_json_path_expr(buf, path)?;
235
236                if let Some(name) = json_path_name {
237                    write_as_json_path_name(buf, name)?;
238                }
239
240                Self::write_columns(columns, buf)?;
241            }
242        }
243        Ok(())
244    }
245}