Skip to main content

Overview

The BatchValues trait provides an iterator-like interface for supplying values to batch statements. Each element in the batch can have its own set of bound values.
Most users will use built-in implementations like tuples, slices, or iterators rather than implementing this trait directly.
Source: scylla-cql/src/serialize/batch.rs:10

Trait Definition

pub trait BatchValues {
    type BatchValuesIter<'r>: BatchValuesIterator<'r>
    where
        Self: 'r;

    fn batch_values_iter(&self) -> Self::BatchValuesIter<'_>;
}

BatchValuesIterator

An iterator-like interface for accessing values in a batch.
pub trait BatchValuesIterator<'bv> {
    fn serialize_next(
        &mut self,
        ctx: &RowSerializationContext<'_>,
        writer: &mut RowWriter,
    ) -> Option<Result<(), SerializationError>>;

    fn is_empty_next(&mut self) -> Option<bool>;

    fn skip_next(&mut self) -> Option<()>;

    fn count(mut self) -> usize
    where
        Self: Sized;
}

Built-in Implementations

Slices

impl<T: SerializeRow> BatchValues for [T]
Each element in the slice provides values for one statement in the batch. Example:
let values = [
    (1, "Alice"),
    (2, "Bob"),
    (3, "Charlie"),
];
session.batch(&batch, values).await?;

Vectors

impl<T: SerializeRow> BatchValues for Vec<T>
Example:
let values = vec![
    (1, "Alice"),
    (2, "Bob"),
];
session.batch(&batch, values).await?;

Single-Element Tuple

impl<T0: SerializeRow> BatchValues for (T0,)
Provides values for a batch with exactly one statement. Example:
let values = ((42, "Alice"),);
session.batch(&batch, values).await?;

Multiple-Element Tuples

impl<T0: SerializeRow, T1: SerializeRow> BatchValues for (T0, T1)
impl<T0: SerializeRow, T1: SerializeRow, T2: SerializeRow> BatchValues for (T0, T1, T2)
// ... up to 16 elements
Each tuple element provides values for one statement. Example:
// Batch with 3 statements
let values = (
    (1, "Alice"),      // Values for first statement
    (2, "Bob"),        // Values for second statement
    (3, "Charlie"),    // Values for third statement
);
session.batch(&batch, values).await?;

References

impl<T: BatchValues + ?Sized> BatchValues for &T

BatchValuesFromIterator

A type that implements BatchValues from an iterator over SerializeRow types.
pub struct BatchValuesFromIterator<'sr, IT> {
    // ... internal fields
}

Methods

new

Creates a new BatchValuesFromIterator.
pub fn new(into_iter: impl IntoIterator<IntoIter = IT>) -> Self
where
    IT: Iterator<Item = &'sr SR> + Clone,
    SR: SerializeRow + 'sr,
Example:
use scylla::serialize::batch::BatchValuesFromIterator;

let users = vec![
    User { id: 1, name: "Alice".to_string() },
    User { id: 2, name: "Bob".to_string() },
];

let values = BatchValuesFromIterator::new(users.iter());
session.batch(&batch, values).await?;

Usage Patterns

Homogeneous Batch Values

All statements in the batch take the same type of values.
let mut batch = Batch::new(BatchType::Logged);
let prepared = session.prepare("INSERT INTO users (id, name) VALUES (?, ?)").await?;

// Add same statement multiple times
batch.append_statement(&prepared);
batch.append_statement(&prepared);
batch.append_statement(&prepared);

// Provide values for each
let values = [
    (1, "Alice"),
    (2, "Bob"),
    (3, "Charlie"),
];

session.batch(&batch, values).await?;

Heterogeneous Batch Values

Statements in the batch take different types of values.
let mut batch = Batch::new(BatchType::Logged);

let insert_user = session.prepare("INSERT INTO users (id, name) VALUES (?, ?)").await?;
let insert_log = session.prepare("INSERT INTO logs (timestamp, message) VALUES (?, ?)").await?;

batch.append_statement(&insert_user);
batch.append_statement(&insert_log);

// Tuple with different types for each statement
let values = (
    (1, "Alice"),                          // For insert_user
    (1234567890i64, "User created"),       // For insert_log
);

session.batch(&batch, values).await?;

