1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
use crate::{handles, Connection, Error}; use odbc_sys::AttrOdbcVersion; use widestring::{U16Str, U16String}; /// 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. pub struct Environment { environment: handles::Environment, } impl Environment { /// Allocates a new ODBC Environment and declares 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. pub unsafe fn new() -> Result<Self, Error> { let environment = crate::handles::Environment::new()?; environment.declare_version(AttrOdbcVersion::Odbc3_80)?; Ok(Self { environment }) } /// Allocates a connection handle and establishes connections to a driver and a data source. /// /// * See [Connecting with SQLConnect][1] /// * See [SQLConnectFunction][2] /// /// # 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 /// /// ```no_run /// use odbc_api::Environment; /// /// // I herby solemnly swear that this is the only ODBC environment in the entire process, thus /// // making this call safe. /// let env = unsafe { /// Environment::new()? /// }; /// /// let mut conn = env.connect("YourDatabase", "SA", "<YourStrong@Passw0rd>")?; /// # Ok::<(), odbc_api::Error>(()) /// ``` /// /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function /// [2]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function pub fn connect( &self, data_source_name: &str, user: &str, pwd: &str, ) -> Result<Connection, Error> { let data_source_name = U16String::from_str(data_source_name); let user = U16String::from_str(user); let pwd = U16String::from_str(pwd); self.connect_utf16(&data_source_name, &user, &pwd) } /// Allocates a connection handle and establishes connections to a driver and a data source. /// /// * See [Connecting with SQLConnect][1] /// * See [SQLConnectFunction][2] /// /// # 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). /// /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function /// [2]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlconnect-function pub fn connect_utf16( &self, data_source_name: &U16Str, user: &U16Str, pwd: &U16Str, ) -> Result<Connection, Error> { let mut connection = self.environment.allocate_connection()?; connection.connect(data_source_name, user, pwd)?; Ok(Connection::new(connection)) } /// 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 /// /// ```no_run /// use odbc_api::Environment; /// /// // I herby solemnly swear that this is the only ODBC environment in the entire process, thus /// // making this call safe. /// let env = unsafe { /// Environment::new()? /// }; /// /// let connection_string = " /// Driver={ODBC Driver 17 for SQL Server};\ /// Server=localhost;\ /// UID=SA;\ /// PWD=<YourStrong@Passw0rd>;\ /// "; /// /// let mut conn = env.connect_with_connection_string(connection_string)?; /// # Ok::<(), odbc_api::Error>(()) /// ``` pub fn connect_with_connection_string( &self, connection_string: &str, ) -> Result<Connection, Error> { let connection_string = U16String::from_str(connection_string); self.connect_with_connection_string_utf16(&connection_string) } /// 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/> pub fn connect_with_connection_string_utf16( &self, connection_string: &U16Str, ) -> Result<Connection, Error> { let mut connection = self.environment.allocate_connection()?; connection.connect_with_connection_string(connection_string)?; Ok(Connection::new(connection)) } }