Connect To HANA DB Using Rust

The hdbconnect package makes it easy to connect to a HANA database using Rust and in this article, we'll take a look at how to establish a connection and perform some queries.

Connecting To HANA


Let's start by creating a new binary project and then configuring Cargo.toml to include the hdbconnect dependency.

cargo new hana_sample
[dependencies]
hdbconnect = "0.25"

Now that hdbconnect has been added as a dependency, we can connect to HANA using the Connection type as shown below in listing 1.

Listing 1

use hdbconnect::Connection;

fn main() {

    let user = "USER_NAME";
    let password = "PASSWORD";
    let host = "HOST:PORT";

    let mut connection = Connection::new(format!("hdbsql://{user}:{password}@{host}")).unwrap();
}

Executing Queries


The Connection type has several methods for executing queries, one of which is the query() method. This method takes an SQL statement as an str and returns a Result. If the Result is successful the Ok variant will contain a ResultSet. You can then use the ResultSet's try_into() method to translate the returned data into Rust types that implement serde::Deserialize.

Let's take a look at a few examples. The query in listing 2 below returns a single row with one value, which is deserialized into a String.

Listing 2

...

let result : String = connection.query("SELECT NOW() FROM DUMMY").unwrap().try_into().unwrap();

Queries that return a single row with multiple columns can be deserialized into a tuple.

Listing 3

let result : (String, String) = connection.query("SELECT CURRENT_DATE, CURRENT_TIME FROM DUMMY").unwrap().try_into().unwrap();

println!("{} {}", result.0, result.1);

In the code samples above, date/time values are deserialized into Strings. You can however use the HanaDate/HanaTime types that provide convenient methods to work with dates and times.

Listing 4

use hdbconnect::time::{ HanaDate, HanaTime };
...
let result : (HanaDate, HanaTime) = connection.query("SELECT CURRENT_DATE, CURRENT_TIME FROM DUMMY").unwrap().try_into().unwrap();

println!("Day : {}, Hour : {}", result.0.day(), result.1.hour());

Queries that return multiple rows can be deserialized into a vector that can then be used to iterate over the rows.

Listing 5

let result : Vec<(String, String)> = connection.query("SELECT SCHEMA_NAME, TABLE_NAME FROM SYS.TABLES LIMIT 5").unwrap().try_into().unwrap();

for row in result{
    println!("{} - {}", row.0, row.1);
}

Alternatively, as the ResultSet type implements the Iterator trait, we can rewrite the code above as the following.

Listing 6

for result in connection.query("SELECT SCHEMA_NAME, TABLE_NAME FROM SYS.TABLES LIMIT 5").unwrap(){
    let data : (String, String) = result.unwrap().try_into().unwrap();

    println!("{} - {}", data.0, data.1);
}

Queries that return null values need to be handled correctly as Rust does not support NULL. For example, the code below will generate an error causing the program to crash with the following error thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Deserialization { source: ValueType("The value cannot be converted into type String") }'

Listing 7

let result : String = connection.query("SELECT NULL FROM DUMMY").unwrap().try_into().unwrap();

We can fix this error either by returning a default value using the IFNULL function in the SQL query or deserializing the value into an Option enum type as shown below.

Listing 8

// The query returns an empty string if the column value is null
let result : String = connection.query("SELECT IFNULL(NULL, '') FROM DUMMY").unwrap().try_into().unwrap();

// Deserialize the value into an Option enum type
let result : Option<String> = connection.query("SELECT NULL FROM DUMMY").unwrap().try_into().unwrap();


SAP Business One

HANA DB

Rust

Java

Node.js