Core/DBLayer: Relaxed restrictions on which Field class member function can be used to access column value from "strict match" to "must not truncate"

This commit is contained in:
Shauren
2023-05-14 16:30:05 +02:00
parent 8d0620eb05
commit eadafb0347
8 changed files with 322 additions and 230 deletions

View File

@@ -48,7 +48,6 @@ else()
endif()
option(WITH_WARNINGS "Show all warnings during compile" 0)
option(WITH_COREDEBUG "Include additional debug-code in core" 0)
option(WITH_STRICT_DATABASE_TYPE_CHECKS "Enable strict checking of database field value accessors" 0)
option(WITHOUT_METRICS "Disable metrics reporting (i.e. InfluxDB and Grafana)" 0)
option(WITH_DETAILED_METRICS "Enable detailed metrics reporting (i.e. time each session takes to update)" 0)
option(COPY_CONF "Copy authserver and worldserver .conf.dist files to the project dir" 1)

View File

@@ -161,15 +161,6 @@ elseif (WITH_DETAILED_METRICS)
add_definitions(-DWITH_DETAILED_METRICS)
endif()
if(WITH_STRICT_DATABASE_TYPE_CHECKS)
message("")
message(" *** WITH_STRICT_DATABASE_TYPE_CHECKS - WARNING!")
message(" *** Validates uses of database Get***() functions from Field class")
message(" *** invalid calls will result in returning value 0")
message(" *** NOT COMPATIBLE WITH MARIADB!")
add_definitions(-DTRINITY_STRICT_DATABASE_TYPE_CHECKS)
endif()
if(WITH_BOOST_STACKTRACE)
if (BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE)
add_definitions(-DBOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE="${BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE}")

View File

