pacman82/odbc-api

Is iodbc supported?

Closed this issue · 30 comments

The driver I'm trying to use only works with libiodbc.

I was able to use iodbc with odbc-rs so I don't know if anything special is needed.

Hello @bbigras ,

"support" is a strong word, but it is possible to link odbc-api against iodbc. Actually the abstraction does happen on the level of odbc-sys which provides the raw interface declarations to the driver manager.

You could give the following a spin in your Cargo.toml:

[dependencies]
odbc-api = "0.33.0"
odbc-sys = { version = "0.20.0", features = ["iodbc"] }

I don't have a way to test this, so take it with a grain of salt. You would (to my knowledge) be the first person to use odbc-api with iodbc so I am interessted to hear how it goes. While in theory odbc-api should be able to work fine with any driver manager so far it is only tested with windows and UnixODBC, so I can not promise you what it works.

Switching to unixODBC on Mac is also an option, if your data source does provide drivers which works fine with it. In this stack overflow question, sadly the person did not have that option: https://stackoverflow.com/questions/66474954/using-iodbc-in-rust

Cheers, Markus

Thanks for the quick reply!

I tried odbc-sys = { version = "0.20.0", features = ["iodbc"] } but I got the same message, which is:

Jan 20 13:40:01.330 DEBUG odbc_api::environment: ODBC Environment created.
Jan 20 13:40:01.330  WARN odbc_api::handles::logging: State: HY0, Native error: 50, Message:
Error: ODBC emitted an error calling 'SQLSetEnvAttr':
State: HY0, Native error: 50, Message:

One weird thing is that even without the iodbc feature, my binary was linked to libiodbc.so.2.

I'm using:

    let environment = odbc_api::Environment::new()?;
    let mut conn = environment.connect_with_connection_string(&conn_str)?;

conn_str looks like:
DRIVER=wd230hfo64.so;Server Name=some_ip;Server Port=4900;Database=some_database;UID=user;PWD=password

It's a binary driver, and it only works with iodbc. The so file requires libiodbcinst.so.2.

One weird thing is that even without the iodbc feature, my binary was linked to libiodbc.so.2.

I've been pondering about that, too. Maybe a symbolic link from libodbc.so to libiodbc.so.2 had been created?

As mentioned in the other thread: Seems to me iodbc doesn't support connection pooling (in the driver manager, your application code can of course still cache connections). At very least not with the attributes you are using.

Would you mind trying to just create a connection without using the r2d2-odbc-api crate? This way we could find out if we hit any issues once the pooling is out of the way.

Yes, my latest code snippet didn't use r2d2-odbc-api.

    let environment = odbc_api::Environment::new()?;
    let mut conn = environment.connect_with_connection_string(&conn_str)?;
Jan 20 14:03:49.948 DEBUG odbc_api::environment: ODBC Environment created.
Jan 20 14:03:49.948  WARN odbc_api::handles::logging: State: HY0, Native error: 50, Message:
Error: ODBC emitted an error calling 'SQLSetEnvAttr':
State: HY0, Native error: 50, Message:

Does it already fail then calling Environment::new?

the only call to SQLSetEnvAttr here is setting the ODBC version. Which is the very first thing to do after creating an environment. odbc-api uses a fixed ODBC version of 3.80. What version did you use with odbc-rs?

My guess is by the way that you used the much older ODBC 3.0 standard.

I could imagine introducing a complier flag to allow using an older version.

yeah it fails when calling odbc_api::Environment::new()?.

