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
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463

mod types;
mod input;
mod output;
mod prepare;
pub use self::output::Output;
use {ffi, safe, Connection, Return, Result, Raii, Handle};
use ffi::SQLRETURN::*;
use ffi::Nullable;
use std::marker::PhantomData;
pub use self::types::OdbcType;
pub use self::types::{SqlDate, SqlTime, SqlSsTime2, SqlTimestamp, EncodedValue};

// Allocate CHUNK_LEN elements at a time
const CHUNK_LEN: usize = 64;
struct Chunks<T>(Vec<Box<[T; CHUNK_LEN]>>);

/// Heap allocator that will keep allocated element pointers valid until the allocator is dropped or cleared
impl<T: Copy + Default> Chunks<T> {
    fn new() -> Chunks<T> {
        Chunks(Vec::new())
    }

    fn alloc(&mut self, i: usize, value: T) -> *mut T {
        let chunk_no = i / CHUNK_LEN;
        if self.0.len() <= chunk_no {
            // Resizing Vec that holds pointers to heap allocated arrays so we don't invalidate the references
            self.0.resize(chunk_no + 1, Box::new([T::default(); CHUNK_LEN]))
        }
        let v = self.0[chunk_no].get_mut(i % CHUNK_LEN).unwrap();
        *v = value;
        v as *mut T
    }

    fn clear(&mut self) {
        self.0.clear()
    }
}

/// `Statement` state used to represent a freshly allocated connection
pub enum Allocated {}
/// `Statement` state used to represent a statement with a result set cursor. A statement is most
/// likely to enter this state after a `SELECT` query.
pub type Executed = Allocated;
/// `Statement` state used to represent a statement compiled into an access plan. A statement will
/// enter this state after a call to `Statement::prepared`
pub enum Prepared {}
/// `Statement` state used to represent a statement with a result set cursor. A statement is most
/// likely to enter this state after a `SELECT` query.
pub enum HasResult {}
/// `Statement` state used to represent a statement with no result set. A statement is likely to
/// enter this state after executing e.g. a `CREATE TABLE` statement
pub enum NoResult {}

/// Holds a `Statement` after execution of a query.Allocated
///
/// A executed statement may be in one of two states. Either the statement has yielded a result set
/// or not. Keep in mind that some ODBC drivers just yield empty result sets on e.g. `INSERT`
/// Statements
pub enum ResultSetState<'a, 'b, S, AC: AutocommitMode> {
    Data(Statement<'a, 'b, S, HasResult, AC>),
    NoData(Statement<'a, 'b, S, NoResult, AC>),
}
pub use ResultSetState::*;
use std::ptr::null_mut;
use odbc_safe::AutocommitMode;

/// A `Statement` can be used to execute queries and retrieves results.
pub struct Statement<'a, 'b, S, R, AC: AutocommitMode> {
    raii: Raii<'a, ffi::Stmt>,
    state: PhantomData<S>,
    autocommit_mode: PhantomData<AC>,
    // Indicates wether there is an open result set or not associated with this statement.
    result: PhantomData<R>,
    parameters: PhantomData<&'b [u8]>,
    param_ind_buffers: Chunks<ffi::SQLLEN>,
    // encoded values are saved to use its pointer.
    encoded_values: Vec<EncodedValue>,
}

/// Used to retrieve data from the fields of a query result
pub struct Cursor<'s, 'a: 's, 'b: 's, S: 's, AC: AutocommitMode> {
    stmt: &'s mut Statement<'a, 'b, S, HasResult, AC>,
    buffer: Vec<u8>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ColumnDescriptor {
    pub name: String,
    pub data_type: ffi::SqlDataType,
    pub column_size: Option<ffi::SQLULEN>,
    pub decimal_digits: Option<u16>,
    pub nullable: Option<bool>,
}

impl<'a, 'b, S, R, AC: AutocommitMode> Handle for Statement<'a, 'b, S, R, AC> {
    type To = ffi::Stmt;
    unsafe fn handle(&self) -> ffi::SQLHSTMT {
        self.raii.handle()
    }
}

impl<'a, 'b, S, R, AC: AutocommitMode> Statement<'a, 'b, S, R, AC> {
    fn with_raii(raii: Raii<'a, ffi::Stmt>) -> Self {
        Statement {
            raii: raii,
            autocommit_mode: PhantomData,
            state: PhantomData,
            result: PhantomData,
            parameters: PhantomData,
            param_ind_buffers: Chunks::new(),
            encoded_values: Vec::new(),
        }
    }
}

impl<'a, 'b, 'env, AC: AutocommitMode> Statement<'a, 'b, Allocated, NoResult, AC> {
    pub fn with_parent(ds: &'a Connection<'env, AC>) -> Result<Self> {
        let raii = Raii::with_parent(ds).into_result(ds)?;
        Ok(Self::with_raii(raii))
    }

