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}
78
79impl<'buf> CellWriter<'buf> {
80    /// Creates a new cell writer based on an existing Vec.
81    ///
82    /// The newly created row writer will append data to the end of the vec.
83    #[inline]
84    pub fn new(buf: &'buf mut Vec<u8>) -> Self {
85        Self { buf }
86    }
87
88    /// Sets this value to be null, consuming this object.
89    #[inline]
90    pub fn set_null(self) -> WrittenCellProof<'buf> {
91        self.buf.extend_from_slice(&(-1i32).to_be_bytes());
92        WrittenCellProof::new()
93    }
94
95    /// Sets this value to represent an unset value, consuming this object.
96    #[inline]
97    pub fn set_unset(self) -> WrittenCellProof<'buf> {
98        self.buf.extend_from_slice(&(-2i32).to_be_bytes());
99        WrittenCellProof::new()
100    }
101
102    /// Sets this value to a non-zero, non-unset value with given contents.
103    ///
104    /// Prefer this to [`into_value_builder`](CellWriter::into_value_builder)
105    /// if you have all of the contents of the value ready up front (e.g. for
106    /// fixed size types).
107    ///
108    /// Fails if the contents size overflows the maximum allowed CQL cell size
109    /// (which is i32::MAX).
110    #[inline]
111    pub fn set_value(self, contents: &[u8]) -> Result<WrittenCellProof<'buf>, CellOverflowError> {
112        let value_len: i32 = contents.len().try_into().map_err(|_| CellOverflowError)?;
113        self.buf.extend_from_slice(&value_len.to_be_bytes());
114        self.buf.extend_from_slice(contents);
115        Ok(WrittenCellProof::new())
116    }
117
118    /// Turns this writter into a [`CellValueBuilder`] which can be used
119    /// to gradually initialize the CQL value.
120    ///
121    /// This method should be used if you don't have all of the data
122    /// up front, e.g. when serializing compound types such as collections
123    /// or UDTs.
124    #[inline]
125    pub fn into_value_builder(self) -> CellValueBuilder<'buf> {
126        CellValueBuilder::new(self.buf)
127    }
128}
129
130/// Allows appending bytes to a non-null, non-unset cell.
131///
132/// This object needs to be dropped in order for the value to be correctly
133/// serialized. Failing to drop this value will result in a payload that will
134/// not be parsed by the database correctly, but otherwise should not cause
135/// data to be misinterpreted.
136pub struct CellValueBuilder<'buf> {
137    // Buffer that this value should be serialized to.
138    buf: &'buf mut Vec<u8>,
139
140    // Starting position of the value in the buffer.
141    starting_pos: usize,
142}
143
144impl<'buf> CellValueBuilder<'buf> {
145    #[inline]
146    fn new(buf: &'buf mut Vec<u8>) -> Self {
147        // "Length" of a [bytes] frame can either be a non-negative i32,
148        // -1 (null) or -1 (not set). Push an invalid value here. It will be
149        // overwritten eventually either by set_null, set_unset or Drop.
150        // If the CellSerializer is not dropped as it should, this will trigger
151        // an error on the DB side and the serialized data
152        // won't be misinterpreted.
153        let starting_pos = buf.len();
154        buf.extend_from_slice(&(-3i32).to_be_bytes());
155        Self { buf, starting_pos }
156    }
157
158    /// Appends raw bytes to this cell.
159    #[inline]
160    pub fn append_bytes(&mut self, bytes: &[u8]) {
161        self.buf.extend_from_slice(bytes);
162    }
163
164    /// Appends a sub-value to the end of the current contents of the cell
165    /// and returns an object that allows to fill it in.
166    #[inline]
167    pub fn make_sub_writer(&mut self) -> CellWriter<'_> {
168        CellWriter::new(self.buf)
169    }
170
171    /// Finishes serializing the value.
172    ///
173    /// Fails if the constructed cell size overflows the maximum allowed
174    /// CQL cell size (which is i32::MAX).
175    #[inline]
176    pub fn finish(self) -> Result<WrittenCellProof<'buf>, CellOverflowError> {
177        let value_len: i32 = (self.buf.len() - self.starting_pos - 4)
178            .try_into()
179            .map_err(|_| CellOverflowError)?;
180        self.buf[self.starting_pos..self.starting_pos + 4]
181            .copy_from_slice(&value_len.to_be_bytes());
182        Ok(WrittenCellProof::new())
183    }
184}
185
186/// An object that indicates a type-level proof that something was written
187/// by a [`CellWriter`] or [`CellValueBuilder`] with lifetime parameter `'buf`.
188///
189/// This type is returned by [`set_null`](CellWriter::set_null),
190/// [`set_unset`](CellWriter::set_unset),
191/// [`set_value`](CellWriter::set_value)
192/// and also [`CellValueBuilder::finish`] - generally speaking, after
193/// the value is fully initialized and the `CellWriter` is destroyed.
194///
195/// The purpose of this type is to enforce the contract of
196/// [`SerializeValue::serialize`](super::value::SerializeValue::serialize): either
197/// the method succeeds and returns a proof that it serialized itself
198/// into the given value, or it fails and returns an error or panics.
199#[derive(Debug)]
200pub struct WrittenCellProof<'buf> {
201    /// Using *mut &'buf () is deliberate and makes WrittenCellProof invariant
202    /// on the 'buf lifetime parameter.
203    /// Ref: <https://doc.rust-lang.org/reference/subtyping.html>
204    _phantom: std::marker::PhantomData<*mut &'buf ()>,
205}
206
207impl WrittenCellProof<'_> {
208    /// A shorthand for creating the proof.
209    ///
210    /// Do not make it public! It's important that only the row writer defined
211    /// in this module is able to create a proof.
212    #[inline]
213    fn new() -> Self {
214        WrittenCellProof {
215            _phantom: std::marker::PhantomData,
216        }
217    }
218}
219
220/// There was an attempt to produce a CQL value over the maximum size limit (i32::MAX)
221#[derive(Debug, Clone, Copy, Error)]
222#[error("CQL cell overflowed the maximum allowed size of 2^31 - 1")]
223pub struct CellOverflowError;
224
225#[cfg(test)]
226mod tests {
227    use super::{CellWriter, RowWriter};
228
229    #[test]
230    fn test_cell_writer() {
231        let mut data = Vec::new();
232        let writer = CellWriter::new(&mut data);
233        let mut sub_writer = writer.into_value_builder();
234        sub_writer.make_sub_writer().set_null();
235        sub_writer
236            .make_sub_writer()
237            .set_value(&[1, 2, 3, 4])
238            .unwrap();
239        sub_writer.make_sub_writer().set_unset();
240        sub_writer.finish().unwrap();
241
242        assert_eq!(
243            data,
244            [
245                0, 0, 0, 16, // Length of inner data is 16
246                255, 255, 255, 255, // Null (encoded as -1)
247                0, 0, 0, 4, 1, 2, 3, 4, // Four byte value
248                255, 255, 255, 254, // Unset (encoded as -2)
249            ]
250        );
251    }
252
253    #[test]
254    fn test_poisoned_appender() {
255        let mut data = Vec::new();
256        let writer = CellWriter::new(&mut data);
257        let _ = writer.into_value_builder();
258
259        assert_eq!(
260            data,
261            [
262                255, 255, 255, 253, // Invalid value
263            ]
264        );
265    }
266
267    #[test]
268    fn test_row_writer() {
269        let mut data = Vec::new();
270        let mut writer = RowWriter::new(&mut data);
271        writer.make_cell_writer().set_null();
272        writer.make_cell_writer().set_value(&[1, 2, 3, 4]).unwrap();
273        writer.make_cell_writer().set_unset();
274
275        assert_eq!(
276            data,
277            [
278                255, 255, 255, 255, // Null (encoded as -1)
279                0, 0, 0, 4, 1, 2, 3, 4, // Four byte value
280                255, 255, 255, 254, // Unset (encoded as -2)
281            ]
282        )
283    }
284}