sea_query_sqlx/
sqlx_postgres.rs

1use sqlx::Arguments;
2
3use sea_query::Value;
4#[cfg(all(feature = "with-json", feature = "postgres-array"))]
5use sea_query::prelude::Json;
6#[cfg(feature = "postgres-array")]
7use sea_query::{Array, ArrayType};
8
9use crate::SqlxValues;
10
11impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues {
12    fn into_arguments(self) -> sqlx::postgres::PgArguments {
13        let mut args = sqlx::postgres::PgArguments::default();
14        for arg in self.0.into_iter() {
15            match arg {
16                Value::Bool(b) => {
17                    let _ = args.add(b);
18                }
19                Value::TinyInt(i) => {
20                    let _ = args.add(i);
21                }
22                Value::SmallInt(i) => {
23                    let _ = args.add(i);
24                }
25                Value::Int(i) => {
26                    let _ = args.add(i);
27                }
28                Value::BigInt(i) => {
29                    let _ = args.add(i);
30                }
31                Value::TinyUnsigned(i) => {
32                    let _ = args.add(i.map(|i| i as i16));
33                }
34                Value::SmallUnsigned(i) => {
35                    let _ = args.add(i.map(|i| i as i32));
36                }
37                Value::Unsigned(i) => {
38                    let _ = args.add(i.map(|i| i as i64));
39                }
40                Value::BigUnsigned(i) => {
41                    let _ = args.add(i.map(|i| <i64 as TryFrom<u64>>::try_from(i).unwrap()));
42                }
43                Value::Float(f) => {
44                    let _ = args.add(f);
45                }
46                Value::Double(d) => {
47                    let _ = args.add(d);
48                }
49                Value::String(s) => {
50                    let _ = args.add(s.as_deref());
51                }
52                Value::Char(c) => {
53                    let _ = args.add(c.map(|c| c.to_string()));
54                }
55                Value::Bytes(b) => {
56                    let _ = args.add(b.as_deref());
57                }
58                #[cfg(feature = "with-chrono")]
59                Value::ChronoDate(d) => {
60                    let _ = args.add(d);
61                }
62                #[cfg(feature = "with-chrono")]
63                Value::ChronoTime(t) => {
64                    let _ = args.add(t);
65                }
66                #[cfg(feature = "with-chrono")]
67                Value::ChronoDateTime(t) => {
68                    let _ = args.add(t);
69                }
70                #[cfg(feature = "with-chrono")]
71                Value::ChronoDateTimeUtc(t) => {
72                    let _ = args.add(t);
73                }
74                #[cfg(feature = "with-chrono")]
75                Value::ChronoDateTimeLocal(t) => {
76                    let _ = args.add(t);
77                }
78                #[cfg(feature = "with-chrono")]
79                Value::ChronoDateTimeWithTimeZone(t) => {
80                    let _ = args.add(t);
81                }
82                #[cfg(feature = "with-time")]
83                Value::TimeDate(t) => {
84                    let _ = args.add(t);
85                }
86                #[cfg(feature = "with-time")]
87                Value::TimeTime(t) => {
88                    let _ = args.add(t);
89                }
90                #[cfg(feature = "with-time")]
91                Value::TimeDateTime(t) => {
92                    let _ = args.add(t);
93                }
94                #[cfg(feature = "with-time")]
95                Value::TimeDateTimeWithTimeZone(t) => {
96                    let _ = args.add(t);
97                }
98                #[cfg(feature = "with-uuid")]
99                Value::Uuid(uuid) => {
100                    let _ = args.add(uuid);
101                }
102                #[cfg(feature = "with-rust_decimal")]
103                Value::Decimal(d) => {
104                    let _ = args.add(d);
105                }
106                #[cfg(feature = "with-bigdecimal")]
107                Value::BigDecimal(d) => {
108                    let _ = args.add(d.as_ref());
109                }
110                #[cfg(feature = "with-json")]
111                Value::Json(j) => {
112                    let _ = args.add(j);
113                }
114                #[cfg(feature = "with-ipnetwork")]
115                Value::IpNetwork(ip) => {
116                    let _ = args.add(ip);
117                }
118                #[cfg(feature = "with-mac_address")]
119                Value::MacAddress(mac) => {
120                    let _ = args.add(mac);
121                }
122                #[cfg(feature = "with-jiff")]
123                Value::JiffDate(d) => {
124                    let _ = args.add(d.map(jiff_sqlx::Date::from));
125                }
126                #[cfg(feature = "with-jiff")]
127                Value::JiffTime(t) => {
128                    let _ = args.add(t.map(jiff_sqlx::Time::from));
129                }
130                #[cfg(feature = "with-jiff")]
131                Value::JiffDateTime(dt) => {
132                    let _ = args.add(dt.map(jiff_sqlx::DateTime::from));
133                }
134                #[cfg(feature = "with-jiff")]
135                Value::JiffTimestamp(ts) => {
136                    let _ = args.add(ts.map(jiff_sqlx::Timestamp::from));
137                }
138                #[cfg(feature = "with-jiff")]
139                Value::JiffZoned(z) => {
140                    let _ = args.add(z.map(|z| jiff_sqlx::Timestamp::from(z.timestamp())));
141                }
142                Value::Enum(e) => {
143                    // Bind as TEXT; We will explicit cast it in SQL (e.g., $1::my_enum_type)
144                    let _ = args.add(e.map(|e| e.as_str().to_owned()));
145                }
146                #[cfg(feature = "postgres-array")]
147                Value::Array(arr) => {
148                    match_array(arr, &mut args);
149                }
150                #[cfg(feature = "postgres-vector")]
151                Value::Vector(v) => {
152                    let _ = args.add(v);
153                }
154                #[cfg(feature = "postgres-range")]
155                Value::Range(r) => {
156                    let _ = args.add(r);
157                }
158            }
159        }
160        args
161    }
162}
163
164#[cfg(feature = "postgres-array")]
165fn match_array(arr: Array, args: &mut sqlx::postgres::PgArguments) {
166    match arr {
167        Array::Null(ty) => match_null_array(ty, args),
168        Array::Bool(inner) => {
169            let _ = args.add(inner.into_vec());
170        }
171        Array::TinyInt(inner) => {
172            let _ = args.add(inner.into_vec());
173        }
174        Array::SmallInt(inner) => {
175            let _ = args.add(inner.into_vec());
176        }
177        Array::Int(inner) => {
178            let _ = args.add(inner.into_vec());
179        }
180        Array::BigInt(inner) => {
181            let _ = args.add(inner.into_vec());
182        }
183        Array::TinyUnsigned(inner) => {
184            let v: Vec<Option<i16>> = inner
185                .into_vec()
186                .into_iter()
187                .map(|x| x.map(|y| y as i16))
188                .collect();
189            let _ = args.add(v);
190        }
191        Array::SmallUnsigned(inner) => {
192            let v: Vec<Option<i32>> = inner
193                .into_vec()
194                .into_iter()
195                .map(|x| x.map(|y| y as i32))
196                .collect();
197            let _ = args.add(v);
198        }
199        Array::Unsigned(inner) => {
200            let v: Vec<Option<i64>> = inner
201                .into_vec()
202                .into_iter()
203                .map(|x| x.map(|y| y as i64))
204                .collect();
205            let _ = args.add(v);
206        }
207        Array::BigUnsigned(inner) => {
208            let v: Vec<Option<i64>> = inner
209                .into_vec()
210                .into_iter()
211                .map(|x| x.map(|y| <i64 as TryFrom<u64>>::try_from(y).unwrap()))
212                .collect();
213            let _ = args.add(v);
214        }
215        Array::Float(inner) => {
216            let _ = args.add(inner.into_vec());
217        }
218        Array::Double(inner) => {
219            let _ = args.add(inner.into_vec());
220        }
221        Array::String(inner) => {
222            let _ = args.add(inner.into_vec());
223        }
224        Array::Char(inner) => {
225            let v: Vec<Option<String>> = inner
226                .into_vec()
227                .into_iter()
228                .map(|c| c.map(|x| x.to_string()))
229                .collect();
230            let _ = args.add(v);
231        }
232        Array::Bytes(inner) => {
233            let _ = args.add(inner.into_vec());
234        }
235        #[cfg(feature = "with-json")]
236        Array::Json(inner) => {
237            let _ = args.add(inner.into_vec());
238        }
239        #[cfg(feature = "with-chrono")]
240        Array::ChronoDate(inner) => {
241            let _ = args.add(inner.into_vec());
242        }
243        #[cfg(feature = "with-chrono")]
244        Array::ChronoTime(inner) => {
245            let _ = args.add(inner.into_vec());
246        }
247        #[cfg(feature = "with-chrono")]
248        Array::ChronoDateTime(inner) => {
249            let _ = args.add(inner.into_vec());
250        }
251        #[cfg(feature = "with-chrono")]
252        Array::ChronoDateTimeUtc(inner) => {
253            let _ = args.add(inner.into_vec());
254        }
255        #[cfg(feature = "with-chrono")]
256        Array::ChronoDateTimeLocal(inner) => {
257            let _ = args.add(inner.into_vec());
258        }
259        #[cfg(feature = "with-chrono")]
260        Array::ChronoDateTimeWithTimeZone(inner) => {
261            let _ = args.add(inner.into_vec());
262        }
263        #[cfg(feature = "with-time")]
264        Array::TimeDate(inner) => {
265            let _ = args.add(inner.into_vec());
266        }
267        #[cfg(feature = "with-time")]
268        Array::TimeTime(inner) => {
269            let _ = args.add(inner.into_vec());
270        }
271        #[cfg(feature = "with-time")]
272        Array::TimeDateTime(inner) => {
273            let _ = args.add(inner.into_vec());
274        }
275        #[cfg(feature = "with-time")]
276        Array::TimeDateTimeWithTimeZone(inner) => {
277            let _ = args.add(inner.into_vec());
278        }
279        #[cfg(feature = "with-uuid")]
280        Array::Uuid(inner) => {
281            let _ = args.add(inner.into_vec());
282        }
283        #[cfg(feature = "with-rust_decimal")]
284        Array::Decimal(inner) => {
285            let _ = args.add(inner.into_vec());
286        }
287        #[cfg(feature = "with-bigdecimal")]
288        Array::BigDecimal(inner) => {
289            let _ = args.add(inner.into_vec());
290        }
291        #[cfg(feature = "with-ipnetwork")]
292        Array::IpNetwork(inner) => {
293            let _ = args.add(inner.into_vec());
294        }
295        #[cfg(feature = "with-mac_address")]
296        Array::MacAddress(inner) => {
297            let _ = args.add(inner.into_vec());
298        }
299        #[cfg(feature = "with-jiff")]
300        Array::JiffDate(inner) => {
301            let v: Vec<Option<jiff_sqlx::Date>> = inner
302                .into_vec()
303                .into_iter()
304                .map(|x| x.map(Into::into))
305                .collect();
306            let _ = args.add(v);
307        }
308        #[cfg(feature = "with-jiff")]
309        Array::JiffTime(inner) => {
310            let v: Vec<Option<jiff_sqlx::Time>> = inner
311                .into_vec()
312                .into_iter()
313                .map(|x| x.map(Into::into))
314                .collect();
315            let _ = args.add(v);
316        }
317        #[cfg(feature = "with-jiff")]
318        Array::JiffDateTime(inner) => {
319            let v: Vec<Option<jiff_sqlx::DateTime>> = inner
320                .into_vec()
321                .into_iter()
322                .map(|x| x.map(Into::into))
323                .collect();
324            let _ = args.add(v);
325        }
326        #[cfg(feature = "with-jiff")]
327        Array::JiffTimestamp(inner) => {
328            let v: Vec<Option<jiff_sqlx::Timestamp>> = inner
329                .into_vec()
330                .into_iter()
331                .map(|x| x.map(Into::into))
332                .collect();
333            let _ = args.add(v);
334        }
335        #[cfg(feature = "with-jiff")]
336        Array::JiffZoned(inner) => {
337            let v: Vec<Option<jiff_sqlx::Timestamp>> = inner
338                .into_vec()
339                .into_iter()
340                .map(|x| x.map(|z| z.timestamp().into()))
341                .collect();
342            let _ = args.add(v);
343        }
344        Array::Enum(inner) => {
345            // Bind as TEXT[]; use explicit cast in SQL (e.g., $1::my_enum_type[])
346            let (_, arr) = inner.as_ref();
347            let v: Vec<Option<String>> = arr
348                .iter()
349                .map(|e| e.as_ref().map(|e| e.as_str().to_owned()))
350                .collect();
351            let _ = args.add(v);
352        }
353        _ => {
354            panic!("Unsupported array variant for sea-query-sqlx");
355        }
356    }
357}
358
359#[cfg(feature = "postgres-array")]
360fn match_null_array(ty: ArrayType, args: &mut sqlx::postgres::PgArguments) {
361    match ty {
362        ArrayType::Bool => {
363            let _ = args.add(Option::<Vec<Option<bool>>>::None);
364        }
365        ArrayType::TinyInt => {
366            let _ = args.add(Option::<Vec<Option<i8>>>::None);
367        }
368        ArrayType::SmallInt => {
369            let _ = args.add(Option::<Vec<Option<i16>>>::None);
370        }
371        ArrayType::Int => {
372            let _ = args.add(Option::<Vec<Option<i32>>>::None);
373        }
374        ArrayType::BigInt => {
375            let _ = args.add(Option::<Vec<Option<i64>>>::None);
376        }
377        ArrayType::TinyUnsigned => {
378            let _ = args.add(Option::<Vec<Option<i16>>>::None);
379        }
380        ArrayType::SmallUnsigned => {
381            let _ = args.add(Option::<Vec<Option<i32>>>::None);
382        }
383        ArrayType::Unsigned => {
384            let _ = args.add(Option::<Vec<Option<i64>>>::None);
385        }
386        ArrayType::BigUnsigned => {
387            let _ = args.add(Option::<Vec<Option<i64>>>::None);
388        }
389        ArrayType::Float => {
390            let _ = args.add(Option::<Vec<Option<f32>>>::None);
391        }
392        ArrayType::Double => {
393            let _ = args.add(Option::<Vec<Option<f64>>>::None);
394        }
395        ArrayType::String => {
396            let _ = args.add(Option::<Vec<Option<String>>>::None);
397        }
398        ArrayType::Char => {
399            let _ = args.add(Option::<Vec<Option<String>>>::None);
400        }
401        ArrayType::Bytes => {
402            let _ = args.add(Option::<Vec<Option<Vec<u8>>>>::None);
403        }
404        ArrayType::Enum(_) => {
405            let _ = args.add(Option::<Vec<Option<String>>>::None);
406        }
407        #[cfg(feature = "with-json")]
408        ArrayType::Json => {
409            let _ = args.add(Option::<Vec<Option<Json>>>::None);
410        }
411        #[cfg(feature = "with-chrono")]
412        ArrayType::ChronoDate => {
413            let _ = args.add(Option::<Vec<Option<chrono::NaiveDate>>>::None);
414        }
415        #[cfg(feature = "with-chrono")]
416        ArrayType::ChronoTime => {
417            let _ = args.add(Option::<Vec<Option<chrono::NaiveTime>>>::None);
418        }
419        #[cfg(feature = "with-chrono")]
420        ArrayType::ChronoDateTime => {
421            let _ = args.add(Option::<Vec<Option<chrono::NaiveDateTime>>>::None);
422        }
423        #[cfg(feature = "with-chrono")]
424        ArrayType::ChronoDateTimeUtc => {
425            let _ = args.add(Option::<Vec<Option<chrono::DateTime<chrono::Utc>>>>::None);
426        }
427        #[cfg(feature = "with-chrono")]
428        ArrayType::ChronoDateTimeLocal => {
429            let _ = args.add(Option::<Vec<Option<chrono::DateTime<chrono::Local>>>>::None);
430        }
431        #[cfg(feature = "with-chrono")]
432        ArrayType::ChronoDateTimeWithTimeZone => {
433            let _ = args.add(Option::<Vec<Option<chrono::DateTime<chrono::FixedOffset>>>>::None);
434        }
435        #[cfg(feature = "with-time")]
436        ArrayType::TimeDate => {
437            let _ = args.add(Option::<Vec<Option<time::Date>>>::None);
438        }
439        #[cfg(feature = "with-time")]
440        ArrayType::TimeTime => {
441            let _ = args.add(Option::<Vec<Option<time::Time>>>::None);
442        }
443        #[cfg(feature = "with-time")]
444        ArrayType::TimeDateTime => {
445            let _ = args.add(Option::<Vec<Option<time::PrimitiveDateTime>>>::None);
446        }
447        #[cfg(feature = "with-time")]
448        ArrayType::TimeDateTimeWithTimeZone => {
449            let _ = args.add(Option::<Vec<Option<time::OffsetDateTime>>>::None);
450        }
451        #[cfg(feature = "with-jiff")]
452        ArrayType::JiffDate => {
453            let _ = args.add(Option::<Vec<Option<jiff_sqlx::Date>>>::None);
454        }
455        #[cfg(feature = "with-jiff")]
456        ArrayType::JiffTime => {
457            let _ = args.add(Option::<Vec<Option<jiff_sqlx::Time>>>::None);
458        }
459        #[cfg(feature = "with-jiff")]
460        ArrayType::JiffDateTime => {
461            let _ = args.add(Option::<Vec<Option<jiff_sqlx::DateTime>>>::None);
462        }
463        #[cfg(feature = "with-jiff")]
464        ArrayType::JiffTimestamp => {
465            let _ = args.add(Option::<Vec<Option<jiff_sqlx::Timestamp>>>::None);
466        }
467        #[cfg(feature = "with-jiff")]
468        ArrayType::JiffZoned => {
469            let _ = args.add(Option::<Vec<Option<jiff_sqlx::Timestamp>>>::None);
470        }
471        #[cfg(feature = "with-uuid")]
472        ArrayType::Uuid => {
473            let _ = args.add(Option::<Vec<Option<uuid::Uuid>>>::None);
474        }
475        #[cfg(feature = "with-rust_decimal")]
476        ArrayType::Decimal => {
477            let _ = args.add(Option::<Vec<Option<rust_decimal::Decimal>>>::None);
478        }
479        #[cfg(feature = "with-bigdecimal")]
480        ArrayType::BigDecimal => {
481            let _ = args.add(Option::<Vec<Option<bigdecimal::BigDecimal>>>::None);
482        }
483        #[cfg(feature = "with-ipnetwork")]
484        ArrayType::IpNetwork => {
485            let _ = args.add(Option::<Vec<Option<ipnetwork::IpNetwork>>>::None);
486        }
487        #[cfg(feature = "with-mac_address")]
488        ArrayType::MacAddress => {
489            let _ = args.add(Option::<Vec<Option<mac_address::MacAddress>>>::None);
490        }
491    }
492}
493
494#[cfg(all(test, feature = "postgres-array", feature = "with-json"))]
495mod tests {
496    use super::*;
497    use sqlx::Arguments;
498
499    #[test]
500    fn bind_null_json_array_adds_argument() {
501        let mut args = sqlx::postgres::PgArguments::default();
502        match_array(Array::Null(ArrayType::Json), &mut args);
503        assert_eq!(args.len(), 1);
504    }
505}