From 24fc0dcb1ee627cdec5ff5670a85050afc62d281 Mon Sep 17 00:00:00 2001 From: Shauren Date: Sun, 14 May 2023 16:30:05 +0200 Subject: Core/DBLayer: Relaxed restrictions on which Field class member function can be used to access column value from "strict match" to "must not truncate" (cherry picked from commit eadafb0347d606e5e3e660d21227aa15378bdb21) --- cmake/options.cmake | 1 - cmake/showoptions.cmake | 9 - src/server/database/Database/Field.cpp | 217 ++++----------------- src/server/database/Database/Field.h | 37 ++-- .../database/Database/FieldValueConverter.cpp | 48 +++++ src/server/database/Database/FieldValueConverter.h | 50 +++++ .../database/Database/FieldValueConverters.h | 112 +++++++++++ src/server/database/Database/QueryResult.cpp | 76 ++++++-- 8 files changed, 321 insertions(+), 229 deletions(-) create mode 100644 src/server/database/Database/FieldValueConverter.cpp create mode 100644 src/server/database/Database/FieldValueConverter.h create mode 100644 src/server/database/Database/FieldValueConverters.h diff --git a/cmake/options.cmake b/cmake/options.cmake index 8e7031a683a..4267989b046 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -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) diff --git a/cmake/showoptions.cmake b/cmake/showoptions.cmake index ac10525f532..260431843d2 100644 --- a/cmake/showoptions.cmake +++ b/cmake/showoptions.cmake @@ -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}") diff --git a/src/server/database/Database/Field.cpp b/src/server/database/Database/Field.cpp index cdcb32c6508..6957cec00ad 100644 --- a/src/server/database/Database/Field.cpp +++ b/src/server/database/Database/Field.cpp @@ -17,294 +17,153 @@ #include "Field.h" #include "Errors.h" +#include "FieldValueConverter.h" #include "Log.h" -#include "StringConvert.h" #include -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(data.value); - return Trinity::StringTo(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(data.value); - return Trinity::StringTo(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(data.value); - return Trinity::StringTo(data.value, 10).value_or(0); + return _meta->Converter->GetUInt16(_value, _length, _meta); } int16 Field::GetInt16() const { - if (!data.value) - return 0; - -#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS - if (!IsType(DatabaseFieldTypes::Int16)) - { - LogWrongType(__FUNCTION__); + if (!_value) return 0; - } -#endif - if (data.raw) - return *reinterpret_cast(data.value); - return Trinity::StringTo(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(data.value); - return Trinity::StringTo(data.value, 10).value_or(0); + return _meta->Converter->GetUInt32(_value, _length, _meta); } int32 Field::GetInt32() const { - if (!data.value) - return 0; - -#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS - if (!IsType(DatabaseFieldTypes::Int32)) - { - LogWrongType(__FUNCTION__); + if (!_value) return 0; - } -#endif - if (data.raw) - return *reinterpret_cast(data.value); - return Trinity::StringTo(data.value, 10).value_or(0); + return _meta->Converter->GetInt32(_value, _length, _meta); } uint64 Field::GetUInt64() const { - if (!data.value) - return 0; - -#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS - if (!IsType(DatabaseFieldTypes::Int64)) - { - LogWrongType(__FUNCTION__); + if (!_value) return 0; - } -#endif - if (data.raw) - return *reinterpret_cast(data.value); - return Trinity::StringTo(data.value, 10).value_or(0); + return _meta->Converter->GetUInt64(_value, _length, _meta); } int64 Field::GetInt64() const { - if (!data.value) - return 0; - -#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS - if (!IsType(DatabaseFieldTypes::Int64)) - { - LogWrongType(__FUNCTION__); + if (!_value) return 0; - } -#endif - if (data.raw) - return *reinterpret_cast(data.value); - return Trinity::StringTo(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(data.value); - return Trinity::StringTo(data.value, 10).value_or(0); + return _meta->Converter->GetFloat(_value, _length, _meta); } double Field::GetDouble() const { - if (!data.value) - return 0.0; - -#ifdef TRINITY_STRICT_DATABASE_TYPE_CHECKS - if (!IsType(DatabaseFieldTypes::Double) && !IsType(DatabaseFieldTypes::Decimal)) - { - LogWrongType(__FUNCTION__); + if (!_value) return 0.0; - } -#endif - if (data.raw && !IsType(DatabaseFieldTypes::Decimal)) - return *reinterpret_cast(data.value); - return Trinity::StringTo(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(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 Field::GetBinary() const { std::vector 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; -} - -void Field::SetStructuredValue(char const* newValue, uint32 length) -{ - // 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); + _value = newValue; + _length = length; } -void Field::SetMetadata(QueryResultFieldMetadata const* fieldMeta) +void Field::SetMetadata(QueryResultFieldMetadata const* meta) { - meta = fieldMeta; + _meta = meta; } diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h index 0229c852647..d0613a3a94f 100644 --- a/src/server/database/Database/Field.h +++ b/src/server/database/Database/Field.h @@ -15,22 +15,27 @@ * with this program. If not, see . */ -#ifndef _FIELD_H -#define _FIELD_H +#ifndef TRINITY_DATABASE_FIELD_H +#define TRINITY_DATABASE_FIELD_H #include "Define.h" -#include "DatabaseEnvFwd.h" #include #include #include #include +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; + private: + char const* _value; // Actual data in memory + uint32 _length; // Length - bool IsNumeric() const; + void SetValue(char const* newValue, uint32 length); - private: - QueryResultFieldMetadata const* meta; - void LogWrongType(char const* getter) const; - void SetMetadata(QueryResultFieldMetadata const* fieldMeta); + QueryResultFieldMetadata const* _meta; + void SetMetadata(QueryResultFieldMetadata const* meta); void GetBinarySizeChecked(uint8* buf, size_t size) const; }; diff --git a/src/server/database/Database/FieldValueConverter.cpp b/src/server/database/Database/FieldValueConverter.cpp new file mode 100644 index 00000000000..589d33c8850 --- /dev/null +++ b/src/server/database/Database/FieldValueConverter.cpp @@ -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 . + */ + +#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); +} diff --git a/src/server/database/Database/FieldValueConverter.h b/src/server/database/Database/FieldValueConverter.h new file mode 100644 index 00000000000..15e65b0fa33 --- /dev/null +++ b/src/server/database/Database/FieldValueConverter.h @@ -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 . + */ + +#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 diff --git a/src/server/database/Database/FieldValueConverters.h b/src/server/database/Database/FieldValueConverters.h new file mode 100644 index 00000000000..e050d8052ca --- /dev/null +++ b/src/server/database/Database/FieldValueConverters.h @@ -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 . + */ + +#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 +class FromStringToDatabaseTypeConverter : public BaseDatabaseResultValueConverter +{ +public: + static DatabaseType GetDatabaseValue(char const* data, uint32 size) + { + return Trinity::StringTo({ data, size }).template value_or(0); + } + + static char const* GetStringValue(char const* data) + { + return data; + } +}; + +// converts binary value returned from query to type specified in column metadata +template +class FromBinaryToDatabaseTypeConverter : public BaseDatabaseResultValueConverter +{ +public: + static DatabaseType GetDatabaseValue(char const* data, uint32 /*size*/) + { + return *reinterpret_cast(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 ToDatabaseTypeConverter> +class PrimitiveResultValueConverter : public BaseDatabaseResultValueConverter +{ +public: + template + static T GetNumericValue(char const* data, uint32 size, QueryResultFieldMetadata const* meta, char const* func) + { + DatabaseType source = ToDatabaseTypeConverter::GetDatabaseValue(data, size); + T result = static_cast(source); + if (static_cast(result) != source) + { + LogTruncation(func, meta); + return T(); + } + return result; + } + + uint8 GetUInt8(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetUInt8"); } + int8 GetInt8(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetInt8"); } + uint16 GetUInt16(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetUInt16"); } + int16 GetInt16(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetInt16"); } + uint32 GetUInt32(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetUInt32"); } + int32 GetInt32(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetInt32"); } + uint64 GetUInt64(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetUInt64"); } + int64 GetInt64(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetInt64"); } + float GetFloat(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetFloat"); } + double GetDouble(char const* data, uint32 size, QueryResultFieldMetadata const* meta) const override { return GetNumericValue(data, size, meta, "Field::GetDouble"); } + char const* GetCString(char const* data, uint32 /*size*/, QueryResultFieldMetadata const* meta) const override + { + char const* result = ToDatabaseTypeConverter::GetStringValue(data); + if (data && !result) + LogTruncation("Field::GetCString", meta); + return result; + } +}; + +template<> +class PrimitiveResultValueConverter : 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; + +#endif // TRINITY_FIELD_VALUE_CONVERTERS_H diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp index 2ab20f392f2..e15826729f4 100644 --- a/src/server/database/Database/QueryResult.cpp +++ b/src/server/database/Database/QueryResult.cpp @@ -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" @@ -74,23 +75,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: @@ -118,7 +119,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) { @@ -132,19 +133,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"; @@ -152,15 +153,52 @@ static char const* FieldTypeToString(enum_field_types type) } } -void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex) +std::unique_ptr FromStringValueConverters[14] = +{ + nullptr, + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + nullptr, + std::make_unique() +}; + +std::unique_ptr BinaryValueConverters[14] = +{ + nullptr, + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), + std::make_unique>(), // always sent as string + nullptr, + std::make_unique() +}; + +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(); } } @@ -174,7 +212,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]); } } @@ -229,7 +267,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; @@ -289,7 +327,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); @@ -298,7 +336,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); } @@ -344,7 +382,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; } -- cgit v1.2.3