Expand description
§exasol
Database connector for Exasol implemented using the Websocket protocol. Messages are sent and received in the JSON format.
§Errors
Most, if not all, of the public API is implemented to return a Result due to the nature of the library. Mentioning this here to avoid repeating the same note on all public functions.
§Examples
Using the connect function to conveniently create a connection.
use exasol::error::Result;
use exasol::{connect, bind, QueryResult};
use std::env;
// Credentials can be retrieved however you like
let dsn = env::var("EXA_DSN").unwrap();
let schema = env::var("EXA_SCHEMA").unwrap();
let user = env::var("EXA_USER").unwrap();
let password = env::var("EXA_PASSWORD").unwrap();
// Quick and convenient way to create a connection
let mut exa_con = connect(&dsn, &schema, &user, &password).unwrap();
// Executing a statement
let mut result = exa_con.execute("SELECT '1', '2', '3' UNION ALL SELECT '4', '5', '6'").unwrap();
// Retrieving data associated with the result set.
// A Vec of rows is returned, and the row type in this case will be Vec<String>.
let data = exa_con.iter_result(&mut result).collect::<Result<Vec<Vec<String>>>>().unwrap();
The Connection::iter_result method returns a lazy iterator over a result’s rows. Only a given rows buffer is present at a given time in a ResultSet, which is contained in a QueryResult, if the query had a result set. The buffer size can be changed either in the ConOpts or through the Connection::set_fetch_size method.
§Cleanup
QueryResult structs will automatically close once fully retrieved. If that does not happen, they will behave like PreparedStatement structs in the sense that they should be manually closed, or they will be closed when the Connection is dropped.
§Best practice
As a best practice, you should always close QueryResult and PreparedStatement instances once they are no longer needed.
use exasol::error::Result;
use exasol::{connect, bind, QueryResult, ResultSet};
use std::env;
let dsn = env::var("EXA_DSN").unwrap();
let schema = env::var("EXA_SCHEMA").unwrap();
let user = env::var("EXA_USER").unwrap();
let password = env::var("EXA_PASSWORD").unwrap();
let mut exa_con = connect(&dsn, &schema, &user, &password).unwrap();
let mut result = exa_con.execute("SELECT * FROM EXA_RUST_TEST LIMIT 1000;").unwrap();
let mut counter = 0;
while counter < 3 {
// Only enough calls necessary to retrieve 100 rows will be made to the database.
// Any leftover data in the chunk will still be present in the ResultSet buffer.
// and can be used in later retrievals.
let data: Vec<(String, String, u16)> = exa_con.iter_result(&mut result).take(100).collect::<Result<_>>().unwrap();
// do stuff with data
counter += 1;
}
// Alternatively, you can retrieve row chunks while there still are rows in the result set.
// while result.has_rows() {
// let data: Vec<(String, String, u16)> = exa_con.fetch(&mut result, 100).unwrap();
// }
// Failing to close the result here will leave it up to the
// Connection to do it when it is dropped.
exa_con.close_result(result);
§Custom Row Type
The crate implements a custom deserializer, which is a trimmed down specialized version
of the serde_json
deserializer. Database row deserialization can be attempted to any
type implementing Deserialize.
This is thanks to the magic of serde
. In addition, regular serde
features are supported,
such as flattening or renaming. Note that implicitly, column names are converted to lowercase
for easier deserialization. This can be changed through the Connection::set_lowercase_columns
method.
Map-like and sequence-like types are natively deserialized. For enum
sequence-like variants,
see deserialize_as_seq.
use exasol::{connect, QueryResult, ResultSet};
use exasol::error::Result;
use serde_json::Value;
use serde::Deserialize;
use std::env;
let dsn = env::var("EXA_DSN").unwrap();
let schema = env::var("EXA_SCHEMA").unwrap();
let user = env::var("EXA_USER").unwrap();
let password = env::var("EXA_PASSWORD").unwrap();
let mut exa_con = connect(&dsn, &schema, &user, &password).unwrap();
let mut result = exa_con.execute("SELECT 1, 2 UNION ALL SELECT 1, 2;").unwrap();
// Change the expected row type with the turbofish notation
let mut data: Vec<[u8; 2]> = exa_con.iter_result(&mut result).collect::<Result<_>>().unwrap();
let row1 = data[0];
// You can also rely on type inference.
// Nothing stops you from changing row types
// on the same result set.
let mut data: Vec<Vec<u8>> = exa_con.iter_result(&mut result).collect::<Result<_>>().unwrap();;
let row2 = data.pop();
let mut result = exa_con.execute("SELECT 1 as col1, 2 as col2, 3 as col3 \
UNION ALL \
SELECT 4 as col1, 5 as col2, 6 as col3;").unwrap();
#[derive(Debug, Deserialize)]
struct Test {
col1: u8,
col2: u8,
col3: u8,
}
let data = exa_con.iter_result::<Test>(&mut result).collect::<Result<Vec<_>>>().unwrap();
for row in data {
// do stuff with row
}
§Parameter binding
Queries can be composed by binding named or positional parameters to them through the bind function. The function takes a string and a type implementing the Serialize trait. The second argument must serialize to a sequence or map.
Named parameter values behaviour:
- single value type get parsed to their SQL representation
- sequence-like types get parsed to a parenthesized list with elements in SQL representation
- map-like types get their values parsed to a parenthesized list with elements in SQL representation
use exasol::bind;
use serde::Serialize;
#[derive(Serialize)]
struct Parameters {
col1: String,
col2: u16,
col3: Vec<String>
}
let params = Parameters {
col1: "test".to_owned(),
col2: 10,
col3: vec!["a".to_owned(), "b".to_owned(), "c".to_owned()]
};
let query = "\
SELECT * FROM TEST_TABLE \
WHERE NAME = :col1 \
AND ID = :col2 \
AND VALUE IN :col3;";
let new_query = bind(query, params).unwrap();
assert_eq!(new_query, "\
SELECT * FROM TEST_TABLE \
WHERE NAME = 'test' \
AND ID = 10 \
AND VALUE IN ('a', 'b', 'c');");
§Custom Connection
The Connection struct can be directly instantiated with the use of ConOpts.
use exasol::*;
use std::env;
let dsn = env::var("EXA_DSN").unwrap();
let schema = env::var("EXA_SCHEMA").unwrap();
let user = env::var("EXA_USER").unwrap();
let password = env::var("EXA_PASSWORD").unwrap();
// Only providing fields for which we want custom values
let mut opts = ConOpts::new();
opts.set_dsn(dsn);
opts.set_login_kind(LoginKind::Credentials(Credentials::new(user, password)));
opts.set_schema(Some(schema));
let exa_con = Connection::new(opts).unwrap();
§Features
native-tls
- (disabled by default) enablestungstenite
WSS encryption support through native-tlsnative-tls-vendored
- (disabled by default) enablestungstenite
WSS encryption support through native-tls-vendoredrustls-tls-webpki-roots
- (disabled by default) enablestungstenite
WSS encryption support through rustls-tls-webpki-rootsrustls-tls-native-roots
- (disabled by default) enablestungstenite
WSS encryption support through rustls-tls-native-rootsflate2
- (disabled by default) enables compression support
Enabling these features allows changing additional settings in ConOpts instances.
§Panics
Attempting to use these methods without their respective features enabled results in panics.
let mut opts = ConOpts::new();
opts.set_dsn(dsn);
opts.set_login_kind(LoginKind::Credentials(Credentials::new(user, password)));
opts.set_schema(Some(schema));
opts.set_encryption(true);
opts.set_compression(true);
§Batch Execution
Batch query execution can be achieved through either Connection::execute_batch or PreparedStatement.
§HTTP Transport
Parallel, highly performant, IMPORT/EXPORT operations are supported through Connection::export_to_closure and Connection::import_from_closure.
Modules§
Structs§
- Column
- Struct containing the name and datatype (as seen in Exasol) of a given column.
- ConOpts
- Connection options for Connection The DSN may or may not contain a port - if it does not, the port field in this struct is used as a fallback.
- Connection
- The Connection struct will be what we use to interact with the database. The connection keeps track of all the result sets and prepared statements issued, and, if they are not closed by the user, they will automatically get closed when the connection is dropped.
- Credentials
- Login credentials.
- Data
Type - Struct representing a datatype for a column in a result set.
- Export
Opts - Export options
- Import
Opts - HTTP Transport import options.
- Prepared
Statement - Struct representing a prepared statement
- Query
Result - Struct representing the result of a query.
If the query produced a ResultSet, it will be contained in the
result_set
field. - Result
Set - Struct representing a database result set. You’ll generally only interact with this if you need information about result set columns.
Enums§
- Login
Kind - Login type. The variant chosen dictates which login process is called.
- Protocol
Version - Enum listing the protocol versions that can be used when establishing a websocket connection to Exasol. Defaults to the highest defined protocol version and falls back to the highest protocol version supported by the server.
Traits§
Functions§
- bind
- Binds named or positional parameters from a type implementing Serialize. If the type is map-like, named parameters are needed. For sequence-like types, positional parameters are needed.
- connect
- Convenience function to quickly connect using default options. Returns a Connection set using the default ConOpts
- deserialize_
as_ seq - Deserialization function that can be used through the
serde(deserialize_with = "...")
attribute to aid in deserialization of enum variants as sequences instead of maps.