use crate::frame::frame_errors::CqlRequestSerializationError;
use crate::frame::protocol_features::ProtocolFeatures;
use crate::frame::response::result::cow_bytes::CowBytes;
use bytes::Bytes;
use crate::{
frame::request::{RequestOpcode, SerializableRequest, query},
frame::types,
};
use super::{DeserializableRequest, RequestDeserializationError, query::QueryParameters};
pub use crate::frame::frame_errors::ExecuteSerializationError;
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
#[deprecated(since = "1.4.0", note = "Use ExecuteV2 instead")]
pub struct Execute<'a> {
pub id: Bytes,
pub parameters: query::QueryParameters<'a>,
}
#[expect(deprecated)]
impl SerializableRequest for Execute<'_> {
const OPCODE: RequestOpcode = RequestOpcode::Execute;
fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), CqlRequestSerializationError> {
types::write_short_bytes(&self.id[..], buf)
.map_err(ExecuteSerializationError::StatementIdSerialization)?;
self.parameters
.serialize(buf)
.map_err(ExecuteSerializationError::QueryParametersSerialization)?;
Ok(())
}
}
#[expect(deprecated)]
impl DeserializableRequest for Execute<'_> {
fn deserialize(buf: &mut &[u8]) -> Result<Self, RequestDeserializationError> {
let id = types::read_short_bytes(buf)?.to_vec().into();
let parameters = QueryParameters::deserialize(buf)?;
Ok(Self { id, parameters })
}
}
#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
pub struct ExecuteV2<'a> {
pub id: CowBytes<'a>,
pub result_metadata_id: Option<CowBytes<'a>>,
pub parameters: query::QueryParameters<'a>,
pub tablet_version_block: Option<u8>,
}
impl SerializableRequest for ExecuteV2<'_> {
const OPCODE: RequestOpcode = RequestOpcode::Execute;
fn serialize(&self, buf: &mut Vec<u8>) -> Result<(), CqlRequestSerializationError> {
types::write_short_bytes(self.id.as_ref(), buf)
.map_err(ExecuteSerializationError::StatementIdSerialization)?;
if let Some(id) = self.result_metadata_id.as_ref() {
types::write_short_bytes(id.as_ref(), buf)
.map_err(ExecuteSerializationError::ResultMetadataIdSerialization)?;
}
self.parameters
.serialize(buf)
.map_err(ExecuteSerializationError::QueryParametersSerialization)?;
if let Some(block) = self.tablet_version_block {
buf.push(block);
}
Ok(())
}
}
impl DeserializableRequest for ExecuteV2<'static> {
fn deserialize(buf: &mut &[u8]) -> Result<Self, RequestDeserializationError> {
let id = CowBytes::from(types::read_short_bytes(buf)?).into_owned();
let result_metadata_id = CowBytes::from(types::read_short_bytes(buf)?).into_owned();
let parameters = QueryParameters::deserialize(buf)?;
Ok(Self {
id,
result_metadata_id: Some(result_metadata_id),
parameters,
tablet_version_block: None,
})
}
fn deserialize_with_features(
buf: &mut &[u8],
features: &ProtocolFeatures,
) -> Result<Self, RequestDeserializationError> {
let id = CowBytes::from(types::read_short_bytes(buf)?).into_owned();
let result_metadata_id = if features.scylla_metadata_id_supported {
Some(CowBytes::from(types::read_short_bytes(buf)?).into_owned())
} else {
None
};
let parameters = QueryParameters::deserialize(buf)?;
let tablet_version_block = features
.tablets_v2_supported
.then(|| types::read_byte(buf))
.transpose()?;
Ok(Self {
id,
result_metadata_id,
parameters,
tablet_version_block,
})
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use crate::Consistency;
use crate::frame::protocol_features::ProtocolFeatures;
use crate::frame::request::{DeserializableRequest, SerializableRequest};
use crate::frame::response::result::cow_bytes::CowBytes;
use crate::serialize::row::SerializedValues;
use super::ExecuteV2;
use super::query::{PagingState, QueryParameters};
fn sample_parameters() -> QueryParameters<'static> {
QueryParameters {
consistency: Consistency::One,
serial_consistency: None,
timestamp: None,
page_size: None,
paging_state: PagingState::start(),
skip_metadata: false,
values: Cow::Owned(SerializedValues::new()),
}
}
fn v2_features() -> ProtocolFeatures {
let mut features = ProtocolFeatures::default();
features.tablets_v2_supported = true;
features
}
fn non_v2_features() -> ProtocolFeatures {
let mut features = ProtocolFeatures::default();
features.tablets_v2_supported = false;
features
}
#[test]
fn execute_v2_appends_exactly_one_tablet_block_byte() {
let with_block = ExecuteV2 {
id: CowBytes::from([1u8, 2, 3].as_slice()),
result_metadata_id: None,
parameters: sample_parameters(),
tablet_version_block: Some(0xAB),
};
let without_block = ExecuteV2 {
id: CowBytes::from([1u8, 2, 3].as_slice()),
result_metadata_id: None,
parameters: sample_parameters(),
tablet_version_block: None,
};
let mut buf_with = Vec::new();
with_block.serialize(&mut buf_with).unwrap();
let mut buf_without = Vec::new();
without_block.serialize(&mut buf_without).unwrap();
assert_eq!(buf_with.len(), buf_without.len() + 1);
assert_eq!(&buf_with[..buf_without.len()], buf_without.as_slice());
assert_eq!(*buf_with.last().unwrap(), 0xAB);
}
#[test]
fn execute_v2_tablet_block_roundtrip() {
let execute = ExecuteV2 {
id: CowBytes::from([9u8, 8, 7].as_slice()),
result_metadata_id: None,
parameters: sample_parameters(),
tablet_version_block: Some(0x5C),
};
let mut buf = Vec::new();
execute.serialize(&mut buf).unwrap();
let deserialized =
ExecuteV2::deserialize_with_features(&mut &buf[..], &v2_features()).unwrap();
assert_eq!(deserialized, execute);
}
#[test]
fn execute_v2_no_tablet_block_without_feature() {
let execute = ExecuteV2 {
id: CowBytes::from([9u8, 8, 7].as_slice()),
result_metadata_id: None,
parameters: sample_parameters(),
tablet_version_block: None,
};
let mut buf = Vec::new();
execute.serialize(&mut buf).unwrap();
let deserialized =
ExecuteV2::deserialize_with_features(&mut &buf[..], &non_v2_features()).unwrap();
assert_eq!(deserialized, execute);
assert!(deserialized.tablet_version_block.is_none());
}
}