    pub fn affected_row_count(&self) -> Result<ffi::SQLLEN> {
        self.raii.affected_row_count().into_result(self)
    }

    pub fn tables(self, catalog_name: &String, schema_name: &String, table_name: &String, table_type: &String) -> Result<Statement<'a, 'b, Executed, HasResult, AC>> {
        self.tables_str(catalog_name.as_str(), schema_name.as_str(), table_name.as_str(), table_type.as_str())
    }

    pub fn tables_str(self, catalog_name: &str, schema_name: &str, table_name: &str, table_type: &str) -> Result<Statement<'a, 'b, Executed, HasResult, AC>> {
        self.tables_opt_str(Option::Some(catalog_name), Option::Some(schema_name), Option::Some(table_name), table_type)
    }

    pub fn tables_opt_str(mut self, catalog_name: Option<&str>, schema_name: Option<&str>, table_name:Option<&str>, table_type: &str) -> Result<Statement<'a, 'b, Executed, HasResult, AC>> {
        self.raii.tables(catalog_name, schema_name, table_name, table_type).into_result(&self)?;
        Ok(Statement::with_raii(self.raii))
    }

    /// Executes a preparable statement, using the current values of the parameter marker variables
    /// if any parameters exist in the statement.
    ///
    /// `SQLExecDirect` is the fastest way to submit an SQL statement for one-time execution.
    pub fn exec_direct(mut self, statement_text: &str) -> Result<ResultSetState<'a, 'b, Executed, AC>> {
        if self.raii.exec_direct(statement_text).into_result(&self)? {
            let num_cols = self.raii.num_result_cols().into_result(&self)?;
            if num_cols > 0 {
                Ok(ResultSetState::Data(Statement::with_raii(self.raii)))
            } else {
                Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
            }
        } else {
            Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
        }
    }

    /// Executes a preparable statement, using the current values of the parameter marker variables
    /// if any parameters exist in the statement.
    ///
    /// `SQLExecDirect` is the fastest way to submit an SQL statement for one-time execution.
    pub fn exec_direct_bytes(mut self, bytes: &[u8]) -> Result<ResultSetState<'a, 'b, Executed, AC>> {
        if self.raii.exec_direct_bytes(bytes).into_result(&self)? {
            let num_cols = self.raii.num_result_cols().into_result(&self)?;
            if num_cols > 0 {
                Ok(ResultSetState::Data(Statement::with_raii(self.raii)))
            } else {
                Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
            }
        } else {
            Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
        }
    }
}

impl<'a, 'b, S, AC: AutocommitMode> Statement<'a, 'b, S, HasResult, AC> {

    pub fn affected_row_count(&self) -> Result<ffi::SQLLEN> {
        self.raii.affected_row_count().into_result(self)
    }

    /// The number of columns in a result set
    ///
    /// Can be called successfully only when the statement is in the prepared, executed, or
    /// positioned state. If the statement does not return columns the result will be 0.
    pub fn num_result_cols(&self) -> Result<i16> {
        self.raii.num_result_cols().into_result(self)
    }

    /// Returns description struct for result set column with a given index. Note: indexing is starting from 1.
    pub fn describe_col(&self, idx: u16) -> Result<ColumnDescriptor> {
        self.raii.describe_col(idx).into_result(self)
    }

