ScyllaDB University Live | Free Virtual Training Event
Learn more
ScyllaDB Documentation Logo Documentation
  • Deployments
    • Cloud
    • Server
  • Tools
    • ScyllaDB Manager
    • ScyllaDB Monitoring Stack
    • ScyllaDB Operator
  • Drivers
    • CQL Drivers
    • DynamoDB Drivers
    • Supported Driver Versions
  • Resources
    • ScyllaDB University
    • Community Forum
    • Tutorials
Install
Search Ask AI
ScyllaDB Docs ScyllaDB Rust Driver Executing CQL statements - best practices Bound statement
For AI agents: a documentation index is available at https://rust-driver.docs.scylladb.com/main/llms.txt. A Markdown version of this page is at https://rust-driver.docs.scylladb.com/main/statements/bound.md.

Caution

You're viewing documentation for an unstable version of ScyllaDB Rust Driver. Switch to the latest stable version.

Bound statement¶

A BoundStatement is a prepared statement with its values already bound to it - i.e., serialized and stored inside the statement. It is created by binding values to a PreparedStatement:

use scylla::statement::bound::BoundStatement;
use scylla::statement::prepared::PreparedStatement;

// Prepare the statement ONCE, as always.
let prepared: PreparedStatement = session
    .prepare("INSERT INTO ks.tab (a) VALUES(?)")
    .await?;

// Bind values to it. This serializes the values - and type erases them.
let bound: BoundStatement = prepared.clone().bind(&(12345,))?;

// Execute the bound statement. No values are passed anymore - it carries its own.
session.execute_bound_unpaged(&bound).await?;

Note that PreparedStatement::bind consumes the statement. Because binding is cheap while preparing is not, keep the prepared statement around and clone() it for each binding, as above - PreparedStatement is cheap to clone.

What is it for?¶

With execute_* you pass the values at the moment of execution, so the values must be alive - and of a known Rust type - at that moment. BoundStatement is for the cases when that does not hold: it lets you serialize the values up front and carry the result around as a single, type-erased, SerializeRow-independent value.

This enables, among others:

  • Storing statements ready for execution. A Vec<BoundStatement> can hold statements with completely different value types - the values are already serialized, so nothing about their Rust types leaks into the type of the collection. With PreparedStatement you would need to keep the values alongside it, and they would all have to be of the same type (or boxed behind a trait object).

  • Separating value preparation from execution. The code that knows the values does not have to be the code that executes the statement; the bound statement can be handed over to another layer, task, or a queue, which needs to know nothing about the values’ types.

  • Serializing the values only once. PreparedStatement::calculate_token and Session::execute_* each serialize the values they are given, so computing a statement’s token and then executing it serializes the same values twice. A BoundStatement holds the serialized values, and both token calculation and execution reuse them - one serialization for both.

Execution¶

Session::execute_bound_[unpaged/single_page/iter] mirror the Session::execute_[unpaged/single_page/iter] family, minus the values argument. Everything else - paging, results, errors - works exactly as described for prepared statements.

Token calculation¶

BoundStatement::calculate_token returns the token that the statement will be routed by - no values need to be passed, as the statement already has them:

use scylla::routing::Token;

let prepared = session
    .prepare("INSERT INTO ks.tab (a) VALUES(?)")
    .await?;
let bound = prepared.bind(&(12345,))?;

let token: Option<Token> = bound.calculate_token()?;

// The very same serialized values are then sent with the execution:
// no second serialization takes place.
session.execute_bound_unpaged(&bound).await?;

Compare with the prepared statement equivalent, which serializes (12345,) twice - once inside calculate_token, and again inside execute_unpaged:

let prepared = session
    .prepare("INSERT INTO ks.tab (a) VALUES(?)")
    .await?;

let token = prepared.calculate_token(&(12345,))?; // serializes the values
session.execute_unpaged(&prepared, (12345,)).await?; // serializes them again

Configuration¶

BoundStatement does not expose configuration modifiers. Configure the PreparedStatement (consistency, page size, execution profile, …) before binding - the bound statement inherits all of its settings, and you can inspect them through BoundStatement::prepared.

use scylla::statement::Consistency;

let mut prepared = session
    .prepare("INSERT INTO ks.tab (a) VALUES(?)")
    .await?;

// Set the options first...
prepared.set_consistency(Consistency::One);

// ...then bind. The bound statement will be executed with Consistency::One.
let bound = prepared.bind(&(12345,))?;
assert_eq!(bound.prepared().get_consistency(), Some(Consistency::One));

session.execute_bound_unpaged(&bound).await?;

See BoundStatement API documentation for more.

Was this page helpful?

PREVIOUS
Prepared statement
NEXT
Batch statement
  • Create an issue
  • Edit this page

On this page

  • Bound statement
    • What is it for?
    • Execution
    • Token calculation
    • Configuration
ScyllaDB Rust Driver
Search Ask AI
  • main
    • main
    • v1.8.0
    • v1.7.0
    • v1.6.0
    • v1.5.0
    • v1.4.1
    • v1.4.0
    • v1.3.1
    • v1.3.0
    • v1.2.0
  • ScyllaDB Rust Driver
  • Quick Start
    • Creating a project
    • Connecting and running a simple query
    • Running ScyllaDB using Docker
  • Connecting to the cluster
    • Compression
    • Authentication
    • TLS
    • Client Routes (Private Networking)
  • Executing CQL statements - best practices
    • Unprepared statement
    • Statement values
    • Query result
    • Prepared statement
    • Bound statement
    • Batch statement
    • Paged query
    • USE keyspace
    • Schema agreement
    • Lightweight transaction (LWT) statement
    • Request timeouts
    • Timestamp generators
  • Execution profiles
    • Creating a profile and setting it
    • All options supported by a profile
    • Priorities of execution settings
    • Remapping execution profile handles
  • Data Types
    • Bool, Tinyint, Smallint, Int, Bigint, Float, Double
    • Ascii, Text, Varchar
    • Counter
    • Blob
    • Inet
    • Uuid
    • Timeuuid
    • Date
    • Time
    • Timestamp
    • Duration
    • Decimal
    • Varint
    • List, Set, Map
    • Tuple
    • User defined types
    • Vector
  • Load balancing
    • DefaultPolicy
    • Tablet awareness
  • Retry policy configuration
    • Fallthrough retry policy
    • Default retry policy
    • Downgrading consistency retry policy
  • Speculative execution
    • Simple speculative execution
    • Percentile speculative execution
  • Driver metrics
  • Migration guides
    • Adjusting code to changes in serialization API introduced in 0.11
    • Adjusting code to changes in deserialization API introduced in 0.15
  • Logging
  • Query tracing
    • Tracing a simple/prepared/batch query
    • Tracing a paged query
    • Tracing Session::prepare
    • Query Execution History
  • Schema
Docs Tutorials University Contact Us About Us
© 2026, ScyllaDB. All rights reserved. | Terms of Service | Privacy Policy | ScyllaDB, and ScyllaDB Cloud, are registered trademarks of ScyllaDB, Inc.
Last updated on 02 September 2026.
Powered by Sphinx 9.1.0 & ScyllaDB Theme 1.9.3