Overview

The Firebolt Rust SDK enables Rust developers to connect to and interact with Firebolt databases seamlessly. It provides an async-first interface with comprehensive type safety, OAuth2 authentication, and structured error handling for all Firebolt data types.

Prerequisites

You must have the following prerequisites before you can connect your Firebolt account to Rust:
  • Rust installed and configured on your system. The minimum supported version is 1.70 or higher. If you do not have Rust installed, you can download it from rustup.rs.
  • Firebolt account – You need an active Firebolt account. If you do not have one, you can sign up for one.
  • Firebolt service account – You must have access to an active Firebolt service account, which facilitates programmatic access to Firebolt, its ID and secret.
  • Firebolt user – You must have a user that is associated with your service account. The user should have USAGE permission to query your database, and OPERATE permission to start and stop an engine if it is not already started.
  • Firebolt database and engine (optional) – You can optionally connect to a Firebolt database and/or engine. If you do not have one yet, you can create a database and also create an engine. You would need a database if you want to access stored data in Firebolt and an engine if you want to load and query stored data.

Installation

Add the Firebolt SDK to your Cargo.toml dependencies:
[dependencies]
firebolt-sdk = ">=0.1.0"
tokio = { version = "1.0", features = ["full"] }

Connection Parameters

The Rust SDK uses a builder pattern to configure connections to Firebolt. The SDK supports the following parameters:
  • client_id: Client ID of your service account.
  • client_secret: Client secret of your service account.
  • account_name: The name of your Firebolt account.
  • database: (Optional) The name of the database to connect to.
  • engine: (Optional) The name of the engine to run SQL queries on.

Connect to Firebolt

To establish a connection to a Firebolt database, use the builder pattern with your credentials and database details. The following example shows how to connect to Firebolt:
use firebolt_sdk::FireboltClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Replace with your Firebolt credentials and database details
    let client_id = "your_client_id";
    let client_secret = "your_client_secret";
    let account_name = "your_account_name";
    let database_name = "your_database_name"; // Optional parameter
    let engine_name = "your_engine_name"; // Optional parameter

    let mut client = FireboltClient::builder()
        .with_credentials(client_id.to_string(), client_secret.to_string())
        .with_account(account_name.to_string())
        .with_database(database_name.to_string())
        .with_engine(engine_name.to_string())
        .build()
        .await?;

    println!("Connected to Firebolt successfully!");
    Ok(())
}

Run Queries

Once connected, you can execute SQL queries using the query method. The SDK returns results with type-safe parsing for all Firebolt data types. The following examples show you how to create a table, insert data, and retrieve data:
use firebolt_sdk::FireboltClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = FireboltClient::builder()
        .with_credentials("your_client_id".to_string(), "your_client_secret".to_string())
        .with_account("your_account_name".to_string())
        .with_database("your_database_name".to_string())
        .with_engine("your_engine_name".to_string())
        .build()
        .await?;

    // Create a table
    client.query("CREATE TABLE IF NOT EXISTS test_table (id INT, value TEXT)").await?;

    // Insert data into the table
    client.query("INSERT INTO test_table (id, value) VALUES (1, 'sample value')").await?;

    // Query data from the table
    let result = client.query("SELECT id, value FROM test_table").await?;

    println!("Columns: {}", result.columns.len());
    println!("Rows: {}", result.rows.len());

    // Iterate over the result set
    for row in &result.rows {
        let id: i32 = row.get("id")?;
        let value: String = row.get("value")?;
        println!("Row: id={}, value={}", id, value);
    }

    Ok(())
}

Type-Safe Result Parsing

