diesel/mysql/connection/
bind.rs

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