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 = 77;
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, v75, v76, v77);
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 = "protos";
61
62    #[mz_ore::test]
63    fn test_assert_snapshots_exist() {
64        // Get all of the files in the snapshot directory, with the `.proto` extension.
65        let mut filenames: BTreeSet<_> = fs::read_dir(PROTO_DIRECTORY)
66            .expect("failed to read protos dir")
67            .map(|entry| entry.expect("failed to read dir entry").file_name())
68            .map(|filename| filename.to_str().expect("utf8").to_string())
69            .filter(|filename| filename.ends_with("proto"))
70            .collect();
71
72        // Assert objects.proto exists.
73        assert!(filenames.remove("objects.proto"));
74
75        // Assert snapshots exist for all of the versions we support.
76        for version in MIN_CATALOG_VERSION..=CATALOG_VERSION {
77            let filename = format!("objects_v{version}.proto");
78            assert!(
79                filenames.remove(&filename),
80                "Missing snapshot for v{version}."
81            );
82        }
83
84        // Common case. Check to make sure the user bumped the CATALOG_VERSION.
85        if !filenames.is_empty()
86            && filenames.remove(&format!("objects_v{}.proto", CATALOG_VERSION + 1))
87        {
88            panic!(
89                "Found snapshot for v{}, please also bump `CATALOG_VERSION`.",
90                CATALOG_VERSION + 1
91            )
92        }
93
94        // Assert there aren't any extra snapshots.
95        assert!(
96            filenames.is_empty(),
97            "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`."
98        );
99    }
100
101    #[mz_ore::test]
102    fn test_assert_current_snapshot() {
103        // Read the content from both files.
104        let current = fs::File::open(format!("{PROTO_DIRECTORY}/objects.proto"))
105            .map(BufReader::new)
106            .expect("read current");
107        let snapshot = fs::File::open(format!(
108            "{PROTO_DIRECTORY}/objects_v{CATALOG_VERSION}.proto"
109        ))
110        .map(BufReader::new)
111        .expect("read snapshot");
112
113        // Read in all of the lines so we can compare the content of †he files.
114        let current: Vec<_> = current
115            .lines()
116            .map(|r| r.expect("failed to read line from current"))
117            // Filter out the package name, since we expect that to be different.
118            .filter(|line| line != "package objects;")
119            .collect();
120        let snapshot: Vec<_> = snapshot
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 != &format!("package objects_v{CATALOG_VERSION};"))
125            .collect();
126
127        // Note: objects.proto and objects_v<CATALOG_VERSION>.proto should be exactly the same. The
128        // reason being, when bumping the catalog to the next version, CATALOG_VERSION + 1, we need a
129        // snapshot to migrate _from_, which should be a snapshot of how the protos are today.
130        // Hence why the two files should be exactly the same.
131        similar_asserts::assert_eq!(current, snapshot);
132    }
133}