Skip to main content
CQL provides two text types: text for UTF-8 strings and ascii for ASCII-only strings. Both map to Rust’s String and &str types.

Text

The text type stores UTF-8 encoded strings of arbitrary length.

Using String

use scylla::Session;

// Insert a string
let name = String::from("Hello, ScyllaDB! 🚀");
session.query_unpaged(
    "INSERT INTO users (id, name) VALUES (?, ?)",
    (1, name),
).await?;

// Read back as String
let result = session
    .query_unpaged("SELECT name FROM users WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let name: String = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_text().cloned())
        .unwrap();
    println!("Name: {}", name);
}

Using &str

For string literals and borrowed strings, you can use &str:
// Using a string literal
session.query_unpaged(
    "INSERT INTO users (id, name) VALUES (?, ?)",
    (2, "Alice"),
).await?;

// Using a string slice
let name = String::from("Bob");
session.query_unpaged(
    "INSERT INTO users (id, name) VALUES (?, ?)",
    (3, name.as_str()),
).await?;

UTF-8 Support

The text type fully supports UTF-8, including emojis and international characters:
let messages = vec![
    "Hello, World!",
    "Bonjour, le monde!",
    "こんにちは、世界!",
    "مرحبا بالعالم!",
    "🌍🌎🌏",
];

for (i, msg) in messages.iter().enumerate() {
    session.query_unpaged(
        "INSERT INTO messages (id, text) VALUES (?, ?)",
        (i as i32, *msg),
    ).await?;
}

ASCII

The ascii type stores ASCII-only strings. It’s similar to text but validates that all characters are ASCII.
// Valid ASCII string
let ascii_text = "Hello, World!";
session.query_unpaged(
    "INSERT INTO table (id, ascii_val) VALUES (?, ?)",
    (1, ascii_text),
).await?;

ASCII Validation

When deserializing an ascii column, the driver validates that all bytes are valid ASCII (0-127). Non-ASCII characters will cause a deserialization error:
// This will fail if the database contains non-ASCII data
let result = session
    .query_unpaged("SELECT ascii_val FROM table WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    // This will error if the data contains non-ASCII characters
    let ascii_val: String = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_ascii().cloned())
        .unwrap();
    println!("ASCII value: {}", ascii_val);
}

When to Use ASCII vs Text

  • Use text for general-purpose strings (recommended for most cases)
  • Use ascii only when you need to enforce ASCII-only data at the database level
  • Both types use the same Rust types (String and &str)

String Serialization

Both String and &str can serialize to either text or ascii columns:
// Works with both text and ascii columns
let value = "Hello";

session.query_unpaged(
    "INSERT INTO table (id, text_col, ascii_col) VALUES (?, ?, ?)",
    (1, value, value),
).await?;

Nullable Strings

Use Option<String> or Option<&str> for nullable text columns:
let optional_name: Option<String> = Some("Alice".to_string());
let no_name: Option<String> = None;

session.query_unpaged(
    "INSERT INTO users (id, name) VALUES (?, ?)",
    (1, optional_name),
).await?;

session.query_unpaged(
    "INSERT INTO users (id, name) VALUES (?, ?)",
    (2, no_name),
).await?;

// Reading nullable strings
let result = session
    .query_unpaged("SELECT name FROM users WHERE id = 2", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let name: Option<String> = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_text().cloned());
    match name {
        Some(n) => println!("Name: {}", n),
        None => println!("No name"),
    }
}

String References and Lifetimes

When deserializing, you can use &str to avoid allocating a new String. This requires careful lifetime management:
use scylla::deserialize::value::DeserializeValue;
use scylla::deserialize::FrameSlice;
use scylla::frame::response::result::ColumnType;

// This works when the data is borrowed from the frame
let result = session
    .query_unpaged("SELECT name FROM users WHERE id = 1", &[])
    .await?;

// Typically, you'll use String for simplicity
let name: String = /* deserialize */;

Empty Strings

Empty strings are supported and are different from null:
let empty = "";
let null: Option<&str> = None;

session.query_unpaged(
    "INSERT INTO table (id, text1, text2) VALUES (?, ?, ?)",
    (1, empty, null),
).await?;

// Reading back
let result = session
    .query_unpaged("SELECT text1, text2 FROM table WHERE id = 1", &[])
    .await?;

if let Some(row) = result.rows()?.first() {
    let text1: String = row.columns[0]
        .as_ref()
        .and_then(|v| v.as_text().cloned())
        .unwrap();
    let text2: Option<String> = row.columns[1]
        .as_ref()
        .and_then(|v| v.as_text().cloned());
    
    println!("text1 is empty: {}", text1.is_empty()); // true
    println!("text2 is null: {}", text2.is_none());   // true
}

Performance Tips

  1. Use &str for parameters: When passing string literals or temporary string slices as query parameters, use &str instead of creating owned String instances.
  2. Avoid unnecessary cloning: When reading data, use references where possible to avoid copying strings.
  3. Use Bytes for large text: For very large text data that you don’t need to process as a string, consider using the blob type with Bytes to avoid UTF-8 validation overhead.

Common Patterns

Case-Insensitive Comparison

// CQL doesn't have built-in case-insensitive comparison for regular columns
// Store lowercase versions in a separate column for searching

let name = "Alice";
let name_lower = name.to_lowercase();

session.query_unpaged(
    "INSERT INTO users (id, name, name_lower) VALUES (?, ?, ?)",
    (1, name, name_lower.as_str()),
).await?;

// Search using the lowercase column
let search = "alice";
let result = session
    .query_unpaged(
        "SELECT name FROM users WHERE name_lower = ?",
        (search,),
    )
    .await?;

String Formatting

// Format strings before insertion
let user_id = 123;
let message = format!("User {} logged in", user_id);

session.query_unpaged(
    "INSERT INTO logs (id, message) VALUES (?, ?)",
    (1, message),
).await?;

String Truncation

// Truncate long strings to fit database limits
let long_text = "very long text...".repeat(1000);
let truncated = if long_text.len() > 1000 {
    &long_text[..1000]
} else {
    &long_text
};

session.query_unpaged(
    "INSERT INTO table (id, text) VALUES (?, ?)",
    (1, truncated),
).await?;

See Also

Build docs developers (and LLMs) love