mz_repr/
global_id.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10use std::fmt;
11use std::str::FromStr;
12
13use anyhow::{Error, anyhow};
14use columnar::Columnar;
15use columnation::{Columnation, CopyRegion};
16use mz_lowertest::MzReflect;
17use mz_ore::id_gen::AtomicIdGen;
18use mz_proto::{RustType, TryFromProtoError};
19use proptest_derive::Arbitrary;
20use serde::{Deserialize, Serialize};
21
22use crate::CatalogItemId;
23
24include!(concat!(env!("OUT_DIR"), "/mz_repr.global_id.rs"));
25
26/// The identifier for an item/object.
27///
28/// WARNING: `GlobalId`'s `Ord` implementation does not express a dependency order.
29/// One should explicitly topologically sort objects by their dependencies, rather
30/// than rely on the order of identifiers.
31#[derive(
32    Arbitrary,
33    Clone,
34    Copy,
35    Debug,
36    Eq,
37    PartialEq,
38    Ord,
39    PartialOrd,
40    Hash,
41    Serialize,
42    Deserialize,
43    MzReflect,
44    Columnar,
45)]
46pub enum GlobalId {
47    /// System namespace.
48    System(u64),
49    /// Introspection Source Index namespace.
50    IntrospectionSourceIndex(u64),
51    /// User namespace.
52    User(u64),
53    /// Transient namespace.
54    Transient(u64),
55    /// Dummy id for query being explained
56    Explain,
57}
58
59// `GlobalId`s are serialized often, so it would be nice to try and keep them small. If this assert
60// fails, then there isn't any correctness issues just potential performance issues.
61static_assertions::assert_eq_size!(GlobalId, [u8; 16]);
62
63impl GlobalId {
64    /// Reports whether this ID is in the system namespace.
65    pub fn is_system(&self) -> bool {
66        matches!(
67            self,
68            GlobalId::System(_) | GlobalId::IntrospectionSourceIndex(_)
69        )
70    }
71
72    /// Reports whether this ID is in the user namespace.
73    pub fn is_user(&self) -> bool {
74        matches!(self, GlobalId::User(_))
75    }
76
77    /// Reports whether this ID is in the transient namespace.
78    pub fn is_transient(&self) -> bool {
79        matches!(self, GlobalId::Transient(_))
80    }
81}
82
83impl FromStr for GlobalId {
84    type Err = Error;
85
86    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
87        if s.len() < 2 {
88            return Err(anyhow!("couldn't parse id {}", s));
89        }
90        if s == "Explained Query" {
91            return Ok(GlobalId::Explain);
92        }
93        let tag = s.chars().next().unwrap();
94        s = &s[1..];
95        let variant = match tag {
96            's' => {
97                if Some('i') == s.chars().next() {
98                    s = &s[1..];
99                    GlobalId::IntrospectionSourceIndex
100                } else {
101                    GlobalId::System
102                }
103            }
104            'u' => GlobalId::User,
105            't' => GlobalId::Transient,
106            _ => return Err(anyhow!("couldn't parse id {}", s)),
107        };
108        let val: u64 = s.parse()?;
109        Ok(variant(val))
110    }
111}
112
113impl fmt::Display for GlobalId {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        match self {
116            GlobalId::System(id) => write!(f, "s{}", id),
117            GlobalId::IntrospectionSourceIndex(id) => write!(f, "si{}", id),
118            GlobalId::User(id) => write!(f, "u{}", id),
119            GlobalId::Transient(id) => write!(f, "t{}", id),
120            GlobalId::Explain => write!(f, "Explained Query"),
121        }
122    }
123}
124
125impl RustType<ProtoGlobalId> for GlobalId {
126    fn into_proto(&self) -> ProtoGlobalId {
127        use proto_global_id::Kind::*;
128        ProtoGlobalId {
129            kind: Some(match self {
130                GlobalId::System(x) => System(*x),
131                GlobalId::IntrospectionSourceIndex(x) => IntrospectionSourceIndex(*x),
132                GlobalId::User(x) => User(*x),
133                GlobalId::Transient(x) => Transient(*x),
134                GlobalId::Explain => Explain(()),
135            }),
136        }
137    }
138
139    fn from_proto(proto: ProtoGlobalId) -> Result<Self, TryFromProtoError> {
140        use proto_global_id::Kind::*;
141        match proto.kind {
142            Some(System(x)) => Ok(GlobalId::System(x)),
143            Some(IntrospectionSourceIndex(x)) => Ok(GlobalId::IntrospectionSourceIndex(x)),
144            Some(User(x)) => Ok(GlobalId::User(x)),
145            Some(Transient(x)) => Ok(GlobalId::Transient(x)),
146            Some(Explain(_)) => Ok(GlobalId::Explain),
147            None => Err(TryFromProtoError::missing_field("ProtoGlobalId::kind")),
148        }
149    }
150}
151
152impl Columnation for GlobalId {
153    type InnerRegion = CopyRegion<GlobalId>;
154}
155
156#[derive(Debug)]
157pub struct TransientIdGen(AtomicIdGen);
158
159impl TransientIdGen {
160    pub fn new() -> Self {
161        let inner = AtomicIdGen::default();
162        // Transient IDs start at 1, so throw away the 0 value.
163        let _ = inner.allocate_id();
164        Self(inner)
165    }
166
167    pub fn allocate_id(&self) -> (CatalogItemId, GlobalId) {
168        let inner = self.0.allocate_id();
169        (CatalogItemId::Transient(inner), GlobalId::Transient(inner))
170    }
171}