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
use crate::{
    buffers::{Bit, FixedSizedCType},
    handles::Statement,
    DataType, Error,
};
use odbc_sys::{CDataType, Date, Len, ParamType, Pointer};
use std::{convert::TryInto, mem::size_of, ptr::null_mut};

/// Can be bound to a single `Placeholder` in an SQL statement.
///
/// Users usually won't utilize this trait directly, but implementations of `Parameters` are likely
/// to bind one or more instances of SingleParameter to a statement.
pub unsafe trait Parameter {
    /// # Parameters
    ///
    /// * `stmt`: Statement handle the parameter should be bound to.
    /// * `parameter_number`: 1 based index of the parameter. The index refers to the position of
    ///   the `?` placeholder in the SQL statement text.
    ///
    /// # Safety
    ///
    /// Implementers should take care that the values bound by this method to the statement live at
    /// least for the Duration of `self`. The most straight forward way of achieving this is of
    /// course, to bind members.
    unsafe fn bind_parameter_to(
        &self,
        stmt: &mut Statement,
        parameter_number: u16,
    ) -> Result<(), Error>;
}

// While the C-Type is independent of the Data (SQL) Type in the source, there are often DataTypes
// which are a natural match for the C-Type in question. These can be used to spare the user to
// specify that an i32 is supposed to be bound as an SQL Integer.

macro_rules! impl_single_parameter_for_fixed_sized {
    ($t:ident, $data_type:expr) => {
        unsafe impl Parameter for $t {
            unsafe fn bind_parameter_to(
                &self,
                stmt: &mut Statement,
                parameter_number: u16,
            ) -> Result<(), Error> {
                stmt.bind_parameter(
                    parameter_number,
                    ParamType::Input,
                    $t::C_DATA_TYPE,
                    $data_type,
                    self as *const $t as *mut $t as Pointer,
                    size_of::<$t>() as Len,
                    null_mut(),
                )
            }
        }
    };
}

impl_single_parameter_for_fixed_sized!(f64, DataType::Double);
impl_single_parameter_for_fixed_sized!(f32, DataType::Real);
impl_single_parameter_for_fixed_sized!(Date, DataType::Date);
impl_single_parameter_for_fixed_sized!(i16, DataType::SmallInt);
impl_single_parameter_for_fixed_sized!(i32, DataType::Integer);
impl_single_parameter_for_fixed_sized!(i8, DataType::Tinyint);
impl_single_parameter_for_fixed_sized!(Bit, DataType::Bit);
impl_single_parameter_for_fixed_sized!(i64, DataType::Bigint);

// Support for fixed size types, which are not unsigned. Time, Date and timestamp types could be
// supported, implementation DataType would need to takeinstance into account.

/// Binds a byte array as a VarChar input parameter.
///
/// While a byte array can provide us with a pointer to the start of the array and the length of the
/// array itself, it can not provide us with a pointer to the length of the buffer. So to bind
/// strings which are not zero terminated we need to store the length in a separate value.
pub struct VarCharParam<'a> {
    bytes: &'a [u8],
    /// Will be set to value.len() by constructor.
    length: Len,
}

impl<'a> VarCharParam<'a> {
    pub fn new(value: &'a [u8]) -> Self {
        VarCharParam {
            bytes: value,
            length: value.len().try_into().unwrap(),
        }
    }
}

unsafe impl Parameter for VarCharParam<'_> {
    unsafe fn bind_parameter_to(
        &self,
        stmt: &mut Statement<'_>,
        parameter_number: u16,
    ) -> Result<(), Error> {
        stmt.bind_parameter(
            parameter_number,
            ParamType::Input,
            CDataType::Char,
            DataType::Varchar {
                length: self.bytes.len().try_into().unwrap(),
            },
            self.bytes.as_ptr() as Pointer,
            self.length, // Since we only bind a single input parameter this value should be ignored.
            &self.length as *const Len as *mut Len,
        )
    }
}

/// Annotates an instance of an inner type with an SQL Data type in order to indicate how it should
/// be bound as a parameter to an SQL Statement.
pub struct WithDataType<T> {
    pub value: T,
    pub data_type: DataType,
}

unsafe impl<T> Parameter for WithDataType<T>
where
    T: FixedSizedCType,
{
    unsafe fn bind_parameter_to(
        &self,
        stmt: &mut Statement<'_>,
        parameter_number: u16,
    ) -> Result<(), Error> {
        stmt.bind_parameter(
            parameter_number,
            ParamType::Input,
            T::C_DATA_TYPE,
            DataType::Integer,
            &self.value as *const T as *mut T as Pointer,
            0,
            null_mut(),
        )
    }
}