Skip to main content

drasi_postgres_common/
oid.rs

1// Copyright 2025 The Drasi Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! PostgreSQL type OID constants and information_schema helpers.
16
17pub const BOOL: u32 = 16;
18pub const BYTEA: u32 = 17;
19pub const NAME: u32 = 19;
20pub const INT8: u32 = 20;
21pub const INT2: u32 = 21;
22pub const INT4: u32 = 23;
23pub const TEXT: u32 = 25;
24pub const JSON: u32 = 114;
25pub const FLOAT4: u32 = 700;
26pub const FLOAT8: u32 = 701;
27pub const CHAR: u32 = 1042;
28pub const VARCHAR: u32 = 1043;
29pub const DATE: u32 = 1082;
30pub const TIME: u32 = 1083;
31pub const TIMESTAMP: u32 = 1114;
32pub const TIMESTAMPTZ: u32 = 1184;
33pub const NUMERIC: u32 = 1700;
34pub const UUID: u32 = 2950;
35pub const JSONB: u32 = 3802;
36
37// Common 1-D array OIDs
38pub const BOOL_ARRAY: u32 = 1000;
39pub const BYTEA_ARRAY: u32 = 1001;
40pub const INT2_ARRAY: u32 = 1005;
41pub const INT4_ARRAY: u32 = 1007;
42pub const TEXT_ARRAY: u32 = 1009;
43pub const VARCHAR_ARRAY: u32 = 1015;
44pub const INT8_ARRAY: u32 = 1016;
45pub const FLOAT4_ARRAY: u32 = 1021;
46pub const FLOAT8_ARRAY: u32 = 1022;
47pub const NUMERIC_ARRAY: u32 = 1231;
48pub const UUID_ARRAY: u32 = 2951;
49pub const JSON_ARRAY: u32 = 199;
50pub const JSONB_ARRAY: u32 = 3807;
51pub const TIMESTAMP_ARRAY: u32 = 1115;
52pub const TIMESTAMPTZ_ARRAY: u32 = 1185;
53pub const DATE_ARRAY: u32 = 1182;
54pub const TIME_ARRAY: u32 = 1183;
55
56/// Map an `information_schema.columns.data_type` / `udt_name` pair to a type OID.
57/// Prefer querying `pg_attribute.atttypid` when possible; this is a fallback.
58pub fn oid_from_information_schema(data_type: &str, udt_name: Option<&str>) -> u32 {
59    // Arrays report data_type = ARRAY and the element type in udt_name (e.g. `_int4`).
60    if data_type.eq_ignore_ascii_case("ARRAY") {
61        if let Some(udt) = udt_name {
62            return match udt {
63                "_bool" => BOOL_ARRAY,
64                "_bytea" => BYTEA_ARRAY,
65                "_int2" => INT2_ARRAY,
66                "_int4" => INT4_ARRAY,
67                "_int8" => INT8_ARRAY,
68                "_text" => TEXT_ARRAY,
69                "_varchar" => VARCHAR_ARRAY,
70                "_float4" => FLOAT4_ARRAY,
71                "_float8" => FLOAT8_ARRAY,
72                "_numeric" => NUMERIC_ARRAY,
73                "_uuid" => UUID_ARRAY,
74                "_json" => JSON_ARRAY,
75                "_jsonb" => JSONB_ARRAY,
76                "_timestamp" => TIMESTAMP_ARRAY,
77                "_timestamptz" => TIMESTAMPTZ_ARRAY,
78                "_date" => DATE_ARRAY,
79                "_time" => TIME_ARRAY,
80                _ => TEXT_ARRAY,
81            };
82        }
83        return TEXT_ARRAY;
84    }
85
86    match data_type {
87        "boolean" => BOOL,
88        "bytea" => BYTEA,
89        "name" => NAME,
90        "bigint" => INT8,
91        "smallint" => INT2,
92        "integer" => INT4,
93        "text" => TEXT,
94        "json" => JSON,
95        "real" => FLOAT4,
96        "double precision" => FLOAT8,
97        "character" => CHAR,
98        "character varying" => VARCHAR,
99        "date" => DATE,
100        "time without time zone" | "time with time zone" => TIME,
101        "timestamp without time zone" => TIMESTAMP,
102        "timestamp with time zone" => TIMESTAMPTZ,
103        "numeric" | "decimal" => NUMERIC,
104        "uuid" => UUID,
105        "jsonb" => JSONB,
106        _ => {
107            // Fall back to udt_name for domains / aliases
108            match udt_name.unwrap_or("") {
109                "bool" => BOOL,
110                "bytea" => BYTEA,
111                "int2" => INT2,
112                "int4" => INT4,
113                "int8" => INT8,
114                "float4" => FLOAT4,
115                "float8" => FLOAT8,
116                "numeric" => NUMERIC,
117                "text" => TEXT,
118                "varchar" => VARCHAR,
119                "bpchar" => CHAR,
120                "uuid" => UUID,
121                "json" => JSON,
122                "jsonb" => JSONB,
123                "date" => DATE,
124                "time" | "timetz" => TIME,
125                "timestamp" => TIMESTAMP,
126                "timestamptz" => TIMESTAMPTZ,
127                _ => TEXT,
128            }
129        }
130    }
131}
132
133/// Returns the element OID for a known array OID, if any.
134pub fn array_element_oid(array_oid: u32) -> Option<u32> {
135    match array_oid {
136        BOOL_ARRAY => Some(BOOL),
137        BYTEA_ARRAY => Some(BYTEA),
138        INT2_ARRAY => Some(INT2),
139        INT4_ARRAY => Some(INT4),
140        INT8_ARRAY => Some(INT8),
141        TEXT_ARRAY => Some(TEXT),
142        VARCHAR_ARRAY => Some(VARCHAR),
143        FLOAT4_ARRAY => Some(FLOAT4),
144        FLOAT8_ARRAY => Some(FLOAT8),
145        NUMERIC_ARRAY => Some(NUMERIC),
146        UUID_ARRAY => Some(UUID),
147        JSON_ARRAY => Some(JSON),
148        JSONB_ARRAY => Some(JSONB),
149        TIMESTAMP_ARRAY => Some(TIMESTAMP),
150        TIMESTAMPTZ_ARRAY => Some(TIMESTAMPTZ),
151        DATE_ARRAY => Some(DATE),
152        TIME_ARRAY => Some(TIME),
153        _ => None,
154    }
155}
156
157/// Returns true if the OID is a known array type we can decode.
158pub fn is_array_oid(oid: u32) -> bool {
159    array_element_oid(oid).is_some()
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn information_schema_integer() {
168        assert_eq!(oid_from_information_schema("integer", None), INT4);
169    }
170
171    #[test]
172    fn information_schema_array_int4() {
173        assert_eq!(
174            oid_from_information_schema("ARRAY", Some("_int4")),
175            INT4_ARRAY
176        );
177    }
178
179    #[test]
180    fn information_schema_array_unknown_defaults_to_text_array() {
181        assert_eq!(
182            oid_from_information_schema("ARRAY", Some("_unknown")),
183            TEXT_ARRAY
184        );
185    }
186
187    #[test]
188    fn information_schema_user_defined_uuid() {
189        assert_eq!(
190            oid_from_information_schema("USER-DEFINED", Some("uuid")),
191            UUID
192        );
193    }
194
195    #[test]
196    fn information_schema_unknown_defaults_to_text() {
197        assert_eq!(oid_from_information_schema("totally_unknown", None), TEXT);
198    }
199}