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 proptest_derive::Arbitrary;
19use serde::{Deserialize, Serialize};
20
21use crate::CatalogItemId;
22
23/// The identifier for an item/object.
24///
25/// WARNING: `GlobalId`'s `Ord` implementation does not express a dependency order.
26/// One should explicitly topologically sort objects by their dependencies, rather
27/// than rely on the order of identifiers.
28#[derive(
29    Arbitrary,
30    Clone,
31    Copy,
32    Debug,
33    Eq,
34    PartialEq,
35    Ord,
36    PartialOrd,
37    Hash,
38    Serialize,
39    Deserialize,
40    MzReflect,
41    Columnar,
42)]
43pub enum GlobalId {
44    /// System namespace.
45    System(u64),
46    /// Introspection Source Index namespace.
47    IntrospectionSourceIndex(u64),
48    /// User namespace.
49    User(u64),
50    /// Transient namespace.
51    Transient(u64),
52    /// Dummy id for query being explained
53    Explain,
54}
55
56// `GlobalId`s are serialized often, so it would be nice to try and keep them small. If this assert
57// fails, then there isn't any correctness issues just potential performance issues.
58static_assertions::assert_eq_size!(GlobalId, [u8; 16]);
59
60impl GlobalId {
61    /// Reports whether this ID is in the system namespace.
62    pub fn is_system(&self) -> bool {
63        matches!(
64            self,
65            GlobalId::System(_) | GlobalId::IntrospectionSourceIndex(_)
66        )
67    }
68
69    /// Reports whether this ID is in the user namespace.
70    pub fn is_user(&self) -> bool {
71        matches!(self, GlobalId::User(_))
72    }
73
74    /// Reports whether this ID is in the transient namespace.
75    pub fn is_transient(&self) -> bool {
76        matches!(self, GlobalId::Transient(_))
77    }
78}
79
80impl FromStr for GlobalId {
81    type Err = Error;
82
83    fn from_str(mut s: &str) -> Result<Self, Self::Err> {
84        if s.len() < 2 {
85            return Err(anyhow!("couldn't parse id {}", s));
86        }
87        if s == "Explained Query" {
88            return Ok(GlobalId::Explain);
89        }
90        let tag = s.chars().next().unwrap();
91        s = &s[1..];
92        let variant = match tag {
93            's' => {
94                if Some('i') == s.chars().next() {
95                    s = &s[1..];
96                    GlobalId::IntrospectionSourceIndex
97                } else {
98                    GlobalId::System
99                }
100            }
101            'u' => GlobalId::User,
102            't' => GlobalId::Transient,
103            _ => return Err(anyhow!("couldn't parse id {}", s)),
104        };
105        let val: u64 = s.parse()?;
106        Ok(variant(val))
107    }
108}
109
110impl fmt::Display for GlobalId {
111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112        match self {
113            GlobalId::System(id) => write!(f, "s{}", id),
114            GlobalId::IntrospectionSourceIndex(id) => write!(f, "si{}", id),
115            GlobalId::User(id) => write!(f, "u{}", id),
116            GlobalId::Transient(id) => write!(f, "t{}", id),
117            GlobalId::Explain => write!(f, "Explained Query"),
118        }
119    }
120}
121
122impl Columnation for GlobalId {
123    type InnerRegion = CopyRegion<GlobalId>;
124}
125
126#[derive(Debug)]
127pub struct TransientIdGen(AtomicIdGen);
128
129impl TransientIdGen {
130    pub fn new() -> Self {
131        let inner = AtomicIdGen::default();
132        // Transient IDs start at 1, so throw away the 0 value.
133        let _ = inner.allocate_id();
134        Self(inner)
135    }
136
137    pub fn allocate_id(&self) -> (CatalogItemId, GlobalId) {
138        let inner = self.0.allocate_id();
139        (CatalogItemId::Transient(inner), GlobalId::Transient(inner))
140    }
141}