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}