1use std::borrow::Cow;
6
7use crate::generated::oids::TYPES;
8use crate::oid;
9
10pub type Oid = u32;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct PgType {
16 pub oid: Oid,
17 pub typmod: i32,
18}
19
20impl PgType {
21 pub const fn new(oid: Oid) -> PgType {
23 PgType { oid, typmod: -1 }
24 }
25
26 pub const fn with_typmod(oid: Oid, typmod: i32) -> PgType {
27 PgType { oid, typmod }
28 }
29
30 pub fn info(self) -> Option<&'static TypeInfo> {
32 TypeInfo::get(self.oid)
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub struct TypeInfo {
39 pub oid: Oid,
40 pub name: &'static str,
41 pub kind: u8,
43 pub category: u8,
45 pub len: i16,
47 pub elem: Oid,
49 pub array: Oid,
51 pub delim: u8,
53}
54
55#[allow(clippy::too_many_arguments)]
57pub(crate) const fn t(
58 oid: Oid,
59 name: &'static str,
60 kind: u8,
61 category: u8,
62 len: i16,
63 elem: Oid,
64 array: Oid,
65 delim: u8,
66) -> TypeInfo {
67 TypeInfo { oid, name, kind, category, len, elem, array, delim }
68}
69
70impl TypeInfo {
71 pub fn get(oid: Oid) -> Option<&'static TypeInfo> {
73 TYPES.binary_search_by_key(&oid, |info| info.oid).ok().map(|i| &TYPES[i])
74 }
75
76 pub fn by_name(name: &str) -> Option<&'static TypeInfo> {
78 TYPES.iter().find(|info| info.name == name)
79 }
80
81 pub fn all() -> &'static [TypeInfo] {
83 &TYPES
84 }
85
86 pub fn is_array(&self) -> bool {
88 match self.oid {
89 oid::INT2VECTOR | oid::OIDVECTOR => false,
90 oid::RECORD_ARRAY => true,
91 _ => self.category == b'A' && self.elem != 0,
92 }
93 }
94}
95
96pub fn format_type(oid: Oid) -> Cow<'static, str> {
98 let name = match oid {
99 0 => "-",
100 oid::BOOL => "boolean",
101 oid::CHAR => "\"char\"",
102 oid::INT8 => "bigint",
103 oid::INT2 => "smallint",
104 oid::INT4 => "integer",
105 oid::FLOAT4 => "real",
106 oid::FLOAT8 => "double precision",
107 oid::BPCHAR => "character",
108 oid::VARCHAR => "character varying",
109 oid::TIME => "time without time zone",
110 oid::TIMESTAMP => "timestamp without time zone",
111 oid::TIMESTAMPTZ => "timestamp with time zone",
112 oid::TIMETZ => "time with time zone",
113 oid::VARBIT => "bit varying",
114 oid::ANY => "\"any\"",
115 71 => "pg_type",
116 75 => "pg_attribute",
117 81 => "pg_proc",
118 83 => "pg_class",
119 1248 => "pg_database",
120 2173 => "pg_parameter_acl",
121 2842 => "pg_authid",
122 2843 => "pg_auth_members",
123 4066 => "pg_shseclabel",
124 6101 => "pg_subscription",
125 _ => match TypeInfo::get(oid) {
126 Some(info) if info.is_array() => {
127 return Cow::Owned(format!("{}[]", format_type(info.elem)));
128 }
129 Some(info) => info.name,
130 None => "???",
131 },
132 };
133 Cow::Borrowed(name)
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::oid;
140
141 #[test]
142 fn the_table_has_the_types_that_clients_ask_for() {
143 for (oid, name) in [
145 (oid::BOOL, "bool"),
146 (oid::BYTEA, "bytea"),
147 (oid::CHAR, "char"),
148 (oid::NAME, "name"),
149 (oid::INT8, "int8"),
150 (oid::INT2, "int2"),
151 (oid::INT4, "int4"),
152 (oid::TEXT, "text"),
153 (oid::OID, "oid"),
154 (oid::FLOAT4, "float4"),
155 (oid::FLOAT8, "float8"),
156 (oid::UNKNOWN, "unknown"),
157 (oid::VARCHAR, "varchar"),
158 (oid::BPCHAR, "bpchar"),
159 (oid::DATE, "date"),
160 (oid::TIMESTAMPTZ, "timestamptz"),
161 (oid::NUMERIC, "numeric"),
162 (oid::UUID, "uuid"),
163 (oid::JSONB, "jsonb"),
164 (oid::INT4_ARRAY, "_int4"),
165 (oid::TEXT_ARRAY, "_text"),
166 (oid::RECORD, "record"),
167 ] {
168 assert_eq!(TypeInfo::get(oid).map(|info| info.name), Some(name));
169 assert_eq!(TypeInfo::by_name(name).map(|info| info.oid), Some(oid));
170 }
171 assert_eq!(
172 [oid::BOOL, oid::INT4, oid::TEXT, oid::INT4_ARRAY, oid::RECORD],
173 [16, 23, 25, 1007, 2249]
174 );
175 }
176
177 #[test]
178 fn an_array_points_at_its_element_and_back() {
179 let int4 = PgType::new(oid::INT4).info().unwrap();
180 let array = TypeInfo::get(int4.array).unwrap();
181 assert!(array.is_array() && !int4.is_array());
182 assert_eq!((array.elem, array.len, array.delim), (oid::INT4, -1, b','));
183 assert_eq!(TypeInfo::get(oid::BOX_ARRAY).unwrap().delim, b';');
184 assert!(!TypeInfo::get(oid::NAME).unwrap().is_array());
186 assert!(TypeInfo::all().windows(2).all(|w| w[0].oid < w[1].oid));
187 assert_eq!(TypeInfo::get(0), None);
188 let int2vector = TypeInfo::get(oid::INT2VECTOR).unwrap();
189 assert!(int2vector.category == b'A' && !int2vector.is_array());
190 assert!(TypeInfo::get(oid::RECORD_ARRAY).unwrap().is_array());
191 }
192
193 #[test]
194 fn format_type_gives_the_sql_name() {
195 assert_eq!(format_type(oid::INT4), "integer");
196 assert_eq!(format_type(oid::INT4_ARRAY), "integer[]");
197 assert_eq!(format_type(oid::INT2VECTOR), "int2vector");
198 assert_eq!(format_type(oid::RECORD_ARRAY), "record[]");
199 assert_eq!(format_type(oid::TEXT), "text");
200 assert_eq!((format_type(0), format_type(9999)), ("-".into(), "???".into()));
201 }
202}