Repository: scylladb/scylla-rust-driver
Pull request #569 (2022-10-04 15:41:31)

Revert "Pick connections based on batch first statement's shard"
The new code, and in particualr the fancy GAT workaroound, cause problems when trying to pass batch values by reference, as described in #568.

Fixes: #568
Unfixes: #448

This reverts commit 24ee9545774291607f591e2a4ad2ba1bbe7ab96e.

## Pre-review checklist

<!--
    Make sure you took care of the issues on the list.
    Put 'x' into those boxes which apply.
    You can also create the PR now and click on all relevant checkboxes.
    See CONTRIBUTING.md for more details.
-->

- [x] I have split my patch into logically separate commits.
- [x] All commit messages clearly explain what they change and why.
- [x] I added relevant tests for new features and bug fixes.
- [x] All commits compile, pass static checks and pass test.
- [x] PR description sums up the changes and reasons why they should be introduced.
- [x] I added appropriate `Fixes:` annotations to PR description.



Patch:
diff --git a/scylla-cql/src/frame/request/batch.rs b/scylla-cql/src/frame/request/batch.rs
index ac408852..cee7c66e 100644
--- a/scylla-cql/src/frame/request/batch.rs
+++ b/scylla-cql/src/frame/request/batch.rs
@@ -1,4 +1,4 @@
-use crate::frame::{frame_errors::ParseError, value::BatchValuesIterator};
+use crate::frame::frame_errors::ParseError;
 use bytes::{BufMut, Bytes};
 use std::convert::TryInto;
 
@@ -54,21 +54,9 @@ where
         // Serializing queries
         types::write_short(self.statements_count.try_into()?, buf);
 
-        let mut n_serialized_statements = 0usize;
-        let mut value_lists = self.values.batch_values_iter();
-        for statement in self.statements.clone() {
+        for (statement_num, statement) in self.statements.clone().enumerate() {
             statement.serialize(buf)?;
-            value_lists.write_next_to_request(buf).ok_or_else(|| {
-                ParseError::BadDataToSerialize(
-                    "Mismatch between statement counts for batch query".to_owned(),
-                )
-            })??;
-            n_serialized_statements += 1;
-        }
-        if n_serialized_statements != self.statements_count {
-            return Err(ParseError::BadDataToSerialize(
-                "Mismatch between statement counts for batch query".to_owned(),
-            ));
+            self.values.write_nth_to_request(statement_num, buf)?;
         }
 
         // Serializing consistency
diff --git a/scylla-cql/src/frame/value.rs b/scylla-cql/src/frame/value.rs
index 7c59713f..2a85c450 100644
--- a/scylla-cql/src/frame/value.rs
+++ b/scylla-cql/src/frame/value.rs
@@ -99,6 +99,21 @@ pub trait ValueList {
     }
 }
 
