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