Empty Values

For statements with no bind markers.
let mut batch = Batch::new(BatchType::Logged);
batch.append_statement("INSERT INTO logs (message) VALUES ('Started')");
batch.append_statement("INSERT INTO logs (message) VALUES ('Finished')");

// Empty tuple for each statement
let values = ((), ());
session.batch(&batch, values).await?;

Mixed Empty and Non-Empty

let mut batch = Batch::new(BatchType::Logged);
let prepared = session.prepare("INSERT INTO users (id, name) VALUES (?, ?)").await?;

batch.append_statement(&prepared);
batch.append_statement("INSERT INTO logs (message) VALUES ('Done')");

// First has values, second is empty
let values = (
    (1, "Alice"),
    (),
);

session.batch(&batch, values).await?;

Iterator-based Values

use scylla::serialize::batch::BatchValuesFromIterator;

#[derive(SerializeRow)]
struct User {
    id: i32,
    name: String,
}

let users = vec![
    User { id: 1, name: "Alice".to_string() },
    User { id: 2, name: "Bob".to_string() },
    User { id: 3, name: "Charlie".to_string() },
];

// Create batch with same prepared statement for each user
let mut batch = Batch::new(BatchType::Logged);
let prepared = session.prepare("INSERT INTO users (id, name) VALUES (?, ?)").await?;

for _ in 0..users.len() {
    batch.append_statement(&prepared);
}

// Use iterator to provide values
let values = BatchValuesFromIterator::new(users.iter());
session.batch(&batch, values).await?;

Examples

Basic Batch with Tuple Values

use scylla::statement::batch::{Batch, BatchType};

let mut batch = Batch::new(BatchType::Logged);
let prepared = session
    .prepare("INSERT INTO users (id, name, email) VALUES (?, ?, ?)")
    .await?;

batch.append_statement(&prepared);
batch.append_statement(&prepared);

// Tuple of tuples - each inner tuple is values for one statement
let values = (
    (1, "Alice", "[email protected]"),
    (2, "Bob", "[email protected]"),
);

session.batch(&batch, values).await?;

Batch with Slices

let values = [
    (1, "Alice", "[email protected]"),
    (2, "Bob", "[email protected]"),
    (3, "Charlie", "[email protected]"),
];

session.batch(&batch, &values[..]).await?;

Batch with Vectors

let mut values = Vec::new();
for i in 1..=100 {
    values.push((i, format!("User {}", i), format!("user{}@example.com", i)));
}

session.batch(&batch, values).await?;

Batch with Structs

use scylla::macros::SerializeRow;

#[derive(SerializeRow)]
struct UserInsert {
    id: i32,
    name: String,
    email: String,
}

let users = vec![
    UserInsert {
        id: 1,
        name: "Alice".to_string(),
        email: "[email protected]".to_string(),
    },
    UserInsert {
        id: 2,
        name: "Bob".to_string(),
        email: "[email protected]".to_string(),
    },
];

let mut batch = Batch::new(BatchType::Logged);
let prepared = session
    .prepare("INSERT INTO users (id, name, email) VALUES (?, ?, ?)")
    .await?;

for _ in 0..users.len() {
    batch.append_statement(&prepared);
}

session.batch(&batch, users).await?;

Large Batch with Iterator

use scylla::serialize::batch::BatchValuesFromIterator;

// Generate large number of inserts
let data: Vec<_> = (1..=10000)
    .map(|i| (i, format!("User {}", i)))
    .collect();

let mut batch = Batch::new(BatchType::Unlogged);
let prepared = session
    .prepare("INSERT INTO users (id, name) VALUES (?, ?)")
    .await?;

for _ in 0..data.len() {
    batch.append_statement(&prepared);
}

let values = BatchValuesFromIterator::new(data.iter());
session.batch(&batch, values).await?;

Best Practices

  1. Use slices or vectors for homogeneous batch values
  2. Use tuples when statements need different value types
  3. Use iterators for large batches to avoid allocating all values upfront
  4. Keep batches small - aim for under 1000 statements
  5. Match value count to statement count - mismatches will cause errors
The number of value sets must exactly match the number of statements in the batch. Extra values or missing values will cause serialization errors.

See Also

Build docs developers (and LLMs) love