+/// Represents List of ValueList for Batch statement
+pub trait BatchValues {
+    fn len(&self) -> usize;
+
+    fn write_nth_to_request(
+        &self,
+        n: usize,
+        buf: &mut impl BufMut,
+    ) -> Result<(), SerializeValuesError>;
+
+    fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+}
+
 impl SerializedValues {
     /// Creates empty value list
     pub const fn new() -> Self {
@@ -215,85 +230,6 @@ impl<'a> Iterator for SerializedValuesIterator<'a> {
     }
 }
 
-/// Represents List of ValueList for Batch statement
-///
-/// This trait is not implemented directly, but rather implemented through `BatchValuesGatWorkaround`
-/// (until GATs are made available in Rust)
-pub trait BatchValues: for<'r> BatchValuesGatWorkaround<'r> {}
-impl<T: for<'r> BatchValuesGatWorkaround<'r> + ?Sized> BatchValues for T {}
-
-pub trait BatchValuesGatWorkaround<'r> {
-    type BatchValuesIter: BatchValuesIterator;
-    fn batch_values_iter(&'r self) -> Self::BatchValuesIter;
-    fn len(&'r self) -> usize;
-    fn is_empty(&'r self) -> bool {
-        self.len() == 0
-    }
-}
-
-/// An iterator-like for `ValueList`
-///
-/// An instance of this can be easily obtained from `IT: Iterator<Item: ValueList>`: that would be
-/// `BatchValuesIteratorFromIterator<IT>`
-///
-/// It's just essentially making methods from `ValueList` accessible instead of being an actual iterator because of
-/// several compiler limitations that would otherwise be very complex to overcome.\
-/// (specifically, types being different would require yielding enums for tuple impls, and the trait
-/// bound of `for<'r> <BatchValuesGatWorkaround<'r>::BatchValuesIter as Iterator>::Item: ValueList` is very
-/// hard to express considering several compiler limitations)
-pub trait BatchValuesIterator {
-    fn next_serialized(&mut self) -> Option<SerializedResult<'_>>;
-    fn write_next_to_request(
-        &mut self,
-        buf: &mut impl BufMut,
-    ) -> Option<Result<(), SerializeValuesError>>;
-    fn skip_next(&mut self) -> Option<()>;
-}
-
-/// Implements `BatchValuesIterator` from an `Iterator` over things that implement `ValueList`
-///
-/// Essentially used internally by this lib to provide implementors of `BatchValuesIterator` for cases
-/// that always serialize the same concrete `ValueList` type
-pub struct BatchValuesIteratorFromIterator<IT: Iterator> {
-    it: IT,
-    item_container_for_serialized: Option<IT::Item>,
-}
-
-impl<IT> BatchValuesIterator for BatchValuesIteratorFromIterator<IT>
-where
-    IT: Iterator,
-    IT::Item: ValueList,
-{
-    fn next_serialized(&mut self) -> Option<SerializedResult<'_>> {
-        self.item_container_for_serialized = self.it.next();
-        self.item_container_for_serialized
-            .as_ref()
-            .map(|vl| vl.serialized())
-    }
-    fn write_next_to_request(
-        &mut self,
-        buf: &mut impl BufMut,
-    ) -> Option<Result<(), SerializeValuesError>> {
-        self.it.next().map(|vl| vl.write_to_request(buf))
-    }
-    fn skip_next(&mut self) -> Option<()> {
-        self.it.next().map(|_| ())
-    }
-}
-
-impl<IT> From<IT> for BatchValuesIteratorFromIterator<IT>
-where
-    IT: Iterator,
-    IT::Item: ValueList,
-{
-    fn from(it: IT) -> Self {
-        BatchValuesIteratorFromIterator {
-            it,
-            item_container_for_serialized: None,
-        }
-    }
-}
-
 //
 //  Value impls
 //
@@ -869,149 +805,78 @@ impl<'b> ValueList for Cow<'b, SerializedValues> {
 // BatchValues impls
 //
 
-/// Implements `BatchValues` from an `Iterator` over things that implement `ValueList`
-///
-/// This is to avoid requiring allocating a new `Vec` containing all the `ValueList`s directly:
-/// with this, one can write:
-/// `session.batch(&batch, BatchValuesFromIterator::from(lines_to_insert.iter().map(|l| &l.value_list)))`
-/// where `lines_to_insert` may also contain e.g. data to pick the statement...
-///
-/// The underlying iterator will always be cloned at least once, once to compute the length if it can't be known
-/// in advance, and be re-cloned at every retry.
-/// It is consequently expected that the provided iterator is cheap to clone (e.g. `slice.iter().map(...)`).
-pub struct BatchValuesFromIter<IT> {
-    it: IT,
-}
-
-impl<IT> BatchValuesFromIter<IT>
-where
-    IT: Iterator + Clone,
-    IT::Item: ValueList,
-{
-    pub fn new(into_iter: impl IntoIterator<IntoIter = IT>) -> Self {
-        Self {
-            it: into_iter.into_iter(),
-        }
-    }
-}
-
-impl<IT> From<IT> for BatchValuesFromIter<IT>
-where
-    IT: Iterator + Clone,
-    IT::Item: ValueList,
-{
-    fn from(it: IT) -> Self {
-        Self { it }
-    }
-}
-
-impl<'r, IT> BatchValuesGatWorkaround<'r> for BatchValuesFromIter<IT>
-where
-    IT: Iterator + Clone,
-    IT::Item: ValueList,
-{
-    type BatchValuesIter = BatchValuesIteratorFromIterator<IT>;
-    fn batch_values_iter(&'r self) -> <Self as BatchValuesGatWorkaround>::BatchValuesIter {
-        self.it.clone().into()
-    }
-    fn len(&'r self) -> usize {
-        match self.it.size_hint() {
-            (l, Some(h)) if l == h => l,
-            _ => self.it.clone().count(),
-        }
-    }
-}
-
 // Implement BatchValues for slices of ValueList types
-impl<'r, T: ValueList + 'r> BatchValuesGatWorkaround<'r> for [T] {
-    type BatchValuesIter = BatchValuesIteratorFromIterator<std::slice::Iter<'r, T>>;
-    fn batch_values_iter(&'r self) -> Self::BatchValuesIter {
-        self.iter().into()
+impl<T: ValueList> BatchValues for &[T] {
+    fn len(&self) -> usize {
+        <[T]>::len(*self)
     }
-    fn len(&'r self) -> usize {
-        <[_]>::len(self)
+
+    fn write_nth_to_request(
+        &self,
+        n: usize,
+        buf: &mut impl BufMut,
+    ) -> Result<(), SerializeValuesError> {
+        self[n].write_to_request(buf)?;
+        Ok(())
     }
 }
 
 // Implement BatchValues for Vec<ValueList>
-impl<'r, T: ValueList + 'r> BatchValuesGatWorkaround<'r> for Vec<T> {
-    type BatchValuesIter = BatchValuesIteratorFromIterator<std::slice::Iter<'r, T>>;
-    fn batch_values_iter(&'r self) -> Self::BatchValuesIter {
-        BatchValuesGatWorkaround::<'r>::batch_values_iter(self.as_slice())
+impl<T: ValueList> BatchValues for Vec<T> {
+    fn len(&self) -> usize {
+        Vec::<T>::len(self)
     }
-    fn len(&'r self) -> usize {
-        Vec::len(self)
+
+    fn write_nth_to_request(
+        &self,
+        n: usize,
+        buf: &mut impl BufMut,
+    ) -> Result<(), SerializeValuesError> {
+        self[n].write_to_request(buf)?;
+        Ok(())
     }
 }
 
 // Here is an example implementation for (T0, )
 // Further variants are done using a macro
-impl<'r, T0: ValueList + 'r> BatchValuesGatWorkaround<'r> for (T0,) {
-    type BatchValuesIter = BatchValuesIteratorFromIterator<std::iter::Once<&'r T0>>;
-    fn batch_values_iter(&'r self) -> Self::BatchValuesIter {
-        std::iter::once(&self.0).into()
-    }
-    fn len(&'r self) -> usize {
+impl<T0: ValueList> BatchValues for (T0,) {
+    fn len(&self) -> usize {
         1
     }
-}
 
-pub struct TupleValuesIter<'a, T> {
-    tuple: &'a T,
-    idx: usize,
+    fn write_nth_to_request(
+        &self,
+        n: usize,
+        buf: &mut impl BufMut,
+    ) -> Result<(), SerializeValuesError> {
+        match n {
+            0 => self.0.write_to_request(buf)?,
+            _ => panic!("Tried to serialize ValueList with an out of range index! index: {}, ValueList len: {}", n, 1),
+        };
+
+        Ok(())
+    }
 }
 
 macro_rules! impl_batch_values_for_tuple {
-    ( $($Ti:ident),* ; $($FieldI:tt),* ; $TupleSize:tt) => {
-        impl<'r, $($Ti),+> BatchValuesGatWorkaround<'r> for ($($Ti,)+)
+    ( $($Ti:ident),* ; $($FieldI:tt),* ; $TupleSize:tt ) => {
+        impl<$($Ti),+> BatchValues for ($($Ti,)+)
         where
-            $($Ti: ValueList + 'r),+
+        $($Ti: ValueList),+
         {
-            type BatchValuesIter = TupleValuesIter<'r, ($($Ti,)+)>;
-            fn batch_values_iter(&'r self) -> Self::BatchValuesIter {
-                TupleValuesIter {
-                    tuple: self,
-                    idx: 0,
-                }
-            }
             fn len(&self) -> usize{
                 $TupleSize
             }
-        }
-        impl<'r, $($Ti),+> BatchValuesIterator for TupleValuesIter<'r, ($($Ti,)+)>
-        where
-            $($Ti: ValueList + 'r),+
-        {
-            fn next_serialized(&mut self) -> Option<SerializedResult<'_>> {
-                let serialized_value_res = match self.idx {
-                    $(
-                        $FieldI => self.tuple.$FieldI.serialized(),
-                    )*
-                    _ => return None,
-                };
-                self.idx += 1;
-                Some(serialized_value_res)
-            }
-            fn write_next_to_request(
-                &mut self,
-                buf: &mut impl BufMut,
-            ) -> Option<Result<(), SerializeValuesError>> {
-                let ret = match self.idx {
+
+            fn write_nth_to_request(&self, n: usize, buf: &mut impl BufMut) -> Result<(), SerializeValuesError> {
+                match n {
                     $(
-                        $FieldI => self.tuple.$FieldI.write_to_request(buf),
+                        $FieldI => self.$FieldI.write_to_request(buf) ?,
                     )*
-                    _ => return None,
-                };
-                self.idx += 1;
-                Some(ret)
-            }
-            fn skip_next(&mut self) -> Option<()> {
-                if self.idx < $TupleSize {
-                    self.idx += 1;
-                    Some(())
-                } else {
-                    None
+                    _ => panic!("Tried to serialize ValueList with an out of range index! index: {}, ValueList len: {}", n, $TupleSize),
                 }
+
+                Ok(())
             }
         }
     }
@@ -1041,75 +906,17 @@ impl_batch_values_for_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T
                              0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15; 16);
 
 // Every &impl BatchValues should also implement BatchValues
-impl<'a, 'r, T: BatchValues + ?Sized> BatchValuesGatWorkaround<'r> for &'a T {
-    type BatchValuesIter = <T as BatchValuesGatWorkaround<'a>>::BatchValuesIter;
-    fn batch_values_iter(&'r self) -> Self::BatchValuesIter {
-        <T as BatchValuesGatWorkaround<'a>>::batch_values_iter(*self)
+impl<T: BatchValues> BatchValues for &T {
+    fn len(&self) -> usize {
+        <T as BatchValues>::len(*self)
     }
-    fn len(&'r self) -> usize {
-        <T as BatchValuesGatWorkaround<'a>>::len(*self)
-    }
-}
-
-/// Allows reusing already-serialized first value
-///
-/// We'll need to build a `SerializedValues` for the first ~`ValueList` of a batch to figure out the shard (#448).
-/// Once that is done, we can use that instead of re-serializing.
-///
-/// This struct implements both `BatchValues` and `BatchValuesIterator` for that purpose
-pub struct BatchValuesFirstSerialized<'f, T> {
-    first: Option<&'f SerializedValues>,
-    rest: T,
-}
-
-impl<'f, T: BatchValues> BatchValuesFirstSerialized<'f, T> {
-    pub fn new(batch_values: T, already_serialized_first: Option<&'f SerializedValues>) -> Self {
-        Self {
-            first: already_serialized_first,
-            rest: batch_values,
-        }
-    }
-}
-
-impl<'r, 'f, BV: BatchValues> BatchValuesGatWorkaround<'r> for BatchValuesFirstSerialized<'f, BV> {
-    type BatchValuesIter =
-        BatchValuesFirstSerialized<'f, <BV as BatchValuesGatWorkaround<'r>>::BatchValuesIter>;
-    fn batch_values_iter(&'r self) -> Self::BatchValuesIter {
-        BatchValuesFirstSerialized {
-            first: self.first,
-            rest: self.rest.batch_values_iter(),
-        }
-    }
-    fn len(&'r self) -> usize {
-        self.rest.len()
-    }
-}
 
-impl<'f, IT: BatchValuesIterator> BatchValuesIterator for BatchValuesFirstSerialized<'f, IT> {
-    fn next_serialized(&mut self) -> Option<SerializedResult<'_>> {
-        match self.first.take() {
-            Some(first) => {
-                self.rest.skip_next();
-                Some(Ok(Cow::Borrowed(first)))
-            }
-            None => self.rest.next_serialized(),
-        }
-    }
-    fn write_next_to_request(
-        &mut self,
+    fn write_nth_to_request(
+        &self,
+        n: usize,
         buf: &mut impl BufMut,
-    ) -> Option<Result<(), SerializeValuesError>> {
-        match self.first.take() {
-            Some(first) => {
-                self.rest.skip_next();
-                first.write_to_request(buf);
-                Some(Ok(()))
-            }
-            None => self.rest.write_next_to_request(buf),
-        }
-    }
-    fn skip_next(&mut self) -> Option<()> {
-        self.rest.skip_next();
-        self.first.take().map(|_| ())
+    ) -> Result<(), SerializeValuesError> {
+        <T as BatchValues>::write_nth_to_request(*self, n, buf)?;
+        Ok(())
     }
 }
diff --git a/scylla/src/transport/session.rs b/scylla/src/transport/session.rs
index 002e4a7e..26a2e8db 100644
--- a/scylla/src/transport/session.rs
+++ b/scylla/src/transport/session.rs
@@ -27,9 +27,7 @@ use super::topology::UntranslatedPeer;
 use crate::cql_to_rust::FromRow;
 use crate::frame::response::cql_to_rust::FromRowError;
 use crate::frame::response::result;
-use crate::frame::value::{
-    BatchValues, BatchValuesFirstSerialized, BatchValuesIterator, SerializedValues, ValueList,
-};
+use crate::frame::value::{BatchValues, SerializedValues, ValueList};
 use crate::prepared_statement::{PartitionKeyError, PreparedStatement};
 use crate::query::Query;
 use crate::routing::Token;
@@ -1008,43 +1006,13 @@ impl Session {
         batch: &Batch,
         values: impl BatchValues,
     ) -> Result<QueryResult, QueryError> {
-        // Shard-awareness behavior for batch will be to pick shard based on first batch statement's shard
-        // If users batch statements by shard, they will be rewarded with full shard awareness
-
-        // Extract first serialized_value
-        let mut batch_values_iter_for_first_serialized_value = values.batch_values_iter();
-        let first_serialized_value = batch_values_iter_for_first_serialized_value
-            .next_serialized()
-            .transpose()?;
-        let first_serialized_value = first_serialized_value.as_deref();
-        let statement_info = match (first_serialized_value, batch.statements.first()) {
-            (Some(first_serialized_value), Some(BatchStatement::PreparedStatement(ps))) => {
-                Statement {
-                    token: self.calculate_token(ps, first_serialized_value)?,
-                    keyspace: ps.get_keyspace_name(),
-                }
-            }
-            _ => Statement::default(),
-        };
-        let first_value_token = statement_info.token;
-
-        // Reuse first serialized value when serializing query, and delegate to `BatchValues::write_next_to_request`
-        // directly for others (if they weren't already serialized, possibly don't even allocate the `SerializedValues`)
-        let values = BatchValuesFirstSerialized::new(&values, first_serialized_value);
         let values_ref = &values;
 
         let run_query_result = self
             .run_query(
-                statement_info,
+                Statement::default(),
                 &batch.config,
-                |node: Arc<Node>| async move {
-                    match first_value_token {
-                        Some(first_value_token) => {
-                            node.connection_for_token(first_value_token).await
-                        }
-                        None => node.random_connection().await,
-                    }
-                },
+                |node: Arc<Node>| async move { node.random_connection().await },
                 |connection: Arc<Connection>, consistency: Consistency| async move {
                     connection
                         .batch_with_consistency(batch, values_ref, consistency)


Test patch:
diff --git a/scylla-cql/src/frame/value_tests.rs b/scylla-cql/src/frame/value_tests.rs
index fbbf4f87..7d5a8deb 100644
--- a/scylla-cql/src/frame/value_tests.rs
+++ b/scylla-cql/src/frame/value_tests.rs
@@ -1,5 +1,3 @@
-use crate::frame::value::{BatchValuesGatWorkaround, BatchValuesIterator};
-
 use super::value::{
     BatchValues, Date, MaybeUnset, SerializeValuesError, SerializedValues, Time, Timestamp, Unset,
     Value, ValueList, ValueTooBig,
@@ -393,21 +391,17 @@ fn cow_serialized_values_value_list() {
 fn slice_batch_values() {
     let batch_values: &[&[i8]] = &[&[1, 2], &[2, 3, 4, 5], &[6]];
 
-    assert_eq!(
-        <&[&[i8]] as BatchValuesGatWorkaround<'_>>::len(&batch_values),
-        3
-    );
+    assert_eq!(<&[&[i8]] as BatchValues>::len(&batch_values), 3);
 
-    let mut it = batch_values.batch_values_iter();
     {
         let mut request: Vec<u8> = Vec::new();
-        it.write_next_to_request(&mut request).unwrap().unwrap();
+        batch_values.write_nth_to_request(0, &mut request).unwrap();
         assert_eq!(request, vec![0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2]);
     }
 
     {
         let mut request: Vec<u8> = Vec::new();
-        it.write_next_to_request(&mut request).unwrap().unwrap();
+        batch_values.write_nth_to_request(1, &mut request).unwrap();
         assert_eq!(
             request,
             vec![0, 4, 0, 0, 0, 1, 2, 0, 0, 0, 1, 3, 0, 0, 0, 1, 4, 0, 0, 0, 1, 5]
@@ -416,32 +410,26 @@ fn slice_batch_values() {
 
     {
         let mut request: Vec<u8> = Vec::new();
-        it.write_next_to_request(&mut request).unwrap().unwrap();
+        batch_values.write_nth_to_request(2, &mut request).unwrap();
         assert_eq!(request, vec![0, 1, 0, 0, 0, 1, 6]);
     }
-
-    assert_eq!(it.write_next_to_request(&mut Vec::new()), None);
 }
 
 #[test]
 fn vec_batch_values() {
     let batch_values: Vec<Vec<i8>> = vec![vec![1, 2], vec![2, 3, 4, 5], vec![6]];
 
-    assert_eq!(
-        <Vec<Vec<i8>> as BatchValuesGatWorkaround<'_>>::len(&batch_values),
-        3
-    );
+    assert_eq!(<Vec<Vec<i8>> as BatchValues>::len(&batch_values), 3);
 
-    let mut it = batch_values.batch_values_iter();
     {
         let mut request: Vec<u8> = Vec::new();
-        it.write_next_to_request(&mut request).unwrap().unwrap();
+        batch_values.write_nth_to_request(0, &mut request).unwrap();
         assert_eq!(request, vec![0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2]);
     }
 
     {
         let mut request: Vec<u8> = Vec::new();
-        it.write_next_to_request(&mut request).unwrap().unwrap();
+        batch_values.write_nth_to_request(1, &mut request).unwrap();
         assert_eq!(
             request,
             vec![0, 4, 0, 0, 0, 1, 2, 0, 0, 0, 1, 3, 0, 0, 0, 1, 4, 0, 0, 0, 1, 5]
@@ -450,7 +438,7 @@ fn vec_batch_values() {
 
     {
         let mut request: Vec<u8> = Vec::new();
-        it.write_next_to_request(&mut request).unwrap().unwrap();
+        batch_values.write_nth_to_request(2, &mut request).unwrap();
         assert_eq!(request, vec![0, 1, 0, 0, 0, 1, 6]);
     }
 }
@@ -460,10 +448,9 @@ fn tuple_batch_values() {
     fn check_twoi32_tuple(tuple: impl BatchValues, size: usize) {
         assert_eq!(tuple.len(), size);
 
-        let mut it = tuple.batch_values_iter();
         for i in 0..size {
             let mut request: Vec<u8> = Vec::new();
-            it.write_next_to_request(&mut request).unwrap().unwrap();
+            tuple.write_nth_to_request(i, &mut request).unwrap();
 
             let mut expected: Vec<u8> = Vec::new();
             let i: i32 = i.try_into().unwrap();
@@ -650,13 +637,12 @@ fn tuple_batch_values() {
 fn ref_batch_values() {
     let batch_values: &[&[i8]] = &[&[1, 2], &[2, 3, 4, 5], &[6]];
 
-    assert_eq!(
-        <&&&&[&[i8]] as BatchValuesGatWorkaround<'_>>::len(&&&&batch_values),
-        3
-    );
-    let mut it = <&&&&[&[i8]] as BatchValuesGatWorkaround<'_>>::batch_values_iter(&&&&batch_values);
+    assert_eq!(<&&&&[&[i8]] as BatchValues>::len(&&&&batch_values), 3);
 
-    let mut request: Vec<u8> = Vec::new();
-    it.write_next_to_request(&mut request).unwrap().unwrap();
-    assert_eq!(request, vec![0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2]);
+    {
+        let mut request: Vec<u8> = Vec::new();
+        <&&&&[&[i8]] as BatchValues>::write_nth_to_request(&&&&batch_values, 0, &mut request)
+            .unwrap();
+        assert_eq!(request, vec![0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, 2]);
+    }
 }
diff --git a/scylla/src/transport/session_test.rs b/scylla/src/transport/session_test.rs
index 6214eb7a..872977ee 100644
--- a/scylla/src/transport/session_test.rs
+++ b/scylla/src/transport/session_test.rs
@@ -388,7 +388,8 @@ async fn test_batch() {
     batch.append_statement(&format!("INSERT INTO {}.t_batch (a, b, c) VALUES (7, 11, '')", ks)[..]);
     batch.append_statement(prepared_statement.clone());
 
-    let values = ((1_i32, 2_i32, "abc"), (), (1_i32, 4_i32, "hello"));
+    let four_value: i32 = 4;
+    let values = ((1_i32, 2_i32, "abc"), (), (1_i32, &four_value, "hello"));
 
     session.batch(&batch, values).await.unwrap();