Line data Source code
1 : //! This module defines the value type used by the storage engine.
2 : //!
3 : //! A [`Value`] represents either a completely new value for one Key ([`Value::Image`]),
4 : //! or a "delta" of how to get from previous version of the value to the new one
5 : //! ([`Value::WalRecord`]])
6 : //!
7 : //! Note that the [`Value`] type is used for the permananent storage format, so any
8 : //! changes to it must be backwards compatible.
9 :
10 : use bytes::Bytes;
11 : use serde::{Deserialize, Serialize};
12 :
13 : use crate::models::record::NeonWalRecord;
14 :
15 0 : #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16 : pub enum Value {
17 : /// An Image value contains a full copy of the value
18 : Image(Bytes),
19 : /// A WalRecord value contains a WAL record that needs to be
20 : /// replayed get the full value. Replaying the WAL record
21 : /// might need a previous version of the value (if will_init()
22 : /// returns false), or it may be replayed stand-alone (true).
23 : WalRecord(NeonWalRecord),
24 : }
25 :
26 : impl Value {
27 : #[inline(always)]
28 402 : pub fn is_image(&self) -> bool {
29 402 : matches!(self, Value::Image(_))
30 0 : }
31 :
32 : #[inline(always)]
33 3620363 : pub fn will_init(&self) -> bool {
34 3620363 : match self {
35 3516853 : Value::Image(_) => true,
36 103510 : Value::WalRecord(rec) => rec.will_init(),
37 : }
38 2565282 : }
39 :
40 : #[inline(always)]
41 437 : pub fn estimated_size(&self) -> usize {
42 0 : match self {
43 314 : Value::Image(image) => image.len(),
44 : Value::WalRecord(NeonWalRecord::AuxFile {
45 0 : content: Some(content),
46 : ..
47 0 : }) => content.len(),
48 0 : Value::WalRecord(NeonWalRecord::Postgres { rec, .. }) => rec.len(),
49 0 : Value::WalRecord(NeonWalRecord::ClogSetAborted { xids }) => xids.len() * 4,
50 0 : Value::WalRecord(NeonWalRecord::ClogSetCommitted { xids, .. }) => xids.len() * 4,
51 0 : Value::WalRecord(NeonWalRecord::MultixactMembersCreate { members, .. }) => {
52 0 : members.len() * 8
53 : }
54 123 : _ => 8192, /* use image size as the estimation */
55 : }
56 0 : }
57 : }
58 :
59 : #[derive(Debug, PartialEq)]
60 : pub enum InvalidInput {
61 : TooShortValue,
62 : TooShortPostgresRecord,
63 : }
64 :
65 : /// We could have a ValueRef where everything is `serde(borrow)`. Before implementing that, lets
66 : /// use this type for querying if a slice looks some particular way.
67 : pub struct ValueBytes;
68 :
69 : impl ValueBytes {
70 : #[inline(always)]
71 50 : pub fn will_init(raw: &[u8]) -> Result<bool, InvalidInput> {
72 50 : if raw.len() < 12 {
73 24 : return Err(InvalidInput::TooShortValue);
74 10 : }
75 :
76 26 : let value_discriminator = &raw[0..4];
77 :
78 26 : if value_discriminator == [0, 0, 0, 0] {
79 : // Value::Image always initializes
80 8 : return Ok(true);
81 8 : }
82 :
83 18 : if value_discriminator != [0, 0, 0, 1] {
84 : // not a Value::WalRecord(..)
85 0 : return Ok(false);
86 8 : }
87 :
88 18 : let walrecord_discriminator = &raw[4..8];
89 :
90 18 : if walrecord_discriminator != [0, 0, 0, 0] {
91 : // only NeonWalRecord::Postgres can have will_init
92 1 : return Ok(false);
93 7 : }
94 :
95 17 : if raw.len() < 17 {
96 5 : return Err(InvalidInput::TooShortPostgresRecord);
97 2 : }
98 :
99 12 : Ok(raw[8] == 1)
100 34 : }
101 : }
102 :
103 : #[cfg(test)]
104 : mod test {
105 : use bytes::Bytes;
106 : use utils::bin_ser::BeSer;
107 :
108 : use super::*;
109 :
110 : macro_rules! roundtrip {
111 : ($orig:expr, $expected:expr) => {{
112 : let orig: Value = $orig;
113 :
114 : let actual = Value::ser(&orig).unwrap();
115 : let expected: &[u8] = &$expected;
116 :
117 : assert_eq!(utils::Hex(&actual), utils::Hex(expected));
118 :
119 : let deser = Value::des(&actual).unwrap();
120 :
121 : assert_eq!(orig, deser);
122 : }};
123 : }
124 :
125 : #[test]
126 1 : fn image_roundtrip() {
127 1 : let image = Bytes::from_static(b"foobar");
128 1 : let image = Value::Image(image);
129 :
130 : #[rustfmt::skip]
131 1 : let expected = [
132 1 : // top level discriminator of 4 bytes
133 1 : 0x00, 0x00, 0x00, 0x00,
134 1 : // 8 byte length
135 1 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
136 1 : // foobar
137 1 : 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
138 1 : ];
139 :
140 1 : roundtrip!(image, expected);
141 :
142 1 : assert!(ValueBytes::will_init(&expected).unwrap());
143 1 : }
144 :
145 : #[test]
146 1 : fn walrecord_postgres_roundtrip() {
147 1 : let rec = NeonWalRecord::Postgres {
148 1 : will_init: true,
149 1 : rec: Bytes::from_static(b"foobar"),
150 1 : };
151 1 : let rec = Value::WalRecord(rec);
152 :
153 : #[rustfmt::skip]
154 1 : let expected = [
155 1 : // flattened discriminator of total 8 bytes
156 1 : 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
157 1 : // will_init
158 1 : 0x01,
159 1 : // 8 byte length
160 1 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
161 1 : // foobar
162 1 : 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72
163 1 : ];
164 :
165 1 : roundtrip!(rec, expected);
166 :
167 1 : assert!(ValueBytes::will_init(&expected).unwrap());
168 1 : }
169 :
170 : #[test]
171 1 : fn bytes_inspection_too_short_image() {
172 1 : let rec = Value::Image(Bytes::from_static(b""));
173 :
174 : #[rustfmt::skip]
175 1 : let expected = [
176 1 : // top level discriminator of 4 bytes
177 1 : 0x00, 0x00, 0x00, 0x00,
178 1 : // 8 byte length
179 1 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180 1 : ];
181 :
182 1 : roundtrip!(rec, expected);
183 :
184 1 : assert!(ValueBytes::will_init(&expected).unwrap());
185 1 : assert_eq!(expected.len(), 12);
186 13 : for len in 0..12 {
187 12 : assert_eq!(
188 12 : ValueBytes::will_init(&expected[..len]).unwrap_err(),
189 : InvalidInput::TooShortValue
190 : );
191 : }
192 1 : }
193 :
194 : #[test]
195 1 : fn bytes_inspection_too_short_postgres_record() {
196 1 : let rec = NeonWalRecord::Postgres {
197 1 : will_init: false,
198 1 : rec: Bytes::from_static(b""),
199 1 : };
200 1 : let rec = Value::WalRecord(rec);
201 :
202 : #[rustfmt::skip]
203 1 : let expected = [
204 1 : // flattened discriminator of total 8 bytes
205 1 : 0x00, 0x00, 0x00, 0x01,
206 1 : 0x00, 0x00, 0x00, 0x00,
207 1 : // will_init
208 1 : 0x00,
209 1 : // 8 byte length
210 1 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
211 1 : ];
212 :
213 1 : roundtrip!(rec, expected);
214 :
215 1 : assert!(!ValueBytes::will_init(&expected).unwrap());
216 1 : assert_eq!(expected.len(), 17);
217 6 : for len in 12..17 {
218 5 : assert_eq!(
219 5 : ValueBytes::will_init(&expected[..len]).unwrap_err(),
220 : InvalidInput::TooShortPostgresRecord
221 : )
222 : }
223 13 : for len in 0..12 {
224 12 : assert_eq!(
225 12 : ValueBytes::will_init(&expected[..len]).unwrap_err(),
226 : InvalidInput::TooShortValue
227 : )
228 : }
229 1 : }
230 :
231 : #[test]
232 1 : fn clear_visibility_map_flags_example() {
233 1 : let rec = NeonWalRecord::ClearVisibilityMapFlags {
234 1 : new_heap_blkno: Some(0x11),
235 1 : old_heap_blkno: None,
236 1 : flags: 0x03,
237 1 : };
238 1 : let rec = Value::WalRecord(rec);
239 :
240 : #[rustfmt::skip]
241 1 : let expected = [
242 1 : // discriminators
243 1 : 0x00, 0x00, 0x00, 0x01,
244 1 : 0x00, 0x00, 0x00, 0x01,
245 1 : // Some == 1 followed by 4 bytes
246 1 : 0x01, 0x00, 0x00, 0x00, 0x11,
247 1 : // None == 0
248 1 : 0x00,
249 1 : // flags
250 1 : 0x03
251 1 : ];
252 :
253 1 : roundtrip!(rec, expected);
254 :
255 1 : assert!(!ValueBytes::will_init(&expected).unwrap());
256 1 : }
257 : }
|