odbc_api

Struct Environment

Source
pub struct Environment { /* private fields */ }
Expand description

An ODBC 3.8 environment.

Associated with an Environment is any information that is global in nature, such as:

  • The Environment’s state
  • The current environment-level diagnostics
  • The handles of connections currently allocated on the environment
  • The current stetting of each environment attribute

Creating the environment is the first applications do, then interacting with an ODBC driver manager. There must only be one environment in the entire process.

Implementations§

Source§

impl Environment

Source

pub unsafe fn set_connection_pooling( scheme: AttrConnectionPooling, ) -> Result<(), Error>

Enable or disable (default) connection pooling for ODBC connections. Call this function before creating the ODBC environment for which you want to enable connection pooling.

ODBC specifies an interface to enable the driver manager to enable connection pooling for your application. It is off by default, but if you use ODBC to connect to your data source instead of implementing it in your application, or importing a library you may simply enable it in ODBC instead. Connection Pooling is governed by two attributes. The most important one is the connection pooling scheme which is Off by default. It must be set even before you create your ODBC environment. It is global mutable state on the process level. Setting it in Rust is therefore unsafe.

The other one is changed via Self::set_connection_pooling_matching. It governs how a connection is choosen from the pool. It defaults to strict which means the Connection you get from the pool will have exactly the attributes specified in the connection string.

See: https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/driver-manager-connection-pooling

§Example
use odbc_api::{Environment, sys::{AttrConnectionPooling, AttrCpMatch}};

/// Create an environment with connection pooling enabled.
let env = unsafe {
    Environment::set_connection_pooling(AttrConnectionPooling::DriverAware).unwrap();
    let mut env = Environment::new().unwrap();
    // Strict is the default, and is set here to be explicit about it.
    env.set_connection_pooling_matching(AttrCpMatch::Strict).unwrap();
    env
};
§Safety

An ODBC driver must be fully thread-safe, and connections must not have thread affinity to support connection pooling. This means the driver is able to handle a call on any thread at any time and is able to connect on one thread, to use the connection on another thread, and to disconnect on a third thread.

Also note that this is changes global mutable state for the entire process. As such it is vulnerable to race conditions if called from more than one place in your application. It is recommened to call this in the beginning, before creating any connection.

Source

pub fn set_connection_pooling_matching( &mut self, matching: AttrCpMatch, ) -> Result<(), Error>

Determines how a connection is chosen from a connection pool. When Self::connect, Self::connect_with_connection_string or Self::driver_connect is called, the Driver Manager determines which connection is reused from the pool. The Driver Manager tries to match the connection options in the call and the connection attributes set by the application to the keywords and connection attributes of the connections in the pool. The value of this attribute determines the level of precision of the matching criteria.

The following values are used to set the value of this attribute:

  • crate::sys::AttrCpMatch::Strict = Only connections that exactly match the connection options in the call and the connection attributes set by the application are reused. This is the default.
  • crate::sys::AttrCpMatch::Relaxed = Connections with matching connection string
    keywords can be used. Keywords must match, but not all connection attributes must match.
Source

pub fn new() -> Result<Self, Error>

Entry point into this API. Allocates a new ODBC Environment and declares to the driver manager that the Application wants to use ODBC version 3.8.

§Safety

There may only be one ODBC environment in any process at any time. Take care using this function in unit tests, as these run in parallel by default in Rust. Also no library should probably wrap the creation of an odbc environment into a safe function call. This is because using two of these “safe” libraries at the same time in different parts of your program may lead to race condition thus violating Rust’s safety guarantees.

Creating one environment in your binary is safe however.

Source

pub fn connect( &self, data_source_name: &str, user: &str, pwd: &str, options: ConnectionOptions, ) -> Result<Connection<'_>, Error>

Allocates a connection handle and establishes connections to a driver and a data source.

§Arguments
  • data_source_name - Data source name. The data might be located on the same computer as the program, or on another computer somewhere on a network.
  • user - User identifier.
  • pwd - Authentication string (typically the password).
§Example
use odbc_api::{Environment, ConnectionOptions};

let env = Environment::new()?;

let mut conn = env.connect(
    "YourDatabase", "SA", "My@Test@Password1",
    ConnectionOptions::default()
)?;
Source

pub fn connect_with_connection_string( &self, connection_string: &str, options: ConnectionOptions, ) -> Result<Connection<'_>, Error>

Allocates a connection handle and establishes connections to a driver and a data source.

An alternative to connect. It supports data sources that require more connection information than the three arguments in connect and data sources that are not defined in the system information.

To find out your connection string try: https://www.connectionstrings.com/

§Example
use odbc_api::{ConnectionOptions, Environment};

let env = Environment::new()?;

let connection_string = "
    Driver={ODBC Driver 18 for SQL Server};\
    Server=localhost;\
    UID=SA;\
    PWD=My@Test@Password1;\
";

let mut conn = env.connect_with_connection_string(
    connection_string,
    ConnectionOptions::default()
)?;
Source

