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