1use std::error::Error;
11use std::fmt;
12use std::mem::size_of;
13use std::sync::LazyLock;
14
15use mz_repr::ScalarType;
16use mz_repr::adt::char::{CharLength as AdtCharLength, InvalidCharLengthError};
17use mz_repr::adt::mz_acl_item::{AclItem, MzAclItem};
18use mz_repr::adt::numeric::{
19 InvalidNumericMaxScaleError, NUMERIC_DATUM_MAX_PRECISION, NumericMaxScale,
20};
21use mz_repr::adt::timestamp::{
22 InvalidTimestampPrecisionError, TimestampPrecision as AdtTimestampPrecision,
23};
24use mz_repr::adt::varchar::{InvalidVarCharMaxLengthError, VarCharMaxLength};
25use mz_repr::namespaces::MZ_CATALOG_SCHEMA;
26
27use crate::oid;
28
29const VARHDRSZ: i32 = 4;
33
34const MAX_INTERVAL_PRECISION: i32 = 6;
38
39const MAX_TIMESTAMP_PRECISION: i32 = 6;
43
44const MAX_TIME_PRECISION: i32 = 6;
48
49#[derive(Debug, Clone, Eq, PartialEq)]
54pub enum Type {
55 Array(Box<Type>),
57 Bool,
59 Bytea,
61 Char,
63 Date,
65 Float4,
67 Float8,
69 Int2,
71 Int4,
73 Int8,
75 UInt2,
77 UInt4,
79 UInt8,
81 Interval {
83 constraints: Option<IntervalConstraints>,
85 },
86 Json,
88 Jsonb,
90 List(Box<Type>),
92 Map {
94 value_type: Box<Type>,
96 },
97 Name,
100 Numeric {
102 constraints: Option<NumericConstraints>,
104 },
105 Oid,
107 Record(Vec<Type>),
109 Text,
111 BpChar {
113 length: Option<CharLength>,
117 },
118 VarChar {
120 max_length: Option<CharLength>,
122 },
123 Time {
125 precision: Option<TimePrecision>,
127 },
128 TimeTz {
130 precision: Option<TimePrecision>,
132 },
133 Timestamp {
135 precision: Option<TimestampPrecision>,
137 },
138 TimestampTz {
140 precision: Option<TimestampPrecision>,
142 },
143 Uuid,
145 RegProc,
147 RegType,
149 RegClass,
151 Int2Vector,
153 MzTimestamp,
155 Range {
157 element_type: Box<Type>,
159 },
160 MzAclItem,
163 AclItem,
166}
167
168pub trait TypeConstraint: fmt::Display {
170 fn from_typmod(typmod: i32) -> Result<Option<Self>, String>
172 where
173 Self: Sized;
174
175 fn into_typmod(&self) -> i32;
177}
178
179#[derive(Debug, Clone, Copy, Eq, PartialEq)]
181pub struct CharLength(i32);
182
183impl TypeConstraint for CharLength {
184 fn from_typmod(typmod: i32) -> Result<Option<CharLength>, String> {
185 if typmod >= VARHDRSZ {
187 Ok(Some(CharLength(typmod - VARHDRSZ)))
188 } else {
189 Ok(None)
190 }
191 }
192
193 fn into_typmod(&self) -> i32 {
194 self.0 + VARHDRSZ
196 }
197}
198
199impl CharLength {
200 pub fn into_i32(self) -> i32 {
202 self.0
203 }
204}
205
206impl From<AdtCharLength> for CharLength {
207 fn from(length: AdtCharLength) -> CharLength {
208 CharLength(i32::try_from(length.into_u32()).unwrap())
211 }
212}
213
214impl From<VarCharMaxLength> for CharLength {
215 fn from(length: VarCharMaxLength) -> CharLength {
216 CharLength(i32::try_from(length.into_u32()).unwrap())
219 }
220}
221
222impl fmt::Display for CharLength {
223 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224 write!(f, "({})", self.0)
226 }
227}
228
229#[derive(Debug, Clone, Copy, Eq, PartialEq)]
231pub struct IntervalConstraints {
232 range: i32,
234 precision: i32,
236}
237
238impl TypeConstraint for IntervalConstraints {
239 fn from_typmod(typmod: i32) -> Result<Option<IntervalConstraints>, String> {
240 if typmod < 0 {
241 Ok(None)
242 } else {
243 let range = typmod >> 16 & 0x7fff;
245 let precision = typmod & 0xffff;
246 if precision > MAX_INTERVAL_PRECISION {
247 return Err(format!(
248 "exceeds maximum interval precision {MAX_INTERVAL_PRECISION}"
249 ));
250 }
251 Ok(Some(IntervalConstraints { range, precision }))
252 }
253 }
254
255 fn into_typmod(&self) -> i32 {
256 (self.range << 16) | self.precision
257 }
258}
259
260impl fmt::Display for IntervalConstraints {
261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262 write!(f, "({})", self.precision)
265 }
266}
267
268#[derive(Debug, Clone, Copy, Eq, PartialEq)]
270pub struct TimePrecision(i32);
271
272impl TypeConstraint for TimePrecision {
273 fn from_typmod(typmod: i32) -> Result<Option<TimePrecision>, String> {
274 if typmod > MAX_TIME_PRECISION {
275 Err(format!(
276 "exceeds maximum time precision {MAX_TIME_PRECISION}"
277 ))
278 } else if typmod >= 0 {
279 Ok(Some(TimePrecision(typmod)))
280 } else {
281 Ok(None)
282 }
283 }
284
285 fn into_typmod(&self) -> i32 {
286 self.0
287 }
288}
289
290impl fmt::Display for TimePrecision {
291 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
292 write!(f, "({})", self.0)
294 }
295}
296
297#[derive(Debug, Clone, Copy, Eq, PartialEq)]
299pub struct TimestampPrecision(i32);
300
301impl TimestampPrecision {
302 pub fn into_i32(self) -> i32 {
304 self.0
305 }
306}
307
308impl TypeConstraint for TimestampPrecision {
309 fn from_typmod(typmod: i32) -> Result<Option<TimestampPrecision>, String> {
310 if typmod > MAX_TIMESTAMP_PRECISION {
311 Err(format!(
312 "exceeds maximum timestamp precision {MAX_TIMESTAMP_PRECISION}"
313 ))
314 } else if typmod >= 0 {
315 Ok(Some(TimestampPrecision(typmod)))
316 } else {
317 Ok(None)
318 }
319 }
320
321 fn into_typmod(&self) -> i32 {
322 self.0
323 }
324}
325
326impl From<AdtTimestampPrecision> for TimestampPrecision {
327 fn from(precision: AdtTimestampPrecision) -> TimestampPrecision {
328 TimestampPrecision(i32::from(precision.into_u8()))
329 }
330}
331
332impl fmt::Display for TimestampPrecision {
333 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
334 write!(f, "({})", self.0)
336 }
337}
338
339#[derive(Debug, Clone, Copy, Eq, PartialEq)]
341pub struct NumericConstraints {
342 max_precision: i32,
344 max_scale: i32,
346}
347
348impl NumericConstraints {
349 pub fn max_precision(&self) -> i32 {
351 self.max_precision
352 }
353
354 pub fn max_scale(&self) -> i32 {
356 self.max_scale
357 }
358}
359
360impl TypeConstraint for NumericConstraints {
361 fn from_typmod(typmod: i32) -> Result<Option<NumericConstraints>, String> {
362 if typmod >= VARHDRSZ {
364 Ok(Some(NumericConstraints {
365 max_precision: ((typmod - VARHDRSZ) >> 16) & 0xffff,
366 max_scale: (((typmod - VARHDRSZ) & 0x7ff) ^ 1024) - 1024,
367 }))
368 } else {
369 Ok(None)
370 }
371 }
372
373 fn into_typmod(&self) -> i32 {
374 ((self.max_precision << 16) | (self.max_scale & 0x7ff)) + VARHDRSZ
376 }
377}
378
379impl fmt::Display for NumericConstraints {
380 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
381 write!(f, "({},{})", self.max_precision, self.max_scale)
383 }
384}
385
386pub static LIST: LazyLock<postgres_types::Type> = LazyLock::new(|| {
388 postgres_types::Type::new(
389 "list".to_owned(),
390 oid::TYPE_LIST_OID,
395 postgres_types::Kind::Pseudo,
396 MZ_CATALOG_SCHEMA.to_owned(),
397 )
398});
399
400pub static MAP: LazyLock<postgres_types::Type> = LazyLock::new(|| {
402 postgres_types::Type::new(
403 "map".to_owned(),
404 oid::TYPE_MAP_OID,
406 postgres_types::Kind::Pseudo,
407 MZ_CATALOG_SCHEMA.to_owned(),
408 )
409});
410
411pub static ANYCOMPATIBLELIST: LazyLock<postgres_types::Type> = LazyLock::new(|| {
413 postgres_types::Type::new(
414 "anycompatiblelist".to_owned(),
415 oid::TYPE_ANYCOMPATIBLELIST_OID,
416 postgres_types::Kind::Pseudo,
417 MZ_CATALOG_SCHEMA.to_owned(),
418 )
419});
420
421pub static ANYCOMPATIBLEMAP: LazyLock<postgres_types::Type> = LazyLock::new(|| {
423 postgres_types::Type::new(
424 "anycompatiblemap".to_owned(),
425 oid::TYPE_ANYCOMPATIBLEMAP_OID,
426 postgres_types::Kind::Pseudo,
427 MZ_CATALOG_SCHEMA.to_owned(),
428 )
429});
430
431pub static UINT2: LazyLock<postgres_types::Type> = LazyLock::new(|| {
433 postgres_types::Type::new(
434 "uint2".to_owned(),
435 oid::TYPE_UINT2_OID,
436 postgres_types::Kind::Pseudo,
437 MZ_CATALOG_SCHEMA.to_owned(),
438 )
439});
440
441pub static UINT4: LazyLock<postgres_types::Type> = LazyLock::new(|| {
443 postgres_types::Type::new(
444 "uint4".to_owned(),
445 oid::TYPE_UINT4_OID,
446 postgres_types::Kind::Pseudo,
447 MZ_CATALOG_SCHEMA.to_owned(),
448 )
449});
450
451pub static UINT8: LazyLock<postgres_types::Type> = LazyLock::new(|| {
453 postgres_types::Type::new(
454 "uint8".to_owned(),
455 oid::TYPE_UINT8_OID,
456 postgres_types::Kind::Pseudo,
457 MZ_CATALOG_SCHEMA.to_owned(),
458 )
459});
460
461pub static UINT2_ARRAY: LazyLock<postgres_types::Type> = LazyLock::new(|| {
463 postgres_types::Type::new(
464 "_uint2".to_owned(),
465 oid::TYPE_UINT2_ARRAY_OID,
466 postgres_types::Kind::Pseudo,
467 MZ_CATALOG_SCHEMA.to_owned(),
468 )
469});
470
471pub static UINT4_ARRAY: LazyLock<postgres_types::Type> = LazyLock::new(|| {
473 postgres_types::Type::new(
474 "_uint4".to_owned(),
475 oid::TYPE_UINT4_ARRAY_OID,
476 postgres_types::Kind::Pseudo,
477 MZ_CATALOG_SCHEMA.to_owned(),
478 )
479});
480
481pub static UINT8_ARRAY: LazyLock<postgres_types::Type> = LazyLock::new(|| {
483 postgres_types::Type::new(
484 "_uint8".to_owned(),
485 oid::TYPE_UINT8_ARRAY_OID,
486 postgres_types::Kind::Pseudo,
487 MZ_CATALOG_SCHEMA.to_owned(),
488 )
489});
490
491pub static MZ_TIMESTAMP: LazyLock<postgres_types::Type> = LazyLock::new(|| {
493 postgres_types::Type::new(
494 "mz_timestamp".to_owned(),
495 oid::TYPE_MZ_TIMESTAMP_OID,
496 postgres_types::Kind::Pseudo,
497 MZ_CATALOG_SCHEMA.to_owned(),
498 )
499});
500
501pub static MZ_TIMESTAMP_ARRAY: LazyLock<postgres_types::Type> = LazyLock::new(|| {
503 postgres_types::Type::new(
504 "_mz_timestamp".to_owned(),
505 oid::TYPE_MZ_TIMESTAMP_ARRAY_OID,
506 postgres_types::Kind::Pseudo,
507 MZ_CATALOG_SCHEMA.to_owned(),
508 )
509});
510
511pub static MZ_ACL_ITEM: LazyLock<postgres_types::Type> = LazyLock::new(|| {
513 postgres_types::Type::new(
514 "mz_aclitem".to_owned(),
515 oid::TYPE_MZ_ACL_ITEM_OID,
516 postgres_types::Kind::Pseudo,
517 MZ_CATALOG_SCHEMA.to_owned(),
518 )
519});
520
521pub static MZ_ACL_ITEM_ARRAY: LazyLock<postgres_types::Type> = LazyLock::new(|| {
523 postgres_types::Type::new(
524 "_mz_aclitem".to_owned(),
525 oid::TYPE_MZ_ACL_ITEM_ARRAY_OID,
526 postgres_types::Kind::Pseudo,
527 MZ_CATALOG_SCHEMA.to_owned(),
528 )
529});
530
531impl Type {
532 pub fn from_oid(oid: u32) -> Result<Type, TypeFromOidError> {
534 Type::from_oid_and_typmod(oid, -1)
535 }
536
537 pub fn from_oid_and_typmod(oid: u32, typmod: i32) -> Result<Type, TypeFromOidError> {
547 let typ = postgres_types::Type::from_oid(oid).ok_or(TypeFromOidError::UnknownOid(oid))?;
548 let mut typ = match typ {
549 postgres_types::Type::BOOL => Type::Bool,
550 postgres_types::Type::BYTEA => Type::Bytea,
551 postgres_types::Type::DATE => Type::Date,
552 postgres_types::Type::FLOAT4 => Type::Float4,
553 postgres_types::Type::FLOAT8 => Type::Float8,
554 postgres_types::Type::INT2 => Type::Int2,
555 postgres_types::Type::INT4 => Type::Int4,
556 postgres_types::Type::INT8 => Type::Int8,
557 postgres_types::Type::INTERVAL => Type::Interval { constraints: None },
558 postgres_types::Type::JSON => Type::Json,
559 postgres_types::Type::JSONB => Type::Jsonb,
560 postgres_types::Type::NUMERIC => Type::Numeric { constraints: None },
561 postgres_types::Type::OID => Type::Oid,
562 postgres_types::Type::TEXT => Type::Text,
563 postgres_types::Type::BPCHAR | postgres_types::Type::CHAR => {
564 Type::BpChar { length: None }
565 }
566 postgres_types::Type::VARCHAR => Type::VarChar { max_length: None },
567 postgres_types::Type::TIME => Type::Time { precision: None },
568 postgres_types::Type::TIMETZ => Type::TimeTz { precision: None },
569 postgres_types::Type::TIMESTAMP => Type::Timestamp { precision: None },
570 postgres_types::Type::TIMESTAMPTZ => Type::TimestampTz { precision: None },
571 postgres_types::Type::UUID => Type::Uuid,
572 postgres_types::Type::REGCLASS => Type::RegClass,
573 postgres_types::Type::REGPROC => Type::RegProc,
574 postgres_types::Type::REGTYPE => Type::RegType,
575 postgres_types::Type::BOOL_ARRAY => Type::Array(Box::new(Type::Bool)),
576 postgres_types::Type::BYTEA_ARRAY => Type::Array(Box::new(Type::Bytea)),
577 postgres_types::Type::BPCHAR_ARRAY => {
578 Type::Array(Box::new(Type::BpChar { length: None }))
579 }
580 postgres_types::Type::DATE_ARRAY => Type::Array(Box::new(Type::Date)),
581 postgres_types::Type::FLOAT4_ARRAY => Type::Array(Box::new(Type::Float4)),
582 postgres_types::Type::FLOAT8_ARRAY => Type::Array(Box::new(Type::Float8)),
583 postgres_types::Type::INT2_ARRAY => Type::Array(Box::new(Type::Int2)),
584 postgres_types::Type::INT4_ARRAY => Type::Array(Box::new(Type::Int4)),
585 postgres_types::Type::INT8_ARRAY => Type::Array(Box::new(Type::Int8)),
586 postgres_types::Type::INTERVAL_ARRAY => {
587 Type::Array(Box::new(Type::Interval { constraints: None }))
588 }
589 postgres_types::Type::JSON_ARRAY => Type::Array(Box::new(Type::Json)),
590 postgres_types::Type::JSONB_ARRAY => Type::Array(Box::new(Type::Jsonb)),
591 postgres_types::Type::NUMERIC_ARRAY => {
592 Type::Array(Box::new(Type::Numeric { constraints: None }))
593 }
594 postgres_types::Type::OID_ARRAY => Type::Array(Box::new(Type::Oid)),
595 postgres_types::Type::TEXT_ARRAY => Type::Array(Box::new(Type::Text)),
596 postgres_types::Type::TIME_ARRAY => {
597 Type::Array(Box::new(Type::Time { precision: None }))
598 }
599 postgres_types::Type::TIMETZ_ARRAY => {
600 Type::Array(Box::new(Type::TimeTz { precision: None }))
601 }
602 postgres_types::Type::TIMESTAMP_ARRAY => {
603 Type::Array(Box::new(Type::Timestamp { precision: None }))
604 }
605 postgres_types::Type::TIMESTAMPTZ_ARRAY => {
606 Type::Array(Box::new(Type::TimestampTz { precision: None }))
607 }
608 postgres_types::Type::UUID_ARRAY => Type::Array(Box::new(Type::Uuid)),
609 postgres_types::Type::VARCHAR_ARRAY => {
610 Type::Array(Box::new(Type::VarChar { max_length: None }))
611 }
612 postgres_types::Type::REGCLASS_ARRAY => Type::Array(Box::new(Type::RegClass)),
613 postgres_types::Type::REGPROC_ARRAY => Type::Array(Box::new(Type::RegProc)),
614 postgres_types::Type::REGTYPE_ARRAY => Type::Array(Box::new(Type::RegType)),
615 postgres_types::Type::INT2_VECTOR => Type::Int2Vector,
616 postgres_types::Type::INT2_VECTOR_ARRAY => Type::Array(Box::new(Type::Int2Vector)),
617 postgres_types::Type::INT4_RANGE => Type::Range {
618 element_type: Box::new(Type::Int4),
619 },
620 postgres_types::Type::INT4_RANGE_ARRAY => Type::Array(Box::new(Type::Range {
621 element_type: Box::new(Type::Int4),
622 })),
623 postgres_types::Type::INT8_RANGE => Type::Range {
624 element_type: Box::new(Type::Int8),
625 },
626 postgres_types::Type::INT8_RANGE_ARRAY => Type::Array(Box::new(Type::Range {
627 element_type: Box::new(Type::Int8),
628 })),
629 postgres_types::Type::NUM_RANGE => Type::Range {
630 element_type: Box::new(Type::Numeric { constraints: None }),
631 },
632 postgres_types::Type::NUM_RANGE_ARRAY => Type::Array(Box::new(Type::Range {
633 element_type: Box::new(Type::Numeric { constraints: None }),
634 })),
635 postgres_types::Type::TS_RANGE => Type::Range {
636 element_type: Box::new(Type::Timestamp { precision: None }),
637 },
638 postgres_types::Type::TS_RANGE_ARRAY => Type::Array(Box::new(Type::Range {
639 element_type: Box::new(Type::Timestamp { precision: None }),
640 })),
641 postgres_types::Type::TSTZ_RANGE => Type::Range {
642 element_type: Box::new(Type::TimestampTz { precision: None }),
643 },
644 postgres_types::Type::TSTZ_RANGE_ARRAY => Type::Array(Box::new(Type::Range {
645 element_type: Box::new(Type::TimestampTz { precision: None }),
646 })),
647 postgres_types::Type::DATE_RANGE => Type::Range {
648 element_type: Box::new(Type::Date),
649 },
650 postgres_types::Type::DATE_RANGE_ARRAY => Type::Array(Box::new(Type::Range {
651 element_type: Box::new(Type::Date),
652 })),
653 _ => return Err(TypeFromOidError::UnknownOid(oid)),
654 };
655
656 let res = ({
660 let typ = &mut typ;
661 || {
662 let elem_typ = match typ {
663 Type::Array(typ) => &mut **typ,
664 typ => typ,
665 };
666 match elem_typ {
667 Type::BpChar { length } => *length = CharLength::from_typmod(typmod)?,
668 Type::Numeric { constraints } => {
669 *constraints = NumericConstraints::from_typmod(typmod)?
670 }
671 Type::Interval { constraints } => {
672 *constraints = IntervalConstraints::from_typmod(typmod)?
673 }
674 Type::Time { precision } => *precision = TimePrecision::from_typmod(typmod)?,
675 Type::TimeTz { precision } => *precision = TimePrecision::from_typmod(typmod)?,
676 Type::Timestamp { precision } => {
677 *precision = TimestampPrecision::from_typmod(typmod)?
678 }
679 Type::TimestampTz { precision } => {
680 *precision = TimestampPrecision::from_typmod(typmod)?
681 }
682 Type::VarChar { max_length } => *max_length = CharLength::from_typmod(typmod)?,
683 _ if typmod != -1 => return Err("type does not support type modifiers".into()),
684 _ => (),
685 }
686 Ok(())
687 }
688 })();
689 match res {
690 Ok(()) => Ok(typ),
691 Err(detail) => Err(TypeFromOidError::InvalidTypmod {
692 typ,
693 typmod,
694 detail,
695 }),
696 }
697 }
698
699 pub(crate) fn inner(&self) -> &'static postgres_types::Type {
700 match self {
701 Type::AclItem => &postgres_types::Type::ACLITEM,
702 Type::Array(t) => match &**t {
703 Type::AclItem => &postgres_types::Type::ACLITEM_ARRAY,
704 Type::Array(_) => unreachable!(),
705 Type::Bool => &postgres_types::Type::BOOL_ARRAY,
706 Type::Bytea => &postgres_types::Type::BYTEA_ARRAY,
707 Type::Char => &postgres_types::Type::CHAR_ARRAY,
708 Type::Date => &postgres_types::Type::DATE_ARRAY,
709 Type::Float4 => &postgres_types::Type::FLOAT4_ARRAY,
710 Type::Float8 => &postgres_types::Type::FLOAT8_ARRAY,
711 Type::Int2 => &postgres_types::Type::INT2_ARRAY,
712 Type::Int4 => &postgres_types::Type::INT4_ARRAY,
713 Type::Int8 => &postgres_types::Type::INT8_ARRAY,
714 Type::UInt2 => &UINT2_ARRAY,
715 Type::UInt4 => &UINT4_ARRAY,
716 Type::UInt8 => &UINT8_ARRAY,
717 Type::Interval { .. } => &postgres_types::Type::INTERVAL_ARRAY,
718 Type::Json => &postgres_types::Type::JSON_ARRAY,
719 Type::Jsonb => &postgres_types::Type::JSONB_ARRAY,
720 Type::List(_) => unreachable!(),
721 Type::Map { .. } => unreachable!(),
722 Type::Name { .. } => &postgres_types::Type::NAME_ARRAY,
723 Type::Numeric { .. } => &postgres_types::Type::NUMERIC_ARRAY,
724 Type::Oid => &postgres_types::Type::OID_ARRAY,
725 Type::Record(_) => &postgres_types::Type::RECORD_ARRAY,
726 Type::Text => &postgres_types::Type::TEXT_ARRAY,
727 Type::BpChar { .. } => &postgres_types::Type::BPCHAR_ARRAY,
728 Type::VarChar { .. } => &postgres_types::Type::VARCHAR_ARRAY,
729 Type::Time { .. } => &postgres_types::Type::TIME_ARRAY,
730 Type::TimeTz { .. } => &postgres_types::Type::TIMETZ_ARRAY,
731 Type::Timestamp { .. } => &postgres_types::Type::TIMESTAMP_ARRAY,
732 Type::TimestampTz { .. } => &postgres_types::Type::TIMESTAMPTZ_ARRAY,
733 Type::Uuid => &postgres_types::Type::UUID_ARRAY,
734 Type::RegClass => &postgres_types::Type::REGCLASS_ARRAY,
735 Type::RegProc => &postgres_types::Type::REGPROC_ARRAY,
736 Type::RegType => &postgres_types::Type::REGTYPE_ARRAY,
737 Type::Int2Vector => &postgres_types::Type::INT2_VECTOR_ARRAY,
738 Type::MzTimestamp => &MZ_TIMESTAMP_ARRAY,
739 Type::Range { element_type } => match **element_type {
740 Type::Int4 => &postgres_types::Type::INT4_RANGE_ARRAY,
741 Type::Int8 => &postgres_types::Type::INT8_RANGE_ARRAY,
742 Type::Numeric { .. } => &postgres_types::Type::NUM_RANGE_ARRAY,
743 Type::Timestamp { .. } => &postgres_types::Type::TS_RANGE_ARRAY,
744 Type::TimestampTz { .. } => &postgres_types::Type::TSTZ_RANGE_ARRAY,
745 Type::Date => &postgres_types::Type::DATE_RANGE_ARRAY,
746 _ => unreachable!(),
747 },
748 Type::MzAclItem => &MZ_ACL_ITEM_ARRAY,
749 },
750 Type::Bool => &postgres_types::Type::BOOL,
751 Type::Bytea => &postgres_types::Type::BYTEA,
752 Type::Char => &postgres_types::Type::CHAR,
753 Type::Date => &postgres_types::Type::DATE,
754 Type::Float4 => &postgres_types::Type::FLOAT4,
755 Type::Float8 => &postgres_types::Type::FLOAT8,
756 Type::Int2 => &postgres_types::Type::INT2,
757 Type::Int4 => &postgres_types::Type::INT4,
758 Type::Int8 => &postgres_types::Type::INT8,
759 Type::UInt2 => &UINT2,
760 Type::UInt4 => &UINT4,
761 Type::UInt8 => &UINT8,
762 Type::Interval { .. } => &postgres_types::Type::INTERVAL,
763 Type::Json => &postgres_types::Type::JSON,
764 Type::Jsonb => &postgres_types::Type::JSONB,
765 Type::List(_) => &LIST,
766 Type::Map { .. } => &MAP,
767 Type::Name => &postgres_types::Type::NAME,
768 Type::Numeric { .. } => &postgres_types::Type::NUMERIC,
769 Type::Oid => &postgres_types::Type::OID,
770 Type::Record(_) => &postgres_types::Type::RECORD,
771 Type::Text => &postgres_types::Type::TEXT,
772 Type::BpChar { .. } => &postgres_types::Type::BPCHAR,
773 Type::VarChar { .. } => &postgres_types::Type::VARCHAR,
774 Type::Time { .. } => &postgres_types::Type::TIME,
775 Type::TimeTz { .. } => &postgres_types::Type::TIMETZ,
776 Type::Timestamp { .. } => &postgres_types::Type::TIMESTAMP,
777 Type::TimestampTz { .. } => &postgres_types::Type::TIMESTAMPTZ,
778 Type::Uuid => &postgres_types::Type::UUID,
779 Type::RegClass => &postgres_types::Type::REGCLASS,
780 Type::RegProc => &postgres_types::Type::REGPROC,
781 Type::RegType => &postgres_types::Type::REGTYPE,
782 Type::Int2Vector => &postgres_types::Type::INT2_VECTOR,
783 Type::MzTimestamp => &MZ_TIMESTAMP,
784 Type::Range { element_type } => match &**element_type {
785 Type::Int4 => &postgres_types::Type::INT4_RANGE,
786 Type::Int8 => &postgres_types::Type::INT8_RANGE,
787 Type::Numeric { .. } => &postgres_types::Type::NUM_RANGE,
788 Type::Timestamp { .. } => &postgres_types::Type::TS_RANGE,
789 Type::TimestampTz { .. } => &postgres_types::Type::TSTZ_RANGE,
790 Type::Date => &postgres_types::Type::DATE_RANGE,
791 t => unreachable!("{t:?} is not a range element type"),
792 },
793 Type::MzAclItem => &MZ_ACL_ITEM,
794 }
795 }
796
797 pub fn catalog_name(&self) -> &'static str {
800 self.inner().name()
801 }
802
803 pub fn name(&self) -> &'static str {
805 match self.inner() {
808 &postgres_types::Type::ACLITEM_ARRAY => "aclitem[]",
809 &postgres_types::Type::BOOL_ARRAY => "boolean[]",
810 &postgres_types::Type::BYTEA_ARRAY => "bytea[]",
811 &postgres_types::Type::BPCHAR_ARRAY => "character[]",
812 &postgres_types::Type::DATE_ARRAY => "date[]",
813 &postgres_types::Type::FLOAT4_ARRAY => "real[]",
814 &postgres_types::Type::FLOAT8_ARRAY => "double precision[]",
815 &postgres_types::Type::INT2_ARRAY => "smallint[]",
816 &postgres_types::Type::INT4_ARRAY => "integer[]",
817 &postgres_types::Type::INT8_ARRAY => "bigint[]",
818 &postgres_types::Type::INTERVAL_ARRAY => "interval[]",
819 &postgres_types::Type::JSONB_ARRAY => "jsonb[]",
820 &postgres_types::Type::NUMERIC_ARRAY => "numeric[]",
821 &postgres_types::Type::OID_ARRAY => "oid[]",
822 &postgres_types::Type::RECORD_ARRAY => "record[]",
823 &postgres_types::Type::TEXT_ARRAY => "text[]",
824 &postgres_types::Type::TIME_ARRAY => "time[]",
825 &postgres_types::Type::TIMESTAMP_ARRAY => "timestamp without time zone[]",
826 &postgres_types::Type::TIMESTAMPTZ_ARRAY => "timestamp with time zone[]",
827 &postgres_types::Type::UUID_ARRAY => "uuid[]",
828 &postgres_types::Type::VARCHAR_ARRAY => "character varying[]",
829 &postgres_types::Type::BOOL => "boolean",
830 &postgres_types::Type::BPCHAR => "character",
831 &postgres_types::Type::FLOAT4 => "real",
832 &postgres_types::Type::FLOAT8 => "double precision",
833 &postgres_types::Type::INT2 => "smallint",
834 &postgres_types::Type::INT4 => "integer",
835 &postgres_types::Type::INT8 => "bigint",
836 &postgres_types::Type::TIMESTAMP => "timestamp without time zone",
837 &postgres_types::Type::TIMESTAMPTZ => "timestamp with time zone",
838 &postgres_types::Type::VARCHAR => "character varying",
839 &postgres_types::Type::REGCLASS_ARRAY => "regclass[]",
840 &postgres_types::Type::REGPROC_ARRAY => "regproc[]",
841 &postgres_types::Type::REGTYPE_ARRAY => "regtype[]",
842 &postgres_types::Type::INT2_VECTOR => "int2vector",
843 other => match other.oid() {
844 oid::TYPE_UINT2_ARRAY_OID => "uint2[]",
845 oid::TYPE_UINT4_ARRAY_OID => "uint4[]",
846 oid::TYPE_UINT8_ARRAY_OID => "uint8[]",
847 oid::TYPE_MZ_TIMESTAMP_ARRAY_OID => "mz_timestamp[]",
848 oid::TYPE_MZ_ACL_ITEM_ARRAY_OID => "mz_aclitem[]",
849 _ => other.name(),
850 },
851 }
852 }
853
854 pub fn oid(&self) -> u32 {
858 self.inner().oid()
859 }
860
861 pub fn constraint(&self) -> Option<&dyn TypeConstraint> {
863 match self {
864 Type::BpChar {
865 length: Some(length),
866 } => Some(length),
867 Type::VarChar {
868 max_length: Some(max_length),
869 } => Some(max_length),
870 Type::Numeric {
871 constraints: Some(constraints),
872 } => Some(constraints),
873 Type::Interval {
874 constraints: Some(constraints),
875 } => Some(constraints),
876 Type::Time {
877 precision: Some(precision),
878 } => Some(precision),
879 Type::TimeTz {
880 precision: Some(precision),
881 } => Some(precision),
882 Type::Timestamp {
883 precision: Some(precision),
884 } => Some(precision),
885 Type::TimestampTz {
886 precision: Some(precision),
887 } => Some(precision),
888 Type::AclItem
889 | Type::Array(_)
890 | Type::Bool
891 | Type::Bytea
892 | Type::BpChar { length: None }
893 | Type::Char
894 | Type::Date
895 | Type::Float4
896 | Type::Float8
897 | Type::Int2
898 | Type::Int4
899 | Type::Int8
900 | Type::UInt2
901 | Type::UInt4
902 | Type::UInt8
903 | Type::Interval { constraints: None }
904 | Type::Json
905 | Type::Jsonb
906 | Type::List(_)
907 | Type::Map { .. }
908 | Type::Name
909 | Type::Numeric { constraints: None }
910 | Type::Int2Vector
911 | Type::Oid
912 | Type::Record(_)
913 | Type::RegClass
914 | Type::RegProc
915 | Type::RegType
916 | Type::Text
917 | Type::Time { precision: None }
918 | Type::TimeTz { precision: None }
919 | Type::Timestamp { precision: None }
920 | Type::TimestampTz { precision: None }
921 | Type::Uuid
922 | Type::MzTimestamp
923 | Type::VarChar { max_length: None }
924 | Type::Range { .. }
925 | Type::MzAclItem => None,
926 }
927 }
928
929 pub fn typlen(&self) -> i16 {
932 match self {
933 Type::Array(_) => -1,
934 Type::Bool => 1,
935 Type::Bytea => -1,
936 Type::Char => 1,
937 Type::Date => 4,
938 Type::Float4 => 4,
939 Type::Float8 => 8,
940 Type::Int2 => 2,
941 Type::Int4 => 4,
942 Type::Int8 => 8,
943 Type::UInt2 => 2,
944 Type::UInt4 => 4,
945 Type::UInt8 => 8,
946 Type::Interval { .. } => 16,
947 Type::Json => -1,
948 Type::Jsonb => -1,
949 Type::List(_) => -1,
950 Type::Map { .. } => -1,
951 Type::Name { .. } => 64,
952 Type::Numeric { .. } => -1,
953 Type::Oid => 4,
954 Type::Record(_) => -1,
955 Type::Text => -1,
956 Type::BpChar { .. } => -1,
957 Type::VarChar { .. } => -1,
958 Type::Time { .. } => 4,
959 Type::TimeTz { .. } => 4,
960 Type::Timestamp { .. } => 8,
961 Type::TimestampTz { .. } => 8,
962 Type::Uuid => 16,
963 Type::RegClass => 4,
964 Type::RegProc => 4,
965 Type::RegType => 4,
966 Type::Int2Vector => -1,
967 Type::MzTimestamp => size_of::<mz_repr::Timestamp>()
970 .try_into()
971 .expect("must fit"),
972 Type::Range { .. } => -1,
973 Type::MzAclItem => MzAclItem::binary_size().try_into().expect("must fit"),
974 Type::AclItem => AclItem::binary_size().try_into().expect("must fit"),
975 }
976 }
977
978 pub fn typmod(&self) -> i32 {
987 match self.constraint() {
988 Some(constraint) => constraint.into_typmod(),
989 None => -1,
990 }
991 }
992}
993
994impl fmt::Display for Type {
995 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
996 f.write_str(self.inner().name())?;
997 if let Some(constraint) = self.constraint() {
998 constraint.fmt(f)?;
999 }
1000 Ok(())
1001 }
1002}
1003
1004impl TryFrom<&Type> for ScalarType {
1005 type Error = TypeConversionError;
1006
1007 fn try_from(typ: &Type) -> Result<ScalarType, TypeConversionError> {
1008 match typ {
1009 Type::AclItem => Ok(ScalarType::AclItem),
1010 Type::Array(t) => Ok(ScalarType::Array(Box::new(TryFrom::try_from(&**t)?))),
1011 Type::Bool => Ok(ScalarType::Bool),
1012 Type::Bytea => Ok(ScalarType::Bytes),
1013 Type::Char => Ok(ScalarType::PgLegacyChar),
1014 Type::Date => Ok(ScalarType::Date),
1015 Type::Float4 => Ok(ScalarType::Float32),
1016 Type::Float8 => Ok(ScalarType::Float64),
1017 Type::Int2 => Ok(ScalarType::Int16),
1018 Type::Int4 => Ok(ScalarType::Int32),
1019 Type::Int8 => Ok(ScalarType::Int64),
1020 Type::UInt2 => Ok(ScalarType::UInt16),
1021 Type::UInt4 => Ok(ScalarType::UInt32),
1022 Type::UInt8 => Ok(ScalarType::UInt64),
1023 Type::Interval { .. } => Ok(ScalarType::Interval),
1024 Type::Json => Err(TypeConversionError::UnsupportedType(Type::Json)),
1025 Type::Jsonb => Ok(ScalarType::Jsonb),
1026 Type::List(t) => Ok(ScalarType::List {
1027 element_type: Box::new(TryFrom::try_from(&**t)?),
1028 custom_id: None,
1029 }),
1030 Type::Map { value_type } => Ok(ScalarType::Map {
1031 value_type: Box::new(TryFrom::try_from(&**value_type)?),
1032 custom_id: None,
1033 }),
1034 Type::Name => Ok(ScalarType::PgLegacyName),
1035 Type::Numeric { constraints } => {
1036 let max_scale = match constraints {
1037 Some(constraints) => {
1038 if constraints.max_precision > i32::from(NUMERIC_DATUM_MAX_PRECISION) {
1039 return Err(TypeConversionError::InvalidNumericConstraint(format!(
1040 "precision for type numeric must be between 1 and {}",
1041 NUMERIC_DATUM_MAX_PRECISION,
1042 )));
1043 }
1044 if constraints.max_scale > constraints.max_precision {
1045 return Err(TypeConversionError::InvalidNumericConstraint(format!(
1046 "scale for type numeric must be between 0 and precision {}",
1047 constraints.max_precision,
1048 )));
1049 }
1050 Some(NumericMaxScale::try_from(i64::from(constraints.max_scale))?)
1051 }
1052 None => None,
1053 };
1054 Ok(ScalarType::Numeric { max_scale })
1055 }
1056 Type::Oid => Ok(ScalarType::Oid),
1057 Type::Record(_) => Ok(ScalarType::Record {
1058 fields: [].into(),
1059 custom_id: None,
1060 }),
1061 Type::Text => Ok(ScalarType::String),
1062 Type::Time { precision: None } => Ok(ScalarType::Time),
1063 Type::Time { precision: Some(_) } => {
1064 Err(TypeConversionError::UnsupportedType(typ.clone()))
1065 }
1066 Type::TimeTz { .. } => Err(TypeConversionError::UnsupportedType(typ.clone())),
1067 Type::BpChar { length } => Ok(ScalarType::Char {
1068 length: match length {
1069 Some(length) => Some(AdtCharLength::try_from(i64::from(length.into_i32()))?),
1070 None => None,
1071 },
1072 }),
1073 Type::VarChar { max_length } => Ok(ScalarType::VarChar {
1074 max_length: match max_length {
1075 Some(max_length) => Some(VarCharMaxLength::try_from(i64::from(
1076 max_length.into_i32(),
1077 ))?),
1078 None => None,
1079 },
1080 }),
1081 Type::Timestamp { precision } => Ok(ScalarType::Timestamp {
1082 precision: match precision {
1083 Some(precision) => Some(AdtTimestampPrecision::try_from(i64::from(
1084 precision.into_i32(),
1085 ))?),
1086 None => None,
1087 },
1088 }),
1089 Type::TimestampTz { precision } => Ok(ScalarType::TimestampTz {
1090 precision: match precision {
1091 Some(precision) => Some(AdtTimestampPrecision::try_from(i64::from(
1092 precision.into_i32(),
1093 ))?),
1094 None => None,
1095 },
1096 }),
1097 Type::Uuid => Ok(ScalarType::Uuid),
1098 Type::RegClass => Ok(ScalarType::RegClass),
1099 Type::RegProc => Ok(ScalarType::RegProc),
1100 Type::RegType => Ok(ScalarType::RegType),
1101 Type::Int2Vector => Ok(ScalarType::Int2Vector),
1102 Type::MzTimestamp => Ok(ScalarType::MzTimestamp),
1103 Type::Range { element_type } => Ok(ScalarType::Range {
1104 element_type: Box::new(TryFrom::try_from(&**element_type)?),
1105 }),
1106 Type::MzAclItem => Ok(ScalarType::MzAclItem),
1107 }
1108 }
1109}
1110
1111#[derive(Debug, Clone)]
1113pub enum TypeFromOidError {
1114 UnknownOid(u32),
1116 InvalidTypmod {
1118 typ: Type,
1120 typmod: i32,
1122 detail: String,
1124 },
1125}
1126
1127impl fmt::Display for TypeFromOidError {
1128 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1129 match self {
1130 TypeFromOidError::UnknownOid(oid) => write!(f, "type with OID {oid} is unknown"),
1131 TypeFromOidError::InvalidTypmod {
1132 typ,
1133 typmod,
1134 detail,
1135 } => {
1136 write!(
1137 f,
1138 "typmod {typmod} is invalid for type {}: {detail}",
1139 typ.name()
1140 )
1141 }
1142 }
1143 }
1144}
1145
1146impl Error for TypeFromOidError {}
1147
1148#[derive(Debug, Clone)]
1150pub enum TypeConversionError {
1151 UnsupportedType(Type),
1153 InvalidNumericMaxScale(InvalidNumericMaxScaleError),
1156 InvalidNumericConstraint(String),
1159 InvalidCharLength(InvalidCharLengthError),
1162 InvalidVarCharMaxLength(InvalidVarCharMaxLengthError),
1165 InvalidTimestampPrecision(InvalidTimestampPrecisionError),
1168}
1169
1170impl fmt::Display for TypeConversionError {
1171 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1172 match self {
1173 TypeConversionError::UnsupportedType(ty) => write!(f, "type {ty} not supported"),
1174 TypeConversionError::InvalidNumericMaxScale(e) => e.fmt(f),
1175 TypeConversionError::InvalidNumericConstraint(msg) => f.write_str(msg),
1176 TypeConversionError::InvalidCharLength(e) => e.fmt(f),
1177 TypeConversionError::InvalidVarCharMaxLength(e) => e.fmt(f),
1178 TypeConversionError::InvalidTimestampPrecision(e) => e.fmt(f),
1179 }
1180 }
1181}
1182
1183impl Error for TypeConversionError {}
1184
1185impl From<InvalidNumericMaxScaleError> for TypeConversionError {
1186 fn from(e: InvalidNumericMaxScaleError) -> TypeConversionError {
1187 TypeConversionError::InvalidNumericMaxScale(e)
1188 }
1189}
1190
1191impl From<InvalidCharLengthError> for TypeConversionError {
1192 fn from(e: InvalidCharLengthError) -> TypeConversionError {
1193 TypeConversionError::InvalidCharLength(e)
1194 }
1195}
1196
1197impl From<InvalidVarCharMaxLengthError> for TypeConversionError {
1198 fn from(e: InvalidVarCharMaxLengthError) -> TypeConversionError {
1199 TypeConversionError::InvalidVarCharMaxLength(e)
1200 }
1201}
1202
1203impl From<InvalidTimestampPrecisionError> for TypeConversionError {
1204 fn from(e: InvalidTimestampPrecisionError) -> TypeConversionError {
1205 TypeConversionError::InvalidTimestampPrecision(e)
1206 }
1207}
1208
1209impl From<&ScalarType> for Type {
1210 fn from(typ: &ScalarType) -> Type {
1211 match typ {
1212 ScalarType::AclItem => Type::AclItem,
1213 ScalarType::Array(t) => Type::Array(Box::new(From::from(&**t))),
1214 ScalarType::Bool => Type::Bool,
1215 ScalarType::Bytes => Type::Bytea,
1216 ScalarType::PgLegacyChar => Type::Char,
1217 ScalarType::Date => Type::Date,
1218 ScalarType::Float64 => Type::Float8,
1219 ScalarType::Float32 => Type::Float4,
1220 ScalarType::Int16 => Type::Int2,
1221 ScalarType::Int32 => Type::Int4,
1222 ScalarType::Int64 => Type::Int8,
1223 ScalarType::UInt16 => Type::UInt2,
1224 ScalarType::UInt32 => Type::UInt4,
1225 ScalarType::UInt64 => Type::UInt8,
1226 ScalarType::Interval => Type::Interval { constraints: None },
1227 ScalarType::Jsonb => Type::Jsonb,
1228 ScalarType::List { element_type, .. } => {
1229 Type::List(Box::new(From::from(&**element_type)))
1230 }
1231 ScalarType::Map { value_type, .. } => Type::Map {
1232 value_type: Box::new(From::from(&**value_type)),
1233 },
1234 ScalarType::PgLegacyName => Type::Name,
1235 ScalarType::Oid => Type::Oid,
1236 ScalarType::Record { fields, .. } => Type::Record(
1237 fields
1238 .iter()
1239 .map(|(_name, ty)| Type::from(&ty.scalar_type))
1240 .collect(),
1241 ),
1242 ScalarType::String => Type::Text,
1243 ScalarType::Char { length } => Type::BpChar {
1244 length: (*length).map(CharLength::from),
1245 },
1246 ScalarType::VarChar { max_length } => Type::VarChar {
1247 max_length: (*max_length).map(CharLength::from),
1248 },
1249 ScalarType::Time => Type::Time { precision: None },
1250 ScalarType::Timestamp { precision } => Type::Timestamp {
1251 precision: (*precision).map(TimestampPrecision::from),
1252 },
1253 ScalarType::TimestampTz { precision } => Type::TimestampTz {
1254 precision: (*precision).map(TimestampPrecision::from),
1255 },
1256 ScalarType::Uuid => Type::Uuid,
1257 ScalarType::Numeric { max_scale } => Type::Numeric {
1258 constraints: Some(NumericConstraints {
1259 max_precision: i32::from(NUMERIC_DATUM_MAX_PRECISION),
1260 max_scale: match max_scale {
1261 Some(max_scale) => i32::from(max_scale.into_u8()),
1262 None => i32::from(NUMERIC_DATUM_MAX_PRECISION),
1263 },
1264 }),
1265 },
1266 ScalarType::RegClass => Type::RegClass,
1267 ScalarType::RegProc => Type::RegProc,
1268 ScalarType::RegType => Type::RegType,
1269 ScalarType::Int2Vector => Type::Int2Vector,
1270 ScalarType::MzTimestamp => Type::MzTimestamp,
1271 ScalarType::Range { element_type } => Type::Range {
1272 element_type: Box::new(From::from(&**element_type)),
1273 },
1274 ScalarType::MzAclItem => Type::MzAclItem,
1275 }
1276 }
1277}