    /// Fetches the next rowset of data from the result set and returns data for all bound columns.
    pub fn fetch<'s>(&'s mut self) -> Result<Option<Cursor<'s, 'a, 'b, S, AC>>> {
        if self.raii.fetch().into_result(self)? {
            Ok(Some(Cursor {
                stmt: self,
                buffer: vec![0; 512],
            }))
        } else {
            Ok(None)
        }
    }

    /// Call this method to reuse the statement to execute another query.
    ///
    /// For many drivers allocating new statements is expensive. So reusing a `Statement` is usually
    /// more efficient than freeing an existing and allocating a new one. However to reuse a
    /// statement any open result sets must be closed.
    /// Only call this method if you have already read the result set returned by the previous
    /// query, or if you do no not intend to read it.
    ///
    /// # Example
    ///
    /// ```
    /// # use odbc::*;
    /// # fn reuse () -> Result<()> {
    /// let env = create_environment_v3().map_err(|e| e.unwrap())?;
    /// let conn = env.connect("TestDataSource", "", "")?;
    /// let stmt = Statement::with_parent(&conn)?;
    /// let stmt = match stmt.exec_direct("CREATE TABLE STAGE (A TEXT, B TEXT);")?{
    ///     // Some drivers will return an empty result set. We need to close it before we can use
    ///     // statement again.
    ///     Data(stmt) => stmt.close_cursor()?,
    ///     NoData(stmt) => stmt,
    /// };
    /// let stmt = stmt.exec_direct("INSERT INTO STAGE (A, B) VALUES ('Hello', 'World');")?;
    /// //...
    /// # Ok(())
    /// # };
    /// ```
    pub fn close_cursor(mut self) -> Result<Statement<'a, 'b, S, NoResult, AC>> {
        self.raii.close_cursor().into_result(&self)?;
        Ok(Statement::with_raii(self.raii))
    }
}

impl<'a, 'b, 'c, S, AC: AutocommitMode> Cursor<'a, 'b, 'c, S, AC> {
    /// Retrieves data for a single column in the result set
    ///
    /// ## Panics
    ///
    /// If you try to convert to `&str` but the data can't be converted
    /// without allocating an intermediate buffer.
    pub fn get_data<'d, T>(&'d mut self, col_or_param_num: u16) -> Result<Option<T>>
    where
        T: Output<'d>,
    {
        T::get_data(&mut self.stmt.raii, col_or_param_num, &mut self.buffer).into_result(self.stmt)
    }
}

impl<'p> Raii<'p, ffi::Stmt> {
    fn affected_row_count(&self) -> Return<ffi::SQLLEN> {
        let mut count: ffi::SQLLEN = 0;
        unsafe {
            match ffi::SQLRowCount(self.handle(), &mut count as *mut ffi::SQLLEN) {
                SQL_SUCCESS => Return::Success(count),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(count),
                SQL_ERROR => Return::Error,
                r => panic!("SQLRowCount returned unexpected result: {:?}", r),
            }
        }
    }

    fn num_result_cols(&self) -> Return<i16> {
        let mut num_cols: ffi::SQLSMALLINT = 0;
        unsafe {
            match ffi::SQLNumResultCols(self.handle(), &mut num_cols as *mut ffi::SQLSMALLINT) {
                SQL_SUCCESS => Return::Success(num_cols),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(num_cols),
                SQL_ERROR => Return::Error,
                r => panic!("SQLNumResultCols returned unexpected result: {:?}", r),
            }
        }
    }

    fn describe_col(&self, idx: u16) -> Return<ColumnDescriptor> {
        let mut name_buffer: [u8; 512] = [0; 512];
        let mut name_length: ffi::SQLSMALLINT = 0;
        let mut data_type: ffi::SqlDataType = ffi::SqlDataType::SQL_UNKNOWN_TYPE;
        let mut column_size: ffi::SQLULEN = 0;
        let mut decimal_digits: ffi::SQLSMALLINT = 0;
        let mut nullable: Nullable = Nullable::SQL_NULLABLE_UNKNOWN;
        unsafe {
            match ffi::SQLDescribeCol(
                self.handle(),
                idx,
                name_buffer.as_mut_ptr(),
                name_buffer.len() as ffi::SQLSMALLINT,
                &mut name_length as *mut ffi::SQLSMALLINT,
                &mut data_type as *mut ffi::SqlDataType,
                &mut column_size as *mut ffi::SQLULEN,
                &mut decimal_digits as *mut ffi::SQLSMALLINT,
                &mut nullable as *mut ffi::Nullable,
            ) {
                SQL_SUCCESS => Return::Success(ColumnDescriptor {
                    name: ::environment::DB_ENCODING.decode(&name_buffer[..(name_length as usize)]).0
                        .to_string(),
                    data_type: data_type,
                    column_size: if column_size == 0 {
                        None
                    } else {
                        Some(column_size)
                    },
                    decimal_digits: if decimal_digits == 0 {
                        None
                    } else {
                        Some(decimal_digits as u16)
                    },
                    nullable: match nullable {
                        Nullable::SQL_NULLABLE_UNKNOWN => None,
                        Nullable::SQL_NULLABLE => Some(true),
                        Nullable::SQL_NO_NULLS => Some(false),
                    },
                }),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(ColumnDescriptor {
                    name: ::environment::DB_ENCODING.decode(&name_buffer[..(name_length as usize)]).0
                        .to_string(),
                    data_type: data_type,
                    column_size: if column_size == 0 {
                        None
                    } else {
                        Some(column_size)
                    },
                    decimal_digits: if decimal_digits == 0 {
                        None
                    } else {
                        Some(decimal_digits as u16)
                    },
                    nullable: match nullable {
                        Nullable::SQL_NULLABLE_UNKNOWN => None,
                        Nullable::SQL_NULLABLE => Some(true),
                        Nullable::SQL_NO_NULLS => Some(false),
                    },
                }),
                SQL_ERROR => Return::Error,
                r => panic!("SQLDescribeCol returned unexpected result: {:?}", r),
            }
        }

    }

