risingwave_common/types/postgres_type.rs
1// Copyright 2022 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 { Variant | 1307 | 1308 | variant | variant_in | -1 }
50 }
51 };
52}
53
54#[derive(Debug, thiserror::Error)]
55#[error("Unsupported oid {0}")]
56pub struct UnsupportedOid(i32);
57
58/// Get type information compatible with Postgres type, such as oid, type length.
59impl DataType {
60 /// For a fixed-size type, typlen is the number of bytes in the internal representation of the type.
61 /// But for a variable-length type, typlen is negative.
62 /// -1 indicates a “varlena” type (one that has a length word),
63 /// -2 indicates a null-terminated C string.
64 ///
65 /// <https://www.postgresql.org/docs/15/catalog-pg-type.html#:~:text=of%20the%20type-,typlen,-int2>
66 pub fn type_len(&self) -> i16 {
67 macro_rules! impl_type_len {
68 ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
69 match self {
70 $(
71 DataType::$enum => $len,
72 )*
73 DataType::Serial => 8,
74 DataType::Int256 => -1,
75 DataType::Vector(_) => -1,
76 DataType::List(_) | DataType::Struct(_) | DataType::Map(_) => -1,
77 }
78 }
79 }
80 for_all_base_types! { impl_type_len }
81 }
82
83 // NOTE:
84 // Refer https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_type.dat when add new TypeOid.
85 // Be careful to distinguish oid from array_type_oid.
86 // Such as:
87 // https://github.com/postgres/postgres/blob/master/src/include/catalog/pg_type.dat#L347
88 // For Numeric(aka Decimal): oid = 1700, array_type_oid = 1231
89 pub fn from_oid(oid: i32) -> Result<Self, UnsupportedOid> {
90 macro_rules! impl_from_oid {
91 ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
92 match oid {
93 $(
94 $oid => Ok(DataType::$enum),
95 )*
96 $(
97 $oid_array => Ok(DataType::list(DataType::$enum)),
98 )*
99 // workaround to support text in extended mode.
100 25 => Ok(DataType::Varchar),
101 1009 => Ok(DataType::Varchar.list()),
102 _ => Err(UnsupportedOid(oid)),
103 }
104 }
105 }
106 for_all_base_types! { impl_from_oid }
107 }
108
109 /// Refer to [`Self::from_oid`]
110 pub fn to_oid(&self) -> i32 {
111 macro_rules! impl_to_oid {
112 ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
113 match self {
114 $(
115 DataType::$enum => $oid,
116 )*
117 DataType::List(list) => match list.elem().unnest_list() {
118 $(
119 DataType::$enum => $oid_array,
120 )*
121 DataType::Int256 => 1302,
122 DataType::Serial => 1016,
123 DataType::Struct(_) => 2287, // pseudo-type of array[struct] (see `pg_type.dat`)
124 DataType::List { .. } => unreachable!("Never reach here!"),
125 DataType::Map(_) => 1304,
126 DataType::Vector(_) => 1306,
127 }
128 DataType::Serial => 20,
129 // XXX: what does the oid mean here? Why we don't have from_oid for them?
130 DataType::Int256 => 1301,
131 DataType::Map(_) => 1303,
132 // TODO: Support to give a new oid for custom struct type. #9434
133 DataType::Struct(_) => 2249, // pseudo-type of struct (see `pg_type.dat`)
134 DataType::Vector(_) => 1305,
135 }
136 }
137 }
138 for_all_base_types! { impl_to_oid }
139 }
140
141 pub fn pg_name(&self) -> &'static str {
142 macro_rules! impl_pg_name {
143 ($( { $enum:ident | $oid:literal | $oid_array:literal | $name:ident | $input:ident | $len:literal } )*) => {
144 match self {
145 $(
146 DataType::$enum => stringify!($name),
147 )*
148 DataType::Struct(_) => "struct",
149 DataType::List(_) => "list",
150 DataType::Serial => "serial",
151 DataType::Int256 => "rw_int256",
152 DataType::Map(_) => "map",
153 DataType::Vector(_) => "vector",
154 }
155 }
156 }
157 for_all_base_types! { impl_pg_name }
158 }
159
160 pub fn to_pg_type(&self) -> PgType {
161 let oid = self.to_oid();
162 PgType::from_oid(oid as u32).unwrap()
163 }
164}