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.
910use std::collections::BTreeMap;
1112use mz_ore::cast::CastFrom;
13use mz_repr::explain::{DummyHumanizer, ExprHumanizer};
14use mz_repr::{GlobalId, RelationType, ScalarType};
1516/// A catalog that holds types of objects previously created for the unit test.
17///
18/// This is for the purpose of allowing `MirRelationExpr`s to refer to them
19/// later.
20#[derive(Debug, Default)]
21pub struct TestCatalog {
22 objects: BTreeMap<String, (GlobalId, Vec<String>, RelationType)>,
23 names: BTreeMap<GlobalId, String>,
24}
2526impl<'a> TestCatalog {
27/// Registers an object in the catalog.
28 ///
29 /// Specifying `transient` as true allows the object to be deleted by
30 /// [Self::remove_transient_objects].
31 ///
32 /// Returns the GlobalId assigned by the catalog to the object.
33 ///
34 /// Errors if an object of the same name is already in the catalog.
35pub fn insert(
36&mut self,
37 name: &str,
38 cols: Vec<String>,
39 typ: RelationType,
40 transient: bool,
41 ) -> Result<GlobalId, String> {
42if self.objects.contains_key(name) {
43return Err(format!("Object {name} already exists in catalog"));
44 }
45let id = if transient {
46 GlobalId::Transient(u64::cast_from(self.objects.len()))
47 } else {
48 GlobalId::User(u64::cast_from(self.objects.len()))
49 };
50self.objects.insert(name.to_string(), (id, cols, typ));
51self.names.insert(id, name.to_string());
52Ok(id)
53 }
5455pub fn get(&'a self, name: &str) -> Option<&'a (GlobalId, Vec<String>, RelationType)> {
56self.objects.get(name)
57 }
5859/// Looks up the name of the object referred to as `id`.
60pub fn get_source_name(&'a self, id: &GlobalId) -> Option<&'a String> {
61self.names.get(id)
62 }
6364/// Clears all transient objects from the catalog.
65pub fn remove_transient_objects(&mut self) {
66self.objects
67 .retain(|_, (id, _, _)| !matches!(id, GlobalId::Transient(_)));
68self.names
69 .retain(|k, _| !matches!(k, GlobalId::Transient(_)));
70 }
71}
7273impl ExprHumanizer for TestCatalog {
74fn humanize_id(&self, id: GlobalId) -> Option<String> {
75self.names.get(&id).map(|s| s.to_string())
76 }
7778fn humanize_id_unqualified(&self, id: GlobalId) -> Option<String> {
79self.names.get(&id).map(|s| s.to_string())
80 }
8182fn humanize_id_parts(&self, id: GlobalId) -> Option<Vec<String>> {
83self.humanize_id_unqualified(id).map(|name| vec![name])
84 }
8586fn humanize_scalar_type(&self, ty: &ScalarType, postgres_compat: bool) -> String {
87 DummyHumanizer.humanize_scalar_type(ty, postgres_compat)
88 }
8990fn column_names_for_id(&self, id: GlobalId) -> Option<Vec<String>> {
91let src_name = self.get_source_name(&id)?;
92self.objects.get(src_name).map(|(_, cols, _)| cols.clone())
93 }
9495fn humanize_column(&self, id: GlobalId, column: usize) -> Option<String> {
96let src_name = self.get_source_name(&id)?;
97self.objects
98 .get(src_name)
99 .map(|(_, cols, _)| cols[column].clone())
100 }
101102fn id_exists(&self, id: GlobalId) -> bool {
103self.names.contains_key(&id)
104 }
105}