scylla_cql/serialize/writers.rs
1//! Contains types and traits used for safe serialization of values for a CQL statement.
2
3// Note: When editing above doc-comment edit the corresponding comment on
4// re-export module in scylla crate too.
5
6use thiserror::Error;
7
8use super::row::SerializedValues;
9
10/// An interface that facilitates writing values for a CQL query.
11pub struct RowWriter<'buf> {
12 // Buffer that this value should be serialized to.
13 buf: &'buf mut Vec<u8>,
14
15 // Number of values written so far.
16 value_count: usize,
17}
18
19impl<'buf> RowWriter<'buf> {
20 /// Creates a new row writer based on an existing Vec.
21 ///
22 /// The newly created row writer will append data to the end of the vec.
23 #[inline]
24 pub fn new(buf: &'buf mut Vec<u8>) -> Self {
25 Self {
26 buf,
27 value_count: 0,
28 }
29 }
30
31 /// Returns the number of values that were written so far.
32 ///
33 /// Note that the protocol allows at most u16::MAX to be written into a query,
34 /// but the writer's interface allows more to be written.
35 #[inline]
36 pub fn value_count(&self) -> usize {
37 self.value_count
38 }
39
40 /// Appends a new value to the sequence and returns an object that allows
41 /// to fill it in.
42 #[inline]
43 pub fn make_cell_writer(&mut self) -> CellWriter<'_> {
44 self.value_count += 1;
45 CellWriter::new(self.buf)
46 }
47
48 /// Appends the values from an existing [`SerializedValues`] object to the
49 /// current `RowWriter`.
50 #[inline]
51 pub fn append_serialize_row(&mut self, sv: &SerializedValues) {
52 self.value_count += sv.element_count() as usize;
53 self.buf.extend_from_slice(sv.get_contents())
54 }
55}
56
57/// Represents a handle to a CQL value that needs to be written into.
58///
59/// The writer can either be transformed into a ready value right away
60/// (via [`set_null`](CellWriter::set_null),
61/// [`set_unset`](CellWriter::set_unset)
62/// or [`set_value`](CellWriter::set_value) or transformed into
63/// the [`CellValueBuilder`] in order to gradually initialize
64/// the value when the contents are not available straight away.
65///
66/// After the value is fully initialized, the handle is consumed and
67/// a [`WrittenCellProof`] object is returned
68/// in its stead. This is a type-level proof that the value was fully initialized
69/// and is used in [`SerializeValue::serialize`](`super::value::SerializeValue::serialize`)
70/// in order to enforce the implementer to fully initialize the provided handle
71/// to CQL value.
72///
73/// Dropping this type without calling any of its methods will result
74/// in nothing being written.
75pub struct CellWriter<'buf> {
76 buf: &'buf mut Vec<u8>,
77 write_size: bool,
78}
79
80impl<'buf> CellWriter<'buf> {
81 /// Creates a new cell writer based on an existing Vec.
82 ///
83 /// The newly created row writer will append data to the end of the vec.
84 #[inline]
85 pub fn new(buf: &'buf mut Vec<u8>) -> Self {
86 Self {
87 buf,
88 write_size: true,
89 }
90 }
91
92 // Creates a new cell writer based on an existing Vec, without writing size.
93 ///
94 /// The newly created row writer will append data to the end of the vec.
95 #[inline]
96 pub fn new_without_size(buf: &'buf mut Vec<u8>) -> Self {
97 Self {
98 buf,
99 write_size: false,
100 }
101 }
102
103 /// Sets this value to be null, consuming this object.
104 #[inline]
105 pub fn set_null(self) -> WrittenCellProof<'buf> {
106 self.buf.extend_from_slice(&(-1i32).to_be_bytes());
107 WrittenCellProof::new()
108 }
109
110 /// Sets this value to represent an unset value, consuming this object.
111 #[inline]
112 pub fn set_unset(self) -> WrittenCellProof<'buf> {
113 self.buf.extend_from_slice(&(-2i32).to_be_bytes());
114 WrittenCellProof::new()
115 }
116
117 /// Sets this value to a non-zero, non-unset value with given contents.
118 ///
119 /// Prefer this to [`into_value_builder`](CellWriter::into_value_builder)
120 /// if you have all of the contents of the value ready up front (e.g. for
121 /// fixed size types).
122 ///
123 /// Fails if the contents size overflows the maximum allowed CQL cell size
124 /// (which is i32::MAX).
125 #[inline]
126 pub fn set_value(self, contents: &[u8]) -> Result<WrittenCellProof<'buf>, CellOverflowError> {
127 let value_len: i32 = contents.len().try_into().map_err(|_| CellOverflowError)?;
128 if self.write_size {
129 self.buf.extend_from_slice(&value_len.to_be_bytes());
130 }
131 self.buf.extend_from_slice(contents);
132 Ok(WrittenCellProof::new())
133 }
134
135 /// Turns this writter into a [`CellValueBuilder`] which can be used
136 /// to gradually initialize the CQL value.
137 ///
138 /// This method should be used if you don't have all of the data
139 /// up front, e.g. when serializing compound types such as collections
140 /// or UDTs.
141 #[inline]
142 pub fn into_value_builder(self) -> CellValueBuilder<'buf> {
143 CellValueBuilder::new(self.buf, self.write_size)
144 }
145}
146
147/// Allows appending bytes to a non-null, non-unset cell.
148///
149/// This object needs to be dropped in order for the value to be correctly
150/// serialized. Failing to drop this value will result in a payload that will
151/// not be parsed by the database correctly, but otherwise should not cause
152/// data to be misinterpreted.
153pub struct CellValueBuilder<'buf> {
154 // Buffer that this value should be serialized to.
155 buf: &'buf mut Vec<u8>,
156
157 // Starting position of the value in the buffer.
158 starting_pos: usize,
159
160 // Should we write the size of the value?
161 write_size: bool,
162}
163
164impl<'buf> CellValueBuilder<'buf> {
165 #[inline]
166 fn new(buf: &'buf mut Vec<u8>, write_size: bool) -> Self {
167 // "Length" of a [bytes] frame can either be a non-negative i32,
168 // -1 (null) or -2 (not set). Push an invalid value here. It will be
169 // overwritten eventually either by set_null, set_unset or Drop.
170 // If the CellSerializer is not dropped as it should, this will trigger
171 // an error on the DB side and the serialized data
172 // won't be misinterpreted.
173 let starting_pos = buf.len();
174 if write_size {
175 buf.extend_from_slice(&(-3i32).to_be_bytes());
176 }
177 Self {
178 buf,
179 starting_pos,
180 write_size,
181 }
182 }
183
184 /// Appends raw bytes to this cell.
185 #[inline]
186 pub fn append_bytes(&mut self, bytes: &[u8]) {
187 self.buf.extend_from_slice(bytes);
188 }
189
190 /// Appends a sub-value to the end of the current contents of the cell
191 /// and returns an object that allows to fill it in.
192 #[inline]
193 pub fn make_sub_writer(&mut self) -> CellWriter<'_> {
194 CellWriter::new(self.buf)
195 }
196
197 /// Appends a sub-value to the end of the current contents of the cell
198 /// and returns an object that allows to fill it in, without writing size.
199 #[inline]
200 pub fn make_sub_writer_without_size(&mut self) -> CellWriter<'_> {
201 CellWriter::new_without_size(self.buf)
202 }
203
204 /// Finishes serializing the value.
205 ///
206 /// Fails if the constructed cell size overflows the maximum allowed
207 /// CQL cell size (which is i32::MAX).
208 #[inline]
209 pub fn finish(self) -> Result<WrittenCellProof<'buf>, CellOverflowError> {
210 if self.write_size {
211 let value_len: i32 = (self.buf.len() - self.starting_pos - 4)
212 .try_into()
213 .map_err(|_| CellOverflowError)?;
214 self.buf[self.starting_pos..self.starting_pos + 4]
215 .copy_from_slice(&value_len.to_be_bytes());
216 }
217 Ok(WrittenCellProof::new())
218 }
219}
220
221/// An object that indicates a type-level proof that something was written
222/// by a [`CellWriter`] or [`CellValueBuilder`] with lifetime parameter `'buf`.
223///
224/// This type is returned by [`set_null`](CellWriter::set_null),
225/// [`set_unset`](CellWriter::set_unset),
226/// [`set_value`](CellWriter::set_value)
227/// and also [`CellValueBuilder::finish`] - generally speaking, after
228/// the value is fully initialized and the `CellWriter` is destroyed.
229///
230/// The purpose of this type is to enforce the contract of
231/// [`SerializeValue::serialize`](super::value::SerializeValue::serialize): either
232/// the method succeeds and returns a proof that it serialized itself
233/// into the given value, or it fails and returns an error or panics.
234#[derive(Debug)]
235pub struct WrittenCellProof<'buf> {
236 /// Using *mut &'buf () is deliberate and makes WrittenCellProof invariant
237 /// on the 'buf lifetime parameter.
238 /// Ref: <https://doc.rust-lang.org/reference/subtyping.html>
239 _phantom: std::marker::PhantomData<*mut &'buf ()>,
240}
241
242impl WrittenCellProof<'_> {
243 /// A shorthand for creating the proof.
244 ///
245 /// Do not make it public! It's important that only the row writer defined
246 /// in this module is able to create a proof.
247 #[inline]
248 fn new() -> Self {
249 WrittenCellProof {
250 _phantom: std::marker::PhantomData,
251 }
252 }
253}
254
255/// There was an attempt to produce a CQL value over the maximum size limit (i32::MAX)
256#[derive(Debug, Clone, Copy, Error)]
257#[error("CQL cell overflowed the maximum allowed size of 2^31 - 1")]
258pub struct CellOverflowError;
259
260#[cfg(test)]
261mod tests {
262 use super::{CellWriter, RowWriter};
263
264 #[test]
265 fn test_cell_writer() {
266 let mut data = Vec::new();
267 let writer = CellWriter::new(&mut data);
268 let mut sub_writer = writer.into_value_builder();
269 sub_writer.make_sub_writer().set_null();
270 sub_writer
271 .make_sub_writer()
272 .set_value(&[1, 2, 3, 4])
273 .unwrap();
274 sub_writer.make_sub_writer().set_unset();
275 sub_writer.finish().unwrap();
276
277 assert_eq!(
278 data,
279 [
280 0, 0, 0, 16, // Length of inner data is 16
281 255, 255, 255, 255, // Null (encoded as -1)
282 0, 0, 0, 4, 1, 2, 3, 4, // Four byte value
283 255, 255, 255, 254, // Unset (encoded as -2)
284 ]
285 );
286 }
287
288 #[test]
289 fn test_poisoned_appender() {
290 let mut data = Vec::new();
291 let writer = CellWriter::new(&mut data);
292 let _ = writer.into_value_builder();
293
294 assert_eq!(
295 data,
296 [
297 255, 255, 255, 253, // Invalid value
298 ]
299 );
300 }
301
302 #[test]
303 fn test_row_writer() {
304 let mut data = Vec::new();
305 let mut writer = RowWriter::new(&mut data);
306 writer.make_cell_writer().set_null();
307 writer.make_cell_writer().set_value(&[1, 2, 3, 4]).unwrap();
308 writer.make_cell_writer().set_unset();
309
310 assert_eq!(
311 data,
312 [
313 255, 255, 255, 255, // Null (encoded as -1)
314 0, 0, 0, 4, 1, 2, 3, 4, // Four byte value
315 255, 255, 255, 254, // Unset (encoded as -2)
316 ]
317 )
318 }
319}