diesel/mysql/connection/
bind.rs

1#![allow(unsafe_code)] // module uses ffi
2use mysqlclient_sys as ffi;
3use std::mem;
4use std::mem::MaybeUninit;
5use std::ops::Index;
6use std::os::raw as libc;
7use std::ptr::NonNull;
8
9use super::stmt::MysqlFieldMetadata;
10use super::stmt::StatementUse;
11use crate::mysql::connection::stmt::StatementMetadata;
12use crate::mysql::types::date_and_time::MysqlTime;
13use crate::mysql::{MysqlType, MysqlValue};
14use crate::result::QueryResult;
15
16pub(super) struct PreparedStatementBinds(Binds);
17
18pub(super) struct OutputBinds(Binds);
19
20impl Clone for OutputBinds {
21    fn clone(&self) -> Self {
22        Self(Binds {
23            data: self.0.data.clone(),
24        })
25    }
26}
27
28struct Binds {
29    data: Vec<BindData>,
30}
31
32impl PreparedStatementBinds {
33    pub(super) fn from_input_data<Iter>(input: Iter) -> Self
34    where
35        Iter: IntoIterator<Item = (MysqlType, Option<Vec<u8>>)>,
36    {
37        let data = input
38            .into_iter()
39            .map(BindData::for_input)
40            .collect::<Vec<_>>();
41
42        Self(Binds { data })
43    }
44
45    pub(super) fn with_mysql_binds<F, T>(&mut self, f: F) -> T
46    where
47        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
48    {
49        self.0.with_mysql_binds(f)
50    }
51}
52
53impl OutputBinds {
54    pub(super) fn from_output_types(
55        types: &[Option<MysqlType>],
56        metadata: &StatementMetadata,
57    ) -> Self {
58        let data = metadata
59            .fields()
60            .iter()
61            .zip(types.iter().copied().chain(std::iter::repeat(None)))
62            .map(|(field, tpe)| BindData::for_output(tpe, field))
63            .collect();
64
65        Self(Binds { data })
66    }
67
68    pub(super) fn populate_dynamic_buffers(&mut self, stmt: &StatementUse<'_>) -> QueryResult<()> {
69        for (i, data) in self.0.data.iter_mut().enumerate() {
70            data.did_numeric_overflow_occur()?;
71            // This is safe because we are re-binding the invalidated buffers
72            // at the end of this function
73            unsafe {
74                if let Some((mut bind, offset)) = data.bind_for_truncated_data() {
75                    stmt.fetch_column(&mut bind, i, offset)?
76                } else {
77                    data.update_buffer_length()
78                }
79            }
80        }
81
82        unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) }
83    }
84
85    pub(super) fn update_buffer_lengths(&mut self) {
86        for data in &mut self.0.data {
87            data.update_buffer_length();
88        }
89    }
90
91    pub(super) fn with_mysql_binds<F, T>(&mut self, f: F) -> T
92    where
93        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
94    {
95        self.0.with_mysql_binds(f)
96    }
97}
98
99impl Binds {
100    fn with_mysql_binds<F, T>(&mut self, f: F) -> T
101    where
102        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
103    {
104        let mut binds = self
105            .data
106            .iter_mut()
107            .map(|x| unsafe { x.mysql_bind() })
108            .collect::<Vec<_>>();
109        f(binds.as_mut_ptr())
110    }
111}
112
113impl Index<usize> for OutputBinds {
114    type Output = BindData;
115    fn index(&self, index: usize) -> &Self::Output {
116        &self.0.data[index]
117    }
118}
119
120bitflags::bitflags! {
121    #[derive(Clone, Copy, Debug)]
122    pub(crate) struct Flags: u32 {
123        const NOT_NULL_FLAG = 1;
124        const PRI_KEY_FLAG = 2;
125        const UNIQUE_KEY_FLAG = 4;
126        const MULTIPLE_KEY_FLAG = 8;
127        const BLOB_FLAG = 16;
128        const UNSIGNED_FLAG = 32;
129        const ZEROFILL_FLAG = 64;
130        const BINARY_FLAG = 128;
131        const ENUM_FLAG = 256;
132        const AUTO_INCREMENT_FLAG = 512;
133        const TIMESTAMP_FLAG = 1024;
134        const SET_FLAG = 2048;
135        const NO_DEFAULT_VALUE_FLAG = 4096;
136        const ON_UPDATE_NOW_FLAG = 8192;
137        const NUM_FLAG = 32768;
138        const PART_KEY_FLAG = 16384;
139        const GROUP_FLAG = 32768;
140        const UNIQUE_FLAG = 65536;
141        const BINCMP_FLAG = 130_172;
142        const GET_FIXED_FIELDS_FLAG = (1<<18);
143        const FIELD_IN_PART_FUNC_FLAG = (1 << 19);
144    }
145}
146
147impl From<u32> for Flags {
148    fn from(flags: u32) -> Self {
149        Flags::from_bits(flags).expect(
150            "We encountered an unknown type flag while parsing \
151             Mysql's type information. If you see this error message \
152             please open an issue at diesels github page.",
153        )
154    }
155}
156
157#[derive(Debug)]
158pub(super) struct BindData {
159    tpe: ffi::enum_field_types,
160    bytes: Option<NonNull<u8>>,
161    length: libc::c_ulong,
162    capacity: usize,
163    flags: Flags,
164    is_null: ffi::my_bool,
165    is_truncated: Option<ffi::my_bool>,
166}
167
168// We need to write a manual clone impl
169// as we need to clone the underlying buffer
170// instead of just copying the pointer
171impl Clone for BindData {
172    fn clone(&self) -> Self {
173        let (ptr, len, capacity) = if let Some(ptr) = self.bytes {
174            let slice = unsafe {
175                // We know that this points to a slice and the pointer is not null at this
176                // location
177                // The length pointer is valid as long as none missuses `bind_for_truncated_data`
178                // as this is the only location that updates the length field before the corresponding data are
179                // written. At the time of writing this comment, the `BindData::bind_for_truncated_data`
180                // function is only called by `Binds::populate_dynamic_buffers` which ensures the corresponding
181                // invariant.
182                std::slice::from_raw_parts(
183                    ptr.as_ptr(),
184                    self.length.try_into().expect("usize is at least 32bit"),
185                )
186            };
187            let mut vec = slice.to_owned();
188            let ptr = NonNull::new(vec.as_mut_ptr());
189            let len = vec.len() as libc::c_ulong;
190            let capacity = vec.capacity();
191            mem::forget(vec);
192            (ptr, len, capacity)
193        } else {
194            (None, 0, 0)
195        };
196        Self {
197            tpe: self.tpe,
198            bytes: ptr,
199            length: len,
200            capacity,
201            flags: self.flags,
202            is_null: self.is_null,
203            is_truncated: self.is_truncated,
204        }
205    }
206}
207
208impl Drop for BindData {
209    fn drop(&mut self) {
210        if let Some(bytes) = self.bytes {
211            std::mem::drop(unsafe {
212                // We know that this buffer was allocated by a vector, so constructing a vector from it is fine
213                // We know the correct capacity here
214                // We use 0 as length to prevent situations where the length is already updated but
215                // no date are already written as we could touch uninitialized memory otherwise
216                // Using 0 as length is fine as we don't need to call drop for `u8`
217                // (as there is no drop impl for primitive types)
218                Vec::from_raw_parts(bytes.as_ptr(), 0, self.capacity)
219            });
220            self.bytes = None;
221        }
222    }
223}
224
225impl BindData {
226    fn for_input((tpe, data): (MysqlType, Option<Vec<u8>>)) -> Self {
227        let (tpe, flags) = tpe.into();
228        let is_null = ffi::my_bool::from(data.is_none());
229        let mut bytes = data.unwrap_or_default();
230        let ptr = NonNull::new(bytes.as_mut_ptr());
231        let len = bytes.len() as libc::c_ulong;
232        let capacity = bytes.capacity();
233        mem::forget(bytes);
234        Self {
235            tpe,
236            bytes: ptr,
237            length: len,
238            capacity,
239            flags,
240            is_null,
241            is_truncated: None,
242        }
243    }
244
245    fn for_output(tpe: Option<MysqlType>, metadata: &MysqlFieldMetadata<'_>) -> Self {
246        let (tpe, flags) = if let Some(tpe) = tpe {
247            match (tpe, metadata.field_type()) {
248                // Those are types where we handle the conversion in diesel itself
249                // and do not relay on libmysqlclient
250                (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
251                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_TINY)
252                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_SHORT)
253                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_LONG)
254                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
255                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
256                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_INT24)
257                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
258                | (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
259                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
260                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_TINY)
261                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_SHORT)
262                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_LONG)
263                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
264                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
265                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_INT24)
266                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
267                | (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
268                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
269                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_TINY)
270                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_SHORT)
271                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_LONG)
272                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
273                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
274                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_INT24)
275                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
276                | (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
277                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
278                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_TINY)
279                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_SHORT)
280                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_LONG)
281                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
282                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
283                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_INT24)
284                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
285                | (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
286                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
287                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_TINY)
288                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_SHORT)
289                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_LONG)
290                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
291                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
292                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_INT24)
293                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
294                | (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
295                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
296                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_TINY)
297                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_SHORT)
298                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_LONG)
299                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
300                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
301                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_INT24)
302                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
303                | (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
304                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
305                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_TINY)
306                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_SHORT)
307                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_LONG)
308                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
309                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
310                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_INT24)
311                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
312                | (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
313                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
314                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_TINY)
315                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_SHORT)
316                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_LONG)
317                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
318                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
319                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_INT24)
320                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
321                | (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
322                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
323                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_TINY)
324                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_SHORT)
325                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_LONG)
326                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
327                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
328                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_INT24)
329                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
330                | (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
331                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
332                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_TINY)
333                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_SHORT)
334                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_LONG)
335                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
336                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
337                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_INT24)
338                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
339                | (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
340                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_JSON)
341                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_ENUM)
342                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_SET)
343                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
344                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
345                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
346                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_BLOB)
347                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING)
348                | (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_STRING)
349                | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
350                | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
351                | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
352                | (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_BLOB)
353                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_ENUM)
354                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_SET)
355                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
356                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
357                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
358                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_BLOB)
359                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING)
360                | (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_STRING)
361                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_ENUM)
362                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_SET)
363                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
364                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
365                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
366                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_BLOB)
367                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING)
368                | (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_STRING) => {
369                    (metadata.field_type(), metadata.flags())
370                }
371
372                (tpe, _) => tpe.into(),
373            }
374        } else {
375            (metadata.field_type(), metadata.flags())
376        };
377        Self::from_tpe_and_flags((tpe, flags))
378    }
379
380    fn from_tpe_and_flags((tpe, flags): (ffi::enum_field_types, Flags)) -> Self {
381        // newer mysqlclient versions do not accept a zero sized buffer
382        let len = known_buffer_size_for_ffi_type(tpe).unwrap_or(1);
383        let mut bytes = vec![0; len];
384        let length = bytes.len() as libc::c_ulong;
385        let capacity = bytes.capacity();
386        let ptr = NonNull::new(bytes.as_mut_ptr());
387        mem::forget(bytes);
388
389        Self {
390            tpe,
391            bytes: ptr,
392            length,
393            capacity,
394            flags,
395            is_null: super::raw::ffi_false(),
396            is_truncated: Some(super::raw::ffi_false()),
397        }
398    }
399
400    fn is_truncated(&self) -> bool {
401        self.is_truncated.unwrap_or(super::raw::ffi_false()) != super::raw::ffi_false()
402    }
403
404    fn is_fixed_size_buffer(&self) -> bool {
405        known_buffer_size_for_ffi_type(self.tpe).is_some()
406    }
407
408    pub(super) fn value(&'_ self) -> Option<MysqlValue<'_>> {
409        if self.is_null() {
410            None
411        } else {
412            let data = self.bytes?;
413            let tpe = (self.tpe, self.flags).into();
414            // On some distributions, the mariadb client library returns length 0 for NULL fields of type DECIMAL
415            // instead of using is_null for unknown reasons
416            if self.tpe == self::ffi::enum_field_types::MYSQL_TYPE_LONGLONG && self.length == 0 {
417                return None;
418            }
419
420            let slice = unsafe {
421                // We know that this points to a slice and the pointer is not null at this
422                // location
423                // The length pointer is valid as long as none missuses `bind_for_truncated_data`
424                // as this is the only location that updates the length field before the corresponding data are
425                // written. At the time of writing this comment, the `BindData::bind_for_truncated_data`
426                // function is only called by `Binds::populate_dynamic_buffers` which ensures the corresponding
427                // invariant.
428                std::slice::from_raw_parts(
429                    data.as_ptr(),
430                    self.length.try_into().expect("Usize is at least 32 bit"),
431                )
432            };
433            Some(MysqlValue::new_internal(slice, tpe))
434        }
435    }
436
437    pub(super) fn is_null(&self) -> bool {
438        self.is_null != ffi::my_bool::default()
439    }
440
441    fn update_buffer_length(&mut self) {
442        use std::cmp::min;
443
444        let actual_bytes_in_buffer = min(
445            self.capacity,
446            self.length.try_into().expect("Usize is at least 32 bit"),
447        );
448        self.length = actual_bytes_in_buffer as libc::c_ulong;
449    }
450
451    // This function is marked as unsafe as it returns an owned value
452    // containing a pointer with a lifetime coupled to self.
453    // Callers need to ensure that the returned value cannot outlive `self`
454    unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND {
455        use std::ptr::addr_of_mut;
456
457        let mut bind: MaybeUninit<ffi::MYSQL_BIND> = mem::MaybeUninit::zeroed();
458        let ptr = bind.as_mut_ptr();
459
460        addr_of_mut!((*ptr).buffer_type).write(self.tpe);
461        addr_of_mut!((*ptr).buffer).write(
462            self.bytes
463                .map(|p| p.as_ptr())
464                .unwrap_or(std::ptr::null_mut()) as *mut libc::c_void,
465        );
466        addr_of_mut!((*ptr).buffer_length).write(self.capacity as libc::c_ulong);
467        addr_of_mut!((*ptr).length).write(&mut self.length);
468        addr_of_mut!((*ptr).is_null).write(&mut self.is_null);
469        addr_of_mut!((*ptr).is_unsigned)
470            .write(self.flags.contains(Flags::UNSIGNED_FLAG) as ffi::my_bool);
471
472        if let Some(ref mut is_truncated) = self.is_truncated {
473            addr_of_mut!((*ptr).error).write(is_truncated);
474        }
475
476        // That's what the mysqlclient examples are doing
477        bind.assume_init()
478    }
479
480    /// Resizes the byte buffer to fit the value of `self.length`, and returns
481    /// a tuple of a bind pointing at the truncated data, and the offset to use
482    /// in order to read the truncated data into it.
483    ///
484    /// This invalidates the bind previously returned by `mysql_bind`. Calling
485    /// this function is unsafe unless the binds are immediately rebound.
486    unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> {
487        if self.is_truncated() {
488            if let Some(bytes) = self.bytes {
489                let mut bytes = Vec::from_raw_parts(bytes.as_ptr(), self.capacity, self.capacity);
490                self.bytes = None;
491
492                let offset = self.capacity;
493                let truncated_amount =
494                    usize::try_from(self.length).expect("Usize is at least 32 bit") - offset;
495
496                debug_assert!(
497                    truncated_amount > 0,
498                    "output buffers were invalidated \
499                     without calling `mysql_stmt_bind_result`"
500                );
501
502                // reserve space for any missing byte
503                // we know the exact size here
504                bytes.reserve(truncated_amount);
505                self.capacity = bytes.capacity();
506                self.bytes = NonNull::new(bytes.as_mut_ptr());
507                mem::forget(bytes);
508
509                let mut bind = self.mysql_bind();
510
511                if let Some(ptr) = self.bytes {
512                    // Using offset is safe here as we have a u8 array (where std::mem::size_of::<u8> == 1)
513                    // and we have a buffer that has at least
514                    bind.buffer = ptr.as_ptr().add(offset) as *mut libc::c_void;
515                    bind.buffer_length = truncated_amount as libc::c_ulong;
516                } else {
517                    bind.buffer_length = 0;
518                }
519                Some((bind, offset))
520            } else {
521                // offset is zero here as we don't have a buffer yet
522                // we know the requested length here so we can just request
523                // the correct size
524                let mut vec = vec![0_u8; self.length.try_into().expect("usize is at least 32 bit")];
525                self.capacity = vec.capacity();
526                self.bytes = NonNull::new(vec.as_mut_ptr());
527                mem::forget(vec);
528
529                let bind = self.mysql_bind();
530                // As we did not have a buffer before
531                // we couldn't have loaded any data yet, therefore
532                // request everything
533                Some((bind, 0))
534            }
535        } else {
536            None
537        }
538    }
539
540    fn did_numeric_overflow_occur(&self) -> QueryResult<()> {
541        use crate::result::Error::DeserializationError;
542
543        if self.is_truncated() && self.is_fixed_size_buffer() {
544            Err(DeserializationError(
545                "Numeric overflow/underflow occurred".into(),
546            ))
547        } else {
548            Ok(())
549        }
550    }
551}
552
553impl From<MysqlType> for (ffi::enum_field_types, Flags) {
554    fn from(tpe: MysqlType) -> Self {
555        use self::ffi::enum_field_types;
556        let mut flags = Flags::empty();
557        let tpe = match tpe {
558            MysqlType::Tiny => enum_field_types::MYSQL_TYPE_TINY,
559            MysqlType::Short => enum_field_types::MYSQL_TYPE_SHORT,
560            MysqlType::Long => enum_field_types::MYSQL_TYPE_LONG,
561            MysqlType::LongLong => enum_field_types::MYSQL_TYPE_LONGLONG,
562            MysqlType::Float => enum_field_types::MYSQL_TYPE_FLOAT,
563            MysqlType::Double => enum_field_types::MYSQL_TYPE_DOUBLE,
564            MysqlType::Time => enum_field_types::MYSQL_TYPE_TIME,
565            MysqlType::Date => enum_field_types::MYSQL_TYPE_DATE,
566            MysqlType::DateTime => enum_field_types::MYSQL_TYPE_DATETIME,
567            MysqlType::Timestamp => enum_field_types::MYSQL_TYPE_TIMESTAMP,
568            MysqlType::String => enum_field_types::MYSQL_TYPE_STRING,
569            MysqlType::Blob => enum_field_types::MYSQL_TYPE_BLOB,
570            MysqlType::Numeric => enum_field_types::MYSQL_TYPE_NEWDECIMAL,
571            MysqlType::Bit => enum_field_types::MYSQL_TYPE_BIT,
572            MysqlType::UnsignedTiny => {
573                flags = Flags::UNSIGNED_FLAG;
574                enum_field_types::MYSQL_TYPE_TINY
575            }
576            MysqlType::UnsignedShort => {
577                flags = Flags::UNSIGNED_FLAG;
578                enum_field_types::MYSQL_TYPE_SHORT
579            }
580            MysqlType::UnsignedLong => {
581                flags = Flags::UNSIGNED_FLAG;
582                enum_field_types::MYSQL_TYPE_LONG
583            }
584            MysqlType::UnsignedLongLong => {
585                flags = Flags::UNSIGNED_FLAG;
586                enum_field_types::MYSQL_TYPE_LONGLONG
587            }
588            MysqlType::Set => {
589                flags = Flags::SET_FLAG;
590                enum_field_types::MYSQL_TYPE_STRING
591            }
592            MysqlType::Enum => {
593                flags = Flags::ENUM_FLAG;
594                enum_field_types::MYSQL_TYPE_STRING
595            }
596        };
597        (tpe, flags)
598    }
599}
600
601impl From<(ffi::enum_field_types, Flags)> for MysqlType {
602    fn from((tpe, flags): (ffi::enum_field_types, Flags)) -> Self {
603        use self::ffi::enum_field_types;
604
605        let is_unsigned = flags.contains(Flags::UNSIGNED_FLAG);
606
607        // https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/c-api-data-structures.html
608        // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/binary__log__types_8h.html
609        // https://dev.mysql.com/doc/internals/en/binary-protocol-value.html
610        // https://mariadb.com/kb/en/packet_bindata/
611        match tpe {
612            enum_field_types::MYSQL_TYPE_TINY if is_unsigned => MysqlType::UnsignedTiny,
613            enum_field_types::MYSQL_TYPE_YEAR | enum_field_types::MYSQL_TYPE_SHORT
614                if is_unsigned =>
615            {
616                MysqlType::UnsignedShort
617            }
618            enum_field_types::MYSQL_TYPE_INT24 | enum_field_types::MYSQL_TYPE_LONG
619                if is_unsigned =>
620            {
621                MysqlType::UnsignedLong
622            }
623            enum_field_types::MYSQL_TYPE_LONGLONG if is_unsigned => MysqlType::UnsignedLongLong,
624            enum_field_types::MYSQL_TYPE_TINY => MysqlType::Tiny,
625            enum_field_types::MYSQL_TYPE_SHORT => MysqlType::Short,
626            enum_field_types::MYSQL_TYPE_INT24 | enum_field_types::MYSQL_TYPE_LONG => {
627                MysqlType::Long
628            }
629            enum_field_types::MYSQL_TYPE_LONGLONG => MysqlType::LongLong,
630            enum_field_types::MYSQL_TYPE_FLOAT => MysqlType::Float,
631            enum_field_types::MYSQL_TYPE_DOUBLE => MysqlType::Double,
632            enum_field_types::MYSQL_TYPE_DECIMAL | enum_field_types::MYSQL_TYPE_NEWDECIMAL => {
633                MysqlType::Numeric
634            }
635            enum_field_types::MYSQL_TYPE_BIT => MysqlType::Bit,
636
637            enum_field_types::MYSQL_TYPE_TIME => MysqlType::Time,
638            enum_field_types::MYSQL_TYPE_DATE => MysqlType::Date,
639            enum_field_types::MYSQL_TYPE_DATETIME => MysqlType::DateTime,
640            enum_field_types::MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp,
641            // Treat json as string because even mysql 8.0
642            // throws errors sometimes if we use json for json
643            enum_field_types::MYSQL_TYPE_JSON => MysqlType::String,
644
645            // The documentation states that
646            // MYSQL_TYPE_STRING is used for enums and sets
647            // but experimentation has shown that
648            // just any string like type works, so
649            // better be safe here
650            enum_field_types::MYSQL_TYPE_BLOB
651            | enum_field_types::MYSQL_TYPE_TINY_BLOB
652            | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
653            | enum_field_types::MYSQL_TYPE_LONG_BLOB
654            | enum_field_types::MYSQL_TYPE_VAR_STRING
655            | enum_field_types::MYSQL_TYPE_STRING
656                if flags.contains(Flags::ENUM_FLAG) =>
657            {
658                MysqlType::Enum
659            }
660            enum_field_types::MYSQL_TYPE_BLOB
661            | enum_field_types::MYSQL_TYPE_TINY_BLOB
662            | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
663            | enum_field_types::MYSQL_TYPE_LONG_BLOB
664            | enum_field_types::MYSQL_TYPE_VAR_STRING
665            | enum_field_types::MYSQL_TYPE_STRING
666                if flags.contains(Flags::SET_FLAG) =>
667            {
668                MysqlType::Set
669            }
670
671            // "blobs" may contain binary data
672            // also "strings" can contain binary data
673            // but all only if the binary flag is set
674            // (see the check_all_the_types test case)
675            enum_field_types::MYSQL_TYPE_BLOB
676            | enum_field_types::MYSQL_TYPE_TINY_BLOB
677            | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
678            | enum_field_types::MYSQL_TYPE_LONG_BLOB
679            | enum_field_types::MYSQL_TYPE_VAR_STRING
680            | enum_field_types::MYSQL_TYPE_STRING
681                if flags.contains(Flags::BINARY_FLAG) =>
682            {
683                MysqlType::Blob
684            }
685
686            // If the binary flag is not set consider everything as string
687            enum_field_types::MYSQL_TYPE_BLOB
688            | enum_field_types::MYSQL_TYPE_TINY_BLOB
689            | enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
690            | enum_field_types::MYSQL_TYPE_LONG_BLOB
691            | enum_field_types::MYSQL_TYPE_VAR_STRING
692            | enum_field_types::MYSQL_TYPE_STRING => MysqlType::String,
693
694            // unsigned seems to be set for year in any case
695            enum_field_types::MYSQL_TYPE_YEAR => unreachable!(
696                "The year type should have set the unsigned flag. If you ever \
697                 see this error message, something has gone very wrong. Please \
698                 open an issue at the diesel github repo in this case"
699            ),
700            // Null value
701            enum_field_types::MYSQL_TYPE_NULL => unreachable!(
702                "We ensure at the call side that we do not hit this type here. \
703                 If you ever see this error, something has gone very wrong. \
704                 Please open an issue at the diesel github repo in this case"
705            ),
706            // Those exist in libmysqlclient
707            // but are just not supported
708            //
709            enum_field_types::MYSQL_TYPE_VARCHAR
710            | enum_field_types::MYSQL_TYPE_ENUM
711            | enum_field_types::MYSQL_TYPE_SET
712            | enum_field_types::MYSQL_TYPE_GEOMETRY => {
713                unimplemented!(
714                    "Hit a type that should be unsupported in libmysqlclient. If \
715                     you ever see this error, they probably have added support for \
716                     one of those types. Please open an issue at the diesel github \
717                     repo in this case."
718                )
719            }
720
721            enum_field_types::MYSQL_TYPE_NEWDATE
722            | enum_field_types::MYSQL_TYPE_TIME2
723            | enum_field_types::MYSQL_TYPE_DATETIME2
724            | enum_field_types::MYSQL_TYPE_TIMESTAMP2 => unreachable!(
725                "The mysql documentation states that these types are \
726                 only used on the server side, so if you see this error \
727                 something has gone wrong. Please open an issue at \
728                 the diesel github repo."
729            ),
730            // depending on the bindings version
731            // there might be no unlisted field type
732            #[allow(unreachable_patterns)]
733            t => unreachable!(
734                "Unsupported type encountered: {t:?}. \
735                 If you ever see this error, something has gone wrong. \
736                 Please open an issue at the diesel github \
737                 repo in this case."
738            ),
739        }
740    }
741}
742
743fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option<usize> {
744    use self::ffi::enum_field_types as t;
745    use std::mem::size_of;
746
747    match tpe {
748        t::MYSQL_TYPE_TINY => Some(1),
749        t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2),
750        t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4),
751        t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8),
752        t::MYSQL_TYPE_TIME
753        | t::MYSQL_TYPE_DATE
754        | t::MYSQL_TYPE_DATETIME
755        | t::MYSQL_TYPE_TIMESTAMP => Some(size_of::<MysqlTime>()),
756        _ => None,
757    }
758}
759
760#[cfg(test)]
761mod tests {
762    use super::*;
763    use crate::connection::statement_cache::MaybeCached;
764    use crate::deserialize::FromSql;
765    use crate::mysql::connection::stmt::Statement;
766    use crate::prelude::*;
767    use crate::sql_types::*;
768    #[cfg(feature = "numeric")]
769    use std::str::FromStr;
770
771    fn to_value<ST, T>(
772        bind: &BindData,
773    ) -> Result<T, Box<(dyn std::error::Error + Send + Sync + 'static)>>
774    where
775        T: FromSql<ST, crate::mysql::Mysql> + std::fmt::Debug,
776    {
777        let meta = (bind.tpe, bind.flags).into();
778        dbg!(meta);
779
780        let value = bind.value().expect("Is not null");
781        let value = MysqlValue::new_internal(value.as_bytes(), meta);
782
783        dbg!(T::from_sql(value))
784    }
785
786    #[cfg(feature = "extras")]
787    #[test]
788    fn check_all_the_types() {
789        let conn = &mut crate::test_helpers::connection();
790
791        crate::sql_query("DROP TABLE IF EXISTS all_mysql_types CASCADE")
792            .execute(conn)
793            .unwrap();
794        crate::sql_query(
795            "CREATE TABLE all_mysql_types (
796                    tiny_int TINYINT NOT NULL,
797                    small_int SMALLINT NOT NULL,
798                    medium_int MEDIUMINT NOT NULL,
799                    int_col INTEGER NOT NULL,
800                    big_int BIGINT NOT NULL,
801                    unsigned_int INTEGER UNSIGNED NOT NULL,
802                    zero_fill_int INTEGER ZEROFILL NOT NULL,
803                    numeric_col NUMERIC(20,5) NOT NULL,
804                    decimal_col DECIMAL(20,5) NOT NULL,
805                    float_col FLOAT NOT NULL,
806                    double_col DOUBLE NOT NULL,
807                    bit_col BIT(8) NOT NULL,
808                    date_col DATE NOT NULL,
809                    date_time DATETIME NOT NULL,
810                    timestamp_col TIMESTAMP NOT NULL,
811                    time_col TIME NOT NULL,
812                    year_col YEAR NOT NULL,
813                    char_col CHAR(30) NOT NULL,
814                    varchar_col VARCHAR(30) NOT NULL,
815                    binary_col BINARY(30) NOT NULL,
816                    varbinary_col VARBINARY(30) NOT NULL,
817                    blob_col BLOB NOT NULL,
818                    text_col TEXT NOT NULL,
819                    enum_col ENUM('red', 'green', 'blue') NOT NULL,
820                    set_col SET('one', 'two') NOT NULL,
821                    geom GEOMETRY NOT NULL,
822                    point_col POINT NOT NULL,
823                    linestring_col LINESTRING NOT NULL,
824                    polygon_col POLYGON NOT NULL,
825                    multipoint_col MULTIPOINT NOT NULL,
826                    multilinestring_col MULTILINESTRING NOT NULL,
827                    multipolygon_col MULTIPOLYGON NOT NULL,
828                    geometry_collection GEOMETRYCOLLECTION NOT NULL,
829                    json_col JSON NOT NULL
830            )",
831        )
832        .execute(conn)
833        .unwrap();
834        crate::sql_query(
835                "INSERT INTO all_mysql_types VALUES (
836                    0, -- tiny_int
837                    1, -- small_int
838                    2, -- medium_int
839                    3, -- int_col
840                    -5, -- big_int
841                    42, -- unsigned_int
842                    1, -- zero_fill_int
843                    -999.999, -- numeric_col,
844                    3.14, -- decimal_col,
845                    1.23, -- float_col
846                    4.5678, -- double_col
847                    b'10101010', -- bit_col
848                    '1000-01-01', -- date_col
849                    '9999-12-31 12:34:45.012345', -- date_time
850                    '2020-01-01 10:10:10', -- timestamp_col
851                    '23:01:01', -- time_col
852                    2020, -- year_col
853                    'abc', -- char_col
854                    'foo', -- varchar_col
855                    'a ', -- binary_col
856                    'a ', -- varbinary_col
857                    'binary', -- blob_col
858                    'some text whatever', -- text_col
859                    'red', -- enum_col
860                    'one', -- set_col
861                    ST_GeomFromText('POINT(1 1)'), -- geom
862                    ST_PointFromText('POINT(1 1)'), -- point_col
863                    ST_LineStringFromText('LINESTRING(0 0,1 1,2 2)'), -- linestring_col
864                    ST_PolygonFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))'), -- polygon_col
865                    ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)'), -- multipoint_col
866                    ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))'), -- multilinestring_col
867                    ST_MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))'), -- multipolygon_col
868                    ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'), -- geometry_collection
869                    '{\"key1\": \"value1\", \"key2\": \"value2\"}' -- json_col
870)",
871            ).execute(conn)
872            .unwrap();
873
874        let stmt = crate::mysql::connection::prepared_query(
875            &crate::sql_query(
876                "SELECT
877                    tiny_int, small_int, medium_int, int_col,
878                    big_int, unsigned_int, zero_fill_int,
879                    numeric_col, decimal_col, float_col, double_col, bit_col,
880                    date_col, date_time, timestamp_col, time_col, year_col,
881                    char_col, varchar_col, binary_col, varbinary_col, blob_col,
882                    text_col, enum_col, set_col, ST_AsText(geom), ST_AsText(point_col), ST_AsText(linestring_col),
883                    ST_AsText(polygon_col), ST_AsText(multipoint_col), ST_AsText(multilinestring_col),
884                    ST_AsText(multipolygon_col), ST_AsText(geometry_collection), json_col
885                 FROM all_mysql_types",
886            ),
887            &mut conn.statement_cache,
888            &mut conn.raw_connection,
889            &mut conn.instrumentation,
890        ).unwrap();
891
892        let metadata = stmt.metadata().unwrap();
893        let mut output_binds =
894            OutputBinds::from_output_types(&vec![None; metadata.fields().len()], &metadata);
895        let stmt = stmt.execute_statement(&mut output_binds).unwrap();
896        stmt.populate_row_buffers(&mut output_binds).unwrap();
897
898        let results: Vec<(BindData, &_)> = output_binds
899            .0
900            .data
901            .into_iter()
902            .zip(metadata.fields())
903            .collect::<Vec<_>>();
904
905        let tiny_int_col = &results[0].0;
906        assert_eq!(tiny_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TINY);
907        assert!(tiny_int_col.flags.contains(Flags::NUM_FLAG));
908        assert!(!tiny_int_col.flags.contains(Flags::UNSIGNED_FLAG));
909        assert!(matches!(to_value::<TinyInt, i8>(tiny_int_col), Ok(0)));
910
911        let small_int_col = &results[1].0;
912        assert_eq!(small_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_SHORT);
913        assert!(small_int_col.flags.contains(Flags::NUM_FLAG));
914        assert!(!small_int_col.flags.contains(Flags::UNSIGNED_FLAG));
915        assert!(matches!(to_value::<SmallInt, i16>(small_int_col), Ok(1)));
916
917        let medium_int_col = &results[2].0;
918        assert_eq!(medium_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_INT24);
919        assert!(medium_int_col.flags.contains(Flags::NUM_FLAG));
920        assert!(!medium_int_col.flags.contains(Flags::UNSIGNED_FLAG));
921        assert!(matches!(to_value::<Integer, i32>(medium_int_col), Ok(2)));
922
923        let int_col = &results[3].0;
924        assert_eq!(int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG);
925        assert!(int_col.flags.contains(Flags::NUM_FLAG));
926        assert!(!int_col.flags.contains(Flags::UNSIGNED_FLAG));
927        assert!(matches!(to_value::<Integer, i32>(int_col), Ok(3)));
928
929        let big_int_col = &results[4].0;
930        assert_eq!(big_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONGLONG);
931        assert!(big_int_col.flags.contains(Flags::NUM_FLAG));
932        assert!(!big_int_col.flags.contains(Flags::UNSIGNED_FLAG));
933        assert!(matches!(to_value::<TinyInt, i8>(big_int_col), Ok(-5)));
934
935        let unsigned_int_col = &results[5].0;
936        assert_eq!(unsigned_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG);
937        assert!(unsigned_int_col.flags.contains(Flags::NUM_FLAG));
938        assert!(unsigned_int_col.flags.contains(Flags::UNSIGNED_FLAG));
939        assert!(matches!(
940            to_value::<Unsigned<Integer>, u32>(unsigned_int_col),
941            Ok(42)
942        ));
943
944        let zero_fill_int_col = &results[6].0;
945        assert_eq!(
946            zero_fill_int_col.tpe,
947            ffi::enum_field_types::MYSQL_TYPE_LONG
948        );
949        assert!(zero_fill_int_col.flags.contains(Flags::NUM_FLAG));
950        assert!(zero_fill_int_col.flags.contains(Flags::ZEROFILL_FLAG));
951        assert!(matches!(to_value::<Integer, i32>(zero_fill_int_col), Ok(1)));
952
953        let numeric_col = &results[7].0;
954        assert_eq!(
955            numeric_col.tpe,
956            ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL
957        );
958        assert!(numeric_col.flags.contains(Flags::NUM_FLAG));
959        assert!(!numeric_col.flags.contains(Flags::UNSIGNED_FLAG));
960        assert_eq!(
961            to_value::<Numeric, bigdecimal::BigDecimal>(numeric_col).unwrap(),
962            bigdecimal::BigDecimal::from_str("-999.99900").unwrap()
963        );
964
965        let decimal_col = &results[8].0;
966        assert_eq!(
967            decimal_col.tpe,
968            ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL
969        );
970        assert!(decimal_col.flags.contains(Flags::NUM_FLAG));
971        assert!(!decimal_col.flags.contains(Flags::UNSIGNED_FLAG));
972        assert_eq!(
973            to_value::<Numeric, bigdecimal::BigDecimal>(decimal_col).unwrap(),
974            bigdecimal::BigDecimal::from_str("3.14000").unwrap()
975        );
976
977        let float_col = &results[9].0;
978        assert_eq!(float_col.tpe, ffi::enum_field_types::MYSQL_TYPE_FLOAT);
979        assert!(float_col.flags.contains(Flags::NUM_FLAG));
980        assert!(!float_col.flags.contains(Flags::UNSIGNED_FLAG));
981        assert_eq!(to_value::<Float, f32>(float_col).unwrap(), 1.23);
982
983        let double_col = &results[10].0;
984        assert_eq!(double_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DOUBLE);
985        assert!(double_col.flags.contains(Flags::NUM_FLAG));
986        assert!(!double_col.flags.contains(Flags::UNSIGNED_FLAG));
987        assert_eq!(to_value::<Double, f64>(double_col).unwrap(), 4.5678);
988
989        let bit_col = &results[11].0;
990        assert_eq!(bit_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BIT);
991        assert!(!bit_col.flags.contains(Flags::NUM_FLAG));
992        assert!(bit_col.flags.contains(Flags::UNSIGNED_FLAG));
993        assert!(!bit_col.flags.contains(Flags::BINARY_FLAG));
994        assert_eq!(to_value::<Blob, Vec<u8>>(bit_col).unwrap(), vec![170]);
995
996        let date_col = &results[12].0;
997        assert_eq!(date_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DATE);
998        assert!(!date_col.flags.contains(Flags::NUM_FLAG));
999        assert_eq!(
1000            to_value::<Date, chrono::NaiveDate>(date_col).unwrap(),
1001            chrono::NaiveDate::from_ymd_opt(1000, 1, 1).unwrap(),
1002        );
1003
1004        let date_time_col = &results[13].0;
1005        assert_eq!(
1006            date_time_col.tpe,
1007            ffi::enum_field_types::MYSQL_TYPE_DATETIME
1008        );
1009        assert!(!date_time_col.flags.contains(Flags::NUM_FLAG));
1010        assert_eq!(
1011            to_value::<Datetime, chrono::NaiveDateTime>(date_time_col).unwrap(),
1012            chrono::NaiveDateTime::parse_from_str("9999-12-31 12:34:45", "%Y-%m-%d %H:%M:%S")
1013                .unwrap()
1014        );
1015
1016        let timestamp_col = &results[14].0;
1017        assert_eq!(
1018            timestamp_col.tpe,
1019            ffi::enum_field_types::MYSQL_TYPE_TIMESTAMP
1020        );
1021        assert!(!timestamp_col.flags.contains(Flags::NUM_FLAG));
1022        assert_eq!(
1023            to_value::<Datetime, chrono::NaiveDateTime>(timestamp_col).unwrap(),
1024            chrono::NaiveDateTime::parse_from_str("2020-01-01 10:10:10", "%Y-%m-%d %H:%M:%S")
1025                .unwrap()
1026        );
1027
1028        let time_col = &results[15].0;
1029        assert_eq!(time_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TIME);
1030        assert!(!time_col.flags.contains(Flags::NUM_FLAG));
1031        assert_eq!(
1032            to_value::<Time, chrono::NaiveTime>(time_col).unwrap(),
1033            chrono::NaiveTime::from_hms_opt(23, 01, 01).unwrap()
1034        );
1035
1036        let year_col = &results[16].0;
1037        assert_eq!(year_col.tpe, ffi::enum_field_types::MYSQL_TYPE_YEAR);
1038        assert!(year_col.flags.contains(Flags::NUM_FLAG));
1039        assert!(year_col.flags.contains(Flags::UNSIGNED_FLAG));
1040        assert!(matches!(to_value::<SmallInt, i16>(year_col), Ok(2020)));
1041
1042        let char_col = &results[17].0;
1043        assert_eq!(char_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1044        assert!(!char_col.flags.contains(Flags::NUM_FLAG));
1045        assert!(!char_col.flags.contains(Flags::BLOB_FLAG));
1046        assert!(!char_col.flags.contains(Flags::SET_FLAG));
1047        assert!(!char_col.flags.contains(Flags::ENUM_FLAG));
1048        assert!(!char_col.flags.contains(Flags::BINARY_FLAG));
1049        assert_eq!(to_value::<Text, String>(char_col).unwrap(), "abc");
1050
1051        let varchar_col = &results[18].0;
1052        assert_eq!(
1053            varchar_col.tpe,
1054            ffi::enum_field_types::MYSQL_TYPE_VAR_STRING
1055        );
1056        assert!(!varchar_col.flags.contains(Flags::NUM_FLAG));
1057        assert!(!varchar_col.flags.contains(Flags::BLOB_FLAG));
1058        assert!(!varchar_col.flags.contains(Flags::SET_FLAG));
1059        assert!(!varchar_col.flags.contains(Flags::ENUM_FLAG));
1060        assert!(!varchar_col.flags.contains(Flags::BINARY_FLAG));
1061        assert_eq!(to_value::<Text, String>(varchar_col).unwrap(), "foo");
1062
1063        let binary_col = &results[19].0;
1064        assert_eq!(binary_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1065        assert!(!binary_col.flags.contains(Flags::NUM_FLAG));
1066        assert!(!binary_col.flags.contains(Flags::BLOB_FLAG));
1067        assert!(!binary_col.flags.contains(Flags::SET_FLAG));
1068        assert!(!binary_col.flags.contains(Flags::ENUM_FLAG));
1069        assert!(binary_col.flags.contains(Flags::BINARY_FLAG));
1070        assert_eq!(
1071            to_value::<Blob, Vec<u8>>(binary_col).unwrap(),
1072            b"a \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
1073        );
1074
1075        let varbinary_col = &results[20].0;
1076        assert_eq!(
1077            varbinary_col.tpe,
1078            ffi::enum_field_types::MYSQL_TYPE_VAR_STRING
1079        );
1080        assert!(!varbinary_col.flags.contains(Flags::NUM_FLAG));
1081        assert!(!varbinary_col.flags.contains(Flags::BLOB_FLAG));
1082        assert!(!varbinary_col.flags.contains(Flags::SET_FLAG));
1083        assert!(!varbinary_col.flags.contains(Flags::ENUM_FLAG));
1084        assert!(varbinary_col.flags.contains(Flags::BINARY_FLAG));
1085        assert_eq!(to_value::<Blob, Vec<u8>>(varbinary_col).unwrap(), b"a ");
1086
1087        let blob_col = &results[21].0;
1088        assert_eq!(blob_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1089        assert!(!blob_col.flags.contains(Flags::NUM_FLAG));
1090        assert!(blob_col.flags.contains(Flags::BLOB_FLAG));
1091        assert!(!blob_col.flags.contains(Flags::SET_FLAG));
1092        assert!(!blob_col.flags.contains(Flags::ENUM_FLAG));
1093        assert!(blob_col.flags.contains(Flags::BINARY_FLAG));
1094        assert_eq!(to_value::<Blob, Vec<u8>>(blob_col).unwrap(), b"binary");
1095
1096        let text_col = &results[22].0;
1097        assert_eq!(text_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1098        assert!(!text_col.flags.contains(Flags::NUM_FLAG));
1099        assert!(text_col.flags.contains(Flags::BLOB_FLAG));
1100        assert!(!text_col.flags.contains(Flags::SET_FLAG));
1101        assert!(!text_col.flags.contains(Flags::ENUM_FLAG));
1102        assert!(!text_col.flags.contains(Flags::BINARY_FLAG));
1103        assert_eq!(
1104            to_value::<Text, String>(text_col).unwrap(),
1105            "some text whatever"
1106        );
1107
1108        let enum_col = &results[23].0;
1109        assert_eq!(enum_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1110        assert!(!enum_col.flags.contains(Flags::NUM_FLAG));
1111        assert!(!enum_col.flags.contains(Flags::BLOB_FLAG));
1112        assert!(!enum_col.flags.contains(Flags::SET_FLAG));
1113        assert!(enum_col.flags.contains(Flags::ENUM_FLAG));
1114        assert!(!enum_col.flags.contains(Flags::BINARY_FLAG));
1115        assert_eq!(to_value::<Text, String>(enum_col).unwrap(), "red");
1116
1117        let set_col = &results[24].0;
1118        assert_eq!(set_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1119        assert!(!set_col.flags.contains(Flags::NUM_FLAG));
1120        assert!(!set_col.flags.contains(Flags::BLOB_FLAG));
1121        assert!(set_col.flags.contains(Flags::SET_FLAG));
1122        assert!(!set_col.flags.contains(Flags::ENUM_FLAG));
1123        assert!(!set_col.flags.contains(Flags::BINARY_FLAG));
1124        assert_eq!(to_value::<Text, String>(set_col).unwrap(), "one");
1125
1126        let geom = &results[25].0;
1127        assert_eq!(geom.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
1128        assert!(!geom.flags.contains(Flags::NUM_FLAG));
1129        assert!(!geom.flags.contains(Flags::BLOB_FLAG));
1130        assert!(!geom.flags.contains(Flags::SET_FLAG));
1131        assert!(!geom.flags.contains(Flags::ENUM_FLAG));
1132        assert!(!geom.flags.contains(Flags::BINARY_FLAG));
1133        assert_eq!(to_value::<Text, String>(geom).unwrap(), "POINT(1 1)");
1134
1135        let point_col = &results[26].0;
1136        assert_eq!(point_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
1137        assert!(!point_col.flags.contains(Flags::NUM_FLAG));
1138        assert!(!point_col.flags.contains(Flags::BLOB_FLAG));
1139        assert!(!point_col.flags.contains(Flags::SET_FLAG));
1140        assert!(!point_col.flags.contains(Flags::ENUM_FLAG));
1141        assert!(!point_col.flags.contains(Flags::BINARY_FLAG));
1142        assert_eq!(to_value::<Text, String>(point_col).unwrap(), "POINT(1 1)");
1143
1144        let linestring_col = &results[27].0;
1145        assert_eq!(
1146            linestring_col.tpe,
1147            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
1148        );
1149        assert!(!linestring_col.flags.contains(Flags::NUM_FLAG));
1150        assert!(!linestring_col.flags.contains(Flags::BLOB_FLAG));
1151        assert!(!linestring_col.flags.contains(Flags::SET_FLAG));
1152        assert!(!linestring_col.flags.contains(Flags::ENUM_FLAG));
1153        assert!(!linestring_col.flags.contains(Flags::BINARY_FLAG));
1154        assert_eq!(
1155            to_value::<Text, String>(linestring_col).unwrap(),
1156            "LINESTRING(0 0,1 1,2 2)"
1157        );
1158
1159        let polygon_col = &results[28].0;
1160        assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
1161        assert!(!polygon_col.flags.contains(Flags::NUM_FLAG));
1162        assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG));
1163        assert!(!polygon_col.flags.contains(Flags::SET_FLAG));
1164        assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG));
1165        assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG));
1166        assert_eq!(
1167            to_value::<Text, String>(polygon_col).unwrap(),
1168            "POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))"
1169        );
1170
1171        let multipoint_col = &results[29].0;
1172        assert_eq!(
1173            multipoint_col.tpe,
1174            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
1175        );
1176        assert!(!multipoint_col.flags.contains(Flags::NUM_FLAG));
1177        assert!(!multipoint_col.flags.contains(Flags::BLOB_FLAG));
1178        assert!(!multipoint_col.flags.contains(Flags::SET_FLAG));
1179        assert!(!multipoint_col.flags.contains(Flags::ENUM_FLAG));
1180        assert!(!multipoint_col.flags.contains(Flags::BINARY_FLAG));
1181        // older mysql and mariadb versions get back another encoding here
1182        // we test for both as there seems to be no clear pattern when one or
1183        // the other is returned
1184        let multipoint_res = to_value::<Text, String>(multipoint_col).unwrap();
1185        assert!(
1186            multipoint_res == "MULTIPOINT((0 0),(10 10),(10 20),(20 20))"
1187                || multipoint_res == "MULTIPOINT(0 0,10 10,10 20,20 20)"
1188        );
1189
1190        let multilinestring_col = &results[30].0;
1191        assert_eq!(
1192            multilinestring_col.tpe,
1193            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
1194        );
1195        assert!(!multilinestring_col.flags.contains(Flags::NUM_FLAG));
1196        assert!(!multilinestring_col.flags.contains(Flags::BLOB_FLAG));
1197        assert!(!multilinestring_col.flags.contains(Flags::SET_FLAG));
1198        assert!(!multilinestring_col.flags.contains(Flags::ENUM_FLAG));
1199        assert!(!multilinestring_col.flags.contains(Flags::BINARY_FLAG));
1200        assert_eq!(
1201            to_value::<Text, String>(multilinestring_col).unwrap(),
1202            "MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))"
1203        );
1204
1205        let polygon_col = &results[31].0;
1206        assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
1207        assert!(!polygon_col.flags.contains(Flags::NUM_FLAG));
1208        assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG));
1209        assert!(!polygon_col.flags.contains(Flags::SET_FLAG));
1210        assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG));
1211        assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG));
1212        assert_eq!(
1213                to_value::<Text, String>(polygon_col).unwrap(),
1214                "MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))"
1215            );
1216
1217        let geometry_collection = &results[32].0;
1218        assert_eq!(
1219            geometry_collection.tpe,
1220            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
1221        );
1222        assert!(!geometry_collection.flags.contains(Flags::NUM_FLAG));
1223        assert!(!geometry_collection.flags.contains(Flags::BLOB_FLAG));
1224        assert!(!geometry_collection.flags.contains(Flags::SET_FLAG));
1225        assert!(!geometry_collection.flags.contains(Flags::ENUM_FLAG));
1226        assert!(!geometry_collection.flags.contains(Flags::BINARY_FLAG));
1227        assert_eq!(
1228            to_value::<Text, String>(geometry_collection).unwrap(),
1229            "GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))"
1230        );
1231
1232        let json_col = &results[33].0;
1233        // mariadb >= 10.2 and mysql >=8.0 are supporting a json type
1234        // from those mariadb >= 10.3 and mysql >= 8.0 are reporting
1235        // json here, so we assert that we get back json
1236        // mariadb 10.5 returns again blob
1237        assert!(
1238            json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_JSON
1239                || json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_BLOB
1240        );
1241        assert!(!json_col.flags.contains(Flags::NUM_FLAG));
1242        assert!(json_col.flags.contains(Flags::BLOB_FLAG));
1243        assert!(!json_col.flags.contains(Flags::SET_FLAG));
1244        assert!(!json_col.flags.contains(Flags::ENUM_FLAG));
1245        assert!(json_col.flags.contains(Flags::BINARY_FLAG));
1246        assert_eq!(
1247            to_value::<Text, String>(json_col).unwrap(),
1248            "{\"key1\": \"value1\", \"key2\": \"value2\"}"
1249        );
1250    }
1251
1252    fn query_single_table(
1253        query: &'static str,
1254        conn: &MysqlConnection,
1255        bind_tpe: impl Into<(ffi::enum_field_types, Flags)>,
1256    ) -> BindData {
1257        let stmt: Statement = conn.raw_connection.prepare(query).unwrap();
1258        let stmt = MaybeCached::CannotCache(stmt);
1259
1260        let bind = BindData::from_tpe_and_flags(bind_tpe.into());
1261
1262        let mut binds = OutputBinds(Binds { data: vec![bind] });
1263
1264        let stmt = stmt.execute_statement(&mut binds).unwrap();
1265        stmt.populate_row_buffers(&mut binds).unwrap();
1266
1267        binds.0.data.remove(0)
1268    }
1269
1270    fn input_bind(
1271        query: &'static str,
1272        conn: &MysqlConnection,
1273        id: i32,
1274        (mut field, tpe): (Vec<u8>, impl Into<(ffi::enum_field_types, Flags)>),
1275    ) {
1276        let mut stmt = conn.raw_connection.prepare(query).unwrap();
1277        let length = field.len() as _;
1278        let (tpe, flags) = tpe.into();
1279        let capacity = field.capacity();
1280        let ptr = NonNull::new(field.as_mut_ptr());
1281        mem::forget(field);
1282
1283        let field_bind = BindData {
1284            tpe,
1285            bytes: ptr,
1286            capacity,
1287            length,
1288            flags,
1289            is_null: ffi::FALSE,
1290            is_truncated: None,
1291        };
1292
1293        let mut bytes = id.to_be_bytes().to_vec();
1294        let length = bytes.len() as _;
1295        let capacity = bytes.capacity();
1296        let ptr = NonNull::new(bytes.as_mut_ptr());
1297        mem::forget(bytes);
1298
1299        let id_bind = BindData {
1300            tpe: ffi::enum_field_types::MYSQL_TYPE_LONG,
1301            bytes: ptr,
1302            capacity,
1303            length,
1304            flags: Flags::empty(),
1305            is_null: ffi::FALSE,
1306            is_truncated: None,
1307        };
1308
1309        let binds = PreparedStatementBinds(Binds {
1310            data: vec![id_bind, field_bind],
1311        });
1312        stmt.input_bind(binds).unwrap();
1313        stmt.did_an_error_occur().unwrap();
1314        let stmt = MaybeCached::CannotCache(stmt);
1315        unsafe {
1316            stmt.execute().unwrap();
1317        }
1318    }
1319
1320    #[test]
1321    fn check_json_bind() {
1322        table! {
1323            json_test {
1324                id -> Integer,
1325                json_field -> Text,
1326            }
1327        }
1328
1329        let conn = &mut crate::test_helpers::connection();
1330
1331        crate::sql_query("DROP TABLE IF EXISTS json_test CASCADE")
1332            .execute(conn)
1333            .unwrap();
1334
1335        crate::sql_query(
1336            "CREATE TABLE json_test(id INTEGER PRIMARY KEY, json_field JSON NOT NULL)",
1337        )
1338        .execute(conn)
1339        .unwrap();
1340
1341        crate::sql_query("INSERT INTO json_test(id, json_field) VALUES (1, '{\"key1\": \"value1\", \"key2\": \"value2\"}')").execute(conn).unwrap();
1342
1343        let json_col_as_json = query_single_table(
1344            "SELECT json_field FROM json_test",
1345            conn,
1346            (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1347        );
1348
1349        assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
1350        assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
1351        assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
1352        assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
1353        assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
1354        assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
1355        assert_eq!(
1356            to_value::<Text, String>(&json_col_as_json).unwrap(),
1357            "{\"key1\": \"value1\", \"key2\": \"value2\"}"
1358        );
1359
1360        let json_col_as_text = query_single_table(
1361            "SELECT json_field FROM json_test",
1362            conn,
1363            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1364        );
1365
1366        assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1367        assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
1368        assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
1369        assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
1370        assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
1371        assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
1372        assert_eq!(
1373            to_value::<Text, String>(&json_col_as_text).unwrap(),
1374            "{\"key1\": \"value1\", \"key2\": \"value2\"}"
1375        );
1376        assert_eq!(
1377            json_col_as_json.value().unwrap().as_bytes(),
1378            json_col_as_text.value().unwrap().as_bytes()
1379        );
1380
1381        crate::sql_query("DELETE FROM json_test")
1382            .execute(conn)
1383            .unwrap();
1384
1385        input_bind(
1386            "INSERT INTO json_test(id, json_field) VALUES (?, ?)",
1387            conn,
1388            41,
1389            (
1390                b"{\"abc\": 42}".to_vec(),
1391                MysqlType::String,
1392                //                (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1393            ),
1394        );
1395
1396        let json_col_as_json = query_single_table(
1397            "SELECT json_field FROM json_test",
1398            conn,
1399            (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1400        );
1401
1402        assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
1403        assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
1404        assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
1405        assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
1406        assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
1407        assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
1408        assert_eq!(
1409            to_value::<Text, String>(&json_col_as_json).unwrap(),
1410            "{\"abc\": 42}"
1411        );
1412
1413        let json_col_as_text = query_single_table(
1414            "SELECT json_field FROM json_test",
1415            conn,
1416            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1417        );
1418
1419        assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1420        assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
1421        assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
1422        assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
1423        assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
1424        assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
1425        assert_eq!(
1426            to_value::<Text, String>(&json_col_as_text).unwrap(),
1427            "{\"abc\": 42}"
1428        );
1429        assert_eq!(
1430            json_col_as_json.value().unwrap().as_bytes(),
1431            json_col_as_text.value().unwrap().as_bytes()
1432        );
1433
1434        crate::sql_query("DELETE FROM json_test")
1435            .execute(conn)
1436            .unwrap();
1437
1438        input_bind(
1439            "INSERT INTO json_test(id, json_field) VALUES (?, ?)",
1440            conn,
1441            41,
1442            (b"{\"abca\": 42}".to_vec(), MysqlType::String),
1443        );
1444
1445        let json_col_as_json = query_single_table(
1446            "SELECT json_field FROM json_test",
1447            conn,
1448            (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1449        );
1450
1451        assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
1452        assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
1453        assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
1454        assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
1455        assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
1456        assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
1457        assert_eq!(
1458            to_value::<Text, String>(&json_col_as_json).unwrap(),
1459            "{\"abca\": 42}"
1460        );
1461
1462        let json_col_as_text = query_single_table(
1463            "SELECT json_field FROM json_test",
1464            conn,
1465            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1466        );
1467
1468        assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1469        assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
1470        assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
1471        assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
1472        assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
1473        assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
1474        assert_eq!(
1475            to_value::<Text, String>(&json_col_as_text).unwrap(),
1476            "{\"abca\": 42}"
1477        );
1478        assert_eq!(
1479            json_col_as_json.value().unwrap().as_bytes(),
1480            json_col_as_text.value().unwrap().as_bytes()
1481        );
1482    }
1483
1484    #[test]
1485    fn check_enum_bind() {
1486        let conn = &mut crate::test_helpers::connection();
1487
1488        crate::sql_query("DROP TABLE IF EXISTS enum_test CASCADE")
1489            .execute(conn)
1490            .unwrap();
1491
1492        crate::sql_query("CREATE TABLE enum_test(id INTEGER PRIMARY KEY, enum_field ENUM('red', 'green', 'blue') NOT NULL)").execute(conn)
1493            .unwrap();
1494
1495        crate::sql_query("INSERT INTO enum_test(id, enum_field) VALUES (1, 'green')")
1496            .execute(conn)
1497            .unwrap();
1498
1499        let enum_col_as_enum: BindData =
1500            query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
1501
1502        assert_eq!(
1503            enum_col_as_enum.tpe,
1504            ffi::enum_field_types::MYSQL_TYPE_STRING
1505        );
1506        assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
1507        assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
1508        assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
1509        assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
1510        assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
1511        assert_eq!(
1512            to_value::<Text, String>(&enum_col_as_enum).unwrap(),
1513            "green"
1514        );
1515
1516        for tpe in &[
1517            ffi::enum_field_types::MYSQL_TYPE_BLOB,
1518            ffi::enum_field_types::MYSQL_TYPE_VAR_STRING,
1519            ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB,
1520            ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB,
1521            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB,
1522        ] {
1523            let enum_col_as_text = query_single_table(
1524                "SELECT enum_field FROM enum_test",
1525                conn,
1526                (*tpe, Flags::ENUM_FLAG),
1527            );
1528
1529            assert_eq!(enum_col_as_text.tpe, *tpe);
1530            assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1531            assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1532            assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1533            assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1534            assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1535            assert_eq!(
1536                to_value::<Text, String>(&enum_col_as_text).unwrap(),
1537                "green"
1538            );
1539            assert_eq!(
1540                enum_col_as_enum.value().unwrap().as_bytes(),
1541                enum_col_as_text.value().unwrap().as_bytes()
1542            );
1543        }
1544
1545        let enum_col_as_text = query_single_table(
1546            "SELECT enum_field FROM enum_test",
1547            conn,
1548            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1549        );
1550
1551        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1552        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1553        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1554        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1555        assert!(!enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1556        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1557        assert_eq!(
1558            to_value::<Text, String>(&enum_col_as_text).unwrap(),
1559            "green"
1560        );
1561        assert_eq!(
1562            enum_col_as_enum.value().unwrap().as_bytes(),
1563            enum_col_as_text.value().unwrap().as_bytes()
1564        );
1565
1566        crate::sql_query("DELETE FROM enum_test")
1567            .execute(conn)
1568            .unwrap();
1569
1570        input_bind(
1571            "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)",
1572            conn,
1573            41,
1574            (b"blue".to_vec(), MysqlType::Enum),
1575        );
1576
1577        let enum_col_as_enum =
1578            query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
1579
1580        assert_eq!(
1581            enum_col_as_enum.tpe,
1582            ffi::enum_field_types::MYSQL_TYPE_STRING
1583        );
1584        assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
1585        assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
1586        assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
1587        assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
1588        assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
1589        assert_eq!(to_value::<Text, String>(&enum_col_as_enum).unwrap(), "blue");
1590
1591        let enum_col_as_text = query_single_table(
1592            "SELECT enum_field FROM enum_test",
1593            conn,
1594            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1595        );
1596
1597        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1598        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1599        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1600        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1601        assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1602        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1603        assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "blue");
1604        assert_eq!(
1605            enum_col_as_enum.value().unwrap().as_bytes(),
1606            enum_col_as_text.value().unwrap().as_bytes()
1607        );
1608
1609        let enum_col_as_text = query_single_table(
1610            "SELECT enum_field FROM enum_test",
1611            conn,
1612            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1613        );
1614
1615        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1616        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1617        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1618        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1619        assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1620        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1621        assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "blue");
1622        assert_eq!(
1623            enum_col_as_enum.value().unwrap().as_bytes(),
1624            enum_col_as_text.value().unwrap().as_bytes()
1625        );
1626
1627        crate::sql_query("DELETE FROM enum_test")
1628            .execute(conn)
1629            .unwrap();
1630
1631        input_bind(
1632            "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)",
1633            conn,
1634            41,
1635            (
1636                b"red".to_vec(),
1637                (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1638            ),
1639        );
1640
1641        let enum_col_as_enum =
1642            query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
1643
1644        assert_eq!(
1645            enum_col_as_enum.tpe,
1646            ffi::enum_field_types::MYSQL_TYPE_STRING
1647        );
1648        assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
1649        assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
1650        assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
1651        assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
1652        assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
1653        assert_eq!(to_value::<Text, String>(&enum_col_as_enum).unwrap(), "red");
1654
1655        let enum_col_as_text = query_single_table(
1656            "SELECT enum_field FROM enum_test",
1657            conn,
1658            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1659        );
1660
1661        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1662        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1663        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1664        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1665        assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1666        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1667        assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "red");
1668        assert_eq!(
1669            enum_col_as_enum.value().unwrap().as_bytes(),
1670            enum_col_as_text.value().unwrap().as_bytes()
1671        );
1672    }
1673
1674    #[test]
1675    fn check_set_bind() {
1676        let conn = &mut crate::test_helpers::connection();
1677
1678        crate::sql_query("DROP TABLE IF EXISTS set_test CASCADE")
1679            .execute(conn)
1680            .unwrap();
1681
1682        crate::sql_query("CREATE TABLE set_test(id INTEGER PRIMARY KEY, set_field SET('red', 'green', 'blue') NOT NULL)").execute(conn)
1683            .unwrap();
1684
1685        crate::sql_query("INSERT INTO set_test(id, set_field) VALUES (1, 'green')")
1686            .execute(conn)
1687            .unwrap();
1688
1689        let set_col_as_set: BindData =
1690            query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
1691
1692        assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1693        assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
1694        assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
1695        assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
1696        assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
1697        assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
1698        assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "green");
1699
1700        for tpe in &[
1701            ffi::enum_field_types::MYSQL_TYPE_BLOB,
1702            ffi::enum_field_types::MYSQL_TYPE_VAR_STRING,
1703            ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB,
1704            ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB,
1705            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB,
1706        ] {
1707            let set_col_as_text = query_single_table(
1708                "SELECT set_field FROM set_test",
1709                conn,
1710                (*tpe, Flags::SET_FLAG),
1711            );
1712
1713            assert_eq!(set_col_as_text.tpe, *tpe);
1714            assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1715            assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1716            assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
1717            assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1718            assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1719            assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "green");
1720            assert_eq!(
1721                set_col_as_set.value().unwrap().as_bytes(),
1722                set_col_as_text.value().unwrap().as_bytes()
1723            );
1724        }
1725        let set_col_as_text = query_single_table(
1726            "SELECT set_field FROM set_test",
1727            conn,
1728            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1729        );
1730
1731        assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1732        assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1733        assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1734        assert!(!set_col_as_text.flags.contains(Flags::SET_FLAG));
1735        assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1736        assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1737        assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "green");
1738        assert_eq!(
1739            set_col_as_set.value().unwrap().as_bytes(),
1740            set_col_as_text.value().unwrap().as_bytes()
1741        );
1742
1743        crate::sql_query("DELETE FROM set_test")
1744            .execute(conn)
1745            .unwrap();
1746
1747        input_bind(
1748            "INSERT INTO set_test(id, set_field) VALUES (?, ?)",
1749            conn,
1750            41,
1751            (b"blue".to_vec(), MysqlType::Set),
1752        );
1753
1754        let set_col_as_set =
1755            query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
1756
1757        assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1758        assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
1759        assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
1760        assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
1761        assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
1762        assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
1763        assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "blue");
1764
1765        let set_col_as_text = query_single_table(
1766            "SELECT set_field FROM set_test",
1767            conn,
1768            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG),
1769        );
1770
1771        assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1772        assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1773        assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1774        assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
1775        assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1776        assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1777        assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "blue");
1778        assert_eq!(
1779            set_col_as_set.value().unwrap().as_bytes(),
1780            set_col_as_text.value().unwrap().as_bytes()
1781        );
1782
1783        crate::sql_query("DELETE FROM set_test")
1784            .execute(conn)
1785            .unwrap();
1786
1787        input_bind(
1788            "INSERT INTO set_test(id, set_field) VALUES (?, ?)",
1789            conn,
1790            41,
1791            (b"red".to_vec(), MysqlType::String),
1792        );
1793
1794        let set_col_as_set =
1795            query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
1796
1797        assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1798        assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
1799        assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
1800        assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
1801        assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
1802        assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
1803        assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "red");
1804
1805        let set_col_as_text = query_single_table(
1806            "SELECT set_field FROM set_test",
1807            conn,
1808            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG),
1809        );
1810
1811        assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1812        assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1813        assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1814        assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
1815        assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1816        assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1817        assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "red");
1818        assert_eq!(
1819            set_col_as_set.value().unwrap().as_bytes(),
1820            set_col_as_text.value().unwrap().as_bytes()
1821        );
1822    }
1823}