A Rust SDK for connecting to and interacting with Firebolt databases. This SDK provides an async-first interface for executing SQL queries and managing database connections with comprehensive type safety and error handling.
The Firebolt Rust SDK enables Rust developers to connect to Firebolt databases seamlessly. It provides a builder pattern for client configuration, OAuth2 authentication, and type-safe result parsing for all Firebolt data types.
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.
Add the Firebolt SDK to your Cargo.toml dependencies:
[dependencies]
firebolt = ">=0.0.1"
tokio = { version = "1.0", features = ["full"] }The SDK uses the following parameters to connect to Firebolt:
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.
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::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?;
println!("Connected to Firebolt successfully!");
Ok(())
}Once connected, you can execute SQL queries using the query method. The SDK returns results with type-safe parsing for all Firebolt data types.
use firebolt::FireboltClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
const CLIENT_ID: &str = "your_client_id";
const CLIENT_SECRET: &str = "your_client_secret";
const ACCOUNT_NAME: &str = "your_account_name";
const DATABASE_NAME: &str = "your_database_name";
const ENGINE_NAME: &str = "your_engine_name";
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?;
let result = client.query("SELECT 1 as number_value, 'hello' as text_value").await?;
println!("Columns: {}", result.columns.len());
println!("Rows: {}", result.rows.len());
let row = &result.rows[0];
// Accessing by name
let num_value: i32 = row.get("number_value")?;
let text_value: String = row.get("text_value")?;
println!("number: {}", num_value);
println!("text: {}", text_value);
// Accessing by index
let first_column: i32 = row.get(0)?;
let second_column: String = row.get(1)?;
println!("First column by index: {}", first_column);
println!("Second column by index: {}", second_column);
Ok(())
}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::FireboltClient;
use num_bigint::BigInt;
use rust_decimal::Decimal;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
const CLIENT_ID: &str = "your_client_id";
const CLIENT_SECRET: &str = "your_client_secret";
const ACCOUNT_NAME: &str = "your_account_name";
const DATABASE_NAME: &str = "your_database_name";
const ENGINE_NAME: &str = "your_engine_name";
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?;
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];
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")?;
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);
// Handle nullable value
match nullable_val {
Some(value) => println!("Nullable Value: {}", value),
None => println!("Nullable Value is NULL"),
}
Ok(())
}The SDK provides comprehensive error handling through the FireboltError enum:
use firebolt::{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(())
}| Error | Likely Cause | Solution |
|---|---|---|
Authentication error: Invalid credentials |
Incorrect client ID or secret | Verify your service account credentials in the Firebolt console |
Configuration error: CLIENT_ID is required |
Missing required parameter | Ensure all required parameters are provided to the builder |
Network error: Failed to get engine URL |
Network connectivity issues | Check your internet connection and firewall settings |
Query error: Line 1, Column 15: relation \"non_existent_table\" does not exist |
Invalid SQL query | Verify your SQL query has correct syntax and uses valid table and column names |