1use crate::lsn::Lsn;
7use bytes::Bytes;
8use std::sync::Arc;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum ReplicaIdentity {
13 Default,
15 Nothing,
17 Full,
19 Index,
21}
22
23impl ReplicaIdentity {
24 pub fn from_char(c: u8) -> Option<Self> {
25 Some(match c {
26 b'd' => ReplicaIdentity::Default,
27 b'n' => ReplicaIdentity::Nothing,
28 b'f' => ReplicaIdentity::Full,
29 b'i' => ReplicaIdentity::Index,
30 _ => return None,
31 })
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct Column {
37 pub name: String,
38 pub type_oid: u32,
39 pub type_modifier: i32,
40 pub is_key: bool,
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct Relation {
47 pub id: u32,
48 pub schema: String,
49 pub name: String,
50 pub replica_identity: ReplicaIdentity,
51 pub columns: Vec<Column>,
52}
53
54impl Relation {
55 pub fn key_columns(&self) -> impl Iterator<Item = (usize, &Column)> {
56 self.columns.iter().enumerate().filter(|(_, c)| c.is_key)
57 }
58
59 pub fn qualified_name(&self) -> String {
60 format!("{}.{}", self.schema, self.name)
61 }
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum Value {
66 Null,
67 Unchanged,
69 Text(Bytes),
71 Binary(Bytes),
73}
74
75impl Value {
76 pub fn byte_len(&self) -> usize {
77 match self {
78 Value::Null | Value::Unchanged => 0,
79 Value::Text(b) | Value::Binary(b) => b.len(),
80 }
81 }
82
83 pub fn text(s: &str) -> Value {
84 Value::Text(Bytes::copy_from_slice(s.as_bytes()))
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq, Default)]
89pub struct Tuple {
90 pub values: Vec<Value>,
91}
92
93impl Tuple {
94 pub fn byte_len(&self) -> usize {
95 self.values.iter().map(|v| v.byte_len() + 8).sum()
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub enum Op {
101 Insert {
102 relation: u32,
103 new: Tuple,
104 },
105 Update {
106 relation: u32,
107 key: Option<Tuple>,
109 old: Option<Tuple>,
111 new: Tuple,
112 },
113 Delete {
114 relation: u32,
115 key: Option<Tuple>,
116 old: Option<Tuple>,
117 },
118 Truncate {
119 relations: Vec<u32>,
120 cascade: bool,
121 restart_identity: bool,
122 },
123}
124
125impl Op {
126 pub fn relation_ids(&self) -> Vec<u32> {
127 match self {
128 Op::Insert { relation, .. }
129 | Op::Update { relation, .. }
130 | Op::Delete { relation, .. } => {
131 vec![*relation]
132 }
133 Op::Truncate { relations, .. } => relations.clone(),
134 }
135 }
136
137 pub fn byte_len(&self) -> usize {
138 let base = 32;
139 match self {
140 Op::Insert { new, .. } => base + new.byte_len(),
141 Op::Update { key, old, new, .. } => {
142 base + new.byte_len()
143 + key.as_ref().map_or(0, Tuple::byte_len)
144 + old.as_ref().map_or(0, Tuple::byte_len)
145 }
146 Op::Delete { key, old, .. } => {
147 base + key.as_ref().map_or(0, Tuple::byte_len)
148 + old.as_ref().map_or(0, Tuple::byte_len)
149 }
150 Op::Truncate { relations, .. } => base + relations.len() * 4,
151 }
152 }
153
154 pub fn kind(&self) -> &'static str {
155 match self {
156 Op::Insert { .. } => "insert",
157 Op::Update { .. } => "update",
158 Op::Delete { .. } => "delete",
159 Op::Truncate { .. } => "truncate",
160 }
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub enum SourceEvent {
167 Begin {
168 final_lsn: Lsn,
170 commit_ts: i64,
172 xid: u32,
173 },
174 Relation(Arc<Relation>),
175 Op(Op),
176 Commit {
177 commit_lsn: Lsn,
178 end_lsn: Lsn,
180 commit_ts: i64,
181 },
182 Keepalive {
184 wal_end: Lsn,
185 },
186}
187
188impl SourceEvent {
189 pub fn byte_len(&self) -> usize {
191 match self {
192 SourceEvent::Begin { .. }
193 | SourceEvent::Commit { .. }
194 | SourceEvent::Keepalive { .. } => 32,
195 SourceEvent::Relation(r) => 64 + r.columns.len() * 48,
196 SourceEvent::Op(op) => op.byte_len(),
197 }
198 }
199}
200
201pub fn pg_micros_to_unix_micros(pg: i64) -> i64 {
203 pg + 946_684_800_000_000
204}
205
206pub fn now_pg_micros() -> i64 {
208 let unix = std::time::SystemTime::now()
209 .duration_since(std::time::UNIX_EPOCH)
210 .map(|d| d.as_micros() as i64)
211 .unwrap_or(0);
212 unix - 946_684_800_000_000
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn byte_accounting_counts_payloads() {
221 let t = Tuple {
222 values: vec![Value::text("abc"), Value::Null, Value::Unchanged],
223 };
224 assert_eq!(t.byte_len(), 3 + 3 * 8);
225 let op = Op::Insert {
226 relation: 1,
227 new: t.clone(),
228 };
229 assert_eq!(op.byte_len(), 32 + t.byte_len());
230 assert_eq!(SourceEvent::Op(op).byte_len(), 32 + t.byte_len());
231 assert_eq!(SourceEvent::Keepalive { wal_end: Lsn(1) }.byte_len(), 32);
232 }
233
234 #[test]
235 fn epoch_conversion() {
236 assert_eq!(pg_micros_to_unix_micros(0), 946_684_800_000_000);
238 assert!(now_pg_micros() > 0);
239 }
240
241 #[test]
242 fn replica_identity_chars() {
243 assert_eq!(
244 ReplicaIdentity::from_char(b'd'),
245 Some(ReplicaIdentity::Default)
246 );
247 assert_eq!(
248 ReplicaIdentity::from_char(b'f'),
249 Some(ReplicaIdentity::Full)
250 );
251 assert_eq!(ReplicaIdentity::from_char(b'x'), None);
252 }
253}