#![allow(unsafe_code)] use mysqlclient_sys as ffi;
use std::mem;
use std::mem::MaybeUninit;
use std::ops::Index;
use std::os::raw as libc;
use std::ptr::NonNull;
use super::stmt::MysqlFieldMetadata;
use super::stmt::StatementUse;
use crate::mysql::connection::stmt::StatementMetadata;
use crate::mysql::types::date_and_time::MysqlTime;
use crate::mysql::{MysqlType, MysqlValue};
use crate::result::QueryResult;
pub(super) struct PreparedStatementBinds(Binds);
pub(super) struct OutputBinds(Binds);
impl Clone for OutputBinds {
fn clone(&self) -> Self {
Self(Binds {
data: self.0.data.clone(),
})
}
}
struct Binds {
data: Vec<BindData>,
}
impl PreparedStatementBinds {
pub(super) fn from_input_data<Iter>(input: Iter) -> Self
where
Iter: IntoIterator<Item = (MysqlType, Option<Vec<u8>>)>,
{
let data = input
.into_iter()
.map(BindData::for_input)
.collect::<Vec<_>>();
Self(Binds { data })
}
pub(super) fn with_mysql_binds<F, T>(&mut self, f: F) -> T
where
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
{
self.0.with_mysql_binds(f)
}
}
impl OutputBinds {
pub(super) fn from_output_types(
types: &[Option<MysqlType>],
metadata: &StatementMetadata,
) -> Self {
let data = metadata
.fields()
.iter()
.zip(types.iter().copied().chain(std::iter::repeat(None)))
.map(|(field, tpe)| BindData::for_output(tpe, field))
.collect();
Self(Binds { data })
}
pub(super) fn populate_dynamic_buffers(&mut self, stmt: &StatementUse<'_>) -> QueryResult<()> {
for (i, data) in self.0.data.iter_mut().enumerate() {
data.did_numeric_overflow_occur()?;
unsafe {
if let Some((mut bind, offset)) = data.bind_for_truncated_data() {
stmt.fetch_column(&mut bind, i, offset)?
} else {
data.update_buffer_length()
}
}
}
unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) }
}
pub(super) fn update_buffer_lengths(&mut self) {
for data in &mut self.0.data {
data.update_buffer_length();
}
}
pub(super) fn with_mysql_binds<F, T>(&mut self, f: F) -> T
where
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
{
self.0.with_mysql_binds(f)
}
}
impl Binds {
fn with_mysql_binds<F, T>(&mut self, f: F) -> T
where
F: FnOnce(*mut ffi::MYSQL_BIND) -> T,
{
let mut binds = self
.data
.iter_mut()
.map(|x| unsafe { x.mysql_bind() })
.collect::<Vec<_>>();
f(binds.as_mut_ptr())
}
}
impl Index<usize> for OutputBinds {
type Output = BindData;
fn index(&self, index: usize) -> &Self::Output {
&self.0.data[index]
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug)]
pub(crate) struct Flags: u32 {
const NOT_NULL_FLAG = 1;
const PRI_KEY_FLAG = 2;
const UNIQUE_KEY_FLAG = 4;
const MULTIPLE_KEY_FLAG = 8;
const BLOB_FLAG = 16;
const UNSIGNED_FLAG = 32;
const ZEROFILL_FLAG = 64;
const BINARY_FLAG = 128;
const ENUM_FLAG = 256;
const AUTO_INCREMENT_FLAG = 512;
const TIMESTAMP_FLAG = 1024;
const SET_FLAG = 2048;
const NO_DEFAULT_VALUE_FLAG = 4096;
const ON_UPDATE_NOW_FLAG = 8192;
const NUM_FLAG = 32768;
const PART_KEY_FLAG = 16384;
const GROUP_FLAG = 32768;
const UNIQUE_FLAG = 65536;
const BINCMP_FLAG = 130_172;
const GET_FIXED_FIELDS_FLAG = (1<<18);
const FIELD_IN_PART_FUNC_FLAG = (1 << 19);
}
}
impl From<u32> for Flags {
fn from(flags: u32) -> Self {
Flags::from_bits(flags).expect(
"We encountered an unknown type flag while parsing \
Mysql's type information. If you see this error message \
please open an issue at diesels github page.",
)
}
}
#[derive(Debug)]
pub(super) struct BindData {
tpe: ffi::enum_field_types,
bytes: Option<NonNull<u8>>,
length: libc::c_ulong,
capacity: usize,
flags: Flags,
is_null: ffi::my_bool,
is_truncated: Option<ffi::my_bool>,
}
impl Clone for BindData {
fn clone(&self) -> Self {
let (ptr, len, capacity) = if let Some(ptr) = self.bytes {
let slice = unsafe {
std::slice::from_raw_parts(
ptr.as_ptr(),
self.length.try_into().expect("usize is at least 32bit"),
)
};
let mut vec = slice.to_owned();
let ptr = NonNull::new(vec.as_mut_ptr());
let len = vec.len() as libc::c_ulong;
let capacity = vec.capacity();
mem::forget(vec);
(ptr, len, capacity)
} else {
(None, 0, 0)
};
Self {
tpe: self.tpe,
bytes: ptr,
length: len,
capacity,
flags: self.flags,
is_null: self.is_null,
is_truncated: self.is_truncated,
}
}
}
impl Drop for BindData {
fn drop(&mut self) {
if let Some(bytes) = self.bytes {
std::mem::drop(unsafe {
Vec::from_raw_parts(bytes.as_ptr(), 0, self.capacity)
});
self.bytes = None;
}
}
}
impl BindData {
fn for_input((tpe, data): (MysqlType, Option<Vec<u8>>)) -> Self {
let (tpe, flags) = tpe.into();
let is_null = ffi::my_bool::from(data.is_none());
let mut bytes = data.unwrap_or_default();
let ptr = NonNull::new(bytes.as_mut_ptr());
let len = bytes.len() as libc::c_ulong;
let capacity = bytes.capacity();
mem::forget(bytes);
Self {
tpe,
bytes: ptr,
length: len,
capacity,
flags,
is_null,
is_truncated: None,
}
}
fn for_output(tpe: Option<MysqlType>, metadata: &MysqlFieldMetadata<'_>) -> Self {
let (tpe, flags) = if let Some(tpe) = tpe {
match (tpe, metadata.field_type()) {
(MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::Tiny, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::UnsignedTiny, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::Short, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::UnsignedShort, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::Long, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::UnsignedLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::LongLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::UnsignedLongLong, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::Float, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_DECIMAL)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_TINY)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_SHORT)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_LONG)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_FLOAT)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_DOUBLE)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_INT24)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL)
| (MysqlType::Numeric, ffi::enum_field_types::MYSQL_TYPE_LONGLONG)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_JSON)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_ENUM)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_SET)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_BLOB)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING)
| (MysqlType::String, ffi::enum_field_types::MYSQL_TYPE_STRING)
| (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
| (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
| (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
| (MysqlType::Blob, ffi::enum_field_types::MYSQL_TYPE_BLOB)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_ENUM)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_SET)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_BLOB)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING)
| (MysqlType::Set, ffi::enum_field_types::MYSQL_TYPE_STRING)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_ENUM)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_SET)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_BLOB)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_VAR_STRING)
| (MysqlType::Enum, ffi::enum_field_types::MYSQL_TYPE_STRING) => {
(metadata.field_type(), metadata.flags())
}
(tpe, _) => tpe.into(),
}
} else {
(metadata.field_type(), metadata.flags())
};
Self::from_tpe_and_flags((tpe, flags))
}
fn from_tpe_and_flags((tpe, flags): (ffi::enum_field_types, Flags)) -> Self {
let len = known_buffer_size_for_ffi_type(tpe).unwrap_or(1);
let mut bytes = vec![0; len];
let length = bytes.len() as libc::c_ulong;
let capacity = bytes.capacity();
let ptr = NonNull::new(bytes.as_mut_ptr());
mem::forget(bytes);
Self {
tpe,
bytes: ptr,
length,
capacity,
flags,
is_null: super::raw::ffi_false(),
is_truncated: Some(super::raw::ffi_false()),
}
}
fn is_truncated(&self) -> bool {
self.is_truncated.unwrap_or(super::raw::ffi_false()) != super::raw::ffi_false()
}
fn is_fixed_size_buffer(&self) -> bool {
known_buffer_size_for_ffi_type(self.tpe).is_some()
}
pub(super) fn value(&'_ self) -> Option<MysqlValue<'_>> {
if self.is_null() {
None
} else {
let data = self.bytes?;
let tpe = (self.tpe, self.flags).into();
let slice = unsafe {
std::slice::from_raw_parts(
data.as_ptr(),
self.length.try_into().expect("Usize is at least 32 bit"),
)
};
Some(MysqlValue::new_internal(slice, tpe))
}
}
pub(super) fn is_null(&self) -> bool {
self.is_null != ffi::my_bool::default()
}
fn update_buffer_length(&mut self) {
use std::cmp::min;
let actual_bytes_in_buffer = min(
self.capacity,
self.length.try_into().expect("Usize is at least 32 bit"),
);
self.length = actual_bytes_in_buffer as libc::c_ulong;
}
unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND {
use std::ptr::addr_of_mut;
let mut bind: MaybeUninit<ffi::MYSQL_BIND> = mem::MaybeUninit::zeroed();
let ptr = bind.as_mut_ptr();
addr_of_mut!((*ptr).buffer_type).write(self.tpe);
addr_of_mut!((*ptr).buffer).write(
self.bytes
.map(|p| p.as_ptr())
.unwrap_or(std::ptr::null_mut()) as *mut libc::c_void,
);
addr_of_mut!((*ptr).buffer_length).write(self.capacity as libc::c_ulong);
addr_of_mut!((*ptr).length).write(&mut self.length);
addr_of_mut!((*ptr).is_null).write(&mut self.is_null);
addr_of_mut!((*ptr).is_unsigned)
.write(self.flags.contains(Flags::UNSIGNED_FLAG) as ffi::my_bool);
if let Some(ref mut is_truncated) = self.is_truncated {
addr_of_mut!((*ptr).error).write(is_truncated);
}
bind.assume_init()
}
unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> {
if self.is_truncated() {
if let Some(bytes) = self.bytes {
let mut bytes = Vec::from_raw_parts(bytes.as_ptr(), self.capacity, self.capacity);
self.bytes = None;
let offset = self.capacity;
let truncated_amount =
usize::try_from(self.length).expect("Usize is at least 32 bit") - offset;
debug_assert!(
truncated_amount > 0,
"output buffers were invalidated \
without calling `mysql_stmt_bind_result`"
);
bytes.reserve(truncated_amount);
self.capacity = bytes.capacity();
self.bytes = NonNull::new(bytes.as_mut_ptr());
mem::forget(bytes);
let mut bind = self.mysql_bind();
if let Some(ptr) = self.bytes {
bind.buffer = ptr.as_ptr().add(offset) as *mut libc::c_void;
bind.buffer_length = truncated_amount as libc::c_ulong;
} else {
bind.buffer_length = 0;
}
Some((bind, offset))
} else {
let mut vec = vec![0_u8; self.length.try_into().expect("usize is at least 32 bit")];
self.capacity = vec.capacity();
self.bytes = NonNull::new(vec.as_mut_ptr());
mem::forget(vec);
let bind = self.mysql_bind();
Some((bind, 0))
}
} else {
None
}
}
fn did_numeric_overflow_occur(&self) -> QueryResult<()> {
use crate::result::Error::DeserializationError;
if self.is_truncated() && self.is_fixed_size_buffer() {
Err(DeserializationError(
"Numeric overflow/underflow occurred".into(),
))
} else {
Ok(())
}
}
}
impl From<MysqlType> for (ffi::enum_field_types, Flags) {
fn from(tpe: MysqlType) -> Self {
use self::ffi::enum_field_types;
let mut flags = Flags::empty();
let tpe = match tpe {
MysqlType::Tiny => enum_field_types::MYSQL_TYPE_TINY,
MysqlType::Short => enum_field_types::MYSQL_TYPE_SHORT,
MysqlType::Long => enum_field_types::MYSQL_TYPE_LONG,
MysqlType::LongLong => enum_field_types::MYSQL_TYPE_LONGLONG,
MysqlType::Float => enum_field_types::MYSQL_TYPE_FLOAT,
MysqlType::Double => enum_field_types::MYSQL_TYPE_DOUBLE,
MysqlType::Time => enum_field_types::MYSQL_TYPE_TIME,
MysqlType::Date => enum_field_types::MYSQL_TYPE_DATE,
MysqlType::DateTime => enum_field_types::MYSQL_TYPE_DATETIME,
MysqlType::Timestamp => enum_field_types::MYSQL_TYPE_TIMESTAMP,
MysqlType::String => enum_field_types::MYSQL_TYPE_STRING,
MysqlType::Blob => enum_field_types::MYSQL_TYPE_BLOB,
MysqlType::Numeric => enum_field_types::MYSQL_TYPE_NEWDECIMAL,
MysqlType::Bit => enum_field_types::MYSQL_TYPE_BIT,
MysqlType::UnsignedTiny => {
flags = Flags::UNSIGNED_FLAG;
enum_field_types::MYSQL_TYPE_TINY
}
MysqlType::UnsignedShort => {
flags = Flags::UNSIGNED_FLAG;
enum_field_types::MYSQL_TYPE_SHORT
}
MysqlType::UnsignedLong => {
flags = Flags::UNSIGNED_FLAG;
enum_field_types::MYSQL_TYPE_LONG
}
MysqlType::UnsignedLongLong => {
flags = Flags::UNSIGNED_FLAG;
enum_field_types::MYSQL_TYPE_LONGLONG
}
MysqlType::Set => {
flags = Flags::SET_FLAG;
enum_field_types::MYSQL_TYPE_STRING
}
MysqlType::Enum => {
flags = Flags::ENUM_FLAG;
enum_field_types::MYSQL_TYPE_STRING
}
};
(tpe, flags)
}
}
impl From<(ffi::enum_field_types, Flags)> for MysqlType {
fn from((tpe, flags): (ffi::enum_field_types, Flags)) -> Self {
use self::ffi::enum_field_types;
let is_unsigned = flags.contains(Flags::UNSIGNED_FLAG);
match tpe {
enum_field_types::MYSQL_TYPE_TINY if is_unsigned => MysqlType::UnsignedTiny,
enum_field_types::MYSQL_TYPE_YEAR | enum_field_types::MYSQL_TYPE_SHORT
if is_unsigned =>
{
MysqlType::UnsignedShort
}
enum_field_types::MYSQL_TYPE_INT24 | enum_field_types::MYSQL_TYPE_LONG
if is_unsigned =>
{
MysqlType::UnsignedLong
}
enum_field_types::MYSQL_TYPE_LONGLONG if is_unsigned => MysqlType::UnsignedLongLong,
enum_field_types::MYSQL_TYPE_TINY => MysqlType::Tiny,
enum_field_types::MYSQL_TYPE_SHORT => MysqlType::Short,
enum_field_types::MYSQL_TYPE_INT24 | enum_field_types::MYSQL_TYPE_LONG => {
MysqlType::Long
}
enum_field_types::MYSQL_TYPE_LONGLONG => MysqlType::LongLong,
enum_field_types::MYSQL_TYPE_FLOAT => MysqlType::Float,
enum_field_types::MYSQL_TYPE_DOUBLE => MysqlType::Double,
enum_field_types::MYSQL_TYPE_DECIMAL | enum_field_types::MYSQL_TYPE_NEWDECIMAL => {
MysqlType::Numeric
}
enum_field_types::MYSQL_TYPE_BIT => MysqlType::Bit,
enum_field_types::MYSQL_TYPE_TIME => MysqlType::Time,
enum_field_types::MYSQL_TYPE_DATE => MysqlType::Date,
enum_field_types::MYSQL_TYPE_DATETIME => MysqlType::DateTime,
enum_field_types::MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp,
enum_field_types::MYSQL_TYPE_JSON => MysqlType::String,
enum_field_types::MYSQL_TYPE_BLOB
| enum_field_types::MYSQL_TYPE_TINY_BLOB
| enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
| enum_field_types::MYSQL_TYPE_LONG_BLOB
| enum_field_types::MYSQL_TYPE_VAR_STRING
| enum_field_types::MYSQL_TYPE_STRING
if flags.contains(Flags::ENUM_FLAG) =>
{
MysqlType::Enum
}
enum_field_types::MYSQL_TYPE_BLOB
| enum_field_types::MYSQL_TYPE_TINY_BLOB
| enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
| enum_field_types::MYSQL_TYPE_LONG_BLOB
| enum_field_types::MYSQL_TYPE_VAR_STRING
| enum_field_types::MYSQL_TYPE_STRING
if flags.contains(Flags::SET_FLAG) =>
{
MysqlType::Set
}
enum_field_types::MYSQL_TYPE_BLOB
| enum_field_types::MYSQL_TYPE_TINY_BLOB
| enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
| enum_field_types::MYSQL_TYPE_LONG_BLOB
| enum_field_types::MYSQL_TYPE_VAR_STRING
| enum_field_types::MYSQL_TYPE_STRING
if flags.contains(Flags::BINARY_FLAG) =>
{
MysqlType::Blob
}
enum_field_types::MYSQL_TYPE_BLOB
| enum_field_types::MYSQL_TYPE_TINY_BLOB
| enum_field_types::MYSQL_TYPE_MEDIUM_BLOB
| enum_field_types::MYSQL_TYPE_LONG_BLOB
| enum_field_types::MYSQL_TYPE_VAR_STRING
| enum_field_types::MYSQL_TYPE_STRING => MysqlType::String,
enum_field_types::MYSQL_TYPE_YEAR => unreachable!(
"The year type should have set the unsigned flag. If you ever \
see this error message, something has gone very wrong. Please \
open an issue at the diesel github repo in this case"
),
enum_field_types::MYSQL_TYPE_NULL => unreachable!(
"We ensure at the call side that we do not hit this type here. \
If you ever see this error, something has gone very wrong. \
Please open an issue at the diesel github repo in this case"
),
enum_field_types::MYSQL_TYPE_VARCHAR
| enum_field_types::MYSQL_TYPE_ENUM
| enum_field_types::MYSQL_TYPE_SET
| enum_field_types::MYSQL_TYPE_GEOMETRY => {
unimplemented!(
"Hit a type that should be unsupported in libmysqlclient. If \
you ever see this error, they probably have added support for \
one of those types. Please open an issue at the diesel github \
repo in this case."
)
}
enum_field_types::MYSQL_TYPE_NEWDATE
| enum_field_types::MYSQL_TYPE_TIME2
| enum_field_types::MYSQL_TYPE_DATETIME2
| enum_field_types::MYSQL_TYPE_TIMESTAMP2 => unreachable!(
"The mysql documentation states that these types are \
only used on the server side, so if you see this error \
something has gone wrong. Please open an issue at \
the diesel github repo."
),
#[allow(unreachable_patterns)]
t => unreachable!(
"Unsupported type encountered: {t:?}. \
If you ever see this error, something has gone wrong. \
Please open an issue at the diesel github \
repo in this case."
),
}
}
}
fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option<usize> {
use self::ffi::enum_field_types as t;
use std::mem::size_of;
match tpe {
t::MYSQL_TYPE_TINY => Some(1),
t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2),
t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4),
t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8),
t::MYSQL_TYPE_TIME
| t::MYSQL_TYPE_DATE
| t::MYSQL_TYPE_DATETIME
| t::MYSQL_TYPE_TIMESTAMP => Some(size_of::<MysqlTime>()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::connection::statement_cache::MaybeCached;
use crate::deserialize::FromSql;
use crate::mysql::connection::stmt::Statement;
use crate::prelude::*;
use crate::sql_types::*;
#[cfg(feature = "numeric")]
use std::str::FromStr;
fn to_value<ST, T>(
bind: &BindData,
) -> Result<T, Box<(dyn std::error::Error + Send + Sync + 'static)>>
where
T: FromSql<ST, crate::mysql::Mysql> + std::fmt::Debug,
{
let meta = (bind.tpe, bind.flags).into();
dbg!(meta);
let value = bind.value().expect("Is not null");
let value = MysqlValue::new_internal(value.as_bytes(), meta);
dbg!(T::from_sql(value))
}
#[cfg(feature = "extras")]
#[test]
fn check_all_the_types() {
let conn = &mut crate::test_helpers::connection();
crate::sql_query("DROP TABLE IF EXISTS all_mysql_types CASCADE")
.execute(conn)
.unwrap();
crate::sql_query(
"CREATE TABLE all_mysql_types (
tiny_int TINYINT NOT NULL,
small_int SMALLINT NOT NULL,
medium_int MEDIUMINT NOT NULL,
int_col INTEGER NOT NULL,
big_int BIGINT NOT NULL,
unsigned_int INTEGER UNSIGNED NOT NULL,
zero_fill_int INTEGER ZEROFILL NOT NULL,
numeric_col NUMERIC(20,5) NOT NULL,
decimal_col DECIMAL(20,5) NOT NULL,
float_col FLOAT NOT NULL,
double_col DOUBLE NOT NULL,
bit_col BIT(8) NOT NULL,
date_col DATE NOT NULL,
date_time DATETIME NOT NULL,
timestamp_col TIMESTAMP NOT NULL,
time_col TIME NOT NULL,
year_col YEAR NOT NULL,
char_col CHAR(30) NOT NULL,
varchar_col VARCHAR(30) NOT NULL,
binary_col BINARY(30) NOT NULL,
varbinary_col VARBINARY(30) NOT NULL,
blob_col BLOB NOT NULL,
text_col TEXT NOT NULL,
enum_col ENUM('red', 'green', 'blue') NOT NULL,
set_col SET('one', 'two') NOT NULL,
geom GEOMETRY NOT NULL,
point_col POINT NOT NULL,
linestring_col LINESTRING NOT NULL,
polygon_col POLYGON NOT NULL,
multipoint_col MULTIPOINT NOT NULL,
multilinestring_col MULTILINESTRING NOT NULL,
multipolygon_col MULTIPOLYGON NOT NULL,
geometry_collection GEOMETRYCOLLECTION NOT NULL,
json_col JSON NOT NULL
)",
)
.execute(conn)
.unwrap();
crate::sql_query(
"INSERT INTO all_mysql_types VALUES (
0, -- tiny_int
1, -- small_int
2, -- medium_int
3, -- int_col
-5, -- big_int
42, -- unsigned_int
1, -- zero_fill_int
-999.999, -- numeric_col,
3.14, -- decimal_col,
1.23, -- float_col
4.5678, -- double_col
b'10101010', -- bit_col
'1000-01-01', -- date_col
'9999-12-31 12:34:45.012345', -- date_time
'2020-01-01 10:10:10', -- timestamp_col
'23:01:01', -- time_col
2020, -- year_col
'abc', -- char_col
'foo', -- varchar_col
'a ', -- binary_col
'a ', -- varbinary_col
'binary', -- blob_col
'some text whatever', -- text_col
'red', -- enum_col
'one', -- set_col
ST_GeomFromText('POINT(1 1)'), -- geom
ST_PointFromText('POINT(1 1)'), -- point_col
ST_LineStringFromText('LINESTRING(0 0,1 1,2 2)'), -- linestring_col
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
ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)'), -- multipoint_col
ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))'), -- multilinestring_col
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
ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'), -- geometry_collection
'{\"key1\": \"value1\", \"key2\": \"value2\"}' -- json_col
)",
).execute(conn)
.unwrap();
let stmt = crate::mysql::connection::prepared_query(
&crate::sql_query(
"SELECT
tiny_int, small_int, medium_int, int_col,
big_int, unsigned_int, zero_fill_int,
numeric_col, decimal_col, float_col, double_col, bit_col,
date_col, date_time, timestamp_col, time_col, year_col,
char_col, varchar_col, binary_col, varbinary_col, blob_col,
text_col, enum_col, set_col, ST_AsText(geom), ST_AsText(point_col), ST_AsText(linestring_col),
ST_AsText(polygon_col), ST_AsText(multipoint_col), ST_AsText(multilinestring_col),
ST_AsText(multipolygon_col), ST_AsText(geometry_collection), json_col
FROM all_mysql_types",
),
&mut conn.statement_cache,
&mut conn.raw_connection,
&mut conn.instrumentation,
).unwrap();
let metadata = stmt.metadata().unwrap();
let mut output_binds =
OutputBinds::from_output_types(&vec![None; metadata.fields().len()], &metadata);
let stmt = stmt.execute_statement(&mut output_binds).unwrap();
stmt.populate_row_buffers(&mut output_binds).unwrap();
let results: Vec<(BindData, &_)> = output_binds
.0
.data
.into_iter()
.zip(metadata.fields())
.collect::<Vec<_>>();
let tiny_int_col = &results[0].0;
assert_eq!(tiny_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TINY);
assert!(tiny_int_col.flags.contains(Flags::NUM_FLAG));
assert!(!tiny_int_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(to_value::<TinyInt, i8>(tiny_int_col), Ok(0)));
let small_int_col = &results[1].0;
assert_eq!(small_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_SHORT);
assert!(small_int_col.flags.contains(Flags::NUM_FLAG));
assert!(!small_int_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(to_value::<SmallInt, i16>(small_int_col), Ok(1)));
let medium_int_col = &results[2].0;
assert_eq!(medium_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_INT24);
assert!(medium_int_col.flags.contains(Flags::NUM_FLAG));
assert!(!medium_int_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(to_value::<Integer, i32>(medium_int_col), Ok(2)));
let int_col = &results[3].0;
assert_eq!(int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG);
assert!(int_col.flags.contains(Flags::NUM_FLAG));
assert!(!int_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(to_value::<Integer, i32>(int_col), Ok(3)));
let big_int_col = &results[4].0;
assert_eq!(big_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONGLONG);
assert!(big_int_col.flags.contains(Flags::NUM_FLAG));
assert!(!big_int_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(to_value::<TinyInt, i8>(big_int_col), Ok(-5)));
let unsigned_int_col = &results[5].0;
assert_eq!(unsigned_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG);
assert!(unsigned_int_col.flags.contains(Flags::NUM_FLAG));
assert!(unsigned_int_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(
to_value::<Unsigned<Integer>, u32>(unsigned_int_col),
Ok(42)
));
let zero_fill_int_col = &results[6].0;
assert_eq!(
zero_fill_int_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_LONG
);
assert!(zero_fill_int_col.flags.contains(Flags::NUM_FLAG));
assert!(zero_fill_int_col.flags.contains(Flags::ZEROFILL_FLAG));
assert!(matches!(to_value::<Integer, i32>(zero_fill_int_col), Ok(1)));
let numeric_col = &results[7].0;
assert_eq!(
numeric_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL
);
assert!(numeric_col.flags.contains(Flags::NUM_FLAG));
assert!(!numeric_col.flags.contains(Flags::UNSIGNED_FLAG));
assert_eq!(
to_value::<Numeric, bigdecimal::BigDecimal>(numeric_col).unwrap(),
bigdecimal::BigDecimal::from_str("-999.99900").unwrap()
);
let decimal_col = &results[8].0;
assert_eq!(
decimal_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL
);
assert!(decimal_col.flags.contains(Flags::NUM_FLAG));
assert!(!decimal_col.flags.contains(Flags::UNSIGNED_FLAG));
assert_eq!(
to_value::<Numeric, bigdecimal::BigDecimal>(decimal_col).unwrap(),
bigdecimal::BigDecimal::from_str("3.14000").unwrap()
);
let float_col = &results[9].0;
assert_eq!(float_col.tpe, ffi::enum_field_types::MYSQL_TYPE_FLOAT);
assert!(float_col.flags.contains(Flags::NUM_FLAG));
assert!(!float_col.flags.contains(Flags::UNSIGNED_FLAG));
assert_eq!(to_value::<Float, f32>(float_col).unwrap(), 1.23);
let double_col = &results[10].0;
assert_eq!(double_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DOUBLE);
assert!(double_col.flags.contains(Flags::NUM_FLAG));
assert!(!double_col.flags.contains(Flags::UNSIGNED_FLAG));
assert_eq!(to_value::<Double, f64>(double_col).unwrap(), 4.5678);
let bit_col = &results[11].0;
assert_eq!(bit_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BIT);
assert!(!bit_col.flags.contains(Flags::NUM_FLAG));
assert!(bit_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(!bit_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Blob, Vec<u8>>(bit_col).unwrap(), vec![170]);
let date_col = &results[12].0;
assert_eq!(date_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DATE);
assert!(!date_col.flags.contains(Flags::NUM_FLAG));
assert_eq!(
to_value::<Date, chrono::NaiveDate>(date_col).unwrap(),
chrono::NaiveDate::from_ymd_opt(1000, 1, 1).unwrap(),
);
let date_time_col = &results[13].0;
assert_eq!(
date_time_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_DATETIME
);
assert!(!date_time_col.flags.contains(Flags::NUM_FLAG));
assert_eq!(
to_value::<Datetime, chrono::NaiveDateTime>(date_time_col).unwrap(),
chrono::NaiveDateTime::parse_from_str("9999-12-31 12:34:45", "%Y-%m-%d %H:%M:%S")
.unwrap()
);
let timestamp_col = &results[14].0;
assert_eq!(
timestamp_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_TIMESTAMP
);
assert!(!timestamp_col.flags.contains(Flags::NUM_FLAG));
assert_eq!(
to_value::<Datetime, chrono::NaiveDateTime>(timestamp_col).unwrap(),
chrono::NaiveDateTime::parse_from_str("2020-01-01 10:10:10", "%Y-%m-%d %H:%M:%S")
.unwrap()
);
let time_col = &results[15].0;
assert_eq!(time_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TIME);
assert!(!time_col.flags.contains(Flags::NUM_FLAG));
assert_eq!(
to_value::<Time, chrono::NaiveTime>(time_col).unwrap(),
chrono::NaiveTime::from_hms_opt(23, 01, 01).unwrap()
);
let year_col = &results[16].0;
assert_eq!(year_col.tpe, ffi::enum_field_types::MYSQL_TYPE_YEAR);
assert!(year_col.flags.contains(Flags::NUM_FLAG));
assert!(year_col.flags.contains(Flags::UNSIGNED_FLAG));
assert!(matches!(to_value::<SmallInt, i16>(year_col), Ok(2020)));
let char_col = &results[17].0;
assert_eq!(char_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!char_col.flags.contains(Flags::NUM_FLAG));
assert!(!char_col.flags.contains(Flags::BLOB_FLAG));
assert!(!char_col.flags.contains(Flags::SET_FLAG));
assert!(!char_col.flags.contains(Flags::ENUM_FLAG));
assert!(!char_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(char_col).unwrap(), "abc");
let varchar_col = &results[18].0;
assert_eq!(
varchar_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_VAR_STRING
);
assert!(!varchar_col.flags.contains(Flags::NUM_FLAG));
assert!(!varchar_col.flags.contains(Flags::BLOB_FLAG));
assert!(!varchar_col.flags.contains(Flags::SET_FLAG));
assert!(!varchar_col.flags.contains(Flags::ENUM_FLAG));
assert!(!varchar_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(varchar_col).unwrap(), "foo");
let binary_col = &results[19].0;
assert_eq!(binary_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!binary_col.flags.contains(Flags::NUM_FLAG));
assert!(!binary_col.flags.contains(Flags::BLOB_FLAG));
assert!(!binary_col.flags.contains(Flags::SET_FLAG));
assert!(!binary_col.flags.contains(Flags::ENUM_FLAG));
assert!(binary_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Blob, Vec<u8>>(binary_col).unwrap(),
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"
);
let varbinary_col = &results[20].0;
assert_eq!(
varbinary_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_VAR_STRING
);
assert!(!varbinary_col.flags.contains(Flags::NUM_FLAG));
assert!(!varbinary_col.flags.contains(Flags::BLOB_FLAG));
assert!(!varbinary_col.flags.contains(Flags::SET_FLAG));
assert!(!varbinary_col.flags.contains(Flags::ENUM_FLAG));
assert!(varbinary_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Blob, Vec<u8>>(varbinary_col).unwrap(), b"a ");
let blob_col = &results[21].0;
assert_eq!(blob_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!blob_col.flags.contains(Flags::NUM_FLAG));
assert!(blob_col.flags.contains(Flags::BLOB_FLAG));
assert!(!blob_col.flags.contains(Flags::SET_FLAG));
assert!(!blob_col.flags.contains(Flags::ENUM_FLAG));
assert!(blob_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Blob, Vec<u8>>(blob_col).unwrap(), b"binary");
let text_col = &results[22].0;
assert_eq!(text_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!text_col.flags.contains(Flags::NUM_FLAG));
assert!(text_col.flags.contains(Flags::BLOB_FLAG));
assert!(!text_col.flags.contains(Flags::SET_FLAG));
assert!(!text_col.flags.contains(Flags::ENUM_FLAG));
assert!(!text_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(text_col).unwrap(),
"some text whatever"
);
let enum_col = &results[23].0;
assert_eq!(enum_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!enum_col.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col.flags.contains(Flags::SET_FLAG));
assert!(enum_col.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(enum_col).unwrap(), "red");
let set_col = &results[24].0;
assert_eq!(set_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!set_col.flags.contains(Flags::NUM_FLAG));
assert!(!set_col.flags.contains(Flags::BLOB_FLAG));
assert!(set_col.flags.contains(Flags::SET_FLAG));
assert!(!set_col.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(set_col).unwrap(), "one");
let geom = &results[25].0;
assert_eq!(geom.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
assert!(!geom.flags.contains(Flags::NUM_FLAG));
assert!(!geom.flags.contains(Flags::BLOB_FLAG));
assert!(!geom.flags.contains(Flags::SET_FLAG));
assert!(!geom.flags.contains(Flags::ENUM_FLAG));
assert!(!geom.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(geom).unwrap(), "POINT(1 1)");
let point_col = &results[26].0;
assert_eq!(point_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
assert!(!point_col.flags.contains(Flags::NUM_FLAG));
assert!(!point_col.flags.contains(Flags::BLOB_FLAG));
assert!(!point_col.flags.contains(Flags::SET_FLAG));
assert!(!point_col.flags.contains(Flags::ENUM_FLAG));
assert!(!point_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(point_col).unwrap(), "POINT(1 1)");
let linestring_col = &results[27].0;
assert_eq!(
linestring_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
);
assert!(!linestring_col.flags.contains(Flags::NUM_FLAG));
assert!(!linestring_col.flags.contains(Flags::BLOB_FLAG));
assert!(!linestring_col.flags.contains(Flags::SET_FLAG));
assert!(!linestring_col.flags.contains(Flags::ENUM_FLAG));
assert!(!linestring_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(linestring_col).unwrap(),
"LINESTRING(0 0,1 1,2 2)"
);
let polygon_col = &results[28].0;
assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
assert!(!polygon_col.flags.contains(Flags::NUM_FLAG));
assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG));
assert!(!polygon_col.flags.contains(Flags::SET_FLAG));
assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG));
assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(polygon_col).unwrap(),
"POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))"
);
let multipoint_col = &results[29].0;
assert_eq!(
multipoint_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
);
assert!(!multipoint_col.flags.contains(Flags::NUM_FLAG));
assert!(!multipoint_col.flags.contains(Flags::BLOB_FLAG));
assert!(!multipoint_col.flags.contains(Flags::SET_FLAG));
assert!(!multipoint_col.flags.contains(Flags::ENUM_FLAG));
assert!(!multipoint_col.flags.contains(Flags::BINARY_FLAG));
let multipoint_res = to_value::<Text, String>(multipoint_col).unwrap();
assert!(
multipoint_res == "MULTIPOINT((0 0),(10 10),(10 20),(20 20))"
|| multipoint_res == "MULTIPOINT(0 0,10 10,10 20,20 20)"
);
let multilinestring_col = &results[30].0;
assert_eq!(
multilinestring_col.tpe,
ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
);
assert!(!multilinestring_col.flags.contains(Flags::NUM_FLAG));
assert!(!multilinestring_col.flags.contains(Flags::BLOB_FLAG));
assert!(!multilinestring_col.flags.contains(Flags::SET_FLAG));
assert!(!multilinestring_col.flags.contains(Flags::ENUM_FLAG));
assert!(!multilinestring_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(multilinestring_col).unwrap(),
"MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))"
);
let polygon_col = &results[31].0;
assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB);
assert!(!polygon_col.flags.contains(Flags::NUM_FLAG));
assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG));
assert!(!polygon_col.flags.contains(Flags::SET_FLAG));
assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG));
assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(polygon_col).unwrap(),
"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)))"
);
let geometry_collection = &results[32].0;
assert_eq!(
geometry_collection.tpe,
ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB
);
assert!(!geometry_collection.flags.contains(Flags::NUM_FLAG));
assert!(!geometry_collection.flags.contains(Flags::BLOB_FLAG));
assert!(!geometry_collection.flags.contains(Flags::SET_FLAG));
assert!(!geometry_collection.flags.contains(Flags::ENUM_FLAG));
assert!(!geometry_collection.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(geometry_collection).unwrap(),
"GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))"
);
let json_col = &results[33].0;
assert!(
json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_JSON
|| json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_BLOB
);
assert!(!json_col.flags.contains(Flags::NUM_FLAG));
assert!(json_col.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col.flags.contains(Flags::SET_FLAG));
assert!(!json_col.flags.contains(Flags::ENUM_FLAG));
assert!(json_col.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(json_col).unwrap(),
"{\"key1\": \"value1\", \"key2\": \"value2\"}"
);
}
fn query_single_table(
query: &'static str,
conn: &MysqlConnection,
bind_tpe: impl Into<(ffi::enum_field_types, Flags)>,
) -> BindData {
let stmt: Statement = conn.raw_connection.prepare(query).unwrap();
let stmt = MaybeCached::CannotCache(stmt);
let bind = BindData::from_tpe_and_flags(bind_tpe.into());
let mut binds = OutputBinds(Binds { data: vec![bind] });
let stmt = stmt.execute_statement(&mut binds).unwrap();
stmt.populate_row_buffers(&mut binds).unwrap();
binds.0.data.remove(0)
}
fn input_bind(
query: &'static str,
conn: &MysqlConnection,
id: i32,
(mut field, tpe): (Vec<u8>, impl Into<(ffi::enum_field_types, Flags)>),
) {
let mut stmt = conn.raw_connection.prepare(query).unwrap();
let length = field.len() as _;
let (tpe, flags) = tpe.into();
let capacity = field.capacity();
let ptr = NonNull::new(field.as_mut_ptr());
mem::forget(field);
let field_bind = BindData {
tpe,
bytes: ptr,
capacity,
length,
flags,
is_null: ffi::FALSE,
is_truncated: None,
};
let mut bytes = id.to_be_bytes().to_vec();
let length = bytes.len() as _;
let capacity = bytes.capacity();
let ptr = NonNull::new(bytes.as_mut_ptr());
mem::forget(bytes);
let id_bind = BindData {
tpe: ffi::enum_field_types::MYSQL_TYPE_LONG,
bytes: ptr,
capacity,
length,
flags: Flags::empty(),
is_null: ffi::FALSE,
is_truncated: None,
};
let binds = PreparedStatementBinds(Binds {
data: vec![id_bind, field_bind],
});
stmt.input_bind(binds).unwrap();
stmt.did_an_error_occur().unwrap();
let stmt = MaybeCached::CannotCache(stmt);
unsafe {
stmt.execute().unwrap();
}
}
#[test]
fn check_json_bind() {
table! {
json_test {
id -> Integer,
json_field -> Text,
}
}
let conn = &mut crate::test_helpers::connection();
crate::sql_query("DROP TABLE IF EXISTS json_test CASCADE")
.execute(conn)
.unwrap();
crate::sql_query(
"CREATE TABLE json_test(id INTEGER PRIMARY KEY, json_field JSON NOT NULL)",
)
.execute(conn)
.unwrap();
crate::sql_query("INSERT INTO json_test(id, json_field) VALUES (1, '{\"key1\": \"value1\", \"key2\": \"value2\"}')").execute(conn).unwrap();
let json_col_as_json = query_single_table(
"SELECT json_field FROM json_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
);
assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&json_col_as_json).unwrap(),
"{\"key1\": \"value1\", \"key2\": \"value2\"}"
);
let json_col_as_text = query_single_table(
"SELECT json_field FROM json_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
);
assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&json_col_as_text).unwrap(),
"{\"key1\": \"value1\", \"key2\": \"value2\"}"
);
assert_eq!(
json_col_as_json.value().unwrap().as_bytes(),
json_col_as_text.value().unwrap().as_bytes()
);
crate::sql_query("DELETE FROM json_test")
.execute(conn)
.unwrap();
input_bind(
"INSERT INTO json_test(id, json_field) VALUES (?, ?)",
conn,
41,
(
b"{\"abc\": 42}".to_vec(),
MysqlType::String,
),
);
let json_col_as_json = query_single_table(
"SELECT json_field FROM json_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
);
assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&json_col_as_json).unwrap(),
"{\"abc\": 42}"
);
let json_col_as_text = query_single_table(
"SELECT json_field FROM json_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
);
assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&json_col_as_text).unwrap(),
"{\"abc\": 42}"
);
assert_eq!(
json_col_as_json.value().unwrap().as_bytes(),
json_col_as_text.value().unwrap().as_bytes()
);
crate::sql_query("DELETE FROM json_test")
.execute(conn)
.unwrap();
input_bind(
"INSERT INTO json_test(id, json_field) VALUES (?, ?)",
conn,
41,
(b"{\"abca\": 42}".to_vec(), MysqlType::String),
);
let json_col_as_json = query_single_table(
"SELECT json_field FROM json_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()),
);
assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON);
assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG));
assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&json_col_as_json).unwrap(),
"{\"abca\": 42}"
);
let json_col_as_text = query_single_table(
"SELECT json_field FROM json_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
);
assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&json_col_as_text).unwrap(),
"{\"abca\": 42}"
);
assert_eq!(
json_col_as_json.value().unwrap().as_bytes(),
json_col_as_text.value().unwrap().as_bytes()
);
}
#[test]
fn check_enum_bind() {
let conn = &mut crate::test_helpers::connection();
crate::sql_query("DROP TABLE IF EXISTS enum_test CASCADE")
.execute(conn)
.unwrap();
crate::sql_query("CREATE TABLE enum_test(id INTEGER PRIMARY KEY, enum_field ENUM('red', 'green', 'blue') NOT NULL)").execute(conn)
.unwrap();
crate::sql_query("INSERT INTO enum_test(id, enum_field) VALUES (1, 'green')")
.execute(conn)
.unwrap();
let enum_col_as_enum: BindData =
query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
assert_eq!(
enum_col_as_enum.tpe,
ffi::enum_field_types::MYSQL_TYPE_STRING
);
assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&enum_col_as_enum).unwrap(),
"green"
);
for tpe in &[
ffi::enum_field_types::MYSQL_TYPE_BLOB,
ffi::enum_field_types::MYSQL_TYPE_VAR_STRING,
ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB,
ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB,
ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB,
] {
let enum_col_as_text = query_single_table(
"SELECT enum_field FROM enum_test",
conn,
(*tpe, Flags::ENUM_FLAG),
);
assert_eq!(enum_col_as_text.tpe, *tpe);
assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&enum_col_as_text).unwrap(),
"green"
);
assert_eq!(
enum_col_as_enum.value().unwrap().as_bytes(),
enum_col_as_text.value().unwrap().as_bytes()
);
}
let enum_col_as_text = query_single_table(
"SELECT enum_field FROM enum_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
);
assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(
to_value::<Text, String>(&enum_col_as_text).unwrap(),
"green"
);
assert_eq!(
enum_col_as_enum.value().unwrap().as_bytes(),
enum_col_as_text.value().unwrap().as_bytes()
);
crate::sql_query("DELETE FROM enum_test")
.execute(conn)
.unwrap();
input_bind(
"INSERT INTO enum_test(id, enum_field) VALUES (?, ?)",
conn,
41,
(b"blue".to_vec(), MysqlType::Enum),
);
let enum_col_as_enum =
query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
assert_eq!(
enum_col_as_enum.tpe,
ffi::enum_field_types::MYSQL_TYPE_STRING
);
assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&enum_col_as_enum).unwrap(), "blue");
let enum_col_as_text = query_single_table(
"SELECT enum_field FROM enum_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
);
assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "blue");
assert_eq!(
enum_col_as_enum.value().unwrap().as_bytes(),
enum_col_as_text.value().unwrap().as_bytes()
);
let enum_col_as_text = query_single_table(
"SELECT enum_field FROM enum_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
);
assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "blue");
assert_eq!(
enum_col_as_enum.value().unwrap().as_bytes(),
enum_col_as_text.value().unwrap().as_bytes()
);
crate::sql_query("DELETE FROM enum_test")
.execute(conn)
.unwrap();
input_bind(
"INSERT INTO enum_test(id, enum_field) VALUES (?, ?)",
conn,
41,
(
b"red".to_vec(),
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
),
);
let enum_col_as_enum =
query_single_table("SELECT enum_field FROM enum_test", conn, MysqlType::Enum);
assert_eq!(
enum_col_as_enum.tpe,
ffi::enum_field_types::MYSQL_TYPE_STRING
);
assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&enum_col_as_enum).unwrap(), "red");
let enum_col_as_text = query_single_table(
"SELECT enum_field FROM enum_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG),
);
assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&enum_col_as_text).unwrap(), "red");
assert_eq!(
enum_col_as_enum.value().unwrap().as_bytes(),
enum_col_as_text.value().unwrap().as_bytes()
);
}
#[test]
fn check_set_bind() {
let conn = &mut crate::test_helpers::connection();
crate::sql_query("DROP TABLE IF EXISTS set_test CASCADE")
.execute(conn)
.unwrap();
crate::sql_query("CREATE TABLE set_test(id INTEGER PRIMARY KEY, set_field SET('red', 'green', 'blue') NOT NULL)").execute(conn)
.unwrap();
crate::sql_query("INSERT INTO set_test(id, set_field) VALUES (1, 'green')")
.execute(conn)
.unwrap();
let set_col_as_set: BindData =
query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "green");
for tpe in &[
ffi::enum_field_types::MYSQL_TYPE_BLOB,
ffi::enum_field_types::MYSQL_TYPE_VAR_STRING,
ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB,
ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB,
ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB,
] {
let set_col_as_text = query_single_table(
"SELECT set_field FROM set_test",
conn,
(*tpe, Flags::SET_FLAG),
);
assert_eq!(set_col_as_text.tpe, *tpe);
assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "green");
assert_eq!(
set_col_as_set.value().unwrap().as_bytes(),
set_col_as_text.value().unwrap().as_bytes()
);
}
let set_col_as_text = query_single_table(
"SELECT set_field FROM set_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()),
);
assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "green");
assert_eq!(
set_col_as_set.value().unwrap().as_bytes(),
set_col_as_text.value().unwrap().as_bytes()
);
crate::sql_query("DELETE FROM set_test")
.execute(conn)
.unwrap();
input_bind(
"INSERT INTO set_test(id, set_field) VALUES (?, ?)",
conn,
41,
(b"blue".to_vec(), MysqlType::Set),
);
let set_col_as_set =
query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "blue");
let set_col_as_text = query_single_table(
"SELECT set_field FROM set_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG),
);
assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "blue");
assert_eq!(
set_col_as_set.value().unwrap().as_bytes(),
set_col_as_text.value().unwrap().as_bytes()
);
crate::sql_query("DELETE FROM set_test")
.execute(conn)
.unwrap();
input_bind(
"INSERT INTO set_test(id, set_field) VALUES (?, ?)",
conn,
41,
(b"red".to_vec(), MysqlType::String),
);
let set_col_as_set =
query_single_table("SELECT set_field FROM set_test", conn, MysqlType::Set);
assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING);
assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG));
assert!(set_col_as_set.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_set).unwrap(), "red");
let set_col_as_text = query_single_table(
"SELECT set_field FROM set_test",
conn,
(ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG),
);
assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB);
assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG));
assert!(set_col_as_text.flags.contains(Flags::SET_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG));
assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG));
assert_eq!(to_value::<Text, String>(&set_col_as_text).unwrap(), "red");
assert_eq!(
set_col_as_set.value().unwrap().as_bytes(),
set_col_as_text.value().unwrap().as_bytes()
);
}
}