mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-16 04:08:10 +00:00
Added thirdparty: boost library
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_TYPING_GET_TYPE_INDEX_HPP
|
||||
#define BOOST_MYSQL_DETAIL_TYPING_GET_TYPE_INDEX_HPP
|
||||
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/list.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
constexpr std::size_t index_not_found = static_cast<std::size_t>(-1);
|
||||
|
||||
template <class SpanRowType, class... RowType>
|
||||
constexpr std::size_t get_type_index() noexcept
|
||||
{
|
||||
using lunique = mp11::mp_unique<mp11::mp_list<RowType...>>;
|
||||
using index_t = mp11::mp_find<lunique, SpanRowType>;
|
||||
return index_t::value < mp11::mp_size<lunique>::value ? index_t::value : index_not_found;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
//
|
||||
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_TYPING_META_CHECK_CONTEXT_HPP
|
||||
#define BOOST_MYSQL_DETAIL_TYPING_META_CHECK_CONTEXT_HPP
|
||||
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/column_type.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/access.hpp>
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
#include <boost/mysql/detail/typing/pos_map.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
inline const char* column_type_to_str(const metadata& meta) noexcept
|
||||
{
|
||||
switch (meta.type())
|
||||
{
|
||||
case column_type::tinyint: return meta.is_unsigned() ? "TINYINT UNSIGNED" : "TINYINT";
|
||||
case column_type::smallint: return meta.is_unsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
|
||||
case column_type::mediumint: return meta.is_unsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT";
|
||||
case column_type::int_: return meta.is_unsigned() ? "INT UNSIGNED" : "INT";
|
||||
case column_type::bigint: return meta.is_unsigned() ? "BIGINT UNSIGNED" : "BIGINT";
|
||||
case column_type::float_: return "FLOAT";
|
||||
case column_type::double_: return "DOUBLE";
|
||||
case column_type::decimal: return "DECIMAL";
|
||||
case column_type::bit: return "BIT";
|
||||
case column_type::year: return "YEAR";
|
||||
case column_type::time: return "TIME";
|
||||
case column_type::date: return "DATE";
|
||||
case column_type::datetime: return "DATETIME";
|
||||
case column_type::timestamp: return "TIMESTAMP";
|
||||
case column_type::char_: return "CHAR";
|
||||
case column_type::varchar: return "VARCHAR";
|
||||
case column_type::binary: return "BINARY";
|
||||
case column_type::varbinary: return "VARBINARY";
|
||||
case column_type::text: return "TEXT";
|
||||
case column_type::blob: return "BLOB";
|
||||
case column_type::enum_: return "ENUM";
|
||||
case column_type::set: return "SET";
|
||||
case column_type::json: return "JSON";
|
||||
case column_type::geometry: return "GEOMETRY";
|
||||
default: return "<unknown column type>";
|
||||
}
|
||||
}
|
||||
|
||||
class meta_check_context
|
||||
{
|
||||
std::unique_ptr<std::ostringstream> errors_;
|
||||
std::size_t current_index_{};
|
||||
span<const std::size_t> pos_map_;
|
||||
name_table_t name_table_;
|
||||
metadata_collection_view meta_{};
|
||||
bool nullability_checked_{};
|
||||
|
||||
std::ostringstream& add_error()
|
||||
{
|
||||
if (!errors_)
|
||||
errors_.reset(new std::ostringstream);
|
||||
else
|
||||
*errors_ << '\n';
|
||||
return *errors_;
|
||||
}
|
||||
|
||||
void insert_field_name(std::ostringstream& os)
|
||||
{
|
||||
if (has_field_names(name_table_))
|
||||
os << "'" << name_table_[current_index_] << "'";
|
||||
else
|
||||
os << "in position " << current_index_;
|
||||
}
|
||||
|
||||
public:
|
||||
meta_check_context(
|
||||
span<const std::size_t> pos_map,
|
||||
name_table_t name_table,
|
||||
metadata_collection_view meta
|
||||
) noexcept
|
||||
: pos_map_(pos_map), name_table_(name_table), meta_(meta)
|
||||
{
|
||||
}
|
||||
|
||||
// Accessors
|
||||
const metadata& current_meta() const noexcept { return map_metadata(pos_map_, current_index_, meta_); }
|
||||
bool is_current_field_absent() const noexcept { return pos_map_[current_index_] == pos_absent; }
|
||||
|
||||
// Iteration
|
||||
void advance() noexcept
|
||||
{
|
||||
nullability_checked_ = false;
|
||||
++current_index_;
|
||||
}
|
||||
|
||||
// Nullability
|
||||
void set_nullability_checked() noexcept { nullability_checked_ = true; }
|
||||
bool nullability_checked() const noexcept { return nullability_checked_; }
|
||||
|
||||
// Error reporting
|
||||
BOOST_MYSQL_DECL
|
||||
void add_field_absent_error();
|
||||
|
||||
BOOST_MYSQL_DECL
|
||||
void add_type_mismatch_error(const char* cpp_type_name);
|
||||
|
||||
BOOST_MYSQL_DECL
|
||||
void add_nullability_error();
|
||||
|
||||
BOOST_MYSQL_DECL
|
||||
error_code check_errors(diagnostics& diag) const;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#ifdef BOOST_MYSQL_HEADER_ONLY
|
||||
#include <boost/mysql/impl/meta_check_context.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_TYPING_POS_MAP_HPP
|
||||
#define BOOST_MYSQL_DETAIL_TYPING_POS_MAP_HPP
|
||||
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/core/span.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// These functions map C++ type positions to positions to positions in the DB query
|
||||
|
||||
constexpr std::size_t pos_absent = static_cast<std::size_t>(-1);
|
||||
using name_table_t = boost::span<const string_view>;
|
||||
|
||||
inline bool has_field_names(name_table_t name_table) noexcept { return !name_table.empty(); }
|
||||
|
||||
inline void pos_map_reset(span<std::size_t> self) noexcept
|
||||
{
|
||||
for (std::size_t i = 0; i < self.size(); ++i)
|
||||
self.data()[i] = pos_absent;
|
||||
}
|
||||
|
||||
inline void pos_map_add_field(
|
||||
span<std::size_t> self,
|
||||
name_table_t name_table,
|
||||
std::size_t db_index,
|
||||
string_view field_name
|
||||
) noexcept
|
||||
{
|
||||
if (has_field_names(name_table))
|
||||
{
|
||||
BOOST_ASSERT(self.size() == name_table.size());
|
||||
|
||||
// We're mapping fields by name. Try to find where in our target struct
|
||||
// is the current field located
|
||||
auto it = std::find(name_table.begin(), name_table.end(), field_name);
|
||||
if (it != name_table.end())
|
||||
{
|
||||
std::size_t cpp_index = it - name_table.begin();
|
||||
self[cpp_index] = db_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're mapping by position. Any extra trailing fields are discarded
|
||||
if (db_index < self.size())
|
||||
{
|
||||
self[db_index] = db_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline field_view map_field_view(
|
||||
span<const std::size_t> self,
|
||||
std::size_t cpp_index,
|
||||
span<const field_view> array
|
||||
) noexcept
|
||||
{
|
||||
BOOST_ASSERT(cpp_index < self.size());
|
||||
return array[self[cpp_index]];
|
||||
}
|
||||
|
||||
inline const metadata& map_metadata(
|
||||
span<const std::size_t> self,
|
||||
std::size_t cpp_index,
|
||||
metadata_collection_view meta
|
||||
) noexcept
|
||||
{
|
||||
BOOST_ASSERT(cpp_index < self.size());
|
||||
return meta[self[cpp_index]];
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+622
@@ -0,0 +1,622 @@
|
||||
//
|
||||
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
|
||||
#define BOOST_MYSQL_DETAIL_TYPING_READABLE_FIELD_TRAITS_HPP
|
||||
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/date.hpp>
|
||||
#include <boost/mysql/datetime.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/field_kind.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
#include <boost/mysql/time.hpp>
|
||||
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
#include <boost/mysql/detail/typing/meta_check_context.hpp>
|
||||
#include <boost/mysql/detail/typing/pos_map.hpp>
|
||||
#include <boost/mysql/detail/void_t.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// Helpers for integers
|
||||
template <class SignedInt>
|
||||
error_code parse_signed_int(field_view input, SignedInt& output)
|
||||
{
|
||||
using unsigned_t = typename std::make_unsigned<SignedInt>::type;
|
||||
using limits_t = std::numeric_limits<SignedInt>;
|
||||
|
||||
auto kind = input.kind();
|
||||
if (kind == field_kind::int64)
|
||||
{
|
||||
auto v = input.get_int64();
|
||||
if (v < (limits_t::min)() || v > (limits_t::max)())
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = static_cast<SignedInt>(v);
|
||||
return error_code();
|
||||
}
|
||||
else if (kind == field_kind::uint64)
|
||||
{
|
||||
auto v = input.get_uint64();
|
||||
if (v > static_cast<unsigned_t>((limits_t::max)()))
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = static_cast<SignedInt>(v);
|
||||
return error_code();
|
||||
}
|
||||
else
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
}
|
||||
|
||||
template <class UnsignedInt>
|
||||
error_code parse_unsigned_int(field_view input, UnsignedInt& output)
|
||||
{
|
||||
if (input.kind() != field_kind::uint64)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
auto v = input.get_uint64();
|
||||
if (v > (std::numeric_limits<UnsignedInt>::max)())
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = static_cast<UnsignedInt>(v);
|
||||
return error_code();
|
||||
}
|
||||
|
||||
// We want all integer types to be allowed as fields. Some integers
|
||||
// may have the same width as others, but different type (e.g. long and long long
|
||||
// may both be 64-bit, but different types). Auxiliar int_traits to allow this to work
|
||||
template <class T, bool is_signed = std::is_signed<T>::value, std::size_t width = sizeof(T)>
|
||||
struct int_traits
|
||||
{
|
||||
static constexpr bool is_supported = false;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, true, 1>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "int8_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint: return !ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, false, 1>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "uint8_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint: return ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, true, 2>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "int16_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint: return true;
|
||||
case column_type::smallint:
|
||||
case column_type::year: return !ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, false, 2>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "uint16_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint:
|
||||
case column_type::smallint:
|
||||
case column_type::year: return ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, true, 4>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "int32_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint:
|
||||
case column_type::smallint:
|
||||
case column_type::year:
|
||||
case column_type::mediumint: return true;
|
||||
case column_type::int_: return !ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, false, 4>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "uint32_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint:
|
||||
case column_type::smallint:
|
||||
case column_type::year:
|
||||
case column_type::mediumint:
|
||||
case column_type::int_: return ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_unsigned_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, true, 8>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "int64_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint:
|
||||
case column_type::smallint:
|
||||
case column_type::year:
|
||||
case column_type::mediumint:
|
||||
case column_type::int_: return true;
|
||||
case column_type::bigint: return !ctx.current_meta().is_unsigned();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, T& output) { return parse_signed_int(input, output); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct int_traits<T, false, 8>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "uint64_t";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::tinyint:
|
||||
case column_type::smallint:
|
||||
case column_type::year:
|
||||
case column_type::mediumint:
|
||||
case column_type::int_:
|
||||
case column_type::bigint: return ctx.current_meta().is_unsigned();
|
||||
case column_type::bit: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, std::uint64_t& output)
|
||||
{
|
||||
return parse_unsigned_int(input, output);
|
||||
}
|
||||
};
|
||||
|
||||
// Traits
|
||||
template <typename T, class EnableIf = void>
|
||||
struct readable_field_traits
|
||||
{
|
||||
static constexpr bool is_supported = false;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<char, void> : int_traits<char>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<signed char, void> : int_traits<signed char>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<unsigned char, void> : int_traits<unsigned char>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<short, void> : int_traits<short>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<unsigned short, void> : int_traits<unsigned short>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<int, void> : int_traits<int>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<unsigned int, void> : int_traits<unsigned int>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<long, void> : int_traits<long>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<unsigned long, void> : int_traits<unsigned long>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<long long, void> : int_traits<long long>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<unsigned long long, void> : int_traits<unsigned long long>
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<bool, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "bool";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
return ctx.current_meta().type() == column_type::tinyint && !ctx.current_meta().is_unsigned();
|
||||
}
|
||||
static error_code parse(field_view input, bool& output)
|
||||
{
|
||||
if (input.kind() != field_kind::int64)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = input.get_int64() != 0;
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<float, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "float";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
return ctx.current_meta().type() == column_type::float_;
|
||||
}
|
||||
static error_code parse(field_view input, float& output)
|
||||
{
|
||||
if (input.kind() != field_kind::float_)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = input.get_float();
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<double, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "double";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::float_:
|
||||
case column_type::double_: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, double& output)
|
||||
{
|
||||
auto kind = input.kind();
|
||||
if (kind == field_kind::float_)
|
||||
{
|
||||
output = input.get_float();
|
||||
return error_code();
|
||||
}
|
||||
else if (kind == field_kind::double_)
|
||||
{
|
||||
output = input.get_double();
|
||||
return error_code();
|
||||
}
|
||||
else
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class Allocator>
|
||||
struct readable_field_traits<std::basic_string<char, std::char_traits<char>, Allocator>, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "string";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::decimal:
|
||||
case column_type::char_:
|
||||
case column_type::varchar:
|
||||
case column_type::text:
|
||||
case column_type::enum_:
|
||||
case column_type::set:
|
||||
case column_type::json: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(
|
||||
field_view input,
|
||||
std::basic_string<char, std::char_traits<char>, Allocator>& output
|
||||
)
|
||||
{
|
||||
if (input.kind() != field_kind::string)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = input.get_string();
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Allocator>
|
||||
struct readable_field_traits<std::vector<unsigned char, Allocator>, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "blob";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::binary:
|
||||
case column_type::varbinary:
|
||||
case column_type::blob:
|
||||
case column_type::geometry:
|
||||
case column_type::unknown: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, std::vector<unsigned char, Allocator>& output)
|
||||
{
|
||||
if (input.kind() != field_kind::blob)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
auto view = input.get_blob();
|
||||
output.assign(view.begin(), view.end());
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<date, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "date";
|
||||
static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::date; }
|
||||
static error_code parse(field_view input, date& output)
|
||||
{
|
||||
if (input.kind() != field_kind::date)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = input.get_date();
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<datetime, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "datetime";
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
switch (ctx.current_meta().type())
|
||||
{
|
||||
case column_type::datetime:
|
||||
case column_type::timestamp: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
static error_code parse(field_view input, datetime& output)
|
||||
{
|
||||
if (input.kind() != field_kind::datetime)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = input.get_datetime();
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct readable_field_traits<time, void>
|
||||
{
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = "time";
|
||||
static bool meta_check(meta_check_context& ctx) { return ctx.current_meta().type() == column_type::time; }
|
||||
static error_code parse(field_view input, time& output)
|
||||
{
|
||||
if (input.kind() != field_kind::time)
|
||||
{
|
||||
return client_errc::static_row_parsing_error;
|
||||
}
|
||||
output = input.get_time();
|
||||
return error_code();
|
||||
}
|
||||
};
|
||||
|
||||
// std::optional<T> and boost::optional<T>. To avoid dependencies,
|
||||
// this is achieved through a "concept"
|
||||
template <class T, class = void>
|
||||
struct is_readable_optional : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_readable_optional<
|
||||
T,
|
||||
void_t<
|
||||
typename std::enable_if<
|
||||
std::is_same<decltype(std::declval<T&>().value()), typename T::value_type&>::value>::type,
|
||||
decltype(std::declval<T&>().emplace()), // T should be default constructible
|
||||
decltype(std::declval<T&>().reset())>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct readable_field_traits<
|
||||
T,
|
||||
typename std::enable_if<
|
||||
is_readable_optional<T>::value && readable_field_traits<typename T::value_type>::is_supported>::type>
|
||||
{
|
||||
using value_type = typename T::value_type;
|
||||
static constexpr bool is_supported = true;
|
||||
static constexpr const char* type_name = readable_field_traits<value_type>::type_name;
|
||||
static bool meta_check(meta_check_context& ctx)
|
||||
{
|
||||
ctx.set_nullability_checked();
|
||||
return readable_field_traits<value_type>::meta_check(ctx);
|
||||
}
|
||||
static error_code parse(field_view input, T& output)
|
||||
{
|
||||
if (input.is_null())
|
||||
{
|
||||
output.reset();
|
||||
return error_code();
|
||||
}
|
||||
else
|
||||
{
|
||||
output.emplace();
|
||||
return readable_field_traits<value_type>::parse(input, output.value());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct is_readable_field
|
||||
{
|
||||
static constexpr bool value = readable_field_traits<T>::is_supported;
|
||||
};
|
||||
|
||||
template <typename ReadableField>
|
||||
void meta_check_field_impl(meta_check_context& ctx)
|
||||
{
|
||||
using traits_t = readable_field_traits<ReadableField>;
|
||||
|
||||
// Verify that the field is present
|
||||
if (ctx.is_current_field_absent())
|
||||
{
|
||||
ctx.add_field_absent_error();
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform the check
|
||||
bool ok = traits_t::meta_check(ctx);
|
||||
if (!ok)
|
||||
{
|
||||
ctx.add_type_mismatch_error(traits_t::type_name);
|
||||
}
|
||||
|
||||
// Check nullability
|
||||
if (!ctx.nullability_checked() && !ctx.current_meta().is_not_null())
|
||||
{
|
||||
ctx.add_nullability_error();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ReadableField>
|
||||
void meta_check_field(meta_check_context& ctx)
|
||||
{
|
||||
static_assert(is_readable_field<ReadableField>::value, "Should be a ReadableField");
|
||||
meta_check_field_impl<ReadableField>(ctx);
|
||||
ctx.advance();
|
||||
}
|
||||
|
||||
struct meta_check_field_fn
|
||||
{
|
||||
meta_check_context ctx;
|
||||
|
||||
template <class T>
|
||||
void operator()(T)
|
||||
{
|
||||
meta_check_field<typename T::type>(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ReadableFieldList>
|
||||
error_code meta_check_field_type_list(
|
||||
span<const std::size_t> field_map,
|
||||
name_table_t name_table,
|
||||
metadata_collection_view meta,
|
||||
diagnostics& diag
|
||||
)
|
||||
{
|
||||
meta_check_field_fn fn{meta_check_context(field_map, name_table, meta)};
|
||||
boost::mp11::mp_for_each<ReadableFieldList>(fn);
|
||||
return fn.ctx.check_errors(diag);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
//
|
||||
// Copyright (c) 2019-2023 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BOOST_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
|
||||
#define BOOST_MYSQL_DETAIL_TYPING_ROW_TRAITS_HPP
|
||||
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
|
||||
#ifdef BOOST_MYSQL_CXX14
|
||||
|
||||
#include <boost/mysql/client_errc.hpp>
|
||||
#include <boost/mysql/diagnostics.hpp>
|
||||
#include <boost/mysql/error_code.hpp>
|
||||
#include <boost/mysql/field_view.hpp>
|
||||
#include <boost/mysql/metadata.hpp>
|
||||
#include <boost/mysql/metadata_collection_view.hpp>
|
||||
#include <boost/mysql/string_view.hpp>
|
||||
|
||||
#include <boost/mysql/detail/config.hpp>
|
||||
#include <boost/mysql/detail/typing/meta_check_context.hpp>
|
||||
#include <boost/mysql/detail/typing/pos_map.hpp>
|
||||
#include <boost/mysql/detail/typing/readable_field_traits.hpp>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/describe/members.hpp>
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
#include <boost/mp11/utility.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace mysql {
|
||||
namespace detail {
|
||||
|
||||
// Helpers to check that all the fields satisfy ReadableField
|
||||
// and produce meaningful error messages, with the offending field type, at least
|
||||
|
||||
// Workaround clang 3.6 not liking generic lambdas in the below constexpr function
|
||||
struct readable_field_checker
|
||||
{
|
||||
template <class TypeIdentity>
|
||||
constexpr void operator()(TypeIdentity) const noexcept
|
||||
{
|
||||
using T = typename TypeIdentity::type;
|
||||
static_assert(
|
||||
is_readable_field<T>::value,
|
||||
"You're trying to use an unsupported field type in a row type. Review your row type definitions."
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template <class TypeList>
|
||||
static constexpr bool check_readable_field() noexcept
|
||||
{
|
||||
mp11::mp_for_each<TypeList>(readable_field_checker{});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Workaround std::array::data not being constexpr in C++14
|
||||
template <class T, std::size_t N>
|
||||
struct array_wrapper
|
||||
{
|
||||
T data_[N];
|
||||
|
||||
constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(data_); }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct array_wrapper<T, 0>
|
||||
{
|
||||
struct
|
||||
{
|
||||
} data_; // allow empty brace initialization
|
||||
|
||||
constexpr boost::span<const T> span() const noexcept { return boost::span<const T>(); }
|
||||
};
|
||||
|
||||
// Workaround for char_traits::length not being constexpr in C++14
|
||||
// Only used to retrieve Describe member name lengths
|
||||
constexpr std::size_t get_length(const char* s) noexcept
|
||||
{
|
||||
const char* p = s;
|
||||
while (*p)
|
||||
++p;
|
||||
return p - s;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
class parse_functor
|
||||
{
|
||||
span<const std::size_t> pos_map_;
|
||||
span<const field_view> fields_;
|
||||
std::size_t index_{};
|
||||
error_code ec_;
|
||||
|
||||
public:
|
||||
parse_functor(span<const std::size_t> pos_map, span<const field_view> fields) noexcept
|
||||
: pos_map_(pos_map), fields_(fields)
|
||||
{
|
||||
}
|
||||
|
||||
template <class ReadableField>
|
||||
void operator()(ReadableField& output)
|
||||
{
|
||||
auto ec = readable_field_traits<ReadableField>::parse(
|
||||
map_field_view(pos_map_, index_++, fields_),
|
||||
output
|
||||
);
|
||||
if (!ec_)
|
||||
ec_ = ec;
|
||||
}
|
||||
|
||||
error_code error() const noexcept { return ec_; }
|
||||
};
|
||||
|
||||
// Base template
|
||||
template <class T, bool is_describe_struct = boost::describe::has_describe_members<T>::value>
|
||||
class row_traits;
|
||||
|
||||
// Describe structs
|
||||
template <class DescribeStruct>
|
||||
using row_members = boost::describe::
|
||||
describe_members<DescribeStruct, boost::describe::mod_public | boost::describe::mod_inherited>;
|
||||
|
||||
template <class MemberDescriptor>
|
||||
constexpr string_view get_member_name(MemberDescriptor d) noexcept
|
||||
{
|
||||
return string_view(d.name, get_length(d.name));
|
||||
}
|
||||
|
||||
template <template <class...> class ListType, class... MemberDescriptor>
|
||||
constexpr array_wrapper<string_view, sizeof...(MemberDescriptor)> get_describe_names(ListType<
|
||||
MemberDescriptor...>)
|
||||
{
|
||||
return {{get_member_name(MemberDescriptor())...}};
|
||||
}
|
||||
|
||||
template <class DescribeStruct>
|
||||
constexpr auto describe_names_storage = get_describe_names(row_members<DescribeStruct>{});
|
||||
|
||||
template <class DescribeStruct>
|
||||
class row_traits<DescribeStruct, true>
|
||||
{
|
||||
using members = row_members<DescribeStruct>;
|
||||
|
||||
template <class D>
|
||||
struct descriptor_to_type
|
||||
{
|
||||
using helper = decltype(std::declval<DescribeStruct>().*std::declval<D>().pointer);
|
||||
using type = typename std::remove_reference<helper>::type;
|
||||
};
|
||||
|
||||
using member_types = mp11::mp_transform<descriptor_to_type, members>;
|
||||
|
||||
static_assert(check_readable_field<member_types>(), "");
|
||||
|
||||
public:
|
||||
using types = member_types;
|
||||
|
||||
static constexpr std::size_t size() noexcept { return boost::mp11::mp_size<members>::value; }
|
||||
|
||||
static constexpr name_table_t name_table() noexcept
|
||||
{
|
||||
return describe_names_storage<DescribeStruct>.span();
|
||||
}
|
||||
|
||||
static void parse(parse_functor& parser, DescribeStruct& to)
|
||||
{
|
||||
boost::mp11::mp_for_each<members>([&](auto D) { parser(to.*D.pointer); });
|
||||
}
|
||||
};
|
||||
|
||||
// Tuples
|
||||
template <class T>
|
||||
struct is_tuple : std::false_type
|
||||
{
|
||||
};
|
||||
template <class... T>
|
||||
struct is_tuple<std::tuple<T...>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
template <class... ReadableField>
|
||||
class row_traits<std::tuple<ReadableField...>, false>
|
||||
{
|
||||
using tuple_type = std::tuple<ReadableField...>;
|
||||
using field_types = boost::mp11::mp_list<boost::mp11::mp_identity<ReadableField>...>;
|
||||
|
||||
static_assert(check_readable_field<field_types>(), "");
|
||||
|
||||
public:
|
||||
using types = field_types;
|
||||
static constexpr std::size_t size() noexcept { return std::tuple_size<tuple_type>::value; }
|
||||
static constexpr name_table_t name_table() noexcept { return name_table_t(); }
|
||||
static void parse(parse_functor& parser, tuple_type& to) { boost::mp11::tuple_for_each(to, parser); }
|
||||
};
|
||||
|
||||
// We want is_static_row to only inspect the shape of the row (i.e. it's a tuple vs. it's nothing we know),
|
||||
// and not individual fields. These are static_assert-ed in individual row_traits. This gives us an error
|
||||
// message that contains the offending types, at least.
|
||||
template <class T>
|
||||
struct is_static_row
|
||||
{
|
||||
static constexpr bool value = is_tuple<T>::value || describe::has_describe_members<T>::value;
|
||||
};
|
||||
|
||||
#ifdef BOOST_MYSQL_HAS_CONCEPTS
|
||||
|
||||
template <class T>
|
||||
concept static_row = is_static_row<T>::value;
|
||||
|
||||
#define BOOST_MYSQL_STATIC_ROW ::boost::mysql::detail::static_row
|
||||
|
||||
#else
|
||||
#define BOOST_MYSQL_STATIC_ROW class
|
||||
#endif
|
||||
|
||||
// External interface
|
||||
template <BOOST_MYSQL_STATIC_ROW StaticRow>
|
||||
constexpr std::size_t get_row_size()
|
||||
{
|
||||
return row_traits<StaticRow>::size();
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_STATIC_ROW StaticRow>
|
||||
constexpr name_table_t get_row_name_table()
|
||||
{
|
||||
return row_traits<StaticRow>::name_table();
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_STATIC_ROW StaticRow>
|
||||
error_code meta_check(span<const std::size_t> pos_map, metadata_collection_view meta, diagnostics& diag)
|
||||
{
|
||||
using fields = typename row_traits<StaticRow>::types;
|
||||
BOOST_ASSERT(pos_map.size() == get_row_size<StaticRow>());
|
||||
return meta_check_field_type_list<fields>(pos_map, get_row_name_table<StaticRow>(), meta, diag);
|
||||
}
|
||||
|
||||
template <BOOST_MYSQL_STATIC_ROW StaticRow>
|
||||
error_code parse(span<const std::size_t> pos_map, span<const field_view> from, StaticRow& to)
|
||||
{
|
||||
BOOST_ASSERT(pos_map.size() == get_row_size<StaticRow>());
|
||||
BOOST_ASSERT(from.size() >= get_row_size<StaticRow>());
|
||||
parse_functor ctx(pos_map, from);
|
||||
row_traits<StaticRow>::parse(ctx, to);
|
||||
return ctx.error();
|
||||
}
|
||||
|
||||
using meta_check_fn_t =
|
||||
error_code (*)(span<const std::size_t> field_map, metadata_collection_view meta, diagnostics& diag);
|
||||
|
||||
// For multi-resultset - helper
|
||||
template <class... StaticRow>
|
||||
constexpr std::size_t max_num_columns = (std::max)({get_row_size<StaticRow>()...});
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mysql
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_MYSQL_CXX14
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user