mongreldb_protocol/
prepared.rs1use core::fmt;
15use std::collections::{BTreeMap, BTreeSet};
16
17use mongreldb_types::ids::{MetadataVersion, SchemaVersion, TableId};
18
19#[repr(transparent)]
23#[derive(
24 Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
25)]
26pub struct StatementId(pub u64);
27
28impl StatementId {
29 pub const ZERO: Self = Self(0);
31
32 pub const fn new(value: u64) -> Self {
34 Self(value)
35 }
36
37 pub const fn get(self) -> u64 {
39 self.0
40 }
41}
42
43impl fmt::Display for StatementId {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "{}", self.0)
46 }
47}
48
49impl fmt::Debug for StatementId {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "StatementId({})", self.0)
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
61pub struct PreparedStatementBinding {
62 pub statement_id: StatementId,
64 pub sql: String,
66 pub parameter_types: Vec<String>,
70 pub catalog_version: MetadataVersion,
72 pub schema_versions: BTreeMap<TableId, SchemaVersion>,
74 pub feature_set: BTreeSet<String>,
79}
80
81impl PreparedStatementBinding {
82 pub fn is_compatible(
91 &self,
92 catalog_version: MetadataVersion,
93 schema_versions: &BTreeMap<TableId, SchemaVersion>,
94 ) -> bool {
95 self.catalog_version == catalog_version
96 && self
97 .schema_versions
98 .iter()
99 .all(|(table, version)| schema_versions.get(table) == Some(version))
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::test_support::assert_serde_round_trip;
107
108 fn binding() -> PreparedStatementBinding {
109 let mut schema_versions = BTreeMap::new();
110 schema_versions.insert(TableId::new(1), SchemaVersion::new(10));
111 schema_versions.insert(TableId::new(2), SchemaVersion::new(20));
112 let mut feature_set = BTreeSet::new();
113 feature_set.insert("ann-index".to_owned());
114 feature_set.insert("cdc".to_owned());
115 PreparedStatementBinding {
116 statement_id: StatementId::new(7),
117 sql: "SELECT * FROM events WHERE tenant = ?".to_owned(),
118 parameter_types: vec!["INT64".to_owned()],
119 catalog_version: MetadataVersion::new(100),
120 schema_versions,
121 feature_set,
122 }
123 }
124
125 #[test]
126 fn statement_id_basics_and_serde() {
127 assert_eq!(StatementId::ZERO.get(), 0);
128 let id = StatementId::new(42);
129 assert_eq!(id.get(), 42);
130 assert_eq!(id.to_string(), "42");
131 assert_eq!(format!("{id:?}"), "StatementId(42)");
132 assert_serde_round_trip(&id);
133 assert_serde_round_trip(&StatementId::ZERO);
134 }
135
136 #[test]
137 fn binding_serde_round_trip() {
138 assert_serde_round_trip(&binding());
139 let empty = PreparedStatementBinding {
140 statement_id: StatementId::ZERO,
141 sql: String::new(),
142 parameter_types: vec![],
143 catalog_version: MetadataVersion::ZERO,
144 schema_versions: BTreeMap::new(),
145 feature_set: BTreeSet::new(),
146 };
147 assert_serde_round_trip(&empty);
148 }
149
150 #[test]
151 fn invalidation_matrix() {
152 let binding = binding();
153 let current = binding.schema_versions.clone();
154
155 assert!(binding.is_compatible(MetadataVersion::new(100), ¤t));
157
158 assert!(!binding.is_compatible(MetadataVersion::new(101), ¤t));
160
161 let mut altered = current.clone();
163 altered.insert(TableId::new(1), SchemaVersion::new(11));
164 assert!(!binding.is_compatible(MetadataVersion::new(100), &altered));
165
166 let mut dropped = current.clone();
168 dropped.remove(&TableId::new(2));
169 assert!(!binding.is_compatible(MetadataVersion::new(100), &dropped));
170
171 let mut unrelated = current.clone();
173 unrelated.insert(TableId::new(3), SchemaVersion::new(1));
174 assert!(binding.is_compatible(MetadataVersion::new(100), &unrelated));
175
176 let no_tables = PreparedStatementBinding {
178 schema_versions: BTreeMap::new(),
179 ..binding.clone()
180 };
181 assert!(no_tables.is_compatible(MetadataVersion::new(100), &BTreeMap::new()));
182 assert!(!no_tables.is_compatible(MetadataVersion::new(99), &BTreeMap::new()));
183 }
184}