mz_catalog_protos/lib.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
10//! All of the protobuf types we durably persist in the Catalog.
11//!
12//! These exist outside of the `mz_catalog` crate because the code generated by
13//! [`prost`] for the protobuf files, takes a considerable (>40s) amount of
14//! time to `build` and even `check`, yet the protobuf files rarely change. So
15//! moving them into a separate crate allows folks to iterate on the
16//! `mz_catalog` crate without having to pay the cost of re-compiling these
17//! protobuf files.
18
19pub mod audit_log;
20pub mod serialization;
21
22/// The current version of the `Catalog`.
23///
24/// We will initialize new `Catalog`s with this version, and migrate existing `Catalog`s to this
25/// version. Whenever the `Catalog` changes, e.g. the protobufs we serialize in the `Catalog`
26/// change, we need to bump this version.
27pub const CATALOG_VERSION: u64 = 74;
28
29/// The minimum `Catalog` version number that we support migrating from.
30///
31/// After bumping this we can delete the old migrations.
32pub const MIN_CATALOG_VERSION: u64 = 67;
33
34macro_rules! proto_objects {
35 ( $( $x:ident ),* ) => {
36 paste::paste! {
37 $(
38 pub mod [<objects_ $x>] {
39 include!(concat!(env!("OUT_DIR"), "/objects_", stringify!($x), ".rs"));
40 }
41 )*
42 pub mod objects {
43 include!(concat!(env!("OUT_DIR"), "/objects.rs"));
44 }
45 }
46 };
47}
48
49proto_objects!(v67, v68, v69, v70, v71, v72, v73, v74);
50
51#[cfg(test)]
52mod tests {
53 use std::collections::BTreeSet;
54 use std::fs;
55 use std::io::{BufRead, BufReader};
56
57 use crate::{CATALOG_VERSION, MIN_CATALOG_VERSION};
58
59 // Note: Feel free to update this path if the protos move.
60 const PROTO_DIRECTORY: &str = {
61 if mz_build_tools::is_bazel_build() {
62 "src/catalog/protos"
63 } else {
64 "protos"
65 }
66 };
67
68 #[mz_ore::test]
69 fn test_assert_snapshots_exist() {
70 // Get all of the files in the snapshot directory, with the `.proto` extension.
71 let mut filenames: BTreeSet<_> = fs::read_dir(PROTO_DIRECTORY)
72 .expect("failed to read protos dir")
73 .map(|entry| entry.expect("failed to read dir entry").file_name())
74 .map(|filename| filename.to_str().expect("utf8").to_string())
75 .filter(|filename| filename.ends_with("proto"))
76 .collect();
77
78 // Assert objects.proto exists.
79 assert!(filenames.remove("objects.proto"));
80
81 // Assert snapshots exist for all of the versions we support.
82 for version in MIN_CATALOG_VERSION..=CATALOG_VERSION {
83 let filename = format!("objects_v{version}.proto");
84 assert!(
85 filenames.remove(&filename),
86 "Missing snapshot for v{version}."
87 );
88 }
89
90 // Common case. Check to make sure the user bumped the CATALOG_VERSION.
91 if !filenames.is_empty()
92 && filenames.remove(&format!("objects_v{}.proto", CATALOG_VERSION + 1))
93 {
94 panic!(
95 "Found snapshot for v{}, please also bump `CATALOG_VERSION`.",
96 CATALOG_VERSION + 1
97 )
98 }
99
100 // Assert there aren't any extra snapshots.
101 assert!(
102 filenames.is_empty(),
103 "Found snapshots for unsupported catalog versions {filenames:?}.\nIf you just increased `MIN_CATALOG_VERSION`, then please delete the old snapshots. If you created a new snapshot, please bump `CATALOG_VERSION`."
104 );
105 }
106
107 #[mz_ore::test]
108 fn test_assert_current_snapshot() {
109 // Read the content from both files.
110 let current = fs::File::open(format!("{PROTO_DIRECTORY}/objects.proto"))
111 .map(BufReader::new)
112 .expect("read current");
113 let snapshot = fs::File::open(format!(
114 "{PROTO_DIRECTORY}/objects_v{CATALOG_VERSION}.proto"
115 ))
116 .map(BufReader::new)
117 .expect("read snapshot");
118
119 // Read in all of the lines so we can compare the content of †he files.
120 let current: Vec<_> = current
121 .lines()
122 .map(|r| r.expect("failed to read line from current"))
123 // Filter out the package name, since we expect that to be different.
124 .filter(|line| line != "package objects;")
125 .collect();
126 let snapshot: Vec<_> = snapshot
127 .lines()
128 .map(|r| r.expect("failed to read line from current"))
129 // Filter out the package name, since we expect that to be different.
130 .filter(|line| line != &format!("package objects_v{CATALOG_VERSION};"))
131 .collect();
132
133 // Note: objects.proto and objects_v<CATALOG_VERSION>.proto should be exactly the same. The
134 // reason being, when bumping the catalog to the next version, CATALOG_VERSION + 1, we need a
135 // snapshot to migrate _from_, which should be a snapshot of how the protos are today.
136 // Hence why the two files should be exactly the same.
137 similar_asserts::assert_eq!(current, snapshot);
138 }
139}