Skip to main content
Unprepared statements (represented by the Statement type) are CQL query strings that are sent directly to the database without prior preparation.

Creating Statements

You can create unprepared statements in several ways:
use scylla::statement::unprepared::Statement;

// From a string
let statement = Statement::new("SELECT * FROM ks.table");

// From a String
let query_string = String::from("SELECT * FROM ks.table");
let statement = Statement::from(query_string);

// From a string slice
let statement: Statement = "SELECT * FROM ks.table".into();

Executing Unprepared Statements

The driver provides three methods for executing unprepared statements:

query_unpaged - Single Unpaged Request

Executes a query and returns all results in a single response. Use this for non-SELECT queries or when you know the result set is small.
use scylla::client::session::Session;

// Without bind values
session
    .query_unpaged(
        "INSERT INTO examples_ks.basic (a, b, c) VALUES (1, 2, 'abc')",
        &[],
    )
    .await?;

// With bind values
session
    .query_unpaged(
        "INSERT INTO examples_ks.basic (a, b, c) VALUES (?, ?, ?)",
        (3, 4, "def"),
    )
    .await?;
For large result sets, query_unpaged() can cause high memory footprint and latency. Use query_iter() for SELECT queries that may return many rows.

query_single_page - Manual Paging

Fetches a single page of results with manual control over pagination:
use scylla::statement::unprepared::Statement;
use scylla::response::PagingState;
use std::ops::ControlFlow;

let paged_query = Statement::new("SELECT a, b, c FROM examples_ks.select_paging")
    .with_page_size(6);

let mut paging_state = PagingState::start();
loop {
    let (res, paging_state_response) = session
        .query_single_page(paged_query.clone(), &[], paging_state)
        .await?;

    let res = res.into_rows_result()?;
    println!("Fetched {} rows", res.rows_num());

    match paging_state_response.into_paging_control_flow() {
        ControlFlow::Break(()) => {
            // No more pages to fetch
            break;
        }
        ControlFlow::Continue(new_paging_state) => {
            // Continue with next page
            paging_state = new_paging_state;
        }
    }
}

query_iter - Automatic Paging

Returns an async iterator that automatically fetches all pages:
use futures::stream::StreamExt;

let mut rows_stream = session
    .query_iter("SELECT a, b, c FROM examples_ks.basic", &[])
    .await?
    .rows_stream::<(i32, i32, String)>()?;

while let Some(next_row_res) = rows_stream.next().await {
    let (a, b, c) = next_row_res?;
    println!("a, b, c: {}, {}, {}", a, b, c);
}

Configuring Statements

Unprepared statements support various configuration options:

Page Size

Control how many rows are returned per page (default: 5000):
let mut statement = Statement::new("SELECT * FROM ks.table");
statement.set_page_size(1000);

// Or use the builder pattern
let statement = Statement::new("SELECT * FROM ks.table")
    .with_page_size(1000);

Consistency Level

Set the consistency level for the query:
use scylla::statement::Consistency;

let mut statement = Statement::new("SELECT * FROM ks.table");
statement.set_consistency(Consistency::Quorum);

// Or unset to use profile default
statement.unset_consistency();

Serial Consistency (for LWT)

Set serial consistency for lightweight transactions:
use scylla::statement::SerialConsistency;

let mut statement = Statement::new(
    "UPDATE ks.table SET value = ? WHERE id = ? IF value = ?"
);
statement.set_serial_consistency(Some(SerialConsistency::LocalSerial));

Request Timeout

Set a client-side timeout for the query:
use std::time::Duration;

let mut statement = Statement::new("SELECT * FROM ks.table");
statement.set_request_timeout(Some(Duration::from_secs(30)));

Idempotence

Mark a statement as idempotent to enable safe retries:
let mut statement = Statement::new("UPDATE ks.table SET value = 5 WHERE id = ?");
statement.set_is_idempotent(true);

Tracing

Enable CQL tracing to get detailed execution information:
let mut statement = Statement::new("SELECT * FROM ks.table");
statement.set_tracing(true);

let result = session.query_unpaged(&statement, &[]).await?;
if let Some(tracing_id) = result.tracing_id() {
    println!("Tracing ID: {}", tracing_id);
}

When to Use Unprepared Statements

Good Use Cases

  • Schema-altering statements (CREATE, ALTER, DROP)
  • One-time administrative queries
  • Queries without bind values
  • Dynamic queries built at runtime

Avoid For

  • Queries executed repeatedly
  • SELECT queries with bind values
  • Performance-critical operations
  • Queries needing token-aware routing
When using query_unpaged() or query_iter() with non-empty values, the driver must prepare the statement internally, resulting in 2 round trips instead of 1. For better performance, use execute_unpaged() or execute_iter() with a manually prepared statement.

Complete Example

use scylla::client::session::Session;
use scylla::client::session_builder::SessionBuilder;
use scylla::statement::unprepared::Statement;
use scylla::statement::Consistency;
use futures::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let session: Session = SessionBuilder::new()
        .known_node("127.0.0.1:9042")
        .build()
        .await?;

    // Create keyspace and table
    session
        .query_unpaged(
            "CREATE KEYSPACE IF NOT EXISTS examples_ks \
             WITH REPLICATION = {'class': 'NetworkTopologyStrategy', 'replication_factor': 1}",
            &[],
        )
        .await?;

    session
        .query_unpaged(
            "CREATE TABLE IF NOT EXISTS examples_ks.basic \
             (a int, b int, c text, primary key (a, b))",
            &[],
        )
        .await?;

    // Insert data
    session
        .query_unpaged(
            "INSERT INTO examples_ks.basic (a, b, c) VALUES (?, ?, ?)",
            (3, 4, "def"),
        )
        .await?;

    // Query with paging
    let mut statement = Statement::new("SELECT a, b, c FROM examples_ks.basic");
    statement.set_page_size(100);
    statement.set_consistency(Consistency::One);

    let mut rows_stream = session
        .query_iter(statement, &[])
        .await?
        .rows_stream::<(i32, i32, String)>()?;

    while let Some((a, b, c)) = rows_stream.try_next().await? {
        println!("a: {}, b: {}, c: {}", a, b, c);
    }

    Ok(())
}

See Also

Build docs developers (and LLMs) love