pgx/datum/
uuid.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*/
9use crate::{pg_sys, FromDatum, IntoDatum, PgMemoryContexts};
10use core::fmt::Write;
11use pgx_sql_entity_graph::metadata::{
12    ArgumentError, Returns, ReturnsError, SqlMapping, SqlTranslatable,
13};
14use std::ops::{Deref, DerefMut};
15
16const UUID_BYTES_LEN: usize = 16;
17pub type UuidBytes = [u8; UUID_BYTES_LEN];
18
19/// A Universally Unique Identifier (`UUID`) from PostgreSQL
20#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
21#[repr(transparent)]
22pub struct Uuid(UuidBytes);
23
24impl IntoDatum for Uuid {
25    #[inline]
26    fn into_datum(self) -> Option<pg_sys::Datum> {
27        let ptr = unsafe {
28            // SAFETY:  CurrentMemoryContext is always valid
29            PgMemoryContexts::CurrentMemoryContext.palloc_slice::<u8>(UUID_BYTES_LEN)
30        };
31        ptr.clone_from_slice(&self.0);
32
33        Some(ptr.as_ptr().into())
34    }
35
36    #[inline]
37    fn type_oid() -> pg_sys::Oid {
38        pg_sys::UUIDOID
39    }
40}
41
42impl FromDatum for Uuid {
43    #[inline]
44    unsafe fn from_polymorphic_datum(
45        datum: pg_sys::Datum,
46        is_null: bool,
47        _typoid: pg_sys::Oid,
48    ) -> Option<Uuid> {
49        if is_null {
50            None
51        } else {
52            let bytes =
53                std::slice::from_raw_parts(datum.cast_mut_ptr::<u8>() as *const u8, UUID_BYTES_LEN);
54            if let Ok(uuid) = Uuid::from_slice(bytes) {
55                Some(uuid)
56            } else {
57                None
58            }
59        }
60    }
61}
62
63enum UuidFormatCase {
64    Lowercase,
65    Uppercase,
66}
67
68impl Uuid {
69    pub fn from_bytes(b: UuidBytes) -> Self {
70        Uuid(b)
71    }
72
73    pub const fn as_bytes(&self) -> &UuidBytes {
74        &self.0
75    }
76
77    pub fn from_slice(b: &[u8]) -> Result<Uuid, String> {
78        let len = b.len();
79
80        if len != UUID_BYTES_LEN {
81            Err(format!("Expected UUID to be {} bytes, got {}", UUID_BYTES_LEN, len))?;
82        }
83
84        let mut bytes = [0; UUID_BYTES_LEN];
85        bytes.copy_from_slice(b);
86        Ok(Uuid::from_bytes(bytes))
87    }
88
89    fn format(&self, f: &mut std::fmt::Formatter<'_>, case: UuidFormatCase) -> std::fmt::Result {
90        let hyphenated = f.sign_minus();
91        for (i, b) in self.0.iter().enumerate() {
92            if hyphenated && (i == 4 || i == 6 || i == 8 || i == 10) {
93                f.write_char('-')?;
94            }
95            match case {
96                UuidFormatCase::Lowercase => write!(f, "{:02x}", b)?,
97                UuidFormatCase::Uppercase => write!(f, "{:02X}", b)?,
98            };
99        }
100        Ok(())
101    }
102}
103
104impl Deref for Uuid {
105    type Target = UuidBytes;
106
107    fn deref(&self) -> &Self::Target {
108        &self.0
109    }
110}
111
112impl DerefMut for Uuid {
113    fn deref_mut(&mut self) -> &mut Self::Target {
114        &mut self.0
115    }
116}
117
118impl std::fmt::Display for Uuid {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        write!(f, "{:-x}", self)
121    }
122}
123
124impl<'a> std::fmt::LowerHex for Uuid {
125    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
126        self.format(f, UuidFormatCase::Lowercase)
127    }
128}
129
130impl<'a> std::fmt::UpperHex for Uuid {
131    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
132        self.format(f, UuidFormatCase::Uppercase)
133    }
134}
135
136unsafe impl SqlTranslatable for crate::datum::Uuid {
137    fn argument_sql() -> Result<SqlMapping, ArgumentError> {
138        Ok(SqlMapping::literal("uuid"))
139    }
140    fn return_sql() -> Result<Returns, ReturnsError> {
141        Ok(Returns::One(SqlMapping::literal("uuid")))
142    }
143}