risingwave_common/types/
postgres_type.rs

1// Copyright 2025 RisingWave Labs
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
15use postgres_types::Type as PgType;
16
17use super::DataType;
18
19/// `DataType` information extracted from PostgreSQL `pg_type`
20///
21/// ```sql
22/// select oid, typarray, typname, typinput, typlen from pg_type
23/// where oid in (16, 21, 23, 20, 1700, 700, 701, 1043, 17, 1082, 1114, 1184, 1083, 1186, 3802);
24/// ```
25///
26/// See also:
27/// * <https://www.postgresql.org/docs/15/catalog-pg-type.html>
28/// * <https://github.com/postgres/postgres/blob/REL_15_4/src/include/catalog/pg_type.dat>
29#[macro_export]
30macro_rules! for_all_base_types {
31    ($macro:ident $(, $x:tt)*) => {
32        $macro! {
33            $($x, )*
34            { Boolean     |   16 |     1000 | bool        | boolin         |      1 }
35            { Bytea       |   17 |     1001 | bytea       | byteain        |     -1 }
36            { Int64       |   20 |     1016 | int8        | int8in         |      8 }
37            { Int16       |   21 |     1005 | int2        | int2in         |      2 }
38            { Int32       |   23 |     1007 | int4        | int4in         |      4 }
39            { Float32     |  700 |     1021 | float4      | float4in       |      4 }
40            { Float64     |  701 |     1022 | float8      | float8in       |      8 }
41            { Varchar     | 1043 |     1015 | varchar     | varcharin      |     -1 }
42            { Date        | 1082 |     1182 | date        | date_in        |      4 }
43            { Time        | 1083 |     1183 | time        | time_in        |      8 }
44            { Timestamp   | 1114 |     1115 | timestamp   | timestamp_in   |      8 }
45            { Timestamptz | 1184 |     1185 | timestamptz | timestamptz_in |      8 }
46            { Interval    | 1186 |     1187 | interval    | interval_in    |     16 }
47            { Decimal     | 1700 |     1231 | numeric     | numeric_in     |     -1 }
48            { Jsonb       | 3802 |     3807 | jsonb       | jsonb_in       |     -1 }
49        }
50    };
51}
52
53#[derive(Debug, thiserror::Error)]
54#[error("Unsupported oid {0}")]
55pub struct UnsupportedOid(i32);
56
57/// Get type information compatible with Postgres type, such as oid, type length.
58impl DataType {
59    /// For a fixed-size type, typlen is the number of bytes in the internal representation of the type.
60    /// But for a variable-length type, typlen is negative.
61    /// -1 indicates a “varlena” type (one that has a length word),
62    /// -2 indicates a null-terminated C string.
63    ///
64    /// <https://www.postgresql.org/docs/15/catalog-pg-type.html#:~:text=of%20the%20type-,typlen,-int2>
65    pub fn type_len(&self) -> i16 {
66        macro_rules! impl_type_len {
67            ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
68                match self {
69                    $(
70                    DataType::$enum => $len,
71                    )*
72                    DataType::Serial => 8,
73                    DataType::Int256 => -1,
74                    DataType::Vector(_) => -1,
75                    DataType::List(_) | DataType::Struct(_) | DataType::Map(_) => -1,
76                }
77            }
78        }
79        for_all_base_types! { impl_type_len }
80    }
81
82    // NOTE:
83    // Refer https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_type.dat when add new TypeOid.
84    // Be careful to distinguish oid from array_type_oid.
85    // Such as:
86    //  https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_type.dat#L347
87    //  For Numeric(aka Decimal): oid = 1700, array_type_oid = 1231
88    pub fn from_oid(oid: i32) -> Result<Self, UnsupportedOid> {
89        macro_rules! impl_from_oid {
90            ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
91                match oid {
92                    $(
93                    $oid => Ok(DataType::$enum),
94                    )*
95                    $(
96                    $oid_array => Ok(DataType::List(Box::new(DataType::$enum))),
97                    )*
98                    // workaround to support text in extended mode.
99                    25 => Ok(DataType::Varchar),
100                    1009 => Ok(DataType::List(Box::new(DataType::Varchar))),
101                    _ => Err(UnsupportedOid(oid)),
102                }
103            }
104        }
105        for_all_base_types! { impl_from_oid }
106    }
107
108    /// Refer to [`Self::from_oid`]
109    pub fn to_oid(&self) -> i32 {
110        macro_rules! impl_to_oid {
111            ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
112                match self {
113                    $(
114                    DataType::$enum => $oid,
115                    )*
116                    DataType::List(inner) => match inner.unnest_list() {
117                        $(
118                        DataType::$enum => $oid_array,
119                        )*
120                        DataType::Int256 => 1302,
121                        DataType::Serial => 1016,
122                        DataType::Struct(_) => 2287, // pseudo-type of array[struct] (see `pg_type.dat`)
123                        DataType::List { .. } => unreachable!("Never reach here!"),
124                        DataType::Map(_) => 1304,
125                        DataType::Vector(_) => todo!("VECTOR_PLACEHOLDER"),
126                    }
127                    DataType::Serial => 20,
128                    // XXX: what does the oid mean here? Why we don't have from_oid for them?
129                    DataType::Int256 => 1301,
130                    DataType::Map(_) => 1303,
131                    // TODO: Support to give a new oid for custom struct type. #9434
132                    DataType::Struct(_) => 2249,  // pseudo-type of struct (see `pg_type.dat`)
133                    DataType::Vector(_) => todo!("VECTOR_PLACEHOLDER"),
134                }
135            }
136        }
137        for_all_base_types! { impl_to_oid }
138    }
139
140    pub fn pg_name(&self) -> &'static str {
141        macro_rules! impl_pg_name {
142            ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
143                match self {
144                    $(
145                    DataType::$enum => stringify!($name),
146                    )*
147                    DataType::Struct(_) => "struct",
148                    DataType::List(_) => "list",
149                    DataType::Serial => "serial",
150                    DataType::Int256 => "rw_int256",
151                    DataType::Map(_) => "map",
152                    DataType::Vector(_) => "vector",
153                }
154            }
155        }
156        for_all_base_types! { impl_pg_name }
157    }
158
159    pub fn to_pg_type(&self) -> PgType {
160        let oid = self.to_oid();
161        PgType::from_oid(oid as u32).unwrap()
162    }
163}