    fn exec_direct(&mut self, statement_text: &str) -> Return<bool> {
        let bytes = unsafe { crate::environment::DB_ENCODING }.encode(statement_text).0;

        let length = bytes.len();
        if length > ffi::SQLINTEGER::max_value() as usize {
            panic!("Statement text too long");
        }
        match unsafe {
            ffi::SQLExecDirect(
                self.handle(),
                bytes.as_ptr(),
                length as ffi::SQLINTEGER,
            )
        } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
        }
    }

    fn exec_direct_bytes(&mut self, bytes: &[u8]) -> Return<bool> {
        let length = bytes.len();
        if length > ffi::SQLINTEGER::max_value() as usize {
            panic!("Statement text too long");
        }
        match unsafe {
            ffi::SQLExecDirect(
                self.handle(),
                bytes.as_ptr(),
                length as ffi::SQLINTEGER,
            )
        } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
        }
    }

    /// Fetches the next rowset of data from the result set and returns data for all bound columns.
    fn fetch(&mut self) -> Return<bool> {
        match unsafe { ffi::SQLFetch(self.handle()) } {
            ffi::SQL_SUCCESS => Return::Success(true),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
            ffi::SQL_ERROR => Return::Error,
            ffi::SQL_NO_DATA => Return::Success(false),
            r => panic!("SQLFetch returned unexpected result: {:?}", r),
        }
    }

    fn tables(&mut self, catalog_name: Option<&str>, schema_name: Option<&str>, table_name: Option<&str>, table_type: &str) -> Return<()> {
        unsafe {
            let mut catalog: *const odbc_sys::SQLCHAR = null_mut();
            let mut schema: *const odbc_sys::SQLCHAR = null_mut();
            let mut table: *const odbc_sys::SQLCHAR = null_mut();

            let mut catalog_size: odbc_sys::SQLSMALLINT = 0;
            let mut schema_size: odbc_sys::SQLSMALLINT = 0;
            let mut table_size: odbc_sys::SQLSMALLINT = 0;

            if catalog_name.is_some() {
                catalog = catalog_name.unwrap().as_ptr();
                catalog_size = catalog_name.unwrap().len() as odbc_sys::SQLSMALLINT;
            }

            if schema_name.is_some() {
                schema = schema_name.unwrap().as_ptr();
                schema_size = schema_name.unwrap().len() as odbc_sys::SQLSMALLINT;
            }

            if table_name.is_some() {
                table = table_name.unwrap().as_ptr();
                table_size = table_name.unwrap().len() as odbc_sys::SQLSMALLINT;
            }

            match odbc_sys::SQLTables(
                self.handle(),
                catalog,
                catalog_size,
                schema,
                schema_size,
                table,
                table_size,
                table_type.as_ptr(),
                table_type.as_bytes().len() as odbc_sys::SQLSMALLINT,
            ) {
                SQL_SUCCESS => Return::Success(()),
                SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
                SQL_ERROR => Return::Error,
                r => panic!("SQLTables returned: {:?}", r),
            }
        }
    }

    fn close_cursor(&mut self) -> Return<()> {
        unsafe {
            match ffi::SQLCloseCursor(self.handle()) {
                ffi::SQL_SUCCESS => Return::Success(()),
                ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
                ffi::SQL_ERROR => Return::Error,
                r => panic!("unexpected return value from SQLCloseCursor: {:?}", r),
            }
        }
    }
}

unsafe impl<'con, 'param, C, P, AC: AutocommitMode> safe::Handle for Statement<'con, 'param, C, P, AC> {

    const HANDLE_TYPE : ffi::HandleType = ffi::SQL_HANDLE_STMT;

    fn handle(&self) -> ffi::SQLHANDLE {
        <Raii<ffi::Stmt> as safe::Handle>::handle(&self.raii)
    }
}