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}