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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
use super::*; use sys::*; use std::marker::PhantomData; /// A `Statement` is most easily thought of as an SQL statement, such as /// `SELECT * FROM Employee`. /// /// * The statement's state /// * The current statement-level diagnostics /// * The addresses of the application variables bound to the statement's /// parameters and result set /// columns /// * The current settings of each statement attribute /// /// See [Statement Handles][1] /// /// Specific to the rust wrapper of an ODBC Statement is, that we do keep track /// of the lifetimes of /// the parent `Connection`, parameters as well as `columns` bound to the /// `Statement`. Since it is /// possible to unbind the parameters and columns we have to keep track of /// their lifetimes /// seperatly. /// /// [1]: https://docs.microsoft.com/sql/odbc/reference/develop-app/statement-handles #[derive(Debug)] pub struct Statement<'con, 'param, 'col, C = NoCursor, A = Unprepared> { cursor: PhantomData<C>, access_plan: PhantomData<A>, /// Statement may not outlive parameters bound to it. parameters: PhantomData<&'param [u8]>, columns: PhantomData<&'col [u8]>, handle: HStmt<'con>, } /// Cursor state of `Statement`. A statement is likely to enter this state /// after executing e.g a /// `SELECT` query. #[derive(Debug)] #[allow(missing_copy_implementations)] pub enum Open {} /// State used by `Statement`. A statement is likely to enter this state after /// executing e.g. a /// `CREATE TABLE` statement. #[derive(Debug)] #[allow(missing_copy_implementations)] pub enum NoCursor {} /// Cursor state of `Statement`. A statement will enter this state after a /// successful call to /// `fetch()` #[derive(Debug)] #[allow(missing_copy_implementations)] pub enum Positioned {} /// State used by `Statement`. A statement will enter this state after a /// successful call to /// `prepare()`. #[derive(Debug)] #[allow(missing_copy_implementations)] pub enum Prepared {} /// State used by `Statement`. Indicates that no Access Plan has been created, /// yet. #[derive(Debug)] #[allow(missing_copy_implementations)] pub enum Unprepared {} /// Implemented by the `Open` and `Positioned` states for `Statement`. pub trait CursorState {} impl CursorState for Open {} impl CursorState for Positioned {} impl<'con, 'param, 'col, S, A> Statement<'con, 'param, 'col, S, A> { /// Provides access to the raw ODBC Statement Handle pub fn as_raw(&self) -> SQLHSTMT { self.handle.as_raw() } /// Binds a parameter to a parameter marker in an SQL Statement /// /// # Result /// This method will destroy the statement and create a new one which may not outlive the bound /// parameter. This is to ensure that the statement will not derefernce an invalid pointer /// during execution. Use `reset_parameters` to reset the bound parameters and increase the /// `'param` lifetime back to `'static`. /// /// # Arguments /// * `parameter_number` - Index of the marker to bind to the parameter. Starting at `1` /// * `parameter_type` - SQL Type of the parameter /// * `value` - Reference to bind to the marker /// /// See [SQLBindParameter Function][1] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlbindparameter-function#columnsize-argument pub fn bind_input_parameter<'p, T>( mut self, parameter_number: SQLUSMALLINT, parameter_type: DataType, value: &'p T, indicator: Option<&'p SQLLEN>, ) -> Return<Statement<'con, 'p, 'col, S, A>, Self> where T: CDataType + ?Sized, 'param: 'p, { unsafe { match self.handle.bind_input_parameter( parameter_number, parameter_type, value, indicator, ) { Success(()) => Success(self.transit()), Info(()) => Info(self.transit()), Error(()) => Error(self.transit()), } } } /// Binds a buffer and an indicator to a column. /// /// See [SQLBindCol][1]: /// [1]: [https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindcol-function] pub fn bind_col<'col_new, T>( mut self, column_number: SQLUSMALLINT, value: &'col_new mut T, indicator: Option<&'col_new mut SQLLEN>, ) -> Return<Statement<'con, 'param, 'col_new, S, A>, Self> where T: CDataType + ?Sized, 'col: 'col_new, { unsafe { match self.handle.bind_col(column_number, value, indicator) { Success(()) => Success(self.transit()), Info(()) => Info(self.transit()), Error(()) => Error(self.transit()), } } } /// Unbinds the parameters from the parameter markers pub fn reset_parameters(mut self) -> Statement<'con, 'static, 'col, S, A> { self.handle.reset_parameters().unwrap(); self.transit() } /// Unbinds column buffers from result set. pub fn reset_columns(mut self) -> Statement<'con, 'param, 'static, S, A> { self.handle.reset_columns().unwrap(); self.transit() } fn transit<'p, 'c, S2, A2>(self) -> Statement<'con, 'p, 'c, S2, A2> { Statement { handle: self.handle, parameters: PhantomData, columns: PhantomData, cursor: PhantomData, access_plan: PhantomData, } } } impl<'con, 'param, 'col, C, A> Statement<'con, 'param, 'col, C, A> where C: CursorState, { /// Returns the number of columns of the result set /// /// See [SQLNumResultCols][1] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlnumresultcols-function pub fn num_result_cols(&self) -> Return<SQLSMALLINT> { self.handle.num_result_cols() } /// Returns the number of rows affetced by INSERT, UPDATE, etc /// /// See [SQLRowCount][1] /// [1]: https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlrowcount-function pub fn affected_row_count(&self) -> Return<SQLLEN> { self.handle.affected_row_count() } /// Advances Cursor to next row /// /// See [SQLFetch][1] /// See [Fetching a Row of Data][2] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlfetch-function /// [2]: https://docs.microsoft.com/sql/odbc/reference/develop-app/fetching-a-row-of-data pub fn fetch( mut self, ) -> ReturnOption< Statement<'con, 'param, 'col, Positioned, A>, Statement<'con, 'param, 'col, NoCursor, A>, > { match self.handle.fetch() { ReturnOption::Success(()) => ReturnOption::Success(self.transit()), ReturnOption::Info(()) => ReturnOption::Info(self.transit()), ReturnOption::NoData(()) => ReturnOption::NoData(self.transit()), ReturnOption::Error(()) => ReturnOption::Error(self.transit()), } } /// Closes the cursor. Cursors only need to be closed explicitly if the /// Statement handle is /// intended to be reused, but a result set is not consumed. /// /// See [SQLCloseCursor][1] /// See [Closing the Cursor][2] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlclosecursor-function /// [2]: https://docs.microsoft.com/sql/odbc/reference/develop-app/closing-the-cursor pub fn close_cursor( mut self, ) -> Return<Statement<'con, 'param, 'col, NoCursor>, Statement<'con, 'param, 'col, C, A>> { match self.handle.close_cursor() { Success(()) => Success(self.transit()), Info(()) => Info(self.transit()), Error(()) => Error(self.transit()), } } /// Return information about result set column /// /// See [SQLDescribeCol Function][1] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldescribecol-function pub fn describe_col<T>( &mut self, column_number: SQLUSMALLINT, column_name: &mut T, column_name_indicator: &mut SQLSMALLINT, nullable: &mut Nullable, ) -> Return<Option<DataType>> where T: OutputBuffer + ?Sized, { let mut data_type = SQL_UNKNOWN_TYPE; let mut column_size = 0; let mut decimal_digits = 0; self.handle.describe_col( column_number, column_name, column_name_indicator, &mut data_type, &mut column_size, &mut decimal_digits, nullable ).map(|()| DataType::new(data_type, column_size, decimal_digits)) } } impl<'con, 'param, 'col> Statement<'con, 'param, 'col, NoCursor, Unprepared> { /// Allocates a new `Statement` pub fn with_parent<AC: AutocommitMode>(parent: &'con Connection<AC>) -> Return<Self> { HStmt::allocate(parent.as_hdbc()).map(|handle| { Statement { handle, parameters: PhantomData, cursor: PhantomData, columns: PhantomData, access_plan: PhantomData, } }) } /// Prepares a `Statement` for execution by creating an Access Plan. /// /// See [SQLPrepare Function][1] /// See [Prepare and Execute a Statement (ODBC)][2] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlprepare-function /// [2]: https://docs.microsoft.com/sql/relational-databases/native-client-odbc-how-to/execute-queries/prepare-and-execute-a-statement-odbc pub fn prepare<T>( mut self, statement_text: &T, ) -> Return< Statement<'con, 'param, 'col, NoCursor, Prepared>, Statement<'con, 'param, 'col, NoCursor>, > where T: SqlStr + ?Sized, { // According to the state transition table preparing statements which are // already prepared // is possible. However we would need to check the status code in order to // decide which // state the `Statement` is in the case of an error. So for now we only support // preparing // freshly allocated statements until someone has a use case for 'repreparing' // a statement. match self.handle.prepare(statement_text) { Success(()) => Success(self.transit()), Info(()) => Info(self.transit()), Error(()) => Error(self.transit()), } } /// Executes a preparable statement, using the current values of the /// parametr marker variables. /// /// * See [SQLExecDirect][1] /// * See [Direct Execution][2] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlexecdirect-function /// [2]: https://docs.microsoft.com/sql/odbc/reference/develop-app/direct-execution-odbc pub fn exec_direct<T>( mut self, statement_text: &T, ) -> ReturnOption< ResultSet<'con, 'param, 'col, Unprepared>, Statement<'con, 'param, 'col, NoCursor>, > where T: SqlStr + ?Sized, { match self.handle.exec_direct(statement_text) { ReturnOption::Success(()) => ReturnOption::Success(self.transit()), ReturnOption::Info(()) => ReturnOption::Info(self.transit()), ReturnOption::NoData(()) => ReturnOption::NoData(self.transit()), ReturnOption::Error(()) => ReturnOption::Error(self.transit()), } } } impl<'con, 'param, 'col> Statement<'con, 'param, 'col, NoCursor, Prepared> { /// Return information about result set column /// /// See [SQLDescribeCol Function][1] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqldescribecol-function pub fn describe_col<T>( &mut self, column_number: SQLUSMALLINT, column_name: &mut T, column_name_indicator: &mut SQLSMALLINT, nullable: &mut Nullable, ) -> Return<Option<DataType>> where T: OutputBuffer + ?Sized, { let mut data_type = SQL_UNKNOWN_TYPE; let mut column_size = 0; let mut decimal_digits = 0; self.handle.describe_col( column_number, column_name, column_name_indicator, &mut data_type, &mut column_size, &mut decimal_digits, nullable ).map(|()| DataType::new(data_type, column_size, decimal_digits)) } /// Executes a prepared statement, using the current values fo the /// parameter marker variables /// if any parameter markers exist in the statement. /// /// See [SQLExecute Function][1] /// See [Prepared Execution][2] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlexecute-function /// [2]: https://docs.microsoft.com/sql/odbc/reference/develop-app/prepared-execution-odbc pub fn execute(mut self) -> ReturnOption<ResultSet<'con, 'param, 'col, Prepared>, Self> { match self.handle.execute() { ReturnOption::Success(()) => ReturnOption::Success(self.transit()), ReturnOption::Info(()) => ReturnOption::Info(self.transit()), ReturnOption::Error(()) => ReturnOption::Error(self.transit()), ReturnOption::NoData(()) => ReturnOption::NoData(self.transit()), } } } impl<'con, 'param, 'col, A> Statement<'con, 'param, 'col, Positioned, A> { /// Retrieves data for a single column or output parameter. /// /// See [SQLGetData][1] /// [1]: https://docs.microsoft.com/sql/odbc/reference/syntax/sqlgetdata-function pub fn get_data<T>( &mut self, col_or_param_num: SQLUSMALLINT, target: &mut T, ) -> ReturnOption<Indicator> where T: CDataType + ?Sized, { self.handle.get_data(col_or_param_num, target) } } impl<'con, 'param, 'col, C, A> Diagnostics for Statement<'con, 'param, 'col, C, A> { fn diagnostics( &self, rec_number: SQLSMALLINT, message_text: &mut [SQLCHAR], ) -> ReturnOption<DiagResult> { self.handle.diagnostics(rec_number, message_text) } }