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
use {ffi, Return, Result, Raii, Handle, Statement};
use super::types::FixedSizedType;

/// Allows types to be used with `Statement::bind_parameter`
pub unsafe trait InputParameter {
    fn c_data_type(&self) -> ffi::SqlCDataType;
    fn column_size(&self) -> ffi::SQLULEN;
    fn decimal_digits(&self) -> ffi::SQLSMALLINT;
    fn value_ptr(&self) -> ffi::SQLPOINTER;
    fn indicator(&self) -> ffi::SQLLEN;
}

impl<'a, 'b, S, R> Statement<'a, 'b, S, R> {
    /// 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.
    ///
    /// # Arguments
    /// * `parameter_index` - Index of the marker to bind to the parameter. Starting at `1`
    /// * `value` - Reference to bind to the marker
    ///
    /// # Example
    /// ```
    /// # use odbc::*;
    /// # fn do_odbc_stuff() -> std::result::Result<(), Box<std::error::Error>> {
    /// let env = Environment::new()?.set_odbc_version_3()?;
    /// let conn = DataSource::with_parent(&env)?.connect("TestDataSource", "", "")?;
    /// let stmt = Statement::with_parent(&conn)?;
    /// let param = 1968;
    /// let stmt = stmt.bind_parameter(1, &param)?;
    /// let sql_text = "SELECT TITLE FROM MOVIES WHERE YEAR = ?";
    /// if let Data(mut stmt) = stmt.exec_direct(sql_text)? {
    ///     // ...
    /// }
    /// #   Ok(())
    /// # }
    /// ```
    pub fn bind_parameter<'c, T>(mut self,
                                 parameter_index: u16,
                                 value: &'c T)
                                 -> Result<Statement<'a, 'c, S, R>>
        where T: InputParameter,
              T: ?Sized,
              'b: 'c
    {
        self.raii
            .bind_input_parameter(parameter_index, value)
            .into_result(&self)?;
        Ok(self)
    }

    /// Releasing all parameter buffers set by `bind_parameter`. This method consumes the statement
    /// and returns a new one those lifetime is no longer limited by the buffers bound.
    pub fn reset_parameters(mut self) -> Result<Statement<'a, 'a, S, R>> {
        self.raii.reset_parameters().into_result(&mut self)?;
        Ok(Statement::with_raii(self.raii))
    }
}

impl Raii<ffi::Stmt> {
    fn bind_input_parameter<T>(&mut self, parameter_index: u16, value: &T) -> Return<()>
        where T: InputParameter,
              T: ?Sized
    {
        match unsafe {
            ffi::SQLBindParameter(
                self.handle(),
                parameter_index,
                ffi::SQL_PARAM_INPUT,
                value.c_data_type(),
                ffi::SQL_UNKNOWN_TYPE,
                value.column_size(),
                value.decimal_digits(),
                value.value_ptr(),
                0, // buffer length
                &value.indicator() as * const ffi::SQLLEN as * mut ffi::SQLLEN// str len or ind ptr
            )
        } {
            ffi::SQL_SUCCESS => Return::Success(()),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
            ffi::SQL_ERROR => Return::Error,
            r => panic!("Unexpected return from SQLBindParameter: {:?}", r),
        }
    }

    fn reset_parameters(&mut self) -> Return<()> {
        match unsafe { ffi::SQLFreeStmt(self.handle(), ffi::SQL_RESET_PARAMS) } {
            ffi::SQL_SUCCESS => Return::Success(()),
            ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
            ffi::SQL_ERROR => Return::Error,
            r => panic!("SQLFreeStmt returned unexpected result: {:?}", r),
        }
    }
}

unsafe impl InputParameter for str {
    fn c_data_type(&self) -> ffi::SqlCDataType {
        ffi::SQL_C_CHAR
    }

    fn column_size(&self) -> ffi::SQLULEN {
        self.as_bytes().len() as ffi::SQLULEN
    }

    fn decimal_digits(&self) -> ffi::SQLSMALLINT {
        0
    }

    fn value_ptr(&self) -> ffi::SQLPOINTER {
        self.as_bytes().as_ptr() as ffi::SQLPOINTER
    }

    fn indicator(&self) -> ffi::SQLLEN {
        self.as_bytes().len() as ffi::SQLLEN
    }
}

unsafe impl InputParameter for String {
    fn c_data_type(&self) -> ffi::SqlCDataType {
        ffi::SQL_C_CHAR
    }

    fn column_size(&self) -> ffi::SQLULEN {
        self.as_bytes().len() as ffi::SQLULEN
    }

    fn decimal_digits(&self) -> ffi::SQLSMALLINT {
        0
    }

    fn value_ptr(&self) -> ffi::SQLPOINTER {
        self.as_bytes().as_ptr() as ffi::SQLPOINTER
    }

    fn indicator(&self) -> ffi::SQLLEN {
        self.as_bytes().len() as ffi::SQLLEN
    }
}

unsafe impl<T> InputParameter for T
    where T: FixedSizedType
{
    fn c_data_type(&self) -> ffi::SqlCDataType {
        T::c_data_type()
    }

    fn column_size(&self) -> ffi::SQLULEN {
        use std::mem::size_of;
        size_of::<Self>() as ffi::SQLULEN
    }

    fn decimal_digits(&self) -> ffi::SQLSMALLINT {
        0
    }

    fn value_ptr(&self) -> ffi::SQLPOINTER {
        self as *const Self as ffi::SQLPOINTER
    }

    fn indicator(&self) -> ffi::SQLLEN {
        0
    }
}