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