@@ -17,294 +17,153 @@
#include "Field.h"
#include "Errors.h"
#include "FieldValueConverter.h"
#include "Log.h"
#include "StringConvert.h"
#include <cstring>
Field::Field()
Field::Field() : _value(nullptr), _length(0), _meta(nullptr)
{
data.value = nullptr;
data.length = 0;
data.raw = false;
meta = nullptr;
}
Field::~Field() = default;
uint8 Field::GetUInt8() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int8))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint8 const*>(data.value);
return Trinity::StringTo<uint8>(data.value, 10).value_or(0);
return _meta->Converter->GetUInt8(_value, _length, _meta);
}
int8 Field::GetInt8() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int8))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int8 const*>(data.value);
return Trinity::StringTo<int8>(data.value, 10).value_or(0);
return _meta->Converter->GetInt8(_value, _length, _meta);
}
uint16 Field::GetUInt16() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int16))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint16 const*>(data.value);
return Trinity::StringTo<uint16>(data.value, 10).value_or(0);
return _meta->Converter->GetUInt16(_value, _length, _meta);
}
int16 Field::GetInt16() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int16))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int16 const*>(data.value);
return Trinity::StringTo<int16>(data.value, 10).value_or(0);
return _meta->Converter->GetInt16(_value, _length, _meta);
}
uint32 Field::GetUInt32() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int32))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint32 const*>(data.value);
return Trinity::StringTo<uint32>(data.value, 10).value_or(0);
return _meta->Converter->GetUInt32(_value, _length, _meta);
}
int32 Field::GetInt32() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int32))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int32 const*>(data.value);
return Trinity::StringTo<int32>(data.value, 10).value_or(0);
return _meta->Converter->GetInt32(_value, _length, _meta);
}
uint64 Field::GetUInt64() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int64))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint64 const*>(data.value);
return Trinity::StringTo<uint64>(data.value, 10).value_or(0);
return _meta->Converter->GetUInt64(_value, _length, _meta);
}
int64 Field::GetInt64() const
{
if (!data.value)
if (!_value)
return 0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int64))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int64 const*>(data.value);
return Trinity::StringTo<int64>(data.value, 10).value_or(0);
return _meta->Converter->GetInt64(_value, _length, _meta);
}
float Field::GetFloat() const
{
if (!data.value)
if (!_value)
return 0.0f;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Float))
{
LogWrongType(__FUNCTION__);
return 0.0f;
}
#endif
if (data.raw)
return *reinterpret_cast<float const*>(data.value);
return Trinity::StringTo<float>(data.value, 10).value_or(0);
return _meta->Converter->GetFloat(_value, _length, _meta);
}
double Field::GetDouble() const
{
if (!data.value)
if (!_value)
return 0.0;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Double) && !IsType(DatabaseFieldTypes::Decimal))
{
LogWrongType(__FUNCTION__);
return 0.0;
}
#endif
if (data.raw && !IsType(DatabaseFieldTypes::Decimal))
return *reinterpret_cast<double const*>(data.value);
return Trinity::StringTo<double>(data.value, 10).value_or(0);
return _meta->Converter->GetDouble(_value, _length, _meta);
}
char const* Field::GetCString() const
{
if (!data.value)
if (!_value)
return nullptr;
#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS
if (IsNumeric() && data.raw)
{
LogWrongType(__FUNCTION__);
return nullptr;
}
#endif
return static_cast<char const*>(data.value);
return _meta->Converter->GetCString(_value, _length, _meta);
}
std::string Field::GetString() const
{
if (!data.value)
if (!_value)
return "";
char const* string = GetCString();
if (!string)
return "";
return std::string(string, data.length);
return std::string(string, _length);
}
std::string_view Field::GetStringView() const
{
if (!data.value)
if (!_value)
return {};
char const* const string = GetCString();
if (!string)
return {};
return { string, data.length };
return { string, _length };
}
std::vector<uint8> Field::GetBinary() const
{
std::vector<uint8> result;
if (!data.value || !data.length)
if (!_value || !_length)
return result;
result.resize(data.length);
memcpy(result.data(), data.value, data.length);
result.resize(_length);
memcpy(result.data(), _value, _length);
return result;
}
void Field::GetBinarySizeChecked(uint8* buf, size_t length) const
{
ASSERT(data.value && (data.length == length), "Expected %zu-byte binary blob, got %sdata (%u bytes) instead", length, data.value ? "" : "no ", data.length);
memcpy(buf, data.value, length);
ASSERT(_value && (_length == length), "Expected %zu-byte binary blob, got %sdata (%u bytes) instead", length, _value ? "" : "no ", _length);
memcpy(buf, _value, length);
}
void Field::SetByteValue(char const* newValue, uint32 length)
void Field::SetValue(char const* newValue, uint32 length)
{
// This value stores raw bytes that have to be explicitly cast later
data.value = newValue;
data.length = length;
data.raw = true;
_value = newValue;
_length = length;
}
void Field::SetStructuredValue(char const* newValue, uint32 length)
void Field::SetMetadata(QueryResultFieldMetadata const* meta)
{
// This value stores somewhat structured data that needs function style casting
data.value = newValue;
data.length = length;
data.raw = false;
}
bool Field::IsType(DatabaseFieldTypes type) const
{
return meta->Type == type;
}
bool Field::IsNumeric() const
{
return (meta->Type == DatabaseFieldTypes::Int8 ||
meta->Type == DatabaseFieldTypes::Int16 ||
meta->Type == DatabaseFieldTypes::Int32 ||
meta->Type == DatabaseFieldTypes::Int64 ||
meta->Type == DatabaseFieldTypes::Float ||
meta->Type == DatabaseFieldTypes::Double);
}
void Field::LogWrongType(char const* getter) const
{
TC_LOG_WARN("sql.sql", "Warning: {} on {} field {}.{} ({}.{}) at index {}.",
getter, meta->TypeName, meta->TableAlias, meta->Alias, meta->TableName, meta->Name, meta->Index);
}
void Field::SetMetadata(QueryResultFieldMetadata const* fieldMeta)
{
meta = fieldMeta;
_meta = meta;
}

View File

