pub mod auth_response;
pub mod batch;
pub mod execute;
pub mod options;
pub mod prepare;
pub mod query;
pub mod register;
pub mod startup;
use batch::BatchTypeParseError;
use thiserror::Error;
use crate::Consistency;
use crate::frame::protocol_features::ProtocolFeatures;
use crate::frame::request::execute::ExecuteV2;
use crate::serialize::row::SerializedValues;
use bytes::Bytes;
pub use auth_response::AuthResponse;
pub use batch::Batch;
#[expect(deprecated)]
pub use execute::Execute;
pub use options::Options;
pub use prepare::Prepare;
pub use query::Query;
pub use startup::Startup;
use self::batch::BatchStatement;
use super::TryFromPrimitiveError;
use super::frame_errors::{CqlRequestSerializationError, LowLevelDeserializationError};
use super::types::SerialConsistency;
pub use scylla_cql_core::frame::request::CqlRequestKind;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum RequestOpcode {
Startup = 0x01,
Options = 0x05,
Query = 0x07,
Prepare = 0x09,
Execute = 0x0A,
Register = 0x0B,
Batch = 0x0D,
AuthResponse = 0x0F,
}
impl TryFrom<u8> for RequestOpcode {
type Error = TryFromPrimitiveError<u8>;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0x01 => Ok(Self::Startup),
0x05 => Ok(Self::Options),
0x07 => Ok(Self::Query),
0x09 => Ok(Self::Prepare),
0x0A => Ok(Self::Execute),
0x0B => Ok(Self::Register),
0x0D => Ok(Self::Batch),
0x0F => Ok(Self::AuthResponse),
_ => Err(TryFromPrimitiveError::new("RequestOpcode", value)),
}
}
}
pub trait SerializableRequest {
const OPCODE: RequestOpcode;
fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), CqlRequestSerializationError>;
fn to_bytes(&self) -> Result<Bytes, CqlRequestSerializationError> {
let mut v = Vec::new();
self.serialize(&mut v)?;
Ok(v.into())
}
}
pub trait DeserializableRequest: SerializableRequest + Sized {
#[deprecated(since = "1.4.0", note = "Use deserialize_with_features instead")]
fn deserialize(buf: &mut &[u8]) -> Result<Self, RequestDeserializationError>;
fn deserialize_with_features(
buf: &mut &[u8],
#[allow(unused_variables)] features: &ProtocolFeatures,
) -> Result<Self, RequestDeserializationError> {
#[expect(deprecated)]
Self::deserialize(buf)
}
}
#[doc(hidden)]
#[derive(Debug, Error)]
pub enum RequestDeserializationError {
#[error("Low level deser error: {0}")]
LowLevelDeserialization(#[from] LowLevelDeserializationError),
#[error("Io error: {0}")]
IoError(#[from] std::io::Error),
#[error("Specified flags are not recognised: {:02x}", flags)]
UnknownFlags { flags: u8 },
#[error("Named values in frame are currently unsupported")]
NamedValuesUnsupported,
#[error("Expected SerialConsistency, got regular Consistency: {0}")]
ExpectedSerialConsistency(Consistency),
#[error(transparent)]
BatchTypeParse(#[from] BatchTypeParseError),
#[error("Unexpected batch statement kind: {0}")]
UnexpectedBatchStatementKind(u8),
}
#[non_exhaustive] #[deprecated(
since = "1.4.0",
note = "Does not support Scylla metadata id extension. Use RequestV2 instead."
)]
pub enum Request<'r> {
Query(Query<'r>),
#[expect(deprecated)]
Execute(Execute<'r>),
Batch(Batch<'r, BatchStatement<'r>, Vec<SerializedValues>>),
}
#[expect(deprecated)]
impl Request<'_> {
pub fn deserialize(
buf: &mut &[u8],
opcode: RequestOpcode,
) -> Result<Self, RequestDeserializationError> {
match opcode {
RequestOpcode::Query => Query::deserialize(buf).map(Self::Query),
RequestOpcode::Execute => Execute::deserialize(buf).map(Self::Execute),
RequestOpcode::Batch => Batch::deserialize(buf).map(Self::Batch),
_ => unimplemented!(
"Deserialization of opcode {:?} is not yet supported",
opcode
),
}
}
pub fn get_consistency(&self) -> Option<Consistency> {
match self {
Request::Query(q) => Some(q.parameters.consistency),
Request::Execute(e) => Some(e.parameters.consistency),
Request::Batch(b) => Some(b.consistency),
#[expect(unreachable_patterns)] _ => None,
}
}
pub fn get_serial_consistency(&self) -> Option<Option<SerialConsistency>> {
match self {
Request::Query(q) => Some(q.parameters.serial_consistency),
Request::Execute(e) => Some(e.parameters.serial_consistency),
Request::Batch(b) => Some(b.serial_consistency),
#[expect(unreachable_patterns)] _ => None,
}
}
}
#[non_exhaustive] pub enum RequestV2<'r> {
Query(Query<'r>),
Execute(ExecuteV2<'r>),
Batch(Batch<'r, BatchStatement<'r>, Vec<SerializedValues>>),
}
impl RequestV2<'_> {
pub fn deserialize(
buf: &mut &[u8],
opcode: RequestOpcode,
features: &ProtocolFeatures,
) -> Result<Self, RequestDeserializationError> {
match opcode {
RequestOpcode::Query => {
Query::deserialize_with_features(buf, features).map(Self::Query)
}
RequestOpcode::Execute => {
ExecuteV2::deserialize_with_features(buf, features).map(Self::Execute)
}
RequestOpcode::Batch => {
Batch::deserialize_with_features(buf, features).map(Self::Batch)
}
_ => unimplemented!(
"Deserialization of opcode {:?} is not yet supported",
opcode
),
}
}
pub fn get_consistency(&self) -> Option<Consistency> {
match self {
Self::Query(q) => Some(q.parameters.consistency),
Self::Execute(e) => Some(e.parameters.consistency),
Self::Batch(b) => Some(b.consistency),
#[expect(unreachable_patterns)] _ => None,
}
}
pub fn get_serial_consistency(&self) -> Option<Option<SerialConsistency>> {
match self {
Self::Query(q) => Some(q.parameters.serial_consistency),
Self::Execute(e) => Some(e.parameters.serial_consistency),
Self::Batch(b) => Some(b.serial_consistency),
#[expect(unreachable_patterns)] _ => None,
}
}
}
#[cfg(test)]
mod tests {
use std::{borrow::Cow, ops::Deref};
use bytes::Bytes;
use super::query::PagingState;
use crate::Consistency;
use crate::frame::protocol_features::ProtocolFeatures;
use crate::frame::request::batch::{Batch, BatchStatement, BatchType};
#[expect(deprecated)]
use crate::frame::request::execute::Execute;
use crate::frame::request::execute::ExecuteV2;
use crate::frame::request::query::{Query, QueryParameters};
use crate::frame::request::{DeserializableRequest, SerializableRequest};
use crate::frame::response::result::{ColumnType, NativeType};
use crate::frame::types::{self, SerialConsistency};
use crate::serialize::row::SerializedValues;
#[test]
fn request_ser_de_identity() {
let contents = Cow::Borrowed("SELECT host_id from system.peers");
let parameters = QueryParameters {
consistency: Consistency::All,
serial_consistency: Some(SerialConsistency::Serial),
timestamp: None,
page_size: Some(323),
paging_state: PagingState::new_from_raw_bytes(&[2_u8, 1, 3, 7] as &[u8]),
skip_metadata: false,
values: {
let mut vals = SerializedValues::new();
vals.add_value(&2137, &ColumnType::Native(NativeType::Int))
.unwrap();
Cow::Owned(vals)
},
};
let query = Query {
contents,
parameters,
};
{
let mut buf = Vec::new();
query.serialize(&mut buf).unwrap();
let query_deserialized =
Query::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap();
assert_eq!(&query_deserialized, &query);
}
let id: Bytes = vec![2, 4, 5, 2, 6, 7, 3, 1].into();
let parameters = QueryParameters {
consistency: Consistency::Any,
serial_consistency: None,
timestamp: Some(3423434),
page_size: None,
paging_state: PagingState::start(),
skip_metadata: false,
values: {
let mut vals = SerializedValues::new();
vals.add_value(&42, &ColumnType::Native(NativeType::Int))
.unwrap();
vals.add_value(&2137, &ColumnType::Native(NativeType::Int))
.unwrap();
Cow::Owned(vals)
},
};
#[expect(deprecated)]
let execute = Execute {
id,
parameters: parameters.clone(),
};
{
let mut buf = Vec::new();
execute.serialize(&mut buf).unwrap();
#[expect(deprecated)]
let execute_deserialized =
Execute::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap();
assert_eq!(&execute_deserialized, &execute);
}
let id = [2, 4, 5, 2, 6, 7, 3, 1].as_slice().into();
let result_metadata_id = Some([2, 4, 5, 2, 6, 7, 3, 1].as_slice().into());
let execute_with_id = ExecuteV2 {
id,
result_metadata_id,
parameters,
tablet_version_block: None,
};
{
let mut buf = Vec::new();
execute_with_id.serialize(&mut buf).unwrap();
let features = {
let mut default = ProtocolFeatures::default();
default.scylla_metadata_id_supported = true;
default
};
let execute_deserialized =
ExecuteV2::deserialize_with_features(&mut &buf[..], &features).unwrap();
assert_eq!(&execute_deserialized, &execute_with_id);
}
let statements = vec![
BatchStatement::Query {
text: query.contents,
},
BatchStatement::Prepared {
id: Cow::Borrowed(execute_with_id.id.as_ref()),
},
];
let batch = Batch {
statements: Cow::Owned(statements),
batch_type: BatchType::Logged,
consistency: Consistency::EachQuorum,
serial_consistency: Some(SerialConsistency::LocalSerial),
timestamp: Some(32432),
values: vec![
query.parameters.values.deref().clone(),
query.parameters.values.deref().clone(),
],
};
{
let mut buf = Vec::new();
batch.serialize(&mut buf).unwrap();
let batch_deserialized =
Batch::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap();
assert_eq!(&batch_deserialized, &batch);
}
}
#[test]
fn deser_rejects_unknown_flags() {
let contents = Cow::Borrowed("SELECT host_id from system.peers");
let parameters = QueryParameters {
consistency: Default::default(),
serial_consistency: Some(SerialConsistency::LocalSerial),
timestamp: None,
page_size: None,
paging_state: PagingState::start(),
skip_metadata: false,
values: Cow::Borrowed(SerializedValues::EMPTY),
};
let query = Query {
contents: contents.clone(),
parameters,
};
{
let mut buf = Vec::new();
query.serialize(&mut buf).unwrap();
let query_deserialized =
Query::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap();
assert_eq!(&query_deserialized.contents, &query.contents);
assert_eq!(&query_deserialized.parameters, &query.parameters);
let mut buf_ptr = buf.as_slice();
let serialised_contents = types::read_long_string(&mut buf_ptr).unwrap();
assert_eq!(serialised_contents, contents);
let consistency = types::read_consistency(&mut buf_ptr).unwrap();
assert_eq!(consistency, Consistency::default());
let flags_idx = buf.len() - buf_ptr.len();
let flags_mut = &mut buf[flags_idx];
*flags_mut |= 0x80;
let _parse_error =
Query::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap_err();
}
let statements = vec![BatchStatement::Query {
text: query.contents,
}];
let batch = Batch {
statements: Cow::Owned(statements),
batch_type: BatchType::Logged,
consistency: Consistency::EachQuorum,
serial_consistency: None,
timestamp: None,
values: vec![query.parameters.values.deref().clone()],
};
{
let mut buf = Vec::new();
batch.serialize(&mut buf).unwrap();
let batch_deserialized =
Batch::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap();
assert_eq!(batch, batch_deserialized);
let buf_len = buf.len();
let flags_mut = &mut buf[buf_len - 1];
*flags_mut |= 0x80;
let _parse_error =
Batch::deserialize_with_features(&mut &buf[..], &Default::default()).unwrap_err();
}
}
}