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, 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
15pub(super) struct PreparedStatementBinds(Binds);
16
17pub(super) struct OutputBinds(Binds);
18
19impl Clone for OutputBinds {
20    fn clone(&self) -> Self {
21        Self(Binds {
22            data: self.0.data.clone(),
23        })
24    }
25}
26
27struct Binds {
28    data: Vec<BindData>,
29}
30
31impl PreparedStatementBinds {
32    pub(super) fn from_input_data<Iter>(input: Iter) -> Self
33    where
34        Iter: IntoIterator<Item = (MysqlType, Option<Vec<u8>>)>,
35    {
36        let data = input
37            .into_iter()
38            .map(BindData::for_input)
39            .collect::<Vec<_>>();
40
41        Self(Binds { data })
42    }
43
44    pub(super) fn with_mysql_binds<F, T>(&mut self, f: F) -> T
45    where
46        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
47    {
48        self.0.with_mysql_binds(f)
49    }
50}
51
52impl OutputBinds {
53    pub(super) fn from_output_types(
54        types: &[Option<MysqlType>],
55        metadata: &StatementMetadata,
56    ) -> Self {
57        let data = metadata
58            .fields()
59            .iter()
60            .zip(types.iter().copied().chain(std::iter::repeat(None)))
61            .map(|(field, tpe)| BindData::for_output(tpe, field))
62            .collect();
63
64        Self(Binds { data })
65    }
66
67    pub(super) fn populate_dynamic_buffers(&mut self, stmt: &StatementUse<'_>) -> QueryResult<()> {
68        for (i, data) in self.0.data.iter_mut().enumerate() {
69            data.did_numeric_overflow_occur()?;
70            // This is safe because we are re-binding the invalidated buffers
71            // at the end of this function
72            unsafe {
73                if let Some((mut bind, offset)) = data.bind_for_truncated_data() {
74                    stmt.fetch_column(&mut bind, i, offset)?
75                } else {
76                    data.update_buffer_length()
77                }
78            }
79        }
80
81        unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) }
82    }
83
84    pub(super) fn update_buffer_lengths(&mut self) {
85        for data in &mut self.0.data {
86            data.update_buffer_length();
87        }
88    }
89
90    pub(super) fn with_mysql_binds<F, T>(&mut self, f: F) -> T
91    where
92        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
93    {
94        self.0.with_mysql_binds(f)
95    }
96}
97
98impl Binds {
99    fn with_mysql_binds<F, T>(&mut self, f: F) -> T
100    where
101        F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
102    {
103        let mut binds = self
104            .data
105            .iter_mut()
106            .map(|x| unsafe { x.mysql_bind() })
107            .collect::<Vec<_>>();
108        f(binds.as_mut_ptr())
109    }
110}
111
112impl Index<usize> for OutputBinds {
113    type Output = BindData;
114    fn index(&self, index: usize) -> &Self::Output {
115        &self.0.data[index]
116    }
117}
118
119bitflags::bitflags! {
120    #[derive(Clone, Copy, Debug)]
121    pub(crate) struct Flags: u32 {
122        const NOT_NULL_FLAG = 1;
123        const PRI_KEY_FLAG = 2;
124        const UNIQUE_KEY_FLAG = 4;
125        const MULTIPLE_KEY_FLAG = 8;
126        const BLOB_FLAG = 16;
127        const UNSIGNED_FLAG = 32;
128        const ZEROFILL_FLAG = 64;
129        const BINARY_FLAG = 128;
130        const ENUM_FLAG = 256;
131        const AUTO_INCREMENT_FLAG = 512;
132        const TIMESTAMP_FLAG = 1024;
133        const SET_FLAG = 2048;
134        const NO_DEFAULT_VALUE_FLAG = 4096;
135        const ON_UPDATE_NOW_FLAG = 8192;
136        const NUM_FLAG = 32768;
137        const PART_KEY_FLAG = 16384;
138        const GROUP_FLAG = 32768;
139        const UNIQUE_FLAG = 65536;
140        const BINCMP_FLAG = 130_172;
141        const GET_FIXED_FIELDS_FLAG = (1<<18);
142        const FIELD_IN_PART_FUNC_FLAG = (1 << 19);
143    }
144}
145
146impl From<u32> for Flags {
147    fn from(flags: u32) -> Self {
148        Flags::from_bits(flags).expect(
149            "We encountered an unknown type flag while parsing \
150             Mysql's type information. If you see this error message \
151             please open an issue at diesels github page.",
152        )
153    }
154}
155
156#[derive(Debug)]
157pub(super) struct BindData {
158    tpe: ffi::enum_field_types,
159    bytes: Option<NonNull<u8>>,
160    length: libc::c_ulong,
161    capacity: usize,
162    flags: Flags,
163    is_null: ffi::my_bool,
164    is_truncated: Option<ffi::my_bool>,
165}
166
167// We need to write a manual clone impl
168// as we need to clone the underlying buffer
169// instead of just copying the pointer
170impl Clone for BindData {
171    fn clone(&self) -> Self {
172        let (ptr, len, capacity) = if let Some(ptr) = self.bytes {
173            let slice = unsafe {
174                // We know that this points to a slice and the pointer is not null at this
175                // location
176                // The length pointer is valid as long as none missuses `bind_for_truncated_data`
177                // as this is the only location that updates the length field before the corresponding data are
178                // written. At the time of writing this comment, the `BindData::bind_for_truncated_data`
179                // function is only called by `Binds::populate_dynamic_buffers` which ensures the corresponding
180                // invariant.
181                std::slice::from_raw_parts(
182                    ptr.as_ptr(),
183                    self.length.try_into().expect("usize is at least 32bit"),
184                )
185            };
186            let mut vec = slice.to_owned();
187            let ptr = NonNull::new(vec.as_mut_ptr());
188            let len = vec.len() as libc::c_ulong;
189            let capacity = vec.capacity();
190            mem::forget(vec);
191            (ptr, len, capacity)
192        } else {
193            (None, 0, 0)
194        };
195        Self {
196            tpe: self.tpe,
197            bytes: ptr,
198            length: len,
199            capacity,
200            flags: self.flags,
201            is_null: self.is_null,
202            is_truncated: self.is_truncated,
203        }
204    }
205}
206
207impl Drop for BindData {
208    fn drop(&mut self) {
209        if let Some(bytes) = self.bytes {
210            std::mem::drop(unsafe {
211                // We know that this buffer was allocated by a vector, so constructing a vector from it is fine
212                // We know the correct capacity here
213                // We use 0 as length to prevent situations where the length is already updated but
214                // no date are already written as we could touch uninitialized memory otherwise
215                // Using 0 as length is fine as we don't need to call drop for `u8`
216                // (as there is no drop impl for primitive types)
217                Vec::from_raw_parts(bytes.as_ptr(), 0, self.capacity)
218            });
219            self.bytes = None;
220        }
221    }
222}
223
224impl BindData {
225    fn for_input((tpe, data): (MysqlType, Option<Vec<u8>>)) -> Self {
226        let (tpe, flags) = tpe.into();
227        let is_null = ffi::my_bool::from(data.is_none());
228        let mut bytes = data.unwrap_or_default();
229        let ptr = NonNull::new(bytes.as_mut_ptr());
230        let len = bytes.len() as libc::c_ulong;
231        let capacity = bytes.capacity();
232        mem::forget(bytes);
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 mut bytes = vec![0; len];
383        let length = bytes.len() as libc::c_ulong;
384        let capacity = bytes.capacity();
385        let ptr = NonNull::new(bytes.as_mut_ptr());
386        mem::forget(bytes);
387
388        Self {
389            tpe,
390            bytes: ptr,
391            length,
392            capacity,
393            flags,
394            is_null: super::raw::ffi_false(),
395            is_truncated: Some(super::raw::ffi_false()),
396        }
397    }
398
399    fn is_truncated(&self) -> bool {
400        self.is_truncated.unwrap_or(super::raw::ffi_false()) != super::raw::ffi_false()
401    }
402
403    fn is_fixed_size_buffer(&self) -> bool {
404        known_buffer_size_for_ffi_type(self.tpe).is_some()
405    }
406
407    pub(super) fn value(&'_ self) -> Option<MysqlValue<'_>> {
408        if self.is_null() {
409            None
410        } else {
411            let data = self.bytes?;
412            let tpe = (self.tpe, self.flags).into();
413            // On some distributions, the mariadb client library returns length 0 for NULL fields of type DECIMAL
414            // instead of using is_null for unknown reasons
415            if self.tpe == self::ffi::enum_field_types::MYSQL_TYPE_LONGLONG && self.length == 0 {
416                return None;
417            }
418
419            let slice = unsafe {
420                // We know that this points to a slice and the pointer is not null at this
421                // location
422                // The length pointer is valid as long as none missuses `bind_for_truncated_data`
423                // as this is the only location that updates the length field before the corresponding data are
424                // written. At the time of writing this comment, the `BindData::bind_for_truncated_data`
425                // function is only called by `Binds::populate_dynamic_buffers` which ensures the corresponding
426                // invariant.
427                std::slice::from_raw_parts(
428                    data.as_ptr(),
429                    self.length.try_into().expect("Usize is at least 32 bit"),
430                )
431            };
432            Some(MysqlValue::new_internal(slice, tpe))
433        }
434    }
435
436    pub(super) fn is_null(&self) -> bool {
437        self.is_null != ffi::my_bool::default()
438    }
439
440    fn update_buffer_length(&mut self) {
441        use std::cmp::min;
442
443        let actual_bytes_in_buffer = min(
444            self.capacity,
445            self.length.try_into().expect("Usize is at least 32 bit"),
446        );
447        self.length = actual_bytes_in_buffer as libc::c_ulong;
448    }
449
450    // This function is marked as unsafe as it returns an owned value
451    // containing a pointer with a lifetime coupled to self.
452    // Callers need to ensure that the returned value cannot outlive `self`
453    unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND {
454        use std::ptr::addr_of_mut;
455
456        let mut bind: MaybeUninit<ffi::MYSQL_BIND> = mem::MaybeUninit::zeroed();
457        let ptr = bind.as_mut_ptr();
458
459        unsafe {
460            addr_of_mut!((*ptr).buffer_type).write(self.tpe);
461            addr_of_mut!((*ptr).buffer).write(
462                self.bytes
463                    .map(|p| p.as_ptr())
464                    .unwrap_or(std::ptr::null_mut()) as *mut libc::c_void,
465            );
466            addr_of_mut!((*ptr).buffer_length).write(self.capacity as libc::c_ulong);
467            addr_of_mut!((*ptr).length).write(&mut self.length);
468            addr_of_mut!((*ptr).is_null).write(&mut self.is_null);
469            addr_of_mut!((*ptr).is_unsigned)
470                .write(self.flags.contains(Flags::UNSIGNED_FLAG) as ffi::my_bool);
471
472            if let Some(ref mut is_truncated) = self.is_truncated {
473                addr_of_mut!((*ptr).error).write(is_truncated);
474            }
475
476            // That's what the mysqlclient examples are doing
477            bind.assume_init()
478        }
479    }
480
481    /// Resizes the byte buffer to fit the value of `self.length`, and returns
482    /// a tuple of a bind pointing at the truncated data, and the offset to use
483    /// in order to read the truncated data into it.
484    ///
485    /// This invalidates the bind previously returned by `mysql_bind`. Calling
486    /// this function is unsafe unless the binds are immediately rebound.
487    unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> {
488        if self.is_truncated() {
489            if let Some(bytes) = self.bytes {
490                let mut bytes =
491                    unsafe { Vec::from_raw_parts(bytes.as_ptr(), self.capacity, self.capacity) };
492                self.bytes = None;
493
494                let offset = self.capacity;
495                let truncated_amount =
496                    usize::try_from(self.length).expect("Usize is at least 32 bit") - offset;
497
498                debug_assert!(
499                    truncated_amount > 0,
500                    "output buffers were invalidated \
501                     without calling `mysql_stmt_bind_result`"
502                );
503
504                // reserve space for any missing byte
505                // we know the exact size here
506                bytes.reserve(truncated_amount);
507                self.capacity = bytes.capacity();
508                self.bytes = NonNull::new(bytes.as_mut_ptr());
509                mem::forget(bytes);
510
511                let mut bind = unsafe { self.mysql_bind() };
512
513                if let Some(ptr) = self.bytes {
514                    // Using offset is safe here as we have a u8 array (where std::mem::size_of::<u8> == 1)
515                    // and we have a buffer that has at least
516                    bind.buffer = unsafe { ptr.as_ptr().add(offset) as *mut libc::c_void };
517                    bind.buffer_length = truncated_amount as libc::c_ulong;
518                } else {
519                    bind.buffer_length = 0;
520                }
521                Some((bind, offset))
522            } else {
523                // offset is zero here as we don't have a buffer yet
524                // we know the requested length here so we can just request
525                // the correct size
526                let mut vec = vec![0_u8; self.length.try_into().expect("usize is at least 32 bit")];
527                self.capacity = vec.capacity();
528                self.bytes = NonNull::new(vec.as_mut_ptr());
529                mem::forget(vec);
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        (mut 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 capacity = field.capacity();
1288        let ptr = NonNull::new(field.as_mut_ptr());
1289        mem::forget(field);
1290
1291        let field_bind = BindData {
1292            tpe,
1293            bytes: ptr,
1294            capacity,
1295            length,
1296            flags,
1297            is_null: ffi::FALSE,
1298            is_truncated: None,
1299        };
1300
1301        let mut bytes = id.to_be_bytes().to_vec();
1302        let length = bytes.len() as _;
1303        let capacity = bytes.capacity();
1304        let ptr = NonNull::new(bytes.as_mut_ptr());
1305        mem::forget(bytes);
1306
1307        let id_bind = BindData {
1308            tpe: ffi::enum_field_types::MYSQL_TYPE_LONG,
1309            bytes: ptr,
1310            capacity,
1311            length,
1312            flags: Flags::empty(),
1313            is_null: ffi::FALSE,
1314            is_truncated: None,
1315        };
1316
1317        let binds = PreparedStatementBinds(Binds {
1318            data: vec![id_bind, field_bind],
1319        });
1320        stmt.input_bind(binds).unwrap();
1321        stmt.did_an_error_occur().unwrap();
1322        let stmt = MaybeCached::CannotCache(stmt);
1323        unsafe {
1324            stmt.execute().unwrap();
1325        }
1326    }
1327
1328    #[diesel_test_helper::test]
1329    fn check_json_bind() {
1330        table! {
1331            json_test {
1332                id -> Integer,
1333                json_field -> Text,
1334            }
1335        }
1336
1337        let conn = &mut crate::test_helpers::connection();
1338
1339        crate::sql_query("DROP TABLE IF EXISTS json_test CASCADE")
1340            .execute(conn)
1341            .unwrap();
1342
1343        crate::sql_query(
1344            "CREATE TABLE json_test(id INTEGER PRIMARY KEY, json_field JSON NOT NULL)",
1345        )
1346        .execute(conn)
1347        .unwrap();
1348
1349        crate::sql_query("INSERT INTO json_test(id, json_field) VALUES (1, '{\"key1\": \"value1\", \"key2\": \"value2\"}')").execute(conn).unwrap();
1350
1351        let json_col_as_json = query_single_table(
1352            "SELECT json_field FROM json_test",
1353            conn,
1354            (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1355        );
1356
1357        assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
1358        assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
1359        assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
1360        assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
1361        assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
1362        assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
1363        assert_eq!(
1364            to_value::<Text, String>(&json_col_as_json).unwrap(),
1365            "{\"key1\": \"value1\", \"key2\": \"value2\"}"
1366        );
1367
1368        let json_col_as_text = query_single_table(
1369            "SELECT json_field FROM json_test",
1370            conn,
1371            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1372        );
1373
1374        assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1375        assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
1376        assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
1377        assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
1378        assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
1379        assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
1380        assert_eq!(
1381            to_value::<Text, String>(&json_col_as_text).unwrap(),
1382            "{\"key1\": \"value1\", \"key2\": \"value2\"}"
1383        );
1384        assert_eq!(
1385            json_col_as_json.value().unwrap().as_bytes(),
1386            json_col_as_text.value().unwrap().as_bytes()
1387        );
1388
1389        crate::sql_query("DELETE FROM json_test")
1390            .execute(conn)
1391            .unwrap();
1392
1393        input_bind(
1394            "INSERT INTO json_test(id, json_field) VALUES (?, ?)",
1395            conn,
1396            41,
1397            (
1398                b"{\"abc\": 42}".to_vec(),
1399                MysqlType::String,
1400                //                (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1401            ),
1402        );
1403
1404        let json_col_as_json = query_single_table(
1405            "SELECT json_field FROM json_test",
1406            conn,
1407            (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1408        );
1409
1410        assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
1411        assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
1412        assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
1413        assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
1414        assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
1415        assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
1416        assert_eq!(
1417            to_value::<Text, String>(&json_col_as_json).unwrap(),
1418            "{\"abc\": 42}"
1419        );
1420
1421        let json_col_as_text = query_single_table(
1422            "SELECT json_field FROM json_test",
1423            conn,
1424            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1425        );
1426
1427        assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1428        assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
1429        assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
1430        assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
1431        assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
1432        assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
1433        assert_eq!(
1434            to_value::<Text, String>(&json_col_as_text).unwrap(),
1435            "{\"abc\": 42}"
1436        );
1437        assert_eq!(
1438            json_col_as_json.value().unwrap().as_bytes(),
1439            json_col_as_text.value().unwrap().as_bytes()
1440        );
1441
1442        crate::sql_query("DELETE FROM json_test")
1443            .execute(conn)
1444            .unwrap();
1445
1446        input_bind(
1447            "INSERT INTO json_test(id, json_field) VALUES (?, ?)",
1448            conn,
1449            41,
1450            (b"{\"abca\": 42}".to_vec(), MysqlType::String),
1451        );
1452
1453        let json_col_as_json = query_single_table(
1454            "SELECT json_field FROM json_test",
1455            conn,
1456            (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
1457        );
1458
1459        assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
1460        assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
1461        assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
1462        assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
1463        assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
1464        assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
1465        assert_eq!(
1466            to_value::<Text, String>(&json_col_as_json).unwrap(),
1467            "{\"abca\": 42}"
1468        );
1469
1470        let json_col_as_text = query_single_table(
1471            "SELECT json_field FROM json_test",
1472            conn,
1473            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1474        );
1475
1476        assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1477        assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
1478        assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
1479        assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
1480        assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
1481        assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
1482        assert_eq!(
1483            to_value::<Text, String>(&json_col_as_text).unwrap(),
1484            "{\"abca\": 42}"
1485        );
1486        assert_eq!(
1487            json_col_as_json.value().unwrap().as_bytes(),
1488            json_col_as_text.value().unwrap().as_bytes()
1489        );
1490    }
1491
1492    #[diesel_test_helper::test]
1493    fn check_enum_bind() {
1494        let conn = &mut crate::test_helpers::connection();
1495
1496        crate::sql_query("DROP TABLE IF EXISTS enum_test CASCADE")
1497            .execute(conn)
1498            .unwrap();
1499
1500        crate::sql_query("CREATE TABLE enum_test(id INTEGER PRIMARY KEY, enum_field ENUM('red', 'green', 'blue') NOT NULL)").execute(conn)
1501            .unwrap();
1502
1503        crate::sql_query("INSERT INTO enum_test(id, enum_field) VALUES (1, 'green')")
1504            .execute(conn)
1505            .unwrap();
1506
1507        let enum_col_as_enum: BindData =
1508            query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
1509
1510        assert_eq!(
1511            enum_col_as_enum.tpe,
1512            ffi::enum_field_types::MYSQL_TYPE_STRING
1513        );
1514        assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
1515        assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
1516        assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
1517        assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
1518        assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
1519        assert_eq!(
1520            to_value::<Text, String>(&enum_col_as_enum).unwrap(),
1521            "green"
1522        );
1523
1524        for tpe in &[
1525            ffi::enum_field_types::MYSQL_TYPE_BLOB,
1526            ffi::enum_field_types::MYSQL_TYPE_VAR_STRING,
1527            ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB,
1528            ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB,
1529            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB,
1530        ] {
1531            let enum_col_as_text = query_single_table(
1532                "SELECT enum_field FROM enum_test",
1533                conn,
1534                (*tpe, Flags::ENUM_FLAG),
1535            );
1536
1537            assert_eq!(enum_col_as_text.tpe, *tpe);
1538            assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1539            assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1540            assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1541            assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1542            assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1543            assert_eq!(
1544                to_value::<Text, String>(&enum_col_as_text).unwrap(),
1545                "green"
1546            );
1547            assert_eq!(
1548                enum_col_as_enum.value().unwrap().as_bytes(),
1549                enum_col_as_text.value().unwrap().as_bytes()
1550            );
1551        }
1552
1553        let enum_col_as_text = query_single_table(
1554            "SELECT enum_field FROM enum_test",
1555            conn,
1556            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1557        );
1558
1559        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1560        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1561        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1562        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1563        assert!(!enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1564        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1565        assert_eq!(
1566            to_value::<Text, String>(&enum_col_as_text).unwrap(),
1567            "green"
1568        );
1569        assert_eq!(
1570            enum_col_as_enum.value().unwrap().as_bytes(),
1571            enum_col_as_text.value().unwrap().as_bytes()
1572        );
1573
1574        crate::sql_query("DELETE FROM enum_test")
1575            .execute(conn)
1576            .unwrap();
1577
1578        input_bind(
1579            "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)",
1580            conn,
1581            41,
1582            (b"blue".to_vec(), MysqlType::Enum),
1583        );
1584
1585        let enum_col_as_enum =
1586            query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
1587
1588        assert_eq!(
1589            enum_col_as_enum.tpe,
1590            ffi::enum_field_types::MYSQL_TYPE_STRING
1591        );
1592        assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
1593        assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
1594        assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
1595        assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
1596        assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
1597        assert_eq!(to_value::<Text, String>(&enum_col_as_enum).unwrap(), "blue");
1598
1599        let enum_col_as_text = query_single_table(
1600            "SELECT enum_field FROM enum_test",
1601            conn,
1602            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1603        );
1604
1605        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1606        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1607        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1608        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1609        assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1610        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1611        assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "blue");
1612        assert_eq!(
1613            enum_col_as_enum.value().unwrap().as_bytes(),
1614            enum_col_as_text.value().unwrap().as_bytes()
1615        );
1616
1617        let enum_col_as_text = query_single_table(
1618            "SELECT enum_field FROM enum_test",
1619            conn,
1620            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1621        );
1622
1623        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1624        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1625        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1626        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1627        assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1628        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1629        assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "blue");
1630        assert_eq!(
1631            enum_col_as_enum.value().unwrap().as_bytes(),
1632            enum_col_as_text.value().unwrap().as_bytes()
1633        );
1634
1635        crate::sql_query("DELETE FROM enum_test")
1636            .execute(conn)
1637            .unwrap();
1638
1639        input_bind(
1640            "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)",
1641            conn,
1642            41,
1643            (
1644                b"red".to_vec(),
1645                (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1646            ),
1647        );
1648
1649        let enum_col_as_enum =
1650            query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
1651
1652        assert_eq!(
1653            enum_col_as_enum.tpe,
1654            ffi::enum_field_types::MYSQL_TYPE_STRING
1655        );
1656        assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
1657        assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
1658        assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
1659        assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
1660        assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
1661        assert_eq!(to_value::<Text, String>(&enum_col_as_enum).unwrap(), "red");
1662
1663        let enum_col_as_text = query_single_table(
1664            "SELECT enum_field FROM enum_test",
1665            conn,
1666            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
1667        );
1668
1669        assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1670        assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
1671        assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
1672        assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
1673        assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
1674        assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
1675        assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "red");
1676        assert_eq!(
1677            enum_col_as_enum.value().unwrap().as_bytes(),
1678            enum_col_as_text.value().unwrap().as_bytes()
1679        );
1680    }
1681
1682    #[diesel_test_helper::test]
1683    fn check_set_bind() {
1684        let conn = &mut crate::test_helpers::connection();
1685
1686        crate::sql_query("DROP TABLE IF EXISTS set_test CASCADE")
1687            .execute(conn)
1688            .unwrap();
1689
1690        crate::sql_query("CREATE TABLE set_test(id INTEGER PRIMARY KEY, set_field SET('red', 'green', 'blue') NOT NULL)").execute(conn)
1691            .unwrap();
1692
1693        crate::sql_query("INSERT INTO set_test(id, set_field) VALUES (1, 'green')")
1694            .execute(conn)
1695            .unwrap();
1696
1697        let set_col_as_set: BindData =
1698            query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
1699
1700        assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1701        assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
1702        assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
1703        assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
1704        assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
1705        assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
1706        assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "green");
1707
1708        for tpe in &[
1709            ffi::enum_field_types::MYSQL_TYPE_BLOB,
1710            ffi::enum_field_types::MYSQL_TYPE_VAR_STRING,
1711            ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB,
1712            ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB,
1713            ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB,
1714        ] {
1715            let set_col_as_text = query_single_table(
1716                "SELECT set_field FROM set_test",
1717                conn,
1718                (*tpe, Flags::SET_FLAG),
1719            );
1720
1721            assert_eq!(set_col_as_text.tpe, *tpe);
1722            assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1723            assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1724            assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
1725            assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1726            assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1727            assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "green");
1728            assert_eq!(
1729                set_col_as_set.value().unwrap().as_bytes(),
1730                set_col_as_text.value().unwrap().as_bytes()
1731            );
1732        }
1733        let set_col_as_text = query_single_table(
1734            "SELECT set_field FROM set_test",
1735            conn,
1736            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
1737        );
1738
1739        assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1740        assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1741        assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1742        assert!(!set_col_as_text.flags.contains(Flags::SET_FLAG));
1743        assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1744        assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1745        assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "green");
1746        assert_eq!(
1747            set_col_as_set.value().unwrap().as_bytes(),
1748            set_col_as_text.value().unwrap().as_bytes()
1749        );
1750
1751        crate::sql_query("DELETE FROM set_test")
1752            .execute(conn)
1753            .unwrap();
1754
1755        input_bind(
1756            "INSERT INTO set_test(id, set_field) VALUES (?, ?)",
1757            conn,
1758            41,
1759            (b"blue".to_vec(), MysqlType::Set),
1760        );
1761
1762        let set_col_as_set =
1763            query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
1764
1765        assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1766        assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
1767        assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
1768        assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
1769        assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
1770        assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
1771        assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "blue");
1772
1773        let set_col_as_text = query_single_table(
1774            "SELECT set_field FROM set_test",
1775            conn,
1776            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG),
1777        );
1778
1779        assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1780        assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1781        assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1782        assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
1783        assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1784        assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1785        assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "blue");
1786        assert_eq!(
1787            set_col_as_set.value().unwrap().as_bytes(),
1788            set_col_as_text.value().unwrap().as_bytes()
1789        );
1790
1791        crate::sql_query("DELETE FROM set_test")
1792            .execute(conn)
1793            .unwrap();
1794
1795        input_bind(
1796            "INSERT INTO set_test(id, set_field) VALUES (?, ?)",
1797            conn,
1798            41,
1799            (b"red".to_vec(), MysqlType::String),
1800        );
1801
1802        let set_col_as_set =
1803            query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
1804
1805        assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
1806        assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
1807        assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
1808        assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
1809        assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
1810        assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
1811        assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "red");
1812
1813        let set_col_as_text = query_single_table(
1814            "SELECT set_field FROM set_test",
1815            conn,
1816            (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG),
1817        );
1818
1819        assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
1820        assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
1821        assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
1822        assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
1823        assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
1824        assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
1825        assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "red");
1826        assert_eq!(
1827            set_col_as_set.value().unwrap().as_bytes(),
1828            set_col_as_text.value().unwrap().as_bytes()
1829        );
1830    }
1831}