@@ -15,22 +15,27 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _FIELD_H
#define _FIELD_H
#ifndef TRINITY_DATABASE_FIELD_H
#define TRINITY_DATABASE_FIELD_H
#include "Define.h"
#include "DatabaseEnvFwd.h"
#include <array>
#include <string>
#include <string_view>
#include <vector>
class BaseDatabaseResultValueConverter;
enum class DatabaseFieldTypes : uint8
{
Null,
UInt8,
Int8,
UInt16,
Int16,
UInt32,
Int32,
UInt64,
Int64,
Float,
Double,
@@ -48,6 +53,7 @@ struct QueryResultFieldMetadata
char const* TypeName = nullptr;
uint32 Index = 0;
DatabaseFieldTypes Type = DatabaseFieldTypes::Null;
BaseDatabaseResultValueConverter const* Converter = nullptr;
};
/**
@@ -118,28 +124,17 @@ class TC_DATABASE_API Field
bool IsNull() const
{
return data.value == nullptr;
return _value == nullptr;
}
protected:
struct
{
char const* value; // Actual data in memory
uint32 length; // Length
bool raw; // Raw bytes? (Prepared statement or ad hoc)
} data;
void SetByteValue(char const* newValue, uint32 length);
void SetStructuredValue(char const* newValue, uint32 length);
bool IsType(DatabaseFieldTypes type) const;
bool IsNumeric() const;
private:
QueryResultFieldMetadata const* meta;
void LogWrongType(char const* getter) const;
void SetMetadata(QueryResultFieldMetadata const* fieldMeta);
char const* _value; // Actual data in memory
uint32 _length; // Length
void SetValue(char const* newValue, uint32 length);
QueryResultFieldMetadata const* _meta;
void SetMetadata(QueryResultFieldMetadata const* meta);
void GetBinarySizeChecked(uint8* buf, size_t size) const;
};

View File

@@ -0,0 +1,48 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "FieldValueConverter.h"
#include "Errors.h"
#include "Field.h"
BaseDatabaseResultValueConverter::BaseDatabaseResultValueConverter() = default;
BaseDatabaseResultValueConverter::~BaseDatabaseResultValueConverter() = default;
void BaseDatabaseResultValueConverter::LogTruncation(char const* getter, QueryResultFieldMetadata const* meta)
{
char const* expectedAccessor = "";
switch (meta->Type)
{
case DatabaseFieldTypes::UInt8: expectedAccessor = "Field::GetUInt8"; break;
case DatabaseFieldTypes::Int8: expectedAccessor = "Field::GetInt8"; break;
case DatabaseFieldTypes::UInt16: expectedAccessor = "Field::GetUInt16"; break;
case DatabaseFieldTypes::Int16: expectedAccessor = "Field::GetInt16"; break;
case DatabaseFieldTypes::UInt32: expectedAccessor = "Field::GetUIn32"; break;
case DatabaseFieldTypes::Int32: expectedAccessor = "Field::GetInt32"; break;
case DatabaseFieldTypes::UInt64: expectedAccessor = "Field::GetUIn64"; break;
case DatabaseFieldTypes::Int64: expectedAccessor = "Field::GetInt64"; break;
case DatabaseFieldTypes::Float: expectedAccessor = "Field::GetFloat"; break;
case DatabaseFieldTypes::Double: expectedAccessor = "Field::GetDouble"; break;
case DatabaseFieldTypes::Decimal: expectedAccessor = "Field::GetDouble or Field::GetString"; break;
case DatabaseFieldTypes::Binary: expectedAccessor = "Field::GetString or Field::GetBinary"; break;
default:
break;
}
ASSERT(false, "%s on %s field %s.%s (%s.%s) at index %u caused value to be truncated. Use %s instead.",
getter, meta->TypeName, meta->TableAlias, meta->Alias, meta->TableName, meta->Name, meta->Index, expectedAccessor);
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_FIELD_VALUE_CONVERTER_H
#define TRINITY_FIELD_VALUE_CONVERTER_H
#include "Define.h"
struct QueryResultFieldMetadata;
class BaseDatabaseResultValueConverter
{
public:
BaseDatabaseResultValueConverter();
BaseDatabaseResultValueConverter(BaseDatabaseResultValueConverter const&) = delete;
BaseDatabaseResultValueConverter(BaseDatabaseResultValueConverter&&) = delete;
BaseDatabaseResultValueConverter& operator=(BaseDatabaseResultValueConverter const&) = delete;
BaseDatabaseResultValueConverter& operator=(BaseDatabaseResultValueConverter&&) = delete;
virtual ~BaseDatabaseResultValueConverter();
virtual uint8 GetUInt8(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual int8 GetInt8(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual uint16 GetUInt16(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual int16 GetInt16(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual uint32 GetUInt32(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual int32 GetInt32(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual uint64 GetUInt64(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual int64 GetInt64(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual float GetFloat(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual double GetDouble(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
virtual char const* GetCString(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const = 0;
static void LogTruncation(char const* getter, QueryResultFieldMetadata const* meta);
};
#endif // TRINITY_FIELD_VALUE_CONVERTER_H

View File

@@ -0,0 +1,112 @@
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_FIELD_VALUE_CONVERTERS_H
#define TRINITY_FIELD_VALUE_CONVERTERS_H
#include "FieldValueConverter.h"
#include "StringConvert.h"
// converts string value returned from query to type specified in column metadata
template<typename DatabaseType>
class FromStringToDatabaseTypeConverter : public BaseDatabaseResultValueConverter
{
public:
static DatabaseType GetDatabaseValue(char const* data, uint32 size)
{
return Trinity::StringTo<DatabaseType>({ data, size }).template value_or<DatabaseType>(0);
}
static char const* GetStringValue(char const* data)
{
return data;
}
};
// converts binary value returned from query to type specified in column metadata
template<typename DatabaseType>
class FromBinaryToDatabaseTypeConverter : public BaseDatabaseResultValueConverter
{
public:
static DatabaseType GetDatabaseValue(char const* data, uint32 /*size*/)
{
return *reinterpret_cast<DatabaseType const*>(data);
}
static char const* GetStringValue(char const* /*data*/)
{
return nullptr;
}
};
// converts column value from type specified in column metadata to type requested by Field::Get* function
template<typename DatabaseType, template<typename...> typename ToDatabaseTypeConverter>
class PrimitiveResultValueConverter : public BaseDatabaseResultValueConverter
{
public:
template<typename T>
static T GetNumericValue(char const* data, uint32 size, QueryResultFieldMetadata const* meta, char const* func)
{
DatabaseType source = ToDatabaseTypeConverter<DatabaseType>::GetDatabaseValue(data, size);
T result = static_cast<T>(source);
if (static_cast<DatabaseType>(result) != source)
{
LogTruncation(func, meta);
return T();
}
return result;
}
uint8 GetUInt8(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<uint8>(data, size, meta, "Field::GetUInt8"); }
int8 GetInt8(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<int8>(data, size, meta, "Field::GetInt8"); }
uint16 GetUInt16(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<uint16>(data, size, meta, "Field::GetUInt16"); }
int16 GetInt16(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<int16>(data, size, meta, "Field::GetInt16"); }
uint32 GetUInt32(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<uint32>(data, size, meta, "Field::GetUInt32"); }
int32 GetInt32(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<int32>(data, size, meta, "Field::GetInt32"); }
uint64 GetUInt64(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<uint64>(data, size, meta, "Field::GetUInt64"); }
int64 GetInt64(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<int64>(data, size, meta, "Field::GetInt64"); }
float GetFloat(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<float>(data, size, meta, "Field::GetFloat"); }
double GetDouble(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue<double>(data, size, meta, "Field::GetDouble"); }
char const* GetCString(char const* data, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override
{
char const* result = ToDatabaseTypeConverter<DatabaseType>::GetStringValue(data);
if (data && !result)
LogTruncation("Field::GetCString", meta);
return result;
}
};
template<>
class PrimitiveResultValueConverter<char const*, std::void_t> : public BaseDatabaseResultValueConverter
{
public:
uint8 GetUInt8(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetUInt8", meta); return 0; }
int8 GetInt8(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetInt8", meta); return 0; }
uint16 GetUInt16(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetUInt16", meta); return 0; }
int16 GetInt16(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetInt16", meta); return 0; }
uint32 GetUInt32(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetUInt32", meta); return 0; }
int32 GetInt32(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetInt32", meta); return 0; }
uint64 GetUInt64(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetUInt64", meta); return 0; }
int64 GetInt64(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetInt64", meta); return 0; }
float GetFloat(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetFloat", meta); return 0.0f; }
double GetDouble(char const* /*data*/, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override { LogTruncation("Field::GetDouble", meta); return 0.0; }
char const* GetCString(char const* data, uint32 /*size*/, QueryResultFieldMetadata const* /*meta*/) const override { return data; }
};
using StringResultValueConverter = PrimitiveResultValueConverter<char const*, std::void_t>;
#endif // TRINITY_FIELD_VALUE_CONVERTERS_H

View File

@@ -18,6 +18,7 @@
#include "QueryResult.h"
#include "Errors.h"
#include "Field.h"
#include "FieldValueConverters.h"
#include "Log.h"
#include "MySQLHacks.h"
#include "MySQLWorkaround.h"
@@ -75,23 +76,23 @@ static uint32 SizeForType(MYSQL_FIELD* field)
}
}
DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type)
DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type, uint32 flags)
{
switch (type)
{
case MYSQL_TYPE_NULL:
return DatabaseFieldTypes::Null;
case MYSQL_TYPE_TINY:
return DatabaseFieldTypes::Int8;
return (flags & UNSIGNED_FLAG) ? DatabaseFieldTypes::UInt8 : DatabaseFieldTypes::Int8;
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
return DatabaseFieldTypes::Int16;
return (flags & UNSIGNED_FLAG) ? DatabaseFieldTypes::UInt16 : DatabaseFieldTypes::Int16;
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
return DatabaseFieldTypes::Int32;
return (flags & UNSIGNED_FLAG) ? DatabaseFieldTypes::UInt32 : DatabaseFieldTypes::Int32;
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_BIT:
return DatabaseFieldTypes::Int64;
return (flags & UNSIGNED_FLAG) ? DatabaseFieldTypes::UInt64 : DatabaseFieldTypes::Int64;
case MYSQL_TYPE_FLOAT:
return DatabaseFieldTypes::Float;
case MYSQL_TYPE_DOUBLE:
@@ -119,7 +120,7 @@ DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type)
return DatabaseFieldTypes::Null;
}
static char const* FieldTypeToString(enum_field_types type)
static char const* FieldTypeToString(enum_field_types type, uint32 flags)
{
switch (type)
{
@@ -133,19 +134,19 @@ static char const* FieldTypeToString(enum_field_types type)
case MYSQL_TYPE_ENUM: return "ENUM";
case MYSQL_TYPE_FLOAT: return "FLOAT";
case MYSQL_TYPE_GEOMETRY: return "GEOMETRY";
case MYSQL_TYPE_INT24: return "INT24";
case MYSQL_TYPE_LONG: return "LONG";
case MYSQL_TYPE_LONGLONG: return "LONGLONG";
case MYSQL_TYPE_INT24: return (flags & UNSIGNED_FLAG) ? "UNSIGNED INT24" : "INT24";
case MYSQL_TYPE_LONG: return (flags & UNSIGNED_FLAG) ? "UNSIGNED LONG" : "LONG";
case MYSQL_TYPE_LONGLONG: return (flags & UNSIGNED_FLAG) ? "UNSIGNED LONGLONG" : "LONGLONG";
case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB";
case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
case MYSQL_TYPE_NEWDATE: return "NEWDATE";
case MYSQL_TYPE_NULL: return "NULL";
case MYSQL_TYPE_SET: return "SET";
case MYSQL_TYPE_SHORT: return "SHORT";
case MYSQL_TYPE_SHORT: return (flags & UNSIGNED_FLAG) ? "UNSIGNED SHORT" : "SHORT";
case MYSQL_TYPE_STRING: return "STRING";
case MYSQL_TYPE_TIME: return "TIME";
case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP";
case MYSQL_TYPE_TINY: return "TINY";
case MYSQL_TYPE_TINY: return (flags & UNSIGNED_FLAG) ? "UNSIGNED TINY" : "TINY";
case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB";
case MYSQL_TYPE_VAR_STRING: return "VAR_STRING";
case MYSQL_TYPE_YEAR: return "YEAR";
@@ -153,15 +154,52 @@ static char const* FieldTypeToString(enum_field_types type)
}
}
void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex)
std::unique_ptr<BaseDatabaseResultValueConverter> FromStringValueConverters[14] =
{
nullptr,
std::make_unique<PrimitiveResultValueConverter<uint8, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int8, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<uint16, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int16, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<uint32, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int32, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<uint64, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int64, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<float, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<double, FromStringToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<double, FromStringToDatabaseTypeConverter>>(),
nullptr,
std::make_unique<StringResultValueConverter>()
};
std::unique_ptr<BaseDatabaseResultValueConverter> BinaryValueConverters[14] =
{
nullptr,
std::make_unique<PrimitiveResultValueConverter<uint8, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int8, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<uint16, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int16, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<uint32, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int32, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<uint64, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<int64, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<float, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<double, FromBinaryToDatabaseTypeConverter>>(),
std::make_unique<PrimitiveResultValueConverter<double, FromStringToDatabaseTypeConverter>>(), // always sent as string
nullptr,
std::make_unique<StringResultValueConverter>()
};
void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex, bool binaryProtocol)
{
meta->TableName = field->org_table;
meta->TableAlias = field->table;
meta->Name = field->org_name;
meta->Alias = field->name;
meta->TypeName = FieldTypeToString(field->type);
meta->TypeName = FieldTypeToString(field->type, field->flags);
meta->Index = fieldIndex;
meta->Type = MysqlTypeToFieldType(field->type);
meta->Type = MysqlTypeToFieldType(field->type, field->flags);
meta->Converter = binaryProtocol ? BinaryValueConverters[AsUnderlyingType(meta->Type)].get() : FromStringValueConverters[AsUnderlyingType(meta->Type)].get();
}
}
@@ -175,7 +213,7 @@ _fields(fields)
_currentRow = new Field[_fieldCount];
for (uint32 i = 0; i < _fieldCount; i++)
{
InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i);
InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i, false);
_currentRow[i].SetMetadata(&_fieldMetadata[i]);
}
}
@@ -230,7 +268,7 @@ m_metadataResult(result)
uint32 size = SizeForType(&field[i]);
rowSize += size;
InitializeDatabaseFieldMetadata(&m_fieldMetadata[i], &field[i], i);
InitializeDatabaseFieldMetadata(&m_fieldMetadata[i], &field[i], i, true);
m_rBind[i].buffer_type = field[i].type;
m_rBind[i].buffer_length = size;
@@ -290,7 +328,7 @@ m_metadataResult(result)
break;
}
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetValue(
(char const*)buffer,
fetched_length);
@@ -299,7 +337,7 @@ m_metadataResult(result)
}
else
{
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetValue(
nullptr,
*m_rBind[fIndex].length);
}
@@ -345,7 +383,7 @@ bool ResultSet::NextRow()
}
for (uint32 i = 0; i < _fieldCount; i++)
_currentRow[i].SetStructuredValue(row[i], lengths[i]);
_currentRow[i].SetValue(row[i], lengths[i]);
return true;
}