pgx/datum/
mod.rs

1/*
2Portions Copyright 2019-2021 ZomboDB, LLC.
3Portions Copyright 2021-2022 Technology Concepts & Design, Inc. <support@tcdi.com>
4
5All rights reserved.
6
7Use of this source code is governed by the MIT license that can be found in the LICENSE file.
8*/
9
10//! Handing for easily converting Postgres Datum types into their corresponding Rust types
11//! and converting Rust types into their corresponding Postgres types
12mod anyarray;
13mod anyelement;
14mod array;
15mod date;
16mod from;
17mod geo;
18mod inet;
19mod internal;
20mod interval;
21mod into;
22mod item_pointer_data;
23mod json;
24pub mod numeric;
25pub mod numeric_support;
26#[deny(unsafe_op_in_unsafe_fn)]
27mod range;
28mod time;
29mod time_stamp;
30mod time_stamp_with_timezone;
31mod time_with_timezone;
32mod tuples;
33mod uuid;
34mod varlena;
35
36pub use self::time::*;
37pub use self::uuid::*;
38pub use anyarray::*;
39pub use anyelement::*;
40pub use array::*;
41pub use date::*;
42pub use from::*;
43pub use geo::*;
44pub use inet::*;
45pub use internal::*;
46pub use interval::*;
47pub use into::*;
48pub use item_pointer_data::*;
49pub use json::*;
50pub use numeric::{AnyNumeric, Numeric};
51use once_cell::sync::Lazy;
52pub use range::*;
53use std::any::TypeId;
54pub use time_stamp::*;
55pub use time_stamp_with_timezone::*;
56pub use time_with_timezone::*;
57pub use tuples::*;
58pub use varlena::*;
59
60use crate::PgBox;
61use pgx_sql_entity_graph::RustSqlMapping;
62
63/// A tagging trait to indicate a user type is also meant to be used by Postgres
64/// Implemented automatically by `#[derive(PostgresType)]`
65pub trait PostgresType {}
66
67/// A type which can have it's [`core::any::TypeId`]s registered for Rust to SQL mapping.
68///
69/// An example use of this trait:
70///
71/// ```rust
72/// use pgx::prelude::*;
73/// use serde::{Serialize, Deserialize};
74///
75/// #[derive(Debug, Clone, Copy, Serialize, Deserialize, PostgresType)]
76/// struct Treat<'a> { best_part: &'a str, };
77///
78/// let mut mappings = Default::default();
79/// let treat_string = stringify!(Treat).to_string();
80/// <Treat<'static> as pgx::datum::WithTypeIds>::register_with_refs(&mut mappings, treat_string.clone());
81///
82/// assert!(mappings.iter().any(|x| x.id == core::any::TypeId::of::<Treat<'static>>()));
83/// ```
84///
85/// This trait uses the fact that inherent implementations are a higher priority than trait
86/// implementations.
87pub trait WithTypeIds {
88    const ITEM_ID: Lazy<TypeId>;
89    const OPTION_ID: Lazy<Option<TypeId>>;
90    const VEC_ID: Lazy<Option<TypeId>>;
91    const VEC_OPTION_ID: Lazy<Option<TypeId>>;
92    const OPTION_VEC_ID: Lazy<Option<TypeId>>;
93    const OPTION_VEC_OPTION_ID: Lazy<Option<TypeId>>;
94    const ARRAY_ID: Lazy<Option<TypeId>>;
95    const OPTION_ARRAY_ID: Lazy<Option<TypeId>>;
96    const VARIADICARRAY_ID: Lazy<Option<TypeId>>;
97    const OPTION_VARIADICARRAY_ID: Lazy<Option<TypeId>>;
98    const VARLENA_ID: Lazy<Option<TypeId>>;
99    const OPTION_VARLENA_ID: Lazy<Option<TypeId>>;
100
101    fn register_with_refs(map: &mut std::collections::HashSet<RustSqlMapping>, single_sql: String)
102    where
103        Self: 'static,
104    {
105        Self::register(map, single_sql.clone());
106        <&Self as WithTypeIds>::register(map, single_sql.clone());
107        <&mut Self as WithTypeIds>::register(map, single_sql);
108    }
109
110    fn register_sized_with_refs(
111        _map: &mut std::collections::HashSet<RustSqlMapping>,
112        _single_sql: String,
113    ) where
114        Self: 'static,
115    {
116        ()
117    }
118
119    fn register_sized(_map: &mut std::collections::HashSet<RustSqlMapping>, _single_sql: String)
120    where
121        Self: 'static,
122    {
123        ()
124    }
125
126    fn register_varlena_with_refs(
127        _map: &mut std::collections::HashSet<RustSqlMapping>,
128        _single_sql: String,
129    ) where
130        Self: 'static,
131    {
132        ()
133    }
134
135    fn register_varlena(_map: &mut std::collections::HashSet<RustSqlMapping>, _single_sql: String)
136    where
137        Self: 'static,
138    {
139        ()
140    }
141
142    fn register_array_with_refs(
143        _map: &mut std::collections::HashSet<RustSqlMapping>,
144        _single_sql: String,
145    ) where
146        Self: 'static,
147    {
148        ()
149    }
150
151    fn register_array(_map: &mut std::collections::HashSet<RustSqlMapping>, _single_sql: String)
152    where
153        Self: 'static,
154    {
155        ()
156    }
157
158    fn register(set: &mut std::collections::HashSet<RustSqlMapping>, single_sql: String)
159    where
160        Self: 'static,
161    {
162        let rust = core::any::type_name::<Self>();
163        assert_eq!(
164            set.insert(RustSqlMapping {
165                sql: single_sql.clone(),
166                rust: rust.to_string(),
167                id: *Self::ITEM_ID,
168            }),
169            true,
170            "Cannot set mapping of `{}` twice, was already `{}`.",
171            rust,
172            single_sql,
173        );
174    }
175}
176
177impl<T: 'static + ?Sized> WithTypeIds for T {
178    const ITEM_ID: Lazy<TypeId> = Lazy::new(|| TypeId::of::<T>());
179    const OPTION_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
180    const VEC_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
181    const VEC_OPTION_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
182    const OPTION_VEC_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
183    const OPTION_VEC_OPTION_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
184    const ARRAY_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
185    const OPTION_ARRAY_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
186    const VARIADICARRAY_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
187    const OPTION_VARIADICARRAY_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
188    const VARLENA_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
189    const OPTION_VARLENA_ID: Lazy<Option<TypeId>> = Lazy::new(|| None);
190}
191
192/// A type which can have its [`core::any::TypeId`]s registered for Rust to SQL mapping.
193///
194/// An example use of this trait:
195///
196/// ```rust
197/// use pgx::prelude::*;
198/// use serde::{Serialize, Deserialize};
199///
200/// #[derive(Debug, Clone, Copy, Serialize, Deserialize, PostgresType)]
201/// pub struct Treat<'a> { best_part: &'a str, };
202///
203/// let mut mappings = Default::default();
204/// let treat_string = stringify!(Treat).to_string();
205///
206/// pgx::datum::WithSizedTypeIds::<Treat<'static>>::register_sized_with_refs(
207///     &mut mappings,
208///     treat_string.clone()
209/// );
210///
211/// assert!(mappings.iter().any(|x| x.id == core::any::TypeId::of::<Option<Treat<'static>>>()));
212/// ```
213///
214/// This trait uses the fact that inherent implementations are a higher priority than trait
215/// implementations.
216pub struct WithSizedTypeIds<T>(pub core::marker::PhantomData<T>);
217
218impl<T: 'static> WithSizedTypeIds<T> {
219    pub const PG_BOX_ID: Lazy<Option<TypeId>> = Lazy::new(|| Some(TypeId::of::<PgBox<T>>()));
220    pub const PG_BOX_OPTION_ID: Lazy<Option<TypeId>> =
221        Lazy::new(|| Some(TypeId::of::<PgBox<Option<T>>>()));
222    pub const PG_BOX_VEC_ID: Lazy<Option<TypeId>> =
223        Lazy::new(|| Some(TypeId::of::<PgBox<Vec<T>>>()));
224    pub const OPTION_ID: Lazy<Option<TypeId>> = Lazy::new(|| Some(TypeId::of::<Option<T>>()));
225    pub const VEC_ID: Lazy<Option<TypeId>> = Lazy::new(|| Some(TypeId::of::<Vec<T>>()));
226    pub const VEC_OPTION_ID: Lazy<Option<TypeId>> =
227        Lazy::new(|| Some(TypeId::of::<Vec<Option<T>>>()));
228    pub const OPTION_VEC_ID: Lazy<Option<TypeId>> =
229        Lazy::new(|| Some(TypeId::of::<Option<Vec<T>>>()));
230    pub const OPTION_VEC_OPTION_ID: Lazy<Option<TypeId>> =
231        Lazy::new(|| Some(TypeId::of::<Option<Vec<Option<T>>>>()));
232
233    pub fn register_sized_with_refs(
234        map: &mut std::collections::HashSet<RustSqlMapping>,
235        single_sql: String,
236    ) where
237        Self: 'static,
238    {
239        WithSizedTypeIds::<T>::register_sized(map, single_sql.clone());
240        WithSizedTypeIds::<&T>::register_sized(map, single_sql.clone());
241        WithSizedTypeIds::<&mut T>::register_sized(map, single_sql);
242    }
243
244    pub fn register_sized(map: &mut std::collections::HashSet<RustSqlMapping>, single_sql: String) {
245        let set_sql = format!("{}[]", single_sql);
246
247        if let Some(id) = *WithSizedTypeIds::<T>::PG_BOX_ID {
248            let rust = core::any::type_name::<crate::PgBox<T>>().to_string();
249            assert_eq!(
250                map.insert(RustSqlMapping {
251                    sql: single_sql.clone(),
252                    rust: rust.to_string(),
253                    id: id,
254                }),
255                true,
256                "Cannot map `{}` twice.",
257                rust,
258            );
259        }
260
261        if let Some(id) = *WithSizedTypeIds::<T>::PG_BOX_OPTION_ID {
262            let rust = core::any::type_name::<crate::PgBox<Option<T>>>().to_string();
263            assert_eq!(
264                map.insert(RustSqlMapping {
265                    sql: single_sql.clone(),
266                    rust: rust.to_string(),
267                    id: id,
268                }),
269                true,
270                "Cannot map `{}` twice.",
271                rust,
272            );
273        }
274
275        if let Some(id) = *WithSizedTypeIds::<T>::PG_BOX_VEC_ID {
276            let rust = core::any::type_name::<crate::PgBox<Vec<T>>>().to_string();
277            assert_eq!(
278                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
279                true,
280                "Cannot map `{}` twice.",
281                rust,
282            );
283        }
284
285        if let Some(id) = *WithSizedTypeIds::<T>::OPTION_ID {
286            let rust = core::any::type_name::<Option<T>>().to_string();
287            assert_eq!(
288                map.insert(RustSqlMapping {
289                    sql: single_sql.clone(),
290                    rust: rust.to_string(),
291                    id: id,
292                }),
293                true,
294                "Cannot map `{}` twice.",
295                rust,
296            );
297        }
298
299        if let Some(id) = *WithSizedTypeIds::<T>::VEC_ID {
300            let rust = core::any::type_name::<T>().to_string();
301            assert_eq!(
302                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
303                true,
304                "Cannot map `{}` twice.",
305                rust,
306            );
307        }
308        if let Some(id) = *WithSizedTypeIds::<T>::VEC_OPTION_ID {
309            let rust = core::any::type_name::<Vec<Option<T>>>();
310            assert_eq!(
311                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
312                true,
313                "Cannot map `{}` twice.",
314                rust,
315            );
316        }
317        if let Some(id) = *WithSizedTypeIds::<T>::OPTION_VEC_ID {
318            let rust = core::any::type_name::<Option<Vec<T>>>();
319            assert_eq!(
320                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
321                true,
322                "Cannot map `{}` twice.",
323                rust,
324            );
325        }
326        if let Some(id) = *WithSizedTypeIds::<T>::OPTION_VEC_OPTION_ID {
327            let rust = core::any::type_name::<Option<Vec<Option<T>>>>();
328            assert_eq!(
329                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
330                true,
331                "Cannot map `{}` twice.",
332                rust,
333            );
334        }
335    }
336}
337
338/// An [`Array`] compatible type which can have its [`core::any::TypeId`]s registered for Rust to SQL mapping.
339///
340/// An example use of this trait:
341///
342/// ```rust
343/// use pgx::prelude::*;
344/// use serde::{Serialize, Deserialize};
345///
346/// #[derive(Debug, Clone, Serialize, Deserialize, PostgresType)]
347/// pub struct Treat { best_part: String, };
348///
349/// let mut mappings = Default::default();
350/// let treat_string = stringify!(Treat).to_string();
351///
352/// pgx::datum::WithArrayTypeIds::<Treat>::register_array_with_refs(
353///     &mut mappings,
354///     treat_string.clone()
355/// );
356///
357/// assert!(mappings.iter().any(|x| x.id == core::any::TypeId::of::<Array<Treat>>()));
358/// ```
359///
360/// This trait uses the fact that inherent implementations are a higher priority than trait
361/// implementations.
362pub struct WithArrayTypeIds<T>(pub core::marker::PhantomData<T>);
363
364impl<T: FromDatum + 'static> WithArrayTypeIds<T> {
365    pub const ARRAY_ID: Lazy<Option<TypeId>> = Lazy::new(|| Some(TypeId::of::<Array<T>>()));
366    pub const OPTION_ARRAY_ID: Lazy<Option<TypeId>> =
367        Lazy::new(|| Some(TypeId::of::<Option<Array<T>>>()));
368    pub const VARIADICARRAY_ID: Lazy<Option<TypeId>> =
369        Lazy::new(|| Some(TypeId::of::<VariadicArray<T>>()));
370    pub const OPTION_VARIADICARRAY_ID: Lazy<Option<TypeId>> =
371        Lazy::new(|| Some(TypeId::of::<Option<VariadicArray<T>>>()));
372
373    pub fn register_array_with_refs(
374        map: &mut std::collections::HashSet<RustSqlMapping>,
375        single_sql: String,
376    ) where
377        Self: 'static,
378    {
379        WithArrayTypeIds::<T>::register_array(map, single_sql.clone());
380        WithArrayTypeIds::<&T>::register_array(map, single_sql.clone());
381        WithArrayTypeIds::<&mut T>::register_array(map, single_sql);
382    }
383
384    pub fn register_array(map: &mut std::collections::HashSet<RustSqlMapping>, single_sql: String) {
385        let set_sql = format!("{}[]", single_sql);
386
387        if let Some(id) = *WithArrayTypeIds::<T>::ARRAY_ID {
388            let rust = core::any::type_name::<Array<T>>().to_string();
389            assert_eq!(
390                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
391                true,
392                "Cannot map `{}` twice.",
393                rust,
394            );
395        }
396        if let Some(id) = *WithArrayTypeIds::<T>::OPTION_ARRAY_ID {
397            let rust = core::any::type_name::<Option<Array<T>>>().to_string();
398            assert_eq!(
399                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
400                true,
401                "Cannot map `{}` twice.",
402                rust,
403            );
404        }
405
406        if let Some(id) = *WithArrayTypeIds::<T>::VARIADICARRAY_ID {
407            let rust = core::any::type_name::<VariadicArray<T>>().to_string();
408            assert_eq!(
409                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
410                true,
411                "Cannot map `{}` twice.",
412                rust,
413            );
414        }
415        if let Some(id) = *WithArrayTypeIds::<T>::OPTION_VARIADICARRAY_ID {
416            let rust = core::any::type_name::<Option<VariadicArray<T>>>().to_string();
417            assert_eq!(
418                map.insert(RustSqlMapping { sql: set_sql.clone(), rust: rust.to_string(), id: id }),
419                true,
420                "Cannot map `{}` twice.",
421                rust,
422            );
423        }
424    }
425}
426
427/// A [`PgVarlena`] compatible type which can have its [`core::any::TypeId`]s registered for Rust to SQL mapping.
428///
429/// An example use of this trait:
430///
431/// ```rust
432/// use pgx::prelude::*;
433/// use serde::{Serialize, Deserialize};
434///
435/// #[derive(Debug, Clone, Copy, Serialize, Deserialize, PostgresType)]
436/// pub struct Treat<'a> { best_part: &'a str, };
437///
438/// let mut mappings = Default::default();
439/// let treat_string = stringify!(Treat).to_string();
440///
441/// pgx::datum::WithVarlenaTypeIds::<Treat<'static>>::register_varlena_with_refs(
442///     &mut mappings,
443///     treat_string.clone()
444/// );
445///
446/// assert!(mappings.iter().any(|x| x.id == core::any::TypeId::of::<PgVarlena<Treat<'static>>>()));
447/// ```
448///
449/// This trait uses the fact that inherent implementations are a higher priority than trait
450/// implementations.
451pub struct WithVarlenaTypeIds<T>(pub core::marker::PhantomData<T>);
452
453impl<T: Copy + 'static> WithVarlenaTypeIds<T> {
454    pub const VARLENA_ID: Lazy<Option<TypeId>> = Lazy::new(|| Some(TypeId::of::<PgVarlena<T>>()));
455    pub const PG_BOX_VARLENA_ID: Lazy<Option<TypeId>> =
456        Lazy::new(|| Some(TypeId::of::<PgBox<PgVarlena<T>>>()));
457    pub const OPTION_VARLENA_ID: Lazy<Option<TypeId>> =
458        Lazy::new(|| Some(TypeId::of::<Option<PgVarlena<T>>>()));
459
460    pub fn register_varlena_with_refs(
461        map: &mut std::collections::HashSet<RustSqlMapping>,
462        single_sql: String,
463    ) where
464        Self: 'static,
465    {
466        WithVarlenaTypeIds::<T>::register_varlena(map, single_sql.clone());
467        WithVarlenaTypeIds::<&T>::register_varlena(map, single_sql.clone());
468        WithVarlenaTypeIds::<&mut T>::register_varlena(map, single_sql);
469    }
470
471    pub fn register_varlena(
472        map: &mut std::collections::HashSet<RustSqlMapping>,
473        single_sql: String,
474    ) {
475        if let Some(id) = *WithVarlenaTypeIds::<T>::VARLENA_ID {
476            let rust = core::any::type_name::<PgVarlena<T>>();
477            assert_eq!(
478                map.insert(RustSqlMapping {
479                    sql: single_sql.clone(),
480                    rust: rust.to_string(),
481                    id: id,
482                }),
483                true,
484                "Cannot map `{}` twice.",
485                rust,
486            );
487        }
488
489        if let Some(id) = *WithVarlenaTypeIds::<T>::PG_BOX_VARLENA_ID {
490            let rust = core::any::type_name::<PgBox<PgVarlena<T>>>().to_string();
491            assert_eq!(
492                map.insert(RustSqlMapping {
493                    sql: single_sql.clone(),
494                    rust: rust.to_string(),
495                    id: id,
496                }),
497                true,
498                "Cannot map `{}` twice.",
499                rust,
500            );
501        }
502        if let Some(id) = *WithVarlenaTypeIds::<T>::OPTION_VARLENA_ID {
503            let rust = core::any::type_name::<Option<PgVarlena<T>>>().to_string();
504            assert_eq!(
505                map.insert(RustSqlMapping {
506                    sql: single_sql.clone(),
507                    rust: rust.to_string(),
508                    id: id,
509                }),
510                true,
511                "Cannot map `{}` twice.",
512                rust,
513            );
514        }
515    }
516}