The SDK provides comprehensive type conversion for all Firebolt data types. You can access column values by name or index with automatic type conversion:
use firebolt_sdk::FireboltClient;
use num_bigint::BigInt;
use rust_decimal::Decimal;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = FireboltClient::builder()
        .with_credentials("your_client_id".to_string(), "your_client_secret".to_string())
        .with_account("your_account_name".to_string())
        .with_database("your_database_name".to_string())
        .with_engine("your_engine_name".to_string())
        .build()
        .await?;

    let result = client.query(r#"
        SELECT
            42 as int_col,
            30000000000 as long_col,
            3.14::float4 as float_col,
            3.14159265359 as double_col,
            '123.456'::decimal(10,3) as decimal_col,
            'hello world' as text_col,
            true as bool_col,
            [1,2,3] as array_col,
            NULL as nullable_col
    "#).await?;

    let row = &result.rows[0];

    // Type-safe column access by name
    let int_val: i32 = row.get("int_col")?;
    let long_val: BigInt = row.get("long_col")?;
    let float_val: f32 = row.get("float_col")?;
    let double_val: f64 = row.get("double_col")?;
    let decimal_val: Decimal = row.get("decimal_col")?;
    let text_val: String = row.get("text_col")?;
    let bool_val: bool = row.get("bool_col")?;
    let array_val: serde_json::Value = row.get("array_col")?;
    
    // For nullable types use Option<T>
    let nullable_val: Option<i32> = row.get("nullable_col")?;

    // Access by index
    let first_column: i32 = row.get(0)?;
    let second_column: BigInt = row.get(1)?;

    println!("Integer: {}", int_val);
    println!("Long: {}", long_val);
    println!("Float: {}", float_val);
    println!("Double: {}", double_val);
    println!("Decimal: {}", decimal_val);
    println!("Text: {}", text_val);
    println!("Boolean: {}", bool_val);
    println!("Array: {}", array_val);
    println!("First column by index: {}", first_column);
    println!("Second column by index: {}", second_column);
    
    // Handle nullable value
    match nullable_val {
        Some(value) => println!("Nullable Value: {}", value),
        None => println!("Nullable Value is NULL"),
    }

    Ok(())
}
The Rust SDK does not currently support streaming queries for processing large result sets. All query results are loaded into memory at once. For large datasets, consider using LIMIT clauses or pagination techniques to manage memory usage.

Error Handling

The SDK provides comprehensive error handling through the FireboltError enum:
use firebolt_sdk::{FireboltClient, FireboltError};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = FireboltClient::builder()
        .with_credentials("your_client_id".to_string(), "your_client_secret".to_string())
        .with_account("your_account_name".to_string())
        .build()
        .await?;

    match client.query("SELECT * FROM non_existent_table").await {
        Ok(result) => {
            println!("Query succeeded with {} rows", result.rows.len());
        }
        Err(FireboltError::Query(msg)) => {
            println!("Query error: {}", msg);
        }
        Err(FireboltError::Authentication(msg)) => {
            println!("Authentication error: {}", msg);
        }
        Err(FireboltError::Network(msg)) => {
            println!("Network error: {}", msg);
        }
        Err(FireboltError::Configuration(msg)) => {
            println!("Configuration error: {}", msg);
        }
        Err(e) => {
            println!("Other error: {}", e);
        }
    }

    Ok(())
}

Troubleshooting

When building a connection to Firebolt using the Rust SDK, follow these best practices to ensure correct configuration and avoid common errors: Guidelines
  • Ensure all required parameters (client_id, client_secret, account_name) are provided to the builder.
  • Use the exact account name as shown in the Firebolt Console URL, which is usually lowercase with no special characters.
  • Use the exact engine and database names as shown in the Firebolt Workspace.
  • Verify your service account has the necessary permissions for the database and engine you’re trying to access.

Common errors and solutions

Error messageLikely causeSolution
Configuration error: client_id is requiredMissing required parameter in builderEnsure all required parameters are provided to the builder
Authentication error: Invalid credentialsIncorrect client ID or secretVerify your service account credentials in the Firebolt console
Network error: Failed to get engine URLNetwork connectivity issuesCheck your internet connection and firewall settings
Query error: relation "table_name" does not existInvalid SQL query or missing tableVerify your SQL query uses valid table and column names
The Rust SDK does not currently support connecting to Firebolt Core. For Firebolt Core connections, use the Go SDK or other supported SDKs.

Additional Resources