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}