Skip to Content
Querying & DML

Querying & DML

execute_query(...) runs SQL over the KalamDB HTTP API and returns a QueryResponse.

RUST
let response = client    .execute_query("SELECT 1 AS ok", None, None, None)    .await?; println!("success: {}", response.success());

HTTP queries do not require connect().

Parameterized queries

Use $1, $2, … placeholders and pass serde_json::Value parameters:

RUST
let params = vec![    serde_json::json!("conv_42"),    serde_json::json!(false),]; let response = client    .execute_query(        "SELECT * FROM app.messages WHERE conversation_id = $1 AND archived = $2",        None,        Some(params),        None,    )    .await?;

Parameters are JSON-encoded. Use explicit SQL casts when you need richer server-side types.

Namespaces

Pass the namespace as the fourth argument when SQL uses unqualified table names:

RUST
client    .execute_query(        "SELECT * FROM messages ORDER BY created_at DESC LIMIT 10",        None,        None,        Some("chat"),    )    .await?;

Background: SQL Reference: Namespaces.

Reading results

RUST
if !response.success() {    if let Some(err) = &response.error {        eprintln!("{} ({})", err.message, err.code);    }    return Ok(());} for row in response.rows_as_maps() {    let id = row.get("id").and_then(|v| v.as_text());    let body = row.get("body").and_then(|v| v.as_text());    println!("id={id:?} body={body:?}");}

rows_as_maps() returns Vec<HashMap<String, KalamCellValue>>.

Convenience helpers

RUST
response.row_count();response.first_row_as_map();response.get_value("column_name");response.get_i64("big_id_column");

Multiple statements

RUST
let response = client    .execute_query("SELECT 1; SELECT 2;", None, None, None)    .await?; for (i, result) in response.results.iter().enumerate() {    println!("statement[{i}] rows={}", result.row_count);}

Positional rows

response.rows() returns Vec<Vec<KalamCellValue>> when you prefer index-based access.

DML: INSERT / UPDATE / DELETE

All SQL goes through execute_query:

RUST
client    .execute_query(        "INSERT INTO app.messages (conversation_id, role, content) VALUES ($1, $2, $3)",        None,        Some(vec![            serde_json::json!("conv_42"),            serde_json::json!("user"),            serde_json::json!("hello"),        ]),        None,    )    .await?;

Check response.success() and inspect response.error on failure.

File uploads and downloads

Enable the file-uploads feature (or native-full):

TOML
kalam-client = { version = "0.5", features = ["native-sdk", "file-uploads"] }

Upload with FileUpload and execute_with_files:

RUST
use kalam_client::{FileUpload, QueryParam}; let bytes = tokio::fs::read("./avatar.png").await?;let files = vec![    FileUpload::new("avatar", "avatar.png", bytes).with_mime("image/png"),]; client    .execute_with_files(        r#"INSERT INTO app.users (name, avatar) VALUES ($1, FILE("avatar"))"#,        files,        Some(vec![QueryParam::from("Alice")]),        None,    )    .await?;

After querying FILE columns, bind table context with TableId and download bytes:

RUST
use kalam_client::TableId; let table_id = TableId::from_strings("app", "users");let bound = cell.as_bound_file(&table_id).expect("FILE column");let download = client.download_bound_file(&bound, None).await?;

See FILE Columns & Uploads for the full FileRef, BoundFileRef, and download API.

Progress callbacks

execute_query_with_progress(...) reports query execution progress for long-running statements when the server emits progress frames.

Next

Last updated on