[pid 738442] access("/home/bbigras/.odbcinst.ini", R_OK) = -1 ENOENT (No such file or directory)
[pid 738442] newfstatat(AT_FDCWD, "/etc/odbcinst.ini", 0x7ffd2194f250, 0) = -1 ENOENT (No such file or directory)
[pid 738442] access("/home/bbigras/.odbc.ini", R_OK) = -1 ENOENT (No such file or directory)
[pid 738442] newfstatat(AT_FDCWD, "/etc/odbc.ini", 0x7ffd2194f250, 0) = -1 ENOENT (No such file or directory)
[pid 738442] access("/home/bbigras/.odbc.ini", R_OK) = -1 ENOENT (No such file or directory)
[pid 738442] newfstatat(AT_FDCWD, "/etc/odbc.ini", 0x7ffd2194f250, 0) = -1 ENOENT (No such file or directory)
Jan 20 14:46:19.378 DEBUG odbc_api::environment: ODBC Environment created.
Jan 20 14:46:19.378  WARN odbc_api::handles::logging: State: HY0, Native error: 50, Message:
strace: Process 738450 attached
Error: ODBC emitted an error calling 'SQLSetEnvAttr':
State: HY0, Native error: strace: Process 738452 attached
50, Message:
[pid 738450] openat(AT_FDCWD, "/sys/devices/system/cpu/online", O_RDONLY|O_CLOEXEC <unfinished ...>
[pid 738451] +++ exited with 1 +++
[pid 738452] +++ exited with 1 +++
[pid 738448] +++ exited with 1 +++
[pid 738444] +++ exited with 1 +++
[pid 738445] +++ exited with 1 +++
[pid 738450] <... openat resumed>)      = -1 (errno 18446744073709551385)
[pid 738447] +++ exited with 1 +++
[pid 738450] +++ exited with 1 +++
[pid 738449] +++ exited with 1 +++
[pid 738446] +++ exited with 1 +++
[pid 738443] +++ exited with 1 +++
+++ exited with 1 +++

with my working code:

