odbc_api/
environment.rs

1use std::{
2    cmp::max,
3    collections::HashMap,
4    ptr::null_mut,
5    sync::{Mutex, OnceLock},
6};
7
8use crate::{
9    Connection, DriverCompleteOption, Error,
10    connection::ConnectionOptions,
11    error::ExtendResult,
12    handles::{
13        self, OutputStringBuffer, SqlChar, SqlResult, SqlText, State, SzBuffer, log_diagnostics,
14        slice_to_utf8,
15    },
16};
17use log::debug;
18use odbc_sys::{AttrCpMatch, AttrOdbcVersion, FetchOrientation, HWnd};
19
20#[cfg(all(target_os = "windows", feature = "prompt"))]
21// Currently only windows driver manager supports prompt.
22use winit::{
23    application::ApplicationHandler,
24    event::WindowEvent,
25    event_loop::{ActiveEventLoop, EventLoop},
26    platform::run_on_demand::EventLoopExtRunOnDemand,
27    window::{Window, WindowId},
28};
29
30#[cfg(not(feature = "odbc_version_3_5"))]
31const ODBC_API_VERSION: AttrOdbcVersion = AttrOdbcVersion::Odbc3_80;
32
33#[cfg(feature = "odbc_version_3_5")]
34const ODBC_API_VERSION: AttrOdbcVersion = AttrOdbcVersion::Odbc3;
35
36/// An ODBC 3.8 environment.
37///
38/// Associated with an `Environment` is any information that is global in nature, such as:
39///
40/// * The `Environment`'s state
41/// * The current environment-level diagnostics
42/// * The handles of connections currently allocated on the environment
43/// * The current stetting of each environment attribute
44///
45/// Creating the environment is the first applications do, then interacting with an ODBC driver
46/// manager. There must only be one environment in the entire process.
47#[derive(Debug)]
48pub struct Environment {
49    environment: handles::Environment,
50    /// ODBC environments use interior mutability to maintain iterator state then iterating over
51    /// driver and / or data source information. The environment is otherwise protected by interior
52    /// synchronization mechanism, yet in order to be able to access to iterate over information
53    /// using a shared reference we need to protect the interior iteration state with a mutex of its
54    /// own.
55    /// The environment is also mutable with regards to Errors, which are accessed over the handle.
56    /// If multiple fallible operations are executed in parallel, we need the mutex to ensure the
57    /// errors are fetched by the correct thread.
58    internal_state: Mutex<()>,
59}
60
61unsafe impl Sync for Environment {}
62
63impl Environment {
64    /// Enable or disable (default) connection pooling for ODBC connections. Call this function
65    /// before creating the ODBC environment for which you want to enable connection pooling.
66    ///
67    /// ODBC specifies an interface to enable the driver manager to enable connection pooling for
68    /// your application. It is off by default, but if you use ODBC to connect to your data source
69    /// instead of implementing it in your application, or importing a library you may simply enable
70    /// it in ODBC instead.
71    /// Connection Pooling is governed by two attributes. The most important one is the connection
72    /// pooling scheme which is `Off` by default. It must be set even before you create your ODBC
73    /// environment. It is global mutable state on the process level. Setting it in Rust is therefore
74    /// unsafe.
75    ///
76    /// The other one is changed via [`Self::set_connection_pooling_matching`]. It governs how a
77    /// connection is choosen from the pool. It defaults to strict which means the `Connection` you
78    /// get from the pool will have exactly the attributes specified in the connection string.
79    ///
80    /// See:
81    /// <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/driver-manager-connection-pooling>
82    ///
83    /// # Example
84    ///
85    /// ```no_run
86    /// use odbc_api::{Environment, sys::{AttrConnectionPooling, AttrCpMatch}};
87    ///
88    /// /// Create an environment with connection pooling enabled.
89    /// let env = unsafe {
90    ///     Environment::set_connection_pooling(AttrConnectionPooling::DriverAware).unwrap();
91    ///     let mut env = Environment::new().unwrap();
92    ///     // Strict is the default, and is set here to be explicit about it.
93    ///     env.set_connection_pooling_matching(AttrCpMatch::Strict).unwrap();
94    ///     env
95    /// };
96    /// ```
97    ///
98    /// # Safety
99    ///
100    /// > An ODBC driver must be fully thread-safe, and connections must not have thread affinity to
101    /// > support connection pooling. This means the driver is able to handle a call on any thread
102    /// > at any time and is able to connect on one thread, to use the connection on another thread,
103    /// > and to disconnect on a third thread.
104    ///
105    /// Also note that this is changes global mutable state for the entire process. As such it is
106    /// vulnerable to race conditions if called from more than one place in your application. It is
107    /// recommened to call this in the beginning, before creating any connection.
108    pub unsafe fn set_connection_pooling(
109        scheme: odbc_sys::AttrConnectionPooling,
110    ) -> Result<(), Error> {
111        match unsafe { handles::Environment::set_connection_pooling(scheme) } {
112            SqlResult::Error { .. } => Err(Error::FailedSettingConnectionPooling),
113            SqlResult::Success(()) | SqlResult::SuccessWithInfo(()) => Ok(()),
114            other => {
115                panic!("Unexpected return value `{other:?}`.")
116            }
117        }
118    }
119
120    /// Determines how a connection is chosen from a connection pool. When [`Self::connect`],
121    /// [`Self::connect_with_connection_string`] or [`Self::driver_connect`] is called, the Driver
122    /// Manager determines which connection is reused from the pool. The Driver Manager tries to
123    /// match the connection options in the call and the connection attributes set by the
124    /// application to the keywords and connection attributes of the connections in the pool. The
125    /// value of this attribute determines the level of precision of the matching criteria.
126    ///
127    /// The following values are used to set the value of this attribute:
128    ///
129    /// * [`crate::sys::AttrCpMatch::Strict`] = Only connections that exactly match the connection
130    ///   options in the call and the connection attributes set by the application are reused. This
131    ///   is the default.
132    /// * [`crate::sys::AttrCpMatch::Relaxed`] = Connections with matching connection string \
133    ///   keywords can be used. Keywords must match, but not all connection attributes must match.
134    pub fn set_connection_pooling_matching(&mut self, matching: AttrCpMatch) -> Result<(), Error> {
135        self.environment
136            .set_connection_pooling_matching(matching)
137            .into_result(&self.environment)
138    }
139
140    /// Entry point into this API. Allocates a new ODBC Environment and declares to the driver
141    /// manager that the Application wants to use ODBC version 3.8.
142    ///
143    /// # Safety
144    ///
145    /// There may only be one ODBC environment in any process at any time. Take care using this
146    /// function in unit tests, as these run in parallel by default in Rust. Also no library should
147    /// probably wrap the creation of an odbc environment into a safe function call. This is because
148    /// using two of these "safe" libraries at the same time in different parts of your program may
149    /// lead to race condition thus violating Rust's safety guarantees.
150    ///
151    /// Creating one environment in your binary is safe however.
152    pub fn new() -> Result<Self, Error> {
153        let result = handles::Environment::new();
154
155        let environment = match result {
156            SqlResult::Success(env) => env,
157            SqlResult::SuccessWithInfo(env) => {
158                log_diagnostics(&env);
159                env
160            }
161            SqlResult::Error { .. } => return Err(Error::FailedAllocatingEnvironment),
162            other => panic!("Unexpected return value '{other:?}'"),
163        };
164
165        debug!("ODBC Environment created.");
166
167        debug!("Setting ODBC API version to {ODBC_API_VERSION:?}");
168        let result = environment
169            .declare_version(ODBC_API_VERSION)
170            .into_result(&environment);
171
172        // Status code S1009 has been seen with unixODBC 2.3.1. S1009 meant (among other things)
173        // invalid attribute. If we see this then we try to declare the ODBC version it is of course
174        // likely that the driver manager only knows ODBC 2.x.
175        // See: <https://learn.microsoft.com/sql/odbc/reference/develop-app/sqlstate-mappings>
176        const ODBC_2_INVALID_ATTRIBUTE: State = State(*b"S1009");
177
178        // Translate invalid attribute into a more meaningful error, provided the additional
179        // context that we know we tried to set version number.
180        result.provide_context_for_diagnostic(|record, function| match record.state {
181            // INVALID_STATE_TRANSACTION has been seen with some really old version of unixODBC on
182            // a CentOS used to build manylinux wheels, with the preinstalled ODBC version.
183            // INVALID_ATTRIBUTE_VALUE is the correct status code to emit for a driver manager if it
184            // does not know the version and has been seen with an unknown version of unixODBC on an
185            // Oracle Linux.
186            ODBC_2_INVALID_ATTRIBUTE
187            | State::INVALID_STATE_TRANSACTION
188            | State::INVALID_ATTRIBUTE_VALUE => Error::UnsupportedOdbcApiVersion(record),
189            _ => Error::Diagnostics { record, function },
190        })?;
191
192        Ok(Self {
193            environment,
194            internal_state: Mutex::new(()),
195        })
196    }
197
198    /// Allocates a connection handle and establishes connections to a driver and a data source.
199    ///
200    /// * See [Connecting with SQLConnect][1]
201    /// * See [SQLConnectFunction][2]
202    ///
203    /// # Arguments
204    ///
205    /// * `data_source_name` - Data source name. The data might be located on the same computer as
206    ///   the program, or on another computer somewhere on a network.
207    /// * `user` - User identifier.
208    /// * `pwd` - Authentication string (typically the password).
209    ///
210    /// # Example
211    ///
212    /// ```no_run
213    /// use odbc_api::{Environment, ConnectionOptions};
214    ///
215    /// let env = Environment::new()?;
216    ///
217    /// let mut conn = env.connect(
218    ///     "YourDatabase", "SA", "My@Test@Password1",
219    ///     ConnectionOptions::default()
220    /// )?;
221    /// # Ok::<(), odbc_api::Error>(())
222    /// ```
223    ///
224    /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function
225    /// [2]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function
226    pub fn connect(
227        &self,
228        data_source_name: &str,
229        user: &str,
230        pwd: &str,
231        options: ConnectionOptions,
232    ) -> Result<Connection<'_>, Error> {
233        let data_source_name = SqlText::new(data_source_name);
234        let user = SqlText::new(user);
235        let pwd = SqlText::new(pwd);
236
237        let mut connection = self.allocate_connection()?;
238
239        options.apply(&connection)?;
240
241        connection
242            .connect(&data_source_name, &user, &pwd)
243            .into_result(&connection)?;
244        Ok(Connection::new(connection))
245    }
246
247    /// Allocates a connection handle and establishes connections to a driver and a data source.
248    ///
249    /// An alternative to `connect`. It supports data sources that require more connection
250    /// information than the three arguments in `connect` and data sources that are not defined in
251    /// the system information.
252    ///
253    /// To find out your connection string try: <https://www.connectionstrings.com/>
254    ///
255    /// # Example
256    ///
257    /// ```no_run
258    /// use odbc_api::{ConnectionOptions, Environment};
259    ///
260    /// let env = Environment::new()?;
261    ///
262    /// let connection_string = "
263    ///     Driver={ODBC Driver 18 for SQL Server};\
264    ///     Server=localhost;\
265    ///     UID=SA;\
266    ///     PWD=My@Test@Password1;\
267    /// ";
268    ///
269    /// let mut conn = env.connect_with_connection_string(
270    ///     connection_string,
271    ///     ConnectionOptions::default()
272    /// )?;
273    /// # Ok::<(), odbc_api::Error>(())
274    /// ```
275    pub fn connect_with_connection_string(
276        &self,
277        connection_string: &str,
278        options: ConnectionOptions,
279    ) -> Result<Connection<'_>, Error> {
280        let connection_string = SqlText::new(connection_string);
281        let mut connection = self.allocate_connection()?;
282
283        options.apply(&connection)?;
284
285        connection
286            .connect_with_connection_string(&connection_string)
287            .into_result(&connection)?;
288        Ok(Connection::new(connection))
289    }
290
291    /// Allocates a connection handle and establishes connections to a driver and a data source.
292    ///
293    /// An alternative to `connect` and `connect_with_connection_string`. This method can be
294    /// provided with an incomplete or even empty connection string. If any additional information
295    /// is required, the driver manager/driver will attempt to create a prompt to allow the user to
296    /// provide the additional information.
297    ///
298    /// If the connection is successful, the complete connection string (including any information
299    /// provided by the user through a prompt) is returned.
300    ///
301    /// # Parameters
302    ///
303    /// * `connection_string`: Connection string.
304    /// * `completed_connection_string`: Output buffer with the complete connection string. It is
305    ///   recommended to choose a buffer with at least `1024` bytes length. **Note**: Some driver
306    ///   implementation have poor error handling in case the provided buffer is too small. At the
307    ///   time of this writing:
308    ///   * Maria DB crashes with STATUS_TACK_BUFFER_OVERRUN
309    ///   * SQLite does not change the output buffer at all and does not indicate truncation.
310    /// * `driver_completion`: Specifies how and if the driver manager uses a prompt to complete
311    ///   the provided connection string. For arguments other than
312    ///   [`crate::DriverCompleteOption::NoPrompt`] this method is going to create a message only
313    ///   parent window for you on windows. On other platform this method is going to panic. In case
314    ///   you want to provide your own parent window please use [`Self::driver_connect_with_hwnd`].
315    ///
316    /// # Examples
317    ///
318    /// In the first example, we intentionally provide a blank connection string so the user will be
319    /// prompted to select a data source to use. Note that this functionality is only available on
320    /// windows.
321    ///
322    /// ```no_run
323    /// use odbc_api::{Environment, handles::OutputStringBuffer, DriverCompleteOption};
324    ///
325    /// let env = Environment::new()?;
326    ///
327    /// let mut output_buffer = OutputStringBuffer::with_buffer_size(1024);
328    /// let connection = env.driver_connect(
329    ///     "",
330    ///     &mut output_buffer,
331    ///     #[cfg(target_os = "windows")]
332    ///     DriverCompleteOption::Prompt,
333    ///     #[cfg(not(target_os = "windows"))]
334    ///     DriverCompleteOption::NoPrompt,
335    /// )?;
336    ///
337    /// // Check that the output buffer has been large enough to hold the entire connection string.
338    /// assert!(!output_buffer.is_truncated());
339    ///
340    /// // Now `connection_string` will contain the data source selected by the user.
341    /// let connection_string = output_buffer.to_utf8();
342    /// # Ok::<_,odbc_api::Error>(())
343    /// ```
344    ///
345    /// In the following examples we specify a DSN that requires login credentials, but the DSN does
346    /// not provide those credentials. Instead, the user will be prompted for a UID and PWD. The
347    /// returned `connection_string` will contain the `UID` and `PWD` provided by the user. Note
348    /// that this functionality is currently only available on windows targets.
349    ///
350    /// ```
351    /// # use odbc_api::DriverCompleteOption;
352    /// # #[cfg(target_os = "windows")]
353    /// # fn f(
354    /// #    mut output_buffer: odbc_api::handles::OutputStringBuffer,
355    /// #    env: odbc_api::Environment,
356    /// # ) -> Result<(), odbc_api::Error> {
357    /// let without_uid_or_pwd = "DSN=SomeSharedDatabase;";
358    /// let connection = env.driver_connect(
359    ///     &without_uid_or_pwd,
360    ///     &mut output_buffer,
361    ///     DriverCompleteOption::Complete,
362    /// )?;
363    /// let connection_string = output_buffer.to_utf8();
364    ///
365    /// // Now `connection_string` might be something like
366    /// // `DSN=SomeSharedDatabase;UID=SA;PWD=My@Test@Password1;`
367    /// # Ok(()) }
368    /// ```
369    ///
370    /// In this case, we use a DSN that is already sufficient and does not require a prompt. Because
371    /// a prompt is not needed, `window` is also not required. The returned `connection_string` will
372    /// be mostly the same as `already_sufficient` but the driver may append some extra attributes.
373    ///
374    /// ```
375    /// # use odbc_api::DriverCompleteOption;
376    /// # fn f(
377    /// #    mut output_buffer: odbc_api::handles::OutputStringBuffer,
378    /// #    env: odbc_api::Environment,
379    /// # ) -> Result<(), odbc_api::Error> {
380    /// let already_sufficient = "DSN=MicrosoftAccessFile;";
381    /// let connection = env.driver_connect(
382    ///    &already_sufficient,
383    ///    &mut output_buffer,
384    ///    DriverCompleteOption::NoPrompt,
385    /// )?;
386    /// let connection_string = output_buffer.to_utf8();
387    ///
388    /// // Now `connection_string` might be something like
389    /// // `DSN=MicrosoftAccessFile;DBQ=C:\Db\Example.accdb;DriverId=25;FIL=MS Access;MaxBufferSize=2048;`
390    /// # Ok(()) }
391    /// ```
392    pub fn driver_connect(
393        &self,
394        connection_string: &str,
395        completed_connection_string: &mut OutputStringBuffer,
396        driver_completion: DriverCompleteOption,
397    ) -> Result<Connection<'_>, Error> {
398        let mut driver_connect = |hwnd: HWnd| unsafe {
399            self.driver_connect_with_hwnd(
400                connection_string,
401                completed_connection_string,
402                driver_completion,
403                hwnd,
404            )
405        };
406
407        match driver_completion {
408            DriverCompleteOption::NoPrompt => (),
409            #[cfg(all(target_os = "windows", feature = "prompt"))]
410            _ => {
411                // We need a parent window, let's provide a message only window.
412                let mut window_app = MessageOnlyWindowEventHandler {
413                    run_prompt_dialog: Some(driver_connect),
414                    result: None,
415                };
416                let mut event_loop = EventLoop::new().unwrap();
417                event_loop.run_app_on_demand(&mut window_app).unwrap();
418                return window_app.result.unwrap();
419            }
420        };
421        let hwnd = null_mut();
422        driver_connect(hwnd)
423    }
424
425    /// Allows to call driver connect with a user supplied HWnd. Same as [`Self::driver_connect`],
426    /// but with the possibility to provide your own parent window handle in case you want to show
427    /// a prompt to the user.
428    ///
429    /// # Safety
430    ///
431    /// `parent_window` must be a valid window handle, to a window type supported by the ODBC driver
432    /// manager. On windows this is a plain window handle, which is of course understood by the
433    /// windows built in ODBC driver manager. Other working combinations are unknown to the author.
434    pub unsafe fn driver_connect_with_hwnd(
435        &self,
436        connection_string: &str,
437        completed_connection_string: &mut OutputStringBuffer,
438        driver_completion: DriverCompleteOption,
439        parent_window: HWnd,
440    ) -> Result<Connection<'_>, Error> {
441        let mut connection = self.allocate_connection()?;
442        let connection_string = SqlText::new(connection_string);
443
444        let connection_string_is_complete = unsafe {
445            connection.driver_connect(
446                &connection_string,
447                parent_window,
448                completed_connection_string,
449                driver_completion.as_sys(),
450            )
451        }
452        .into_result_bool(&connection)?;
453        if !connection_string_is_complete {
454            return Err(Error::AbortedConnectionStringCompletion);
455        }
456        Ok(Connection::new(connection))
457    }
458
459    /// Get information about available drivers. Only 32 or 64 Bit drivers will be listed, depending
460    /// on whether you are building a 32 Bit or 64 Bit application.
461    ///
462    /// # Example
463    ///
464    /// ```no_run
465    /// use odbc_api::Environment;
466    ///
467    /// let env = Environment::new ()?;
468    /// for driver_info in env.drivers()? {
469    ///     println!("{:#?}", driver_info);
470    /// }
471    ///
472    /// # Ok::<_, odbc_api::Error>(())
473    /// ```
474    pub fn drivers(&self) -> Result<Vec<DriverInfo>, Error> {
475        let mut driver_info = Vec::new();
476
477        // Since we have exclusive ownership of the environment handle and we take the lock, we can
478        // guarantee that this method is currently the only one changing the state of the internal
479        // iterators of the environment.
480        let _lock = self.internal_state.lock().unwrap();
481        unsafe {
482            // Find required buffer size to avoid truncation.
483            let (mut desc_len, mut attr_len) = if let Some(res) = self
484                .environment
485                // Start with first so we are independent of state
486                .drivers_buffer_len(FetchOrientation::First)
487                .into_result_option(&self.environment)?
488            {
489                res
490            } else {
491                // No drivers present
492                return Ok(Vec::new());
493            };
494
495            // If there are, let's loop over the remaining drivers
496            while let Some((candidate_desc_len, candidate_attr_len)) = self
497                .environment
498                .drivers_buffer_len(FetchOrientation::Next)
499                .into_result_option(&self.environment)?
500            {
501                desc_len = max(candidate_desc_len, desc_len);
502                attr_len = max(candidate_attr_len, attr_len);
503            }
504
505            // Allocate +1 character extra for terminating zero
506            let mut desc_buf = SzBuffer::with_capacity(desc_len as usize);
507            // Do **not** use nul terminated buffer, as nul is used to delimit key value pairs of
508            // attributes.
509            let mut attr_buf: Vec<SqlChar> = vec![0; attr_len as usize];
510
511            while self
512                .environment
513                .drivers_buffer_fill(FetchOrientation::Next, desc_buf.mut_buf(), &mut attr_buf)
514                .into_result_bool(&self.environment)?
515            {
516                let description = desc_buf.to_utf8();
517                let attributes =
518                    slice_to_utf8(&attr_buf).expect("Attributes must be interpretable as UTF-8");
519
520                let attributes = attributes_iter(&attributes).collect();
521
522                driver_info.push(DriverInfo {
523                    description,
524                    attributes,
525                });
526            }
527        }
528
529        Ok(driver_info)
530    }
531
532    /// User and system data sources
533    ///
534    /// # Example
535    ///
536    /// ```no_run
537    /// use odbc_api::Environment;
538    ///
539    /// let env = Environment::new()?;
540    /// for data_source in env.data_sources()? {
541    ///     println!("{:#?}", data_source);
542    /// }
543    ///
544    /// # Ok::<_, odbc_api::Error>(())
545    /// ```
546    pub fn data_sources(&self) -> Result<Vec<DataSourceInfo>, Error> {
547        self.data_sources_impl(FetchOrientation::First)
548    }
549
550    /// Only system data sources
551    ///
552    /// # Example
553    ///
554    /// ```no_run
555    /// use odbc_api::Environment;
556    ///
557    /// let env = Environment::new ()?;
558    /// for data_source in env.system_data_sources()? {
559    ///     println!("{:#?}", data_source);
560    /// }
561    ///
562    /// # Ok::<_, odbc_api::Error>(())
563    /// ```
564    pub fn system_data_sources(&self) -> Result<Vec<DataSourceInfo>, Error> {
565        self.data_sources_impl(FetchOrientation::FirstSystem)
566    }
567
568    /// Only user data sources
569    ///
570    /// # Example
571    ///
572    /// ```no_run
573    /// use odbc_api::Environment;
574    ///
575    /// let mut env = unsafe { Environment::new () }?;
576    /// for data_source in env.user_data_sources()? {
577    ///     println!("{:#?}", data_source);
578    /// }
579    ///
580    /// # Ok::<_, odbc_api::Error>(())
581    /// ```
582    pub fn user_data_sources(&self) -> Result<Vec<DataSourceInfo>, Error> {
583        self.data_sources_impl(FetchOrientation::FirstUser)
584    }
585
586    fn data_sources_impl(&self, direction: FetchOrientation) -> Result<Vec<DataSourceInfo>, Error> {
587        let mut data_source_info = Vec::new();
588
589        // Since we have exclusive ownership of the environment handle and we take the lock, we can
590        // guarantee that this method is currently the only one changing the state of the internal
591        // iterators of the environment.
592        let _lock = self.internal_state.lock().unwrap();
593        unsafe {
594            // Find required buffer size to avoid truncation.
595            let (mut server_name_len, mut driver_len) = if let Some(res) = self
596                .environment
597                .data_source_buffer_len(direction)
598                .into_result_option(&self.environment)?
599            {
600                res
601            } else {
602                // No drivers present
603                return Ok(Vec::new());
604            };
605
606            // If there are let's loop over the rest
607            while let Some((candidate_name_len, candidate_decs_len)) = self
608                .environment
609                .drivers_buffer_len(FetchOrientation::Next)
610                .into_result_option(&self.environment)?
611            {
612                server_name_len = max(candidate_name_len, server_name_len);
613                driver_len = max(candidate_decs_len, driver_len);
614            }
615
616            let mut server_name_buf = SzBuffer::with_capacity(server_name_len as usize);
617            let mut driver_buf = SzBuffer::with_capacity(driver_len as usize);
618
619            let mut not_empty = self
620                .environment
621                .data_source_buffer_fill(direction, server_name_buf.mut_buf(), driver_buf.mut_buf())
622                .into_result_bool(&self.environment)?;
623
624            while not_empty {
625                let server_name = server_name_buf.to_utf8();
626                let driver = driver_buf.to_utf8();
627
628                data_source_info.push(DataSourceInfo {
629                    server_name,
630                    driver,
631                });
632                not_empty = self
633                    .environment
634                    .data_source_buffer_fill(
635                        FetchOrientation::Next,
636                        server_name_buf.mut_buf(),
637                        driver_buf.mut_buf(),
638                    )
639                    .into_result_bool(&self.environment)?;
640            }
641        }
642
643        Ok(data_source_info)
644    }
645
646    fn allocate_connection(&self) -> Result<handles::Connection, Error> {
647        // Hold lock diagnostics errors are consumed in this thread.
648        let _lock = self.internal_state.lock().unwrap();
649        self.environment
650            .allocate_connection()
651            .into_result(&self.environment)
652    }
653}
654
655/// An ODBC [`Environment`] with static lifetime. This function always returns a reference to the
656/// same instance. The environment is constructed then the function is called for the first time.
657/// Every time after the initial construction this function must succeed.
658///
659/// Useful if your application uses ODBC for the entirety of its lifetime, since using a static
660/// lifetime means there is one less lifetime you and the borrow checker need to worry about. If
661/// your application only wants to use odbc for part of its runtime, you may want to use
662/// [`Environment`] directly in order to explicitly free its associated resources earlier. No matter
663/// the application, it is recommended to only have one [`Environment`] per process.
664pub fn environment() -> Result<&'static Environment, Error> {
665    static ENV: OnceLock<Environment> = OnceLock::new();
666    if let Some(env) = ENV.get() {
667        // Environment already initialized, nothing to do, but to return it.
668        Ok(env)
669    } else {
670        // ODBC Environment not initialized yet. Let's do so and return it.
671        let env = Environment::new()?;
672        let env = ENV.get_or_init(|| env);
673        Ok(env)
674    }
675}
676
677/// Struct holding information available on a driver. Can be obtained via [`Environment::drivers`].
678#[derive(Clone, Debug, Eq, PartialEq)]
679pub struct DriverInfo {
680    /// Name of the ODBC driver
681    pub description: String,
682    /// Attributes values of the driver by key
683    pub attributes: HashMap<String, String>,
684}
685
686/// Holds name and description of a datasource
687///
688/// Can be obtained via [`Environment::data_sources`]
689#[derive(Clone, Debug, Eq, PartialEq)]
690pub struct DataSourceInfo {
691    /// Name of the data source
692    pub server_name: String,
693    /// Description of the data source
694    pub driver: String,
695}
696
697/// Message loop for prompt dialog. Used by [`Environment::driver_connect`].
698#[cfg(all(target_os = "windows", feature = "prompt"))]
699struct MessageOnlyWindowEventHandler<'a, F> {
700    run_prompt_dialog: Option<F>,
701    result: Option<Result<Connection<'a>, Error>>,
702}
703
704#[cfg(all(target_os = "windows", feature = "prompt"))]
705impl<'a, F> ApplicationHandler for MessageOnlyWindowEventHandler<'a, F>
706where
707    F: FnOnce(HWnd) -> Result<Connection<'a>, Error>,
708{
709    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
710        let parent_window = event_loop
711            .create_window(Window::default_attributes().with_visible(false))
712            .unwrap();
713
714        use winit::raw_window_handle::{HasWindowHandle, RawWindowHandle, Win32WindowHandle};
715
716        let hwnd = match parent_window.window_handle().unwrap().as_raw() {
717            RawWindowHandle::Win32(Win32WindowHandle { hwnd, .. }) => hwnd.get() as HWnd,
718            _ => panic!("ODBC Prompt is only supported on window platforms"),
719        };
720
721        if let Some(run_dialog) = self.run_prompt_dialog.take() {
722            self.result = Some(run_dialog(hwnd))
723        }
724        event_loop.exit();
725    }
726
727    fn window_event(&mut self, _event_loop: &ActiveEventLoop, _id: WindowId, _event: WindowEvent) {}
728}
729
730/// Called by drivers to pares list of attributes
731///
732/// Key value pairs are separated by `\0`. Key and value are separated by `=`
733fn attributes_iter(attributes: &str) -> impl Iterator<Item = (String, String)> + '_ {
734    attributes
735        .split('\0')
736        .take_while(|kv_str| *kv_str != String::new())
737        .map(|kv_str| {
738            let mut iter = kv_str.split('=');
739            let key = iter.next().unwrap();
740            let value = iter.next().unwrap();
741            (key.to_string(), value.to_string())
742        })
743}
744
745#[cfg(test)]
746mod tests {
747
748    use super::*;
749
750    #[test]
751    fn parse_attributes() {
752        let buffer = "APILevel=2\0ConnectFunctions=YYY\0CPTimeout=60\0DriverODBCVer=03.\
753                      50\0FileUsage=0\0SQLLevel=1\0UsageCount=1\0\0";
754        let attributes: HashMap<_, _> = attributes_iter(buffer).collect();
755        assert_eq!(attributes["APILevel"], "2");
756        assert_eq!(attributes["ConnectFunctions"], "YYY");
757        assert_eq!(attributes["CPTimeout"], "60");
758        assert_eq!(attributes["DriverODBCVer"], "03.50");
759        assert_eq!(attributes["FileUsage"], "0");
760        assert_eq!(attributes["SQLLevel"], "1");
761        assert_eq!(attributes["UsageCount"], "1");
762    }
763}