pub fn driver_connect( &self, connection_string: &str, completed_connection_string: &mut OutputStringBuffer, driver_completion: DriverCompleteOption, ) -> Result<Connection<'_>, Error>

Allocates a connection handle and establishes connections to a driver and a data source.

An alternative to connect and connect_with_connection_string. This method can be provided with an incomplete or even empty connection string. If any additional information is required, the driver manager/driver will attempt to create a prompt to allow the user to provide the additional information.

If the connection is successful, the complete connection string (including any information provided by the user through a prompt) is returned.

§Parameters
  • connection_string: Connection string.
  • completed_connection_string: Output buffer with the complete connection string. It is recommended to choose a buffer with at least 1024 bytes length. Note: Some driver implementation have poor error handling in case the provided buffer is too small. At the time of this writing:
    • Maria DB crashes with STATUS_TACK_BUFFER_OVERRUN
    • SQLite does not change the output buffer at all and does not indicate truncation.
  • driver_completion: Specifies how and if the driver manager uses a prompt to complete the provided connection string. For arguments other than crate::DriverCompleteOption::NoPrompt this method is going to create a message only parent window for you on windows. On other platform this method is going to panic. In case you want to provide your own parent window please use Self::driver_connect_with_hwnd.
§Examples

In the first example, we intentionally provide a blank connection string so the user will be prompted to select a data source to use. Note that this functionality is only available on windows.

use odbc_api::{Environment, handles::OutputStringBuffer, DriverCompleteOption};

let env = Environment::new()?;

let mut output_buffer = OutputStringBuffer::with_buffer_size(1024);
let connection = env.driver_connect(
    "",
    &mut output_buffer,
    DriverCompleteOption::Prompt,
)?;

// Check that the output buffer has been large enough to hold the entire connection string.
assert!(!output_buffer.is_truncated());

// Now `connection_string` will contain the data source selected by the user.
let connection_string = output_buffer.to_utf8();

In the following examples we specify a DSN that requires login credentials, but the DSN does not provide those credentials. Instead, the user will be prompted for a UID and PWD. The returned connection_string will contain the UID and PWD provided by the user. Note that this functionality is currently only available on windows targets.

let without_uid_or_pwd = "DSN=SomeSharedDatabase;";
let connection = env.driver_connect(
    &without_uid_or_pwd,
    &mut output_buffer,
    DriverCompleteOption::Complete,
)?;
let connection_string = output_buffer.to_utf8();

// Now `connection_string` might be something like
// `DSN=SomeSharedDatabase;UID=SA;PWD=My@Test@Password1;`

In this case, we use a DSN that is already sufficient and does not require a prompt. Because a prompt is not needed, window is also not required. The returned connection_string will be mostly the same as already_sufficient but the driver may append some extra attributes.

let already_sufficient = "DSN=MicrosoftAccessFile;";
let connection = env.driver_connect(
   &already_sufficient,
   &mut output_buffer,
   DriverCompleteOption::NoPrompt,
)?;
let connection_string = output_buffer.to_utf8();

// Now `connection_string` might be something like
// `DSN=MicrosoftAccessFile;DBQ=C:\Db\Example.accdb;DriverId=25;FIL=MS Access;MaxBufferSize=2048;`
Source

pub unsafe fn driver_connect_with_hwnd( &self, connection_string: &str, completed_connection_string: &mut OutputStringBuffer, driver_completion: DriverCompleteOption, parent_window: HWnd, ) -> Result<Connection<'_>, Error>

Allows to call driver connect with a user supplied HWnd. Same as Self::driver_connect, but with the possibility to provide your own parent window handle in case you want to show a prompt to the user.

§Safety

parent_window must be a valid window handle, to a window type supported by the ODBC driver manager. On windows this is a plain window handle, which is of course understood by the windows built in ODBC driver manager. Other working combinations are unknown to the author.

Source

pub fn drivers(&self) -> Result<Vec<DriverInfo>, Error>

Get information about available drivers. Only 32 or 64 Bit drivers will be listed, depending on whether you are building a 32 Bit or 64 Bit application.

§Example
use odbc_api::Environment;

let env = Environment::new ()?;
for driver_info in env.drivers()? {
    println!("{:#?}", driver_info);
}
Source

pub fn data_sources(&self) -> Result<Vec<DataSourceInfo>, Error>

User and system data sources

§Example
use odbc_api::Environment;

let env = Environment::new()?;
for data_source in env.data_sources()? {
    println!("{:#?}", data_source);
}
Source

pub fn system_data_sources(&self) -> Result<Vec<DataSourceInfo>, Error>

Only system data sources

§Example
use odbc_api::Environment;

let env = Environment::new ()?;
for data_source in env.system_data_sources()? {
    println!("{:#?}", data_source);
}
Source

pub fn user_data_sources(&self) -> Result<Vec<DataSourceInfo>, Error>

Only user data sources

§Example
use odbc_api::Environment;

let mut env = unsafe { Environment::new () }?;
for data_source in env.user_data_sources()? {
    println!("{:#?}", data_source);
}

Trait Implementations§

Source§

impl Debug for Environment

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Sync for Environment

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.