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