Skip to main content

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