Skip to main content

sqlmodel_postgres/types/
mod.rs

1//! PostgreSQL type system and type conversion.
2//!
3//! This module provides:
4//! - OID constants for PostgreSQL built-in types
5//! - Encoding/decoding between Rust types and PostgreSQL wire format
6//! - Type registry for runtime type information
7//!
8//! # Example
9//!
10//! ```rust,ignore
11//! use sqlmodel_postgres::types::{oid, encode::Format, decode::decode_value};
12//!
13//! // Decode a binary integer from PostgreSQL
14//! let value = decode_value(oid::INT4, Some(&[0, 0, 0, 42]), Format::Binary)?;
15//! assert_eq!(value, Value::Int(42));
16//! ```
17
18pub mod decode;
19pub mod encode;
20pub mod oid;
21
22use std::collections::HashMap;
23
24pub use decode::{BinaryDecode, Decode, TextDecode, decode_value};
25pub use encode::{BinaryEncode, Encode, Format, TextEncode, encode_value};
26
27/// Category of a PostgreSQL type.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum TypeCategory {
30    /// Boolean types (bool)
31    Boolean,
32    /// Numeric types (int2, int4, int8, float4, float8, numeric)
33    Numeric,
34    /// String types (text, varchar, char, name)
35    String,
36    /// Date/time types (date, time, timestamp, interval)
37    DateTime,
38    /// Binary types (bytea)
39    Binary,
40    /// JSON types (json, jsonb)
41    Json,
42    /// UUID type
43    Uuid,
44    /// Array types
45    Array,
46    /// Range types
47    Range,
48    /// Composite/record types
49    Composite,
50    /// Network address types (inet, cidr, macaddr)
51    Network,
52    /// Geometric types (point, line, circle, etc.)
53    Geometric,
54    /// Unknown or custom types
55    Unknown,
56}
57
58/// Information about a PostgreSQL type.
59#[derive(Debug, Clone)]
60pub struct TypeInfo {
61    /// The type's OID
62    pub oid: u32,
63    /// The type's name (e.g., "int4", "text")
64    pub name: &'static str,
65    /// OID of the array type for this element type (if any)
66    pub array_oid: Option<u32>,
67    /// OID of the element type (if this is an array type)
68    pub element_oid: Option<u32>,
69    /// Type category
70    pub category: TypeCategory,
71    /// Size in bytes (-1 for variable, -2 for null-terminated)
72    pub size: i16,
73    /// Whether this type supports binary format
74    pub binary_format: bool,
75}
76
77impl TypeInfo {
78    /// Create a new type info.
79    const fn new(
80        oid: u32,
81        name: &'static str,
82        category: TypeCategory,
83        size: i16,
84        array_oid: Option<u32>,
85    ) -> Self {
86        Self {
87            oid,
88            name,
89            array_oid,
90            element_oid: None,
91            category,
92            size,
93            binary_format: true,
94        }
95    }
96
97    /// Create type info for an array type.
98    const fn array(oid: u32, name: &'static str, element_oid: u32) -> Self {
99        Self {
100            oid,
101            name,
102            array_oid: None,
103            element_oid: Some(element_oid),
104            category: TypeCategory::Array,
105            size: -1,
106            binary_format: true,
107        }
108    }
109}
110
111/// Registry of PostgreSQL types.
112///
113/// Provides lookup by OID or name for type information.
114pub struct TypeRegistry {
115    by_oid: HashMap<u32, TypeInfo>,
116    by_name: HashMap<&'static str, u32>,
117}
118
119impl Default for TypeRegistry {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125impl TypeRegistry {
126    /// Create a new type registry with all built-in types.
127    #[must_use]
128    pub fn new() -> Self {
129        let mut registry = Self {
130            by_oid: HashMap::new(),
131            by_name: HashMap::new(),
132        };
133
134        // Register all built-in types
135        registry.register_builtins();
136
137        registry
138    }
139
140    /// Look up type info by OID.
141    #[must_use]
142    pub fn get(&self, oid: u32) -> Option<&TypeInfo> {
143        self.by_oid.get(&oid)
144    }
145
146    /// Look up type info by name.
147    #[must_use]
148    pub fn by_name(&self, name: &str) -> Option<&TypeInfo> {
149        self.by_name.get(name).and_then(|oid| self.by_oid.get(oid))
150    }
151
152    /// Get the type category for an OID.
153    #[must_use]
154    pub fn category(&self, oid: u32) -> TypeCategory {
155        self.get(oid).map_or(TypeCategory::Unknown, |t| t.category)
156    }
157
158    /// Check if a type supports binary format.
159    #[must_use]
160    pub fn supports_binary(&self, oid: u32) -> bool {
161        self.get(oid).is_some_and(|t| t.binary_format)
162    }
163
164    /// Register a custom type.
165    pub fn register(&mut self, info: TypeInfo) {
166        self.by_name.insert(info.name, info.oid);
167        self.by_oid.insert(info.oid, info);
168    }
169
170    fn register_builtins(&mut self) {
171        // Boolean
172        self.register(TypeInfo::new(
173            oid::BOOL,
174            "bool",
175            TypeCategory::Boolean,
176            1,
177            Some(oid::BOOL_ARRAY),
178        ));
179
180        // Integers
181        self.register(TypeInfo::new(
182            oid::INT2,
183            "int2",
184            TypeCategory::Numeric,
185            2,
186            Some(oid::INT2_ARRAY),
187        ));
188        self.register(TypeInfo::new(
189            oid::INT4,
190            "int4",
191            TypeCategory::Numeric,
192            4,
193            Some(oid::INT4_ARRAY),
194        ));
195        self.register(TypeInfo::new(
196            oid::INT8,
197            "int8",
198            TypeCategory::Numeric,
199            8,
200            Some(oid::INT8_ARRAY),
201        ));
202
203        // Floats
204        self.register(TypeInfo::new(
205            oid::FLOAT4,
206            "float4",
207            TypeCategory::Numeric,
208            4,
209            Some(oid::FLOAT4_ARRAY),
210        ));
211        self.register(TypeInfo::new(
212            oid::FLOAT8,
213            "float8",
214            TypeCategory::Numeric,
215            8,
216            Some(oid::FLOAT8_ARRAY),
217        ));
218        self.register(TypeInfo::new(
219            oid::NUMERIC,
220            "numeric",
221            TypeCategory::Numeric,
222            -1,
223            Some(oid::NUMERIC_ARRAY),
224        ));
225
226        // Strings
227        self.register(TypeInfo::new(
228            oid::TEXT,
229            "text",
230            TypeCategory::String,
231            -1,
232            Some(oid::TEXT_ARRAY),
233        ));
234        self.register(TypeInfo::new(
235            oid::VARCHAR,
236            "varchar",
237            TypeCategory::String,
238            -1,
239            Some(oid::VARCHAR_ARRAY),
240        ));
241        self.register(TypeInfo::new(
242            oid::BPCHAR,
243            "bpchar",
244            TypeCategory::String,
245            -1,
246            None,
247        ));
248        self.register(TypeInfo::new(
249            oid::CHAR,
250            "char",
251            TypeCategory::String,
252            1,
253            Some(oid::CHAR_ARRAY),
254        ));
255        self.register(TypeInfo::new(
256            oid::NAME,
257            "name",
258            TypeCategory::String,
259            64,
260            Some(oid::NAME_ARRAY),
261        ));
262
263        // Binary
264        self.register(TypeInfo::new(
265            oid::BYTEA,
266            "bytea",
267            TypeCategory::Binary,
268            -1,
269            Some(oid::BYTEA_ARRAY),
270        ));
271
272        // Date/Time
273        self.register(TypeInfo::new(
274            oid::DATE,
275            "date",
276            TypeCategory::DateTime,
277            4,
278            Some(oid::DATE_ARRAY),
279        ));
280        self.register(TypeInfo::new(
281            oid::TIME,
282            "time",
283            TypeCategory::DateTime,
284            8,
285            Some(oid::TIME_ARRAY),
286        ));
287        self.register(TypeInfo::new(
288            oid::TIMETZ,
289            "timetz",
290            TypeCategory::DateTime,
291            12,
292            None,
293        ));
294        self.register(TypeInfo::new(
295            oid::TIMESTAMP,
296            "timestamp",
297            TypeCategory::DateTime,
298            8,
299            Some(oid::TIMESTAMP_ARRAY),
300        ));
301        self.register(TypeInfo::new(
302            oid::TIMESTAMPTZ,
303            "timestamptz",
304            TypeCategory::DateTime,
305            8,
306            Some(oid::TIMESTAMPTZ_ARRAY),
307        ));
308        self.register(TypeInfo::new(
309            oid::INTERVAL,
310            "interval",
311            TypeCategory::DateTime,
312            16,
313            Some(oid::INTERVAL_ARRAY),
314        ));
315
316        // UUID
317        self.register(TypeInfo::new(
318            oid::UUID,
319            "uuid",
320            TypeCategory::Uuid,
321            16,
322            Some(oid::UUID_ARRAY),
323        ));
324
325        // JSON
326        self.register(TypeInfo::new(
327            oid::JSON,
328            "json",
329            TypeCategory::Json,
330            -1,
331            Some(oid::JSON_ARRAY),
332        ));
333        self.register(TypeInfo::new(
334            oid::JSONB,
335            "jsonb",
336            TypeCategory::Json,
337            -1,
338            Some(oid::JSONB_ARRAY),
339        ));
340
341        // OID types
342        self.register(TypeInfo::new(
343            oid::OID,
344            "oid",
345            TypeCategory::Numeric,
346            4,
347            Some(oid::OID_ARRAY),
348        ));
349        self.register(TypeInfo::new(
350            oid::XID,
351            "xid",
352            TypeCategory::Numeric,
353            4,
354            None,
355        ));
356        self.register(TypeInfo::new(
357            oid::CID,
358            "cid",
359            TypeCategory::Numeric,
360            4,
361            None,
362        ));
363
364        // Network types
365        self.register(TypeInfo::new(
366            oid::INET,
367            "inet",
368            TypeCategory::Network,
369            -1,
370            None,
371        ));
372        self.register(TypeInfo::new(
373            oid::CIDR,
374            "cidr",
375            TypeCategory::Network,
376            -1,
377            None,
378        ));
379        self.register(TypeInfo::new(
380            oid::MACADDR,
381            "macaddr",
382            TypeCategory::Network,
383            6,
384            None,
385        ));
386
387        // Range types
388        self.register(TypeInfo::new(
389            oid::INT4RANGE,
390            "int4range",
391            TypeCategory::Range,
392            -1,
393            None,
394        ));
395        self.register(TypeInfo::new(
396            oid::INT8RANGE,
397            "int8range",
398            TypeCategory::Range,
399            -1,
400            None,
401        ));
402        self.register(TypeInfo::new(
403            oid::NUMRANGE,
404            "numrange",
405            TypeCategory::Range,
406            -1,
407            None,
408        ));
409        self.register(TypeInfo::new(
410            oid::TSRANGE,
411            "tsrange",
412            TypeCategory::Range,
413            -1,
414            None,
415        ));
416        self.register(TypeInfo::new(
417            oid::TSTZRANGE,
418            "tstzrange",
419            TypeCategory::Range,
420            -1,
421            None,
422        ));
423        self.register(TypeInfo::new(
424            oid::DATERANGE,
425            "daterange",
426            TypeCategory::Range,
427            -1,
428            None,
429        ));
430
431        // Array types
432        self.register(TypeInfo::array(oid::BOOL_ARRAY, "bool[]", oid::BOOL));
433        self.register(TypeInfo::array(oid::INT2_ARRAY, "int2[]", oid::INT2));
434        self.register(TypeInfo::array(oid::INT4_ARRAY, "int4[]", oid::INT4));
435        self.register(TypeInfo::array(oid::INT8_ARRAY, "int8[]", oid::INT8));
436        self.register(TypeInfo::array(oid::FLOAT4_ARRAY, "float4[]", oid::FLOAT4));
437        self.register(TypeInfo::array(oid::FLOAT8_ARRAY, "float8[]", oid::FLOAT8));
438        self.register(TypeInfo::array(oid::TEXT_ARRAY, "text[]", oid::TEXT));
439        self.register(TypeInfo::array(
440            oid::VARCHAR_ARRAY,
441            "varchar[]",
442            oid::VARCHAR,
443        ));
444        self.register(TypeInfo::array(oid::BYTEA_ARRAY, "bytea[]", oid::BYTEA));
445        self.register(TypeInfo::array(oid::DATE_ARRAY, "date[]", oid::DATE));
446        self.register(TypeInfo::array(oid::TIME_ARRAY, "time[]", oid::TIME));
447        self.register(TypeInfo::array(
448            oid::TIMESTAMP_ARRAY,
449            "timestamp[]",
450            oid::TIMESTAMP,
451        ));
452        self.register(TypeInfo::array(
453            oid::TIMESTAMPTZ_ARRAY,
454            "timestamptz[]",
455            oid::TIMESTAMPTZ,
456        ));
457        self.register(TypeInfo::array(
458            oid::INTERVAL_ARRAY,
459            "interval[]",
460            oid::INTERVAL,
461        ));
462        self.register(TypeInfo::array(
463            oid::NUMERIC_ARRAY,
464            "numeric[]",
465            oid::NUMERIC,
466        ));
467        self.register(TypeInfo::array(oid::UUID_ARRAY, "uuid[]", oid::UUID));
468        self.register(TypeInfo::array(oid::JSON_ARRAY, "json[]", oid::JSON));
469        self.register(TypeInfo::array(oid::JSONB_ARRAY, "jsonb[]", oid::JSONB));
470        self.register(TypeInfo::array(oid::OID_ARRAY, "oid[]", oid::OID));
471        self.register(TypeInfo::array(oid::CHAR_ARRAY, "char[]", oid::CHAR));
472        self.register(TypeInfo::array(oid::NAME_ARRAY, "name[]", oid::NAME));
473
474        // Special types
475        self.register(TypeInfo::new(
476            oid::UNKNOWN,
477            "unknown",
478            TypeCategory::Unknown,
479            -2,
480            None,
481        ));
482        self.register(TypeInfo::new(
483            oid::VOID,
484            "void",
485            TypeCategory::Unknown,
486            4,
487            None,
488        ));
489    }
490}
491
492#[cfg(test)]
493mod tests {
494    use super::*;
495
496    #[test]
497    fn test_type_registry_creation() {
498        let registry = TypeRegistry::new();
499
500        // Check basic types exist
501        assert!(registry.get(oid::BOOL).is_some());
502        assert!(registry.get(oid::INT4).is_some());
503        assert!(registry.get(oid::TEXT).is_some());
504
505        // Check by name
506        assert!(registry.by_name("int4").is_some());
507        assert!(registry.by_name("text").is_some());
508    }
509
510    #[test]
511    fn test_type_categories() {
512        let registry = TypeRegistry::new();
513
514        assert_eq!(registry.category(oid::BOOL), TypeCategory::Boolean);
515        assert_eq!(registry.category(oid::INT4), TypeCategory::Numeric);
516        assert_eq!(registry.category(oid::TEXT), TypeCategory::String);
517        assert_eq!(registry.category(oid::DATE), TypeCategory::DateTime);
518        assert_eq!(registry.category(oid::BYTEA), TypeCategory::Binary);
519        assert_eq!(registry.category(oid::JSON), TypeCategory::Json);
520        assert_eq!(registry.category(oid::UUID), TypeCategory::Uuid);
521        assert_eq!(registry.category(oid::INT4_ARRAY), TypeCategory::Array);
522    }
523
524    #[test]
525    fn test_array_types() {
526        let registry = TypeRegistry::new();
527
528        let int4 = registry.get(oid::INT4).unwrap();
529        assert_eq!(int4.array_oid, Some(oid::INT4_ARRAY));
530
531        let int4_array = registry.get(oid::INT4_ARRAY).unwrap();
532        assert_eq!(int4_array.element_oid, Some(oid::INT4));
533    }
534
535    #[test]
536    fn test_binary_format_support() {
537        let registry = TypeRegistry::new();
538
539        assert!(registry.supports_binary(oid::INT4));
540        assert!(registry.supports_binary(oid::TEXT));
541        assert!(registry.supports_binary(oid::BOOL));
542    }
543
544    #[test]
545    fn test_unknown_type() {
546        let registry = TypeRegistry::new();
547
548        assert_eq!(registry.category(999_999), TypeCategory::Unknown);
549        assert!(!registry.supports_binary(999_999));
550    }
551}