❯ cargo tree | rg odbc
├── odbc v0.16.1
│   ├── odbc-safe v0.5.0
│   │   └── odbc-sys v0.8.2
│   └── odbc-sys v0.8.2
├── r2d2_odbc v0.2.0 (https://github.com/bbigras/r2d2-odbc.git?branch=latin1#eb9b6b37)
│   ├── odbc v0.16.1 (*)

Another difference between the odbc and odbc-api crate is that the odbc crate allowed to specify the used ODBC API version. This is what the version type parameter was all about. The idea was to only implement the features for the ODBC versions which allow for them.

With odbc-api I figured that it is probably complicated enough to write an application against one version of ODBC and decide the odbc version used for the user. I chose version 3.8 which had been around for a several years already. Making my life and that of my users a bit easier.

This link (http://www.iodbc.org/dataspace/doc/iodbc/wiki/iodbcWiki/ODBCOnUnix#Known%20working%20ODBC%20applications) would indicate that iodbc is at least aware of ODBC version 3.5. It also seems that iodbc is somewhat better supported than I initially thought. Might giving updating your driver manager a try?

If that doesn't work, the next thing to test would be build an odbc-api using ODBC 3.5 instead of ODBC 3.8.

Oh, what may have not become clear from the above explaination. To see which ODBC API version you used in the past you have to look into your source code. Around the place there your environment is created and the version is declared to the application.

Do you mind me asking what your datasource is? If you are using iodbc on linux I may be able to test this after all.

It's HFSQL. The database for windev applications. Probably a pita for anyone to download and test, if it's even possible to download without paying.

so far I found:
pub struct ODBCEnv(Environment<Version3>);

pub type Version3 = safe::Odbc3;

https://docs.rs/odbc-sys/latest/odbc_sys/enum.AttrOdbcVersion.html#variant.Odbc3

Might giving updating your driver manager a try?

You mean my wd230hfo64.so file? If so, I think I got the latest version for the (outdated) server we have.

pub type Version3 = safe::Odbc3;

What's the information I've been looking for. Would you mind testig if it works with pub type Version = Odbc3m8? Probably need to exchange a declare_version with declare_version_3_8 somethere. https://docs.rs/odbc-safe/latest/odbc_safe/struct.Environment.html#method.declare_version_3_8

You mean my wd230hfo64.so file? If so, I think I got the latest version for the (outdated) server we have.

No, that would be your driver. Your driver manager is iodbc. So "mind updating iodbc?", would have been a better way to phrase my request.

What's the information I've been looking for. Would you mind testig if it works with pub type Version = Odbc3m8? Probably need to exchange a declare_version with declare_version_3_8 somethere. https://docs.rs/odbc-safe/latest/odbc_safe/struct.Environment.html#method.declare_version_3_8

I'll try.

btw, with:

diff --git a/odbc-api/src/environment.rs b/odbc-api/src/environment.rs
index 455740f..9b70c37 100644
--- a/odbc-api/src/environment.rs
+++ b/odbc-api/src/environment.rs
@@ -141,7 +141,7 @@ impl Environment {
         debug!("ODBC Environment created.");

         let result = environment
-            .declare_version(AttrOdbcVersion::Odbc3_80)
+            .declare_version(AttrOdbcVersion::Odbc3)
             .into_result(&environment);

         // Translate invalid attribute into a more meaningful error, provided the additional

I get:

Jan 20 15:31:06.267 DEBUG odbc_api::environment: ODBC Environment created.                                                                                                                                 [37/11020]
Jan 20 15:31:06.267  WARN odbc_api::handles::logging: State: IM0, Native error: 48, Message:
thread 'main' panicked at 'rec_number argument of diagnostics must be > 0.', /home/bbigras/src/odbc-api/odbc-api/src/handles/diagnostics.rs:139:29
stack backtrace:
   0: rust_begin_unwind
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/std/src/panicking.rs:577:5
   1: core::panicking::panic_fmt
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/core/src/panicking.rs:110:14
   2: odbc_api::handles::diagnostics::diagnostics
             at /home/bbigras/src/odbc-api/odbc-api/src/handles/diagnostics.rs:139:29
   3: odbc_api::handles::diagnostics::Record::fill_from
             at /home/bbigras/src/odbc-api/odbc-api/src/handles/diagnostics.rs:166:15
   4: odbc_api::handles::logging::log_diagnostics
             at /home/bbigras/src/odbc-api/odbc-api/src/handles/logging.rs:11:11
   5: odbc_api::error::<impl odbc_api::handles::sql_result::SqlResult<T>>::into_result
             at /home/bbigras/src/odbc-api/odbc-api/src/error.rs:83:21
   6: odbc_api::environment::Environment::connect_with_connection_string_utf16
             at /home/bbigras/src/odbc-api/odbc-api/src/environment.rs:276:9
   7: odbc_api::environment::Environment::connect_with_connection_string
             at /home/bbigras/src/odbc-api/odbc-api/src/environment.rs:261:9
   8: graphql_cienapps::main::main::{{closure}}
             at ./src/main.rs:54:20
   9: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/core/src/future/mod.rs:84:19
  10: graphql_cienapps::main::{{closure}}
             at ./src/main.rs:23:1
  11: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/core/src/future/mod.rs:84:19
  12: <async_std::task::builder::SupportTaskLocals<F> as core::future::future::Future>::poll::{{closure}}
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.10.0/src/task/builder.rs:199:17
  13: async_std::task::task_locals_wrapper::TaskLocalsWrapper::set_current::{{closure}}
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.10.0/src/task/task_locals_wrapper.rs:60:13
  14: std::thread::local::LocalKey<T>::try_with
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/std/src/thread/local.rs:413:16
  15: std::thread::local::LocalKey<T>::with
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/std/src/thread/local.rs:389:9
  16: async_std::task::task_locals_wrapper::TaskLocalsWrapper::set_current
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.10.0/src/task/task_locals_wrapper.rs:55:9
  17: <async_std::task::builder::SupportTaskLocals<F> as core::future::future::Future>::poll
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.10.0/src/task/builder.rs:197:13
  18: <futures_lite::future::Or<F1,F2> as core::future::future::Future>::poll
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-lite-1.12.0/src/future.rs:526:33
  19: async_executor::Executor::run::{{closure}}
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-executor-1.4.1/src/lib.rs:242:31
  20: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/core/src/future/mod.rs:84:19
  21: async_executor::LocalExecutor::run::{{closure}}
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-executor-1.4.1/src/lib.rs:447:33
  22: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
             at /rustc/9ad5d82f822b3cb67637f11be2e65c5662b66ec0/library/core/src/future/mod.rs:84:19
  23: async_io::driver::block_on
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-io-1.6.0/src/driver.rs:142:33
  24: async_global_executor::reactor::block_on::{{closure}}
             at /home/bbigras/.cargo/registry/src/github.com-1ecc6299db9ec823/async-global-executor-2.0.2/src/reactor.rs:3:18

Ok, so at least your version of iodbc doesn't seem to be able to handle ODBC 3.8. If you could try updating iodbc to a newer version and see if they added support that would be great. If they didn't I'll try releasing a version of odbc-api which works with ODBC 3.5 or ODBC 3. I could probably start with that earliest thursday next week, due to other obligations. Some effort will probably go into setting up a test environment with iodbc and some iodbc datasource on CI.

sadly, I'm already using libiodbc-3.52.15 (the latest).

Okay, if you are impatient you can checkout odbc-api include it with a path statement in the cargo toml and change the odbcversion locally. See how it goes.

with this patch to odbc-rs, I get:

diff --git a/src/environment/list_data_sources.rs b/src/environment/list_data_sources.rs
index d600444..9a022ff 100644
--- a/src/environment/list_data_sources.rs
+++ b/src/environment/list_data_sources.rs
@@ -25,7 +25,7 @@ pub struct DriverInfo {
     pub attributes: HashMap<String, String>,
 }

-type SqlInfoMethod = fn(&mut safe::Environment<safe::Odbc3>,
+type SqlInfoMethod = fn(&mut safe::Environment<safe::Odbc3m8>,
                         ffi::FetchOrientation,
                         &mut [u8],
                         &mut [u8])
diff --git a/src/environment/mod.rs b/src/environment/mod.rs
index 03a2ee3..1fae324 100644
--- a/src/environment/mod.rs
+++ b/src/environment/mod.rs
@@ -5,7 +5,7 @@ use super::{ffi, into_result, safe, try_into_option, DiagnosticRecord, GetDiagRe
 use std;

 /// Environment state used to represent that environment has been set to odbc version 3
-pub type Version3 = safe::Odbc3;
+pub type Version3 = safe::Odbc3m8;

 pub static mut OS_ENCODING: &encoding_rs::Encoding = encoding_rs::UTF_8;
 pub static mut DB_ENCODING: &encoding_rs::Encoding = encoding_rs::UTF_8;
Jan 20 15:53:20.559 ERROR odbc::result: State: HY010, Native error: 0, Message: [iODBC][Driver Manager]Function sequence error
OLOLO State: HY010, Native error: 0, Message: [iODBC][Driver Manager]Function sequence error

if you are impatient you can checkout odbc-api include it with a path statement in the cargo toml and change the odbcversion locally

Isn't it what I did in #148 (comment) ?

Isn't it what I did in #148 (comment) ?

It is my bad. The call stack panics, because the connection fails, but neither iodbc nor the driver does provide a diagnostic record with an error message. I suspect what narrow connection methods would work better.

So, roadmap with regards to this issue is likely to be:

  • Switch to narrow methods on non-windows platform by default
  • introduce feature flag to allow going against older odbc versions

I'd be helpful to know some free and open source iodbc data sources which could be used for testing against iodbc.

Cheers, Markus

Hello @bbigras,

odbc-api 0.34.0 has just been released. iodbc is not officially supported yet, but the first, and possible biggest step has been taken. If compiled with the narrow feature activated, narrow function calls are used. So if referenced in the Cargo.toml as

odbc-api = { version="0.34.0", features=["narrow"] }

You should be able to create a connection. The compiler flag for declaring the ODBC version has not been included yet. So you would still need to edit that locally. Would you kindly repeat your experiment with the narrow feature activated and tell me how it goes?

Cheers, Markus

Hello @bbigras ,

odbc-api 0.34.1 has just been released. It might be the first version which might work for you, without tinkering beyond setting some feature flags.

[dependencies]
odbc-api = { version = "0.34.1", features = ["odbc_version_3_5", "narrow"]  }

I could at least create an iODBC environment with these settings. However I have little idea how I would set up a datasource, for testing with iodbc. Currently the ODBC version 3.5 feature flag is tested, with UnixODBC. So please tell me how it goe for you. I am fairly confident you should at least be able to create a connection, if that has worked before for you with the odbc crate.

Cheers, Markus

Oh, sorry I forgot to test the last version.

with 0.34.0 I got:

Feb 13 13:52:30.248 DEBUG odbc_api::environment: ODBC Environment created.
Feb 13 13:52:30.248  WARN odbc_api::handles::logging: State: HY024, Native error: 0, Message:
Error: ODBC emitted an error calling 'SQLSetEnvAttr':
State: HY024, Native error: 0, Message:

But with 0.34.1, I'm able to fetch data from a table!! 🎉

with 0.34.1, I'm able to fetch data from a table!! 🎉

Happy to hear that!

The one thing keeping me from implementing a simplified iodbc feature flag, is my inability to set up a working iodbc datasource in CI. If you have any idea of a freely available compatible data source tell me. Otherwise, I'll close this issue.

Cheers, Markus

Won't have time to investigate and find a freely available, easy to set up ODBC compatible with iodbc. Closing this issue. iodbc works, but tests remain exclusively against windows and unixODBC. Not adding "official" iodbc suport for now, but happy to help out users which encounter trouble.