Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
//
// 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_BAD_FIELD_ACCESS_HPP
#define BOOST_MYSQL_BAD_FIELD_ACCESS_HPP
#include <exception>
namespace boost {
namespace mysql {
/// Exception type thrown when trying to access a \ref field
/// or \ref field_view with an incorrect type.
class bad_field_access : public std::exception
{
public:
/// Returns the error message.
const char* what() const noexcept override { return "bad_value_access"; }
};
} // namespace mysql
} // namespace boost
#endif
+22
View File
@@ -0,0 +1,22 @@
//
// 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_BLOB_HPP
#define BOOST_MYSQL_BLOB_HPP
#include <vector>
namespace boost {
namespace mysql {
/// Owning type used to represent binary blobs.
using blob = std::vector<unsigned char>;
} // namespace mysql
} // namespace boost
#endif
+22
View File
@@ -0,0 +1,22 @@
//
// 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_BLOB_VIEW_HPP
#define BOOST_MYSQL_BLOB_VIEW_HPP
#include <boost/core/span.hpp>
namespace boost {
namespace mysql {
/// Non-owning type used to represent binary blobs.
using blob_view = boost::span<const unsigned char>;
} // namespace mysql
} // namespace boost
#endif
+47
View File
@@ -0,0 +1,47 @@
//
// 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_BUFFER_PARAMS_HPP
#define BOOST_MYSQL_BUFFER_PARAMS_HPP
#include <cstddef>
namespace boost {
namespace mysql {
/**
* \brief Buffer configuration parameters for a connection.
*/
class buffer_params
{
std::size_t initial_read_size_;
public:
/// The default value of \ref initial_read_size.
static constexpr std::size_t default_initial_read_size = 1024;
/**
* \brief Initializing constructor.
* \param initial_read_size Initial size of the read buffer. A bigger read buffer
* can increase the number of rows returned by \ref connection::read_some_rows.
*/
constexpr explicit buffer_params(std::size_t initial_read_size = default_initial_read_size) noexcept
: initial_read_size_(initial_read_size)
{
}
/// Gets the initial size of the read buffer.
constexpr std::size_t initial_read_size() const noexcept { return initial_read_size_; }
/// Sets the initial size of the read buffer.
void set_initial_read_size(std::size_t v) noexcept { initial_read_size_ = v; }
};
} // namespace mysql
} // namespace boost
#endif
+100
View File
@@ -0,0 +1,100 @@
//
// 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_CLIENT_ERRC_HPP
#define BOOST_MYSQL_CLIENT_ERRC_HPP
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/system/error_category.hpp>
namespace boost {
namespace mysql {
/**
* \brief MySQL client-defined error codes.
* \details These errors are produced by the client itself, rather than the server.
*/
enum class client_errc : int
{
/// An incomplete message was received from the server (indicates a deserialization error or
/// packet mismatch).
incomplete_message = 1,
/// An unexpected value was found in a server-received message (indicates a deserialization
/// error or packet mismatch).
protocol_value_error,
/// The server does not support the minimum required capabilities to establish the connection.
server_unsupported,
/// Unexpected extra bytes at the end of a message were received (indicates a deserialization
/// error or packet mismatch).
extra_bytes,
/// Mismatched sequence numbers (usually caused by a packet mismatch).
sequence_number_mismatch,
/// The user employs an authentication plugin not known to this library.
unknown_auth_plugin,
/// The authentication plugin requires the connection to use SSL.
auth_plugin_requires_ssl,
/// The number of parameters passed to the prepared statement does not match the number of
/// actual parameters.
wrong_num_params,
/// The connection mandatory SSL, but the server doesn't accept SSL connections.
server_doesnt_support_ssl,
/// The static interface detected a mismatch between your C++ type definitions and what the server
/// returned in the query.
metadata_check_failed,
/// The static interface detected a mismatch between the number of row types passed to `static_results`
/// or `static_execution_state` and the number of resultsets returned by your query.
num_resultsets_mismatch,
/// The StaticRow type passed to read_some_rows does not correspond to the resultset type being read.
row_type_mismatch,
/// The static interface encountered an error when parsing a field into a C++ data structure.
static_row_parsing_error,
};
BOOST_MYSQL_DECL
const boost::system::error_category& get_client_category() noexcept;
/// Creates an \ref error_code from a \ref client_errc.
inline error_code make_error_code(client_errc error)
{
return error_code(static_cast<int>(error), get_client_category());
}
} // namespace mysql
#ifndef BOOST_MYSQL_DOXYGEN
namespace system {
template <>
struct is_error_code_enum<::boost::mysql::client_errc>
{
static constexpr bool value = true;
};
} // namespace system
#endif
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/error_categories.ipp>
#endif
#endif
+70
View File
@@ -0,0 +1,70 @@
//
// 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_COLUMN_TYPE_HPP
#define BOOST_MYSQL_COLUMN_TYPE_HPP
#include <boost/mysql/detail/config.hpp>
#include <iosfwd>
namespace boost {
namespace mysql {
/**
* \brief Represents the database type of a MySQL column.
* \details This represents a database type, as opposed to \ref field_kind, which represents a
* C++ type.
*\n
* Unless otherwise noted, the names in this enumeration
* directly correspond to the names of the types you would use in
* a `CREATE TABLE` statement to create a column of this type
* (e.g. `VARCHAR` corresponds to \ref column_type::varchar).
*/
enum class column_type
{
tinyint, ///< `TINYINT` (signed and unsigned).
smallint, ///< `SMALLINT` (signed and unsigned).
mediumint, ///< `MEDIUMINT` (signed and unsigned).
int_, ///< `INT` (signed and unsigned).
bigint, ///< `BIGINT` (signed and unsigned).
float_, ///< `FLOAT` (warning: FLOAT(p) where p >= 24 creates a DOUBLE column).
double_, ///< `DOUBLE`
decimal, ///< `DECIMAL`
bit, ///< `BIT`
year, ///< `YEAR`
time, ///< `TIME`
date, ///< `DATE`
datetime, ///< `DATETIME`
timestamp, ///< `TIMESTAMP`
char_, ///< `CHAR` (any length)
varchar, ///< `VARCHAR` (any length)
binary, ///< `BINARY` (any length)
varbinary, ///< `VARBINARY` (any length)
text, ///< `TEXT` types (`TINYTEXT`, `MEDIUMTEXT`, `TEXT` and `LONGTEXT`)
blob, ///< `BLOB` types (`TINYBLOB`, `MEDIUMBLOB`, `BLOB` and `LONGBLOB`)
enum_, ///< `ENUM`
set, ///< `SET`
json, ///< `JSON`
geometry, ///< `GEOMETRY`
unknown, ///< None of the known types; maybe a new MySQL type we have no knowledge of.
};
/**
* \brief Streams a `column_type`.
*/
BOOST_MYSQL_DECL
std::ostream& operator<<(std::ostream& os, column_type t);
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/column_type.ipp>
#endif
#endif
File diff suppressed because it is too large Load Diff
+1701
View File
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
//
// 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_DATE_HPP
#define BOOST_MYSQL_DATE_HPP
#include <boost/mysql/days.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/datetime.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
#include <chrono>
#include <cstdint>
#include <iosfwd>
#include <stdexcept>
namespace boost {
namespace mysql {
/**
* \brief Type representing MySQL `DATE` data type.
* \details Represents a broken date by its year, month and day components.
* This type is close to the protocol and should not be used as a vocabulary type.
* Instead, cast it to a `std::chrono::time_point` by calling \ref as_time_point
* or \ref get_time_point.
* \n
* As opposed to `time_point`, this type allows representing invalid and zero dates.
*/
class date
{
public:
/// A `std::chrono::time_point` that can represent any valid `date`.
using time_point = std::chrono::time_point<std::chrono::system_clock, days>;
/**
* \brief Constructs a zero date.
* \details
* Results in a date with all of its components set to zero.
*
* \par Exception safety
* No-throw guarantee.
*/
constexpr date() noexcept = default;
/**
* \brief Constructs a date from its year, month and date components.
* \par Exception safety
* No-throw guarantee.
*/
constexpr date(std::uint16_t year, std::uint8_t month, std::uint8_t day) noexcept
: year_(year), month_(month), day_(day)
{
}
/**
* \brief Constructs a date from a `time_point`.
* \par Exception safety
* Strong guarantee. Throws on invalid input.
* \throws std::out_of_range If the resulting `date` would be
* out of the [\ref min_date, \ref max_date] range.
*/
BOOST_CXX14_CONSTEXPR explicit date(time_point tp)
{
bool ok = detail::days_to_ymd(tp.time_since_epoch().count(), year_, month_, day_);
if (!ok)
BOOST_THROW_EXCEPTION(std::out_of_range("date::date: time_point was out of range"));
}
/**
* \brief Retrieves the year component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint16_t year() const noexcept { return year_; }
/**
* \brief Retrieves the month component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t month() const noexcept { return month_; }
/**
* \brief Retrieves the day component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t day() const noexcept { return day_; }
/**
* \brief Returns `true` if `*this` represents a valid `time_point`.
* \details If any of the individual components is out of range, the date
* doesn't represent an actual `time_point` (e.g. `date(2020, 2, 30)`) or
* the date is not in the [\ref min_date, \ref max_date] validity range,
* returns `false`. Otherwise, returns `true`.
* \par Exception safety
* No-throw guarantee.
*/
constexpr bool valid() const noexcept { return detail::is_valid(year_, month_, day_); }
/**
* \brief Converts `*this` into a `time_point` (unchecked access).
* \par Preconditions
* `this->valid() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR time_point get_time_point() const noexcept
{
BOOST_ASSERT(valid());
return unch_get_time_point();
}
/**
* \brief Converts `*this` into a `time_point` (checked access).
* \par Exception safety
* Strong guarantee.
* \throws std::invalid_argument If `!this->valid()`.
*/
BOOST_CXX14_CONSTEXPR time_point as_time_point() const
{
if (!valid())
BOOST_THROW_EXCEPTION(std::invalid_argument("date::as_time_point: invalid date"));
return unch_get_time_point();
}
/**
* \brief Tests for equality.
* \details Two dates are considered equal if all of its individual components
* are equal. This function works for invalid dates, too.
*
* \par Exception safety
* No-throw guarantee.
*/
constexpr bool operator==(const date& rhs) const noexcept
{
return year_ == rhs.year_ && month_ == rhs.month_ && day_ == rhs.day_;
}
/**
* \brief Tests for inequality.
*
* \par Exception safety
* No-throw guarantee.
*/
constexpr bool operator!=(const date& rhs) const noexcept { return !(rhs == *this); }
/**
* \brief Returns the current system time as a date object.
* \par Exception safety
* Strong guarantee. Only throws if obtaining the current time throws.
*/
static date now()
{
auto now = time_point::clock::now();
return date(std::chrono::time_point_cast<time_point::duration>(now));
}
private:
std::uint16_t year_{};
std::uint8_t month_{};
std::uint8_t day_{};
BOOST_CXX14_CONSTEXPR time_point unch_get_time_point() const noexcept
{
return time_point(days(detail::ymd_to_days(year_, month_, day_)));
}
};
/**
* \relates date
* \brief Streams a date.
* \details This function works for invalid dates, too.
*/
BOOST_MYSQL_DECL
std::ostream& operator<<(std::ostream& os, const date& v);
/// The minimum allowed value for \ref date.
constexpr date min_date{0u, 1u, 1u};
/// The maximum allowed value for \ref date.
constexpr date max_date{9999u, 12u, 31u};
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/date.ipp>
#endif
#endif
+294
View File
@@ -0,0 +1,294 @@
//
// 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_DATETIME_HPP
#define BOOST_MYSQL_DATETIME_HPP
#include <boost/mysql/days.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/datetime.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
#include <chrono>
#include <cstdint>
#include <iosfwd>
#include <ratio>
#include <stdexcept>
namespace boost {
namespace mysql {
/**
* \brief Type representing MySQL `DATETIME` and `TIMESTAMP` data types.
* \details Represents a broken datetime by its year, month, day, hour, minute, second and
* microsecond components. This type is close to the protocol and should not be used as a vocabulary
* type. Instead, cast it to a `std::chrono::time_point` by calling \ref as_time_point or \ref
* get_time_point.
* \n
* As opposed to `time_point`, this type allows representing invalid and zero datetimes.
*/
class datetime
{
public:
/**
* \brief A `std::chrono::time_point` that can represent any valid datetime.
* \details Represents microseconds since the UNIX epoch, with the same precision for all architectures.
*/
using time_point = std::chrono::
time_point<std::chrono::system_clock, std::chrono::duration<std::int64_t, std::micro>>;
/**
* \brief Constructs a zero datetime.
* \details Results in a datetime with all of its components set to zero.
* \par Exception safety
* No-throw guarantee.
*/
constexpr datetime() noexcept = default;
/**
* \brief Constructs a datetime from its individual components.
* \par Exception safety
* No-throw guarantee.
*/
constexpr datetime(
std::uint16_t year,
std::uint8_t month,
std::uint8_t day,
std::uint8_t hour = 0,
std::uint8_t minute = 0,
std::uint8_t second = 0,
std::uint32_t microsecond = 0
) noexcept
: year_(year),
month_(month),
day_(day),
hour_(hour),
minute_(minute),
second_(second),
microsecond_(microsecond)
{
}
/**
* \brief Constructs a datetime from a `time_point`.
* \par Exception safety
* Strong guarantee. Throws on invalid input.
* \throws std::out_of_range If the resulting `datetime` object would be
* out of the [\ref min_datetime, \ref max_datetime] range.
*/
BOOST_CXX14_CONSTEXPR inline explicit datetime(time_point tp);
/**
* \brief Retrieves the year component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint16_t year() const noexcept { return year_; }
/**
* \brief Retrieves the month component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t month() const noexcept { return month_; }
/**
* \brief Retrieves the day component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t day() const noexcept { return day_; }
/**
* \brief Retrieves the hour component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t hour() const noexcept { return hour_; }
/**
* \brief Retrieves the minute component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t minute() const noexcept { return minute_; }
/**
* \brief Retrieves the second component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint8_t second() const noexcept { return second_; }
/**
* \brief Retrieves the microsecond component.
* \par Exception safety
* No-throw guarantee.
*/
constexpr std::uint32_t microsecond() const noexcept { return microsecond_; }
/**
* \brief Returns `true` if `*this` represents a valid `time_point`.
* \details If any of the individual components is out of range, the datetime
* doesn't represent an actual `time_point` (e.g. `datetime(2020, 2, 30)`) or
* the datetime is not in the [\ref min_date, \ref max_date] validity range,
* returns `false`. Otherwise, returns `true`.
*
* \par Exception safety
* No-throw guarantee.
*/
constexpr bool valid() const noexcept
{
return detail::is_valid(year_, month_, day_) && hour_ <= detail::max_hour &&
minute_ <= detail::max_min && second_ <= detail::max_sec && microsecond_ <= detail::max_micro;
}
/**
* \brief Converts `*this` into a `time_point` (unchecked access).
* \par Preconditions
* `this->valid() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR time_point get_time_point() const noexcept
{
BOOST_ASSERT(valid());
return unch_get_time_point();
}
/**
* \brief Converts `*this` into a `time_point` (checked access).
* \par Exception safety
* Strong guarantee.
* \throws std::invalid_argument If `!this->valid()`.
*/
BOOST_CXX14_CONSTEXPR inline time_point as_time_point() const
{
if (!valid())
BOOST_THROW_EXCEPTION(std::invalid_argument("datetime::as_time_point: invalid datetime"));
return unch_get_time_point();
}
/**
* \brief Tests for equality.
* \details Two datetimes are considered equal if all of its individual components
* are equal. This function works for invalid datetimes, too.
*
* \par Exception safety
* No-throw guarantee.
*/
constexpr bool operator==(const datetime& rhs) const noexcept
{
return year_ == rhs.year_ && month_ == rhs.month_ && day_ == rhs.day_ && hour_ == rhs.hour_ &&
minute_ == rhs.minute_ && second_ == rhs.second_ && microsecond_ == rhs.microsecond_;
}
/**
* \brief Tests for inequality.
* \par Exception safety
* No-throw guarantee.
*/
constexpr bool operator!=(const datetime& rhs) const noexcept { return !(*this == rhs); }
/**
* \brief Returns the current system time as a datetime object.
* \par Exception safety
* Strong guarantee. Only throws if obtaining the current time throws.
*/
static datetime now()
{
auto now = time_point::clock::now();
return datetime(std::chrono::time_point_cast<time_point::duration>(now));
}
private:
std::uint16_t year_{};
std::uint8_t month_{};
std::uint8_t day_{};
std::uint8_t hour_{};
std::uint8_t minute_{};
std::uint8_t second_{};
std::uint32_t microsecond_{};
BOOST_CXX14_CONSTEXPR inline time_point unch_get_time_point() const noexcept
{
// Doing time of day independently to prevent overflow
days d(detail::ymd_to_days(year_, month_, day_));
auto time_of_day = std::chrono::hours(hour_) + std::chrono::minutes(minute_) +
std::chrono::seconds(second_) + std::chrono::microseconds(microsecond_);
return time_point(d) + time_of_day;
}
};
/**
* \relates datetime
* \brief Streams a datetime.
* \details This function works for invalid datetimes, too.
*/
BOOST_MYSQL_DECL
std::ostream& operator<<(std::ostream& os, const datetime& v);
/// The minimum allowed value for \ref datetime.
constexpr datetime min_datetime(0u, 1u, 1u);
/// The maximum allowed value for \ref datetime.
constexpr datetime max_datetime(9999u, 12u, 31u, 23u, 59u, 59u, 999999u);
} // namespace mysql
} // namespace boost
// Implementations
BOOST_CXX14_CONSTEXPR boost::mysql::datetime::datetime(time_point tp)
{
using std::chrono::duration_cast;
using std::chrono::hours;
using std::chrono::microseconds;
using std::chrono::minutes;
using std::chrono::seconds;
// Avoiding using -= for durations as it's not constexpr until C++17
auto input_dur = tp.time_since_epoch();
auto rem = input_dur % days(1);
auto num_days = duration_cast<days>(input_dur);
if (rem.count() < 0)
{
rem = rem + days(1);
num_days = num_days - days(1);
}
auto num_hours = duration_cast<hours>(rem);
rem = rem - num_hours;
auto num_minutes = duration_cast<minutes>(rem);
rem = rem - num_minutes;
auto num_seconds = duration_cast<seconds>(rem);
rem = rem - num_seconds;
auto num_microseconds = duration_cast<microseconds>(rem);
BOOST_ASSERT(num_hours.count() >= 0 && num_hours.count() <= detail::max_hour);
BOOST_ASSERT(num_minutes.count() >= 0 && num_minutes.count() <= detail::max_min);
BOOST_ASSERT(num_seconds.count() >= 0 && num_seconds.count() <= detail::max_sec);
BOOST_ASSERT(num_microseconds.count() >= 0 && num_microseconds.count() <= detail::max_micro);
bool ok = detail::days_to_ymd(num_days.count(), year_, month_, day_);
if (!ok)
BOOST_THROW_EXCEPTION(std::out_of_range("datetime::datetime: time_point was out of range"));
microsecond_ = static_cast<std::uint32_t>(num_microseconds.count());
second_ = static_cast<std::uint8_t>(num_seconds.count());
minute_ = static_cast<std::uint8_t>(num_minutes.count());
hour_ = static_cast<std::uint8_t>(num_hours.count());
}
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/datetime.ipp>
#endif
#endif
+26
View File
@@ -0,0 +1,26 @@
//
// 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_DAYS_HPP
#define BOOST_MYSQL_DAYS_HPP
#include <chrono>
namespace boost {
namespace mysql {
/**
* \brief Duration representing a day (24 hours).
* \details Suitable to represent the range of dates MySQL offers.
* May differ in representation from `std::chrono::days` in C++20.
*/
using days = std::chrono::duration<int, std::ratio<3600 * 24>>;
} // namespace mysql
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
//
// 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_ACCESS_HPP
#define BOOST_MYSQL_DETAIL_ACCESS_HPP
#include <utility>
namespace boost {
namespace mysql {
namespace detail {
// Exposes access to the implementation of public access, which is sometimes
// required by library internals.
struct access
{
template <class T>
static decltype(std::declval<T>().impl_)& get_impl(T& obj) noexcept
{
return obj.impl_;
}
template <class T>
static const decltype(std::declval<T>().impl_)& get_impl(const T& obj) noexcept
{
return obj.impl_;
}
template <class T, class... Args>
static T construct(Args&&... args)
{
return T(std::forward<Args>(args)...);
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+48
View File
@@ -0,0 +1,48 @@
//
// 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_ANY_EXECUTION_REQUEST_HPP
#define BOOST_MYSQL_DETAIL_ANY_EXECUTION_REQUEST_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/core/span.hpp>
namespace boost {
namespace mysql {
namespace detail {
struct any_execution_request
{
union data_t
{
string_view query;
struct
{
statement stmt;
span<const field_view> params;
} stmt;
data_t(string_view q) noexcept : query(q) {}
data_t(statement s, span<const field_view> params) noexcept : stmt{s, params} {}
} data;
bool is_query;
any_execution_request(string_view q) noexcept : data(q), is_query(true) {}
any_execution_request(statement s, span<const field_view> params) noexcept
: data(s, params), is_query(false)
{
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+82
View File
@@ -0,0 +1,82 @@
//
// 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_ANY_STREAM_HPP
#define BOOST_MYSQL_DETAIL_ANY_STREAM_HPP
#include <boost/mysql/error_code.hpp>
#include <boost/asio/any_completion_handler.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/buffer.hpp>
#include <cstddef>
namespace boost {
namespace mysql {
namespace detail {
class any_stream
{
public:
any_stream(bool supports_ssl) noexcept
: ssl_state_(supports_ssl ? ssl_state::inactive : ssl_state::unsupported)
{
}
bool ssl_active() const noexcept { return ssl_state_ == ssl_state::active; }
void reset_ssl_active() noexcept
{
if (ssl_state_ == ssl_state::active)
ssl_state_ = ssl_state::inactive;
}
void set_ssl_active() noexcept
{
BOOST_ASSERT(ssl_state_ != ssl_state::unsupported);
ssl_state_ = ssl_state::active;
}
bool supports_ssl() const noexcept { return ssl_state_ != ssl_state::unsupported; }
using executor_type = asio::any_io_executor;
virtual ~any_stream() {}
virtual executor_type get_executor() = 0;
// SSL
virtual void handshake(error_code& ec) = 0;
virtual void async_handshake(asio::any_completion_handler<void(error_code)>) = 0;
virtual void shutdown(error_code& ec) = 0;
virtual void async_shutdown(asio::any_completion_handler<void(error_code)>) = 0;
// Reading
virtual std::size_t read_some(asio::mutable_buffer, error_code& ec) = 0;
virtual void async_read_some(asio::mutable_buffer, asio::any_completion_handler<void(error_code, std::size_t)>) = 0;
// Writing
virtual std::size_t write_some(asio::const_buffer, error_code& ec) = 0;
virtual void async_write_some(asio::const_buffer, asio::any_completion_handler<void(error_code, std::size_t)>) = 0;
// Connect and close - these apply only to SocketStream's
virtual void connect(const void* endpoint, error_code& ec) = 0;
virtual void async_connect(const void* endpoint, asio::any_completion_handler<void(error_code)>) = 0;
virtual void close(error_code& ec) = 0;
virtual bool is_open() const noexcept = 0;
private:
enum class ssl_state
{
inactive,
active,
unsupported
} ssl_state_;
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+307
View File
@@ -0,0 +1,307 @@
//
// 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_ANY_STREAM_IMPL_HPP
#define BOOST_MYSQL_DETAIL_ANY_STREAM_IMPL_HPP
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/any_stream.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/socket_stream.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/config.hpp>
#include <type_traits>
namespace boost {
namespace mysql {
namespace detail {
// Connect and close helpers
template <class Stream>
const typename Stream::lowest_layer_type::endpoint_type cast_endpoint(const void* input) noexcept
{
return *static_cast<const typename Stream::lowest_layer_type::endpoint_type*>(input);
}
template <class Stream>
void do_connect_impl(Stream&, const void*, error_code&, std::false_type)
{
BOOST_ASSERT(false);
}
template <class Stream>
void do_connect_impl(Stream& stream, const void* endpoint, error_code& ec, std::true_type)
{
stream.lowest_layer().connect(cast_endpoint<Stream>(endpoint), ec);
}
template <class Stream>
void do_connect(Stream& stream, const void* endpoint, error_code& ec)
{
do_connect_impl(stream, endpoint, ec, is_socket_stream<Stream>{});
}
template <class Stream>
void do_async_connect_impl(
Stream&,
const void*,
asio::any_completion_handler<void(error_code)>&&,
std::false_type
)
{
BOOST_ASSERT(false);
}
template <class Stream>
void do_async_connect_impl(
Stream& stream,
const void* endpoint,
asio::any_completion_handler<void(error_code)>&& handler,
std::true_type
)
{
stream.lowest_layer().async_connect(cast_endpoint<Stream>(endpoint), std::move(handler));
}
template <class Stream>
void do_async_connect(
Stream& stream,
const void* endpoint,
asio::any_completion_handler<void(error_code)>&& handler
)
{
do_async_connect_impl(stream, endpoint, std::move(handler), is_socket_stream<Stream>{});
}
template <class Stream>
void do_close_impl(Stream&, error_code&, std::false_type)
{
BOOST_ASSERT(false);
}
template <class Stream>
void do_close_impl(Stream& stream, error_code& ec, std::true_type)
{
stream.lowest_layer().shutdown(asio::socket_base::shutdown_both, ec);
stream.lowest_layer().close(ec);
}
template <class Stream>
void do_close(Stream& stream, error_code& ec)
{
do_close_impl(stream, ec, is_socket_stream<Stream>{});
}
template <class Stream>
bool do_is_open_impl(const Stream&, std::false_type) noexcept
{
return false;
}
template <class Stream>
bool do_is_open_impl(const Stream& stream, std::true_type) noexcept
{
return stream.lowest_layer().is_open();
}
template <class Stream>
bool do_is_open(const Stream& stream) noexcept
{
return do_is_open_impl(stream, is_socket_stream<Stream>{});
}
template <class Stream>
class any_stream_impl final : public any_stream
{
Stream stream_;
public:
template <class... Args>
any_stream_impl(Args&&... args) : any_stream(false), stream_(std::forward<Args>(args)...)
{
}
Stream& stream() noexcept { return stream_; }
const Stream& stream() const noexcept { return stream_; }
executor_type get_executor() override final { return stream_.get_executor(); }
// SSL
void handshake(error_code&) final override { BOOST_ASSERT(false); }
void async_handshake(asio::any_completion_handler<void(error_code)>) final override
{
BOOST_ASSERT(false);
}
void shutdown(error_code&) final override { BOOST_ASSERT(false); }
void async_shutdown(asio::any_completion_handler<void(error_code)>) final override
{
BOOST_ASSERT(false);
}
// Reading
std::size_t read_some(boost::asio::mutable_buffer buff, error_code& ec) final override
{
return stream_.read_some(buff, ec);
}
void async_read_some(
boost::asio::mutable_buffer buff,
asio::any_completion_handler<void(error_code, std::size_t)> handler
) final override
{
return stream_.async_read_some(buff, std::move(handler));
}
// Writing
std::size_t write_some(boost::asio::const_buffer buff, error_code& ec) final override
{
return stream_.write_some(buff, ec);
}
void async_write_some(
boost::asio::const_buffer buff,
asio::any_completion_handler<void(error_code, std::size_t)> handler
) final override
{
return stream_.async_write_some(buff, std::move(handler));
}
// Connect and close
void connect(const void* endpoint, error_code& ec) override final { do_connect(stream_, endpoint, ec); }
void async_connect(const void* endpoint, asio::any_completion_handler<void(error_code)> handler)
override final
{
do_async_connect(stream_, endpoint, std::move(handler));
}
void close(error_code& ec) override final { do_close(stream_, ec); }
bool is_open() const noexcept override { return do_is_open(stream_); }
};
template <class Stream>
class any_stream_impl<asio::ssl::stream<Stream>> final : public any_stream
{
asio::ssl::stream<Stream> stream_;
public:
template <class... Args>
any_stream_impl(Args&&... args) : any_stream(true), stream_(std::forward<Args>(args)...)
{
}
asio::ssl::stream<Stream>& stream() noexcept { return stream_; }
const asio::ssl::stream<Stream>& stream() const noexcept { return stream_; }
executor_type get_executor() override final { return stream_.get_executor(); }
// SSL
void handshake(error_code& ec) override final
{
set_ssl_active();
stream_.handshake(boost::asio::ssl::stream_base::client, ec);
}
void async_handshake(asio::any_completion_handler<void(error_code)> handler) override final
{
set_ssl_active();
stream_.async_handshake(boost::asio::ssl::stream_base::client, std::move(handler));
}
void shutdown(error_code& ec) override final { stream_.shutdown(ec); }
void async_shutdown(asio::any_completion_handler<void(error_code)> handler) override final
{
return stream_.async_shutdown(std::move(handler));
}
// Reading
std::size_t read_some(boost::asio::mutable_buffer buff, error_code& ec) override final
{
if (ssl_active())
{
return stream_.read_some(buff, ec);
}
else
{
return stream_.next_layer().read_some(buff, ec);
}
}
void async_read_some(
boost::asio::mutable_buffer buff,
asio::any_completion_handler<void(error_code, std::size_t)> handler
) override final
{
if (ssl_active())
{
return stream_.async_read_some(buff, std::move(handler));
}
else
{
return stream_.next_layer().async_read_some(buff, std::move(handler));
}
}
// Writing
std::size_t write_some(boost::asio::const_buffer buff, error_code& ec) override final
{
if (ssl_active())
{
return stream_.write_some(buff, ec);
}
else
{
return stream_.next_layer().write_some(buff, ec);
}
}
void async_write_some(
boost::asio::const_buffer buff,
asio::any_completion_handler<void(error_code, std::size_t)> handler
) override final
{
if (ssl_active())
{
stream_.async_write_some(buff, std::move(handler));
}
else
{
return stream_.next_layer().async_write_some(buff, std::move(handler));
}
}
// Connect and close
void connect(const void* endpoint, error_code& ec) override final { do_connect(stream_, endpoint, ec); }
void async_connect(const void* endpoint, asio::any_completion_handler<void(error_code)> handler)
override final
{
do_async_connect(stream_, endpoint, std::move(handler));
}
void close(error_code& ec) override final { do_close(stream_, ec); }
bool is_open() const noexcept override { return do_is_open(stream_); }
};
template <class Stream>
const Stream& cast(const any_stream& obj) noexcept
{
return static_cast<const any_stream_impl<Stream>&>(obj).stream();
}
template <class Stream>
Stream& cast(any_stream& obj) noexcept
{
return static_cast<any_stream_impl<Stream>&>(obj).stream();
}
#ifdef BOOST_MYSQL_SEPARATE_COMPILATION
extern template class any_stream_impl<asio::ssl::stream<asio::ip::tcp::socket>>;
extern template class any_stream_impl<asio::ip::tcp::socket>;
#endif
} // namespace detail
} // namespace mysql
} // namespace boost
// any_stream_impl.ipp explicitly instantiates any_stream_impl, so not included here
#endif
+71
View File
@@ -0,0 +1,71 @@
//
// 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_CHANNEL_PTR_HPP
#define BOOST_MYSQL_DETAIL_CHANNEL_PTR_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata_mode.hpp>
#include <boost/mysql/detail/any_stream.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/assert.hpp>
#include <memory>
namespace boost {
namespace mysql {
namespace detail {
class channel;
class channel_ptr
{
std::unique_ptr<channel> chan_;
BOOST_MYSQL_DECL any_stream& get_stream() const;
public:
BOOST_MYSQL_DECL channel_ptr(std::size_t read_buff_size, std::unique_ptr<any_stream>);
channel_ptr(const channel_ptr&) = delete;
BOOST_MYSQL_DECL channel_ptr(channel_ptr&&) noexcept;
channel_ptr& operator=(const channel_ptr&) = delete;
BOOST_MYSQL_DECL channel_ptr& operator=(channel_ptr&&) noexcept;
BOOST_MYSQL_DECL ~channel_ptr();
any_stream& stream() noexcept { return get_stream(); }
const any_stream& stream() const noexcept { return get_stream(); }
channel& get() noexcept
{
BOOST_ASSERT(chan_);
return *chan_;
}
const channel& get() const noexcept
{
BOOST_ASSERT(chan_);
return *chan_;
}
BOOST_MYSQL_DECL metadata_mode meta_mode() const noexcept;
BOOST_MYSQL_DECL void set_meta_mode(metadata_mode v) noexcept;
BOOST_MYSQL_DECL diagnostics& shared_diag() noexcept;
};
BOOST_MYSQL_DECL std::vector<field_view>& get_shared_fields(channel&) noexcept;
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/channel_ptr.ipp>
#endif
#endif
+37
View File
@@ -0,0 +1,37 @@
//
// 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_COLDEF_VIEW_HPP
#define BOOST_MYSQL_DETAIL_COLDEF_VIEW_HPP
#include <boost/mysql/column_type.hpp>
#include <boost/mysql/string_view.hpp>
namespace boost {
namespace mysql {
namespace detail {
struct coldef_view
{
string_view database;
string_view table;
string_view org_table;
string_view name;
string_view org_name;
std::uint16_t collation_id;
std::uint32_t column_length; // maximum length of the field
column_type type;
std::uint16_t flags;
std::uint8_t decimals; // max shown decimal digits. 0x00 for int/static strings; 0x1f for
// dynamic strings, double, float
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+44
View File
@@ -0,0 +1,44 @@
//
// 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_CONFIG_HPP
#define BOOST_MYSQL_DETAIL_CONFIG_HPP
#include <boost/config.hpp>
// clang-format off
// Concepts
#if defined(__has_include)
#if __has_include(<version>)
#include <version>
#if defined(__cpp_concepts) && defined(__cpp_lib_concepts)
#define BOOST_MYSQL_HAS_CONCEPTS
#endif
#endif
#endif
// C++14 conformance
#if BOOST_CXX_VERSION >= 201402L
#define BOOST_MYSQL_CXX14
#endif
// Separate build
#if defined(BOOST_MYSQL_SEPARATE_COMPILATION)
#define BOOST_MYSQL_DECL
#define BOOST_MYSQL_STATIC_IF_COMPILED static
#define BOOST_MYSQL_STATIC_OR_INLINE static
#else
#define BOOST_MYSQL_HEADER_ONLY
#define BOOST_MYSQL_DECL inline
#define BOOST_MYSQL_STATIC_IF_COMPILED
#define BOOST_MYSQL_STATIC_OR_INLINE inline
#endif
// clang-format on
#endif
+104
View File
@@ -0,0 +1,104 @@
//
// 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_DATETIME_HPP
#define BOOST_MYSQL_DETAIL_DATETIME_HPP
// All these algorithms have been taken from:
// http://howardhinnant.github.io/date_algorithms.html
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <cstdint>
#include <limits>
namespace boost {
namespace mysql {
namespace detail {
// Helpers
constexpr unsigned char last_month_day_arr[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
constexpr bool is_leap(std::uint16_t y) noexcept { return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); }
constexpr inline std::uint8_t last_month_day(std::uint16_t y, std::uint8_t m) noexcept
{
return m != 2 || !is_leap(y) ? last_month_day_arr[m - 1] : 29u;
}
// Interface
constexpr std::uint16_t max_year = 9999;
constexpr std::uint8_t max_month = 12;
constexpr std::uint8_t max_day = 31;
constexpr std::uint8_t max_hour = 23;
constexpr std::uint8_t max_min = 59;
constexpr std::uint8_t max_sec = 59;
constexpr std::uint32_t max_micro = 999999;
constexpr inline bool is_valid(std::uint16_t years, std::uint8_t month, std::uint8_t day) noexcept
{
return years <= max_year && month > 0 && month <= max_month && day > 0 &&
day <= last_month_day(years, month);
}
BOOST_CXX14_CONSTEXPR inline int ymd_to_days(
std::uint16_t years,
std::uint8_t month,
std::uint8_t day
) noexcept
{
BOOST_ASSERT(is_valid(years, month, day));
int y = years;
const int m = month;
const int d = day;
y -= m <= 2;
const int era = (y >= 0 ? y : y - 399) / 400;
const unsigned yoe = static_cast<unsigned>(y - era * 400); // [0, 399]
const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
return era * 146097 + static_cast<int>(doe) - 719468;
}
BOOST_CXX14_CONSTEXPR inline bool days_to_ymd(
int num_days,
std::uint16_t& years,
std::uint8_t& month,
std::uint8_t& day
) noexcept
{
// Prevent overflow
constexpr int days_magic = 719468;
if (num_days > (std::numeric_limits<int>::max)() - days_magic)
return false;
num_days += days_magic;
const int era = (num_days >= 0 ? num_days : num_days - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(num_days - era * 146097); // [0, 146096]
const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399]
const int y = static_cast<int>(yoe) + era * 400;
const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
const unsigned mp = (5 * doy + 2) / 153; // [0, 11]
const unsigned d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
const int final_year = y + (m <= 2);
if (final_year < 0 || final_year > static_cast<int>(max_year))
return false;
else
{
years = static_cast<std::uint16_t>(final_year);
month = static_cast<std::uint8_t>(m);
day = static_cast<std::uint8_t>(d);
return true;
}
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+112
View File
@@ -0,0 +1,112 @@
//
// 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_EXECUTION_CONCEPTS_HPP
#define BOOST_MYSQL_DETAIL_EXECUTION_CONCEPTS_HPP
#include <boost/mysql/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <type_traits>
#ifdef BOOST_MYSQL_HAS_CONCEPTS
namespace boost {
namespace mysql {
// Forward decls
template <class... StaticRow>
class static_execution_state;
template <class... StaticRow>
class static_results;
class execution_state;
class results;
namespace detail {
// Execution state
template <class T>
struct is_static_execution_state : std::false_type
{
};
template <class... T>
struct is_static_execution_state<static_execution_state<T...>> : std::true_type
{
};
template <class T>
concept execution_state_type = std::is_same_v<T, execution_state> || is_static_execution_state<T>::value;
// Results
template <class T>
struct is_static_results : std::false_type
{
};
template <class... T>
struct is_static_results<static_results<T...>> : std::true_type
{
};
template <class T>
concept results_type = std::is_same_v<T, results> || is_static_results<T>::value;
// Execution request
template <class T>
struct is_bound_statement_tuple : std::false_type
{
};
template <class T>
struct is_bound_statement_tuple<bound_statement_tuple<T>> : std::true_type
{
};
template <class T>
struct is_bound_statement_range : std::false_type
{
};
template <class T>
struct is_bound_statement_range<bound_statement_iterator_range<T>> : std::true_type
{
};
template <class T>
struct is_execution_request
{
using without_cvref = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
static constexpr bool value = std::is_convertible<T, string_view>::value ||
is_bound_statement_tuple<without_cvref>::value ||
is_bound_statement_range<without_cvref>::value;
};
template <class T>
concept execution_request = is_execution_request<T>::value;
} // namespace detail
} // namespace mysql
} // namespace boost
#define BOOST_MYSQL_EXECUTION_STATE_TYPE ::boost::mysql::detail::execution_state_type
#define BOOST_MYSQL_RESULTS_TYPE ::boost::mysql::detail::results_type
#define BOOST_MYSQL_EXECUTION_REQUEST ::boost::mysql::detail::execution_request
#else
#define BOOST_MYSQL_EXECUTION_STATE_TYPE class
#define BOOST_MYSQL_RESULTS_TYPE class
#define BOOST_MYSQL_EXECUTION_REQUEST class
#endif
#endif
@@ -0,0 +1,217 @@
//
// 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_EXECUTION_PROCESSOR_EXECUTION_PROCESSOR_HPP
#define BOOST_MYSQL_DETAIL_EXECUTION_PROCESSOR_EXECUTION_PROCESSOR_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_mode.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/coldef_view.hpp>
#include <boost/mysql/detail/ok_view.hpp>
#include <boost/mysql/detail/resultset_encoding.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/core/span.hpp>
#include <cstddef>
#include <cstdint>
#include <limits>
namespace boost {
namespace mysql {
namespace detail {
// A type-erased reference to be used as the output range for static_execution_state
class output_ref
{
// Pointer to the first element of the span
void* data_{};
// Number of elements in the span
std::size_t max_size_{(std::numeric_limits<std::size_t>::max)()};
// Identifier for the type of elements. Index in the resultset type list
std::size_t type_index_{};
// Offset into the span's data (static_execution_state). Otherwise unused
std::size_t offset_{};
public:
constexpr output_ref() noexcept = default;
template <class T>
constexpr output_ref(boost::span<T> span, std::size_t type_index, std::size_t offset = 0) noexcept
: data_(span.data()), max_size_(span.size()), type_index_(type_index), offset_(offset)
{
}
std::size_t max_size() const noexcept { return max_size_; }
std::size_t type_index() const noexcept { return type_index_; }
std::size_t offset() const noexcept { return offset_; }
void set_offset(std::size_t v) noexcept { offset_ = v; }
template <class T>
T& span_element() const noexcept
{
BOOST_ASSERT(data_);
return static_cast<T*>(data_)[offset_];
}
};
class execution_processor
{
public:
virtual ~execution_processor() {}
void reset(resultset_encoding enc, metadata_mode mode) noexcept
{
state_ = state_t::reading_first;
encoding_ = enc;
mode_ = mode;
seqnum_ = 0;
remaining_meta_ = 0;
reset_impl();
}
BOOST_ATTRIBUTE_NODISCARD
error_code on_head_ok_packet(const ok_view& pack, diagnostics& diag)
{
BOOST_ASSERT(is_reading_head());
auto err = on_head_ok_packet_impl(pack, diag);
set_state_for_ok(pack);
return err;
}
void on_num_meta(std::size_t num_columns)
{
BOOST_ASSERT(is_reading_head());
on_num_meta_impl(num_columns);
remaining_meta_ = num_columns;
set_state(state_t::reading_metadata);
}
BOOST_ATTRIBUTE_NODISCARD
error_code on_meta(const coldef_view& pack, diagnostics& diag)
{
BOOST_ASSERT(is_reading_meta());
bool is_last = --remaining_meta_ == 0;
auto err = on_meta_impl(pack, is_last, diag);
if (is_last)
set_state(state_t::reading_rows);
return err;
}
void on_row_batch_start()
{
BOOST_ASSERT(is_reading_rows());
on_row_batch_start_impl();
}
void on_row_batch_finish() { on_row_batch_finish_impl(); }
BOOST_ATTRIBUTE_NODISCARD
error_code on_row(span<const std::uint8_t> msg, const output_ref& ref, std::vector<field_view>& storage)
{
BOOST_ASSERT(is_reading_rows());
return on_row_impl(msg, ref, storage);
}
BOOST_ATTRIBUTE_NODISCARD
error_code on_row_ok_packet(const ok_view& pack)
{
BOOST_ASSERT(is_reading_rows());
auto err = on_row_ok_packet_impl(pack);
set_state_for_ok(pack);
return err;
}
bool is_reading_first() const noexcept { return state_ == state_t::reading_first; }
bool is_reading_first_subseq() const noexcept { return state_ == state_t::reading_first_subseq; }
bool is_reading_head() const noexcept
{
return state_ == state_t::reading_first || state_ == state_t::reading_first_subseq;
}
bool is_reading_meta() const noexcept { return state_ == state_t::reading_metadata; }
bool is_reading_rows() const noexcept { return state_ == state_t::reading_rows; }
bool is_complete() const noexcept { return state_ == state_t::complete; }
resultset_encoding encoding() const noexcept { return encoding_; }
std::uint8_t& sequence_number() noexcept { return seqnum_; }
metadata_mode meta_mode() const noexcept { return mode_; }
protected:
virtual void reset_impl() noexcept = 0;
virtual error_code on_head_ok_packet_impl(const ok_view& pack, diagnostics& diag) = 0;
virtual void on_num_meta_impl(std::size_t num_columns) = 0;
virtual error_code on_meta_impl(const coldef_view& coldef, bool is_last, diagnostics& diag) = 0;
virtual error_code on_row_ok_packet_impl(const ok_view& pack) = 0;
virtual error_code on_row_impl(
span<const std::uint8_t> msg,
const output_ref& ref,
std::vector<field_view>& storage
) = 0;
virtual void on_row_batch_start_impl() = 0;
virtual void on_row_batch_finish_impl() = 0;
metadata create_meta(const coldef_view& coldef) const
{
return access::construct<metadata>(coldef, mode_ == metadata_mode::full);
}
private:
enum class state_t
{
// waiting for 1st packet, for the 1st resultset
reading_first,
// same, but for subsequent resultsets (distiguised to provide a cleaner xp to
// the user in (static_)execution_state)
reading_first_subseq,
// waiting for metadata packets
reading_metadata,
// waiting for rows
reading_rows,
// done
complete
};
state_t state_{state_t::reading_first};
resultset_encoding encoding_{resultset_encoding::text};
std::uint8_t seqnum_{};
metadata_mode mode_{metadata_mode::minimal};
std::size_t remaining_meta_{};
void set_state(state_t v) noexcept { state_ = v; }
void set_state_for_ok(const ok_view& pack) noexcept
{
if (pack.more_results())
{
set_state(state_t::reading_first_subseq);
}
else
{
set_state(state_t::complete);
}
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,123 @@
//
// 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_EXECUTION_PROCESSOR_EXECUTION_STATE_IMPL_HPP
#define BOOST_MYSQL_DETAIL_EXECUTION_PROCESSOR_EXECUTION_STATE_IMPL_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/execution_processor/execution_processor.hpp>
#include <boost/assert.hpp>
#include <vector>
namespace boost {
namespace mysql {
namespace detail {
class execution_state_impl final : public execution_processor
{
struct ok_data
{
bool has_value{false}; // The OK packet information is default constructed, or actual data?
std::uint64_t affected_rows{}; // OK packet data
std::uint64_t last_insert_id{}; // OK packet data
std::uint16_t warnings{}; // OK packet data
bool is_out_params{false}; // Does this resultset contain OUT param information?
};
std::vector<metadata> meta_;
ok_data eof_data_;
std::vector<char> info_;
void on_new_resultset() noexcept
{
meta_.clear();
eof_data_ = ok_data{};
info_.clear();
}
BOOST_MYSQL_DECL
void on_ok_packet_impl(const ok_view& pack);
BOOST_MYSQL_DECL
void reset_impl() noexcept override final;
BOOST_MYSQL_DECL
error_code on_head_ok_packet_impl(const ok_view& pack, diagnostics&) override final;
BOOST_MYSQL_DECL
void on_num_meta_impl(std::size_t num_columns) override final;
BOOST_MYSQL_DECL
error_code on_meta_impl(const coldef_view&, bool, diagnostics&) override final;
BOOST_MYSQL_DECL
error_code on_row_impl(span<const std::uint8_t> msg, const output_ref&, std::vector<field_view>& fields)
override final;
BOOST_MYSQL_DECL
error_code on_row_ok_packet_impl(const ok_view& pack) override final;
void on_row_batch_start_impl() noexcept override final {}
void on_row_batch_finish_impl() noexcept override final {}
public:
execution_state_impl() = default;
metadata_collection_view meta() const noexcept { return meta_; }
std::uint64_t get_affected_rows() const noexcept
{
BOOST_ASSERT(eof_data_.has_value);
return eof_data_.affected_rows;
}
std::uint64_t get_last_insert_id() const noexcept
{
BOOST_ASSERT(eof_data_.has_value);
return eof_data_.last_insert_id;
}
unsigned get_warning_count() const noexcept
{
BOOST_ASSERT(eof_data_.has_value);
return eof_data_.warnings;
}
string_view get_info() const noexcept
{
BOOST_ASSERT(eof_data_.has_value);
return string_view(info_.data(), info_.size());
}
bool get_is_out_params() const noexcept
{
BOOST_ASSERT(eof_data_.has_value);
return eof_data_.is_out_params;
}
execution_state_impl& get_interface() noexcept { return *this; }
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/execution_state_impl.ipp>
#endif
#endif
@@ -0,0 +1,219 @@
//
// 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_EXECUTION_PROCESSOR_RESULTS_IMPL_HPP
#define BOOST_MYSQL_DETAIL_EXECUTION_PROCESSOR_RESULTS_IMPL_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/rows_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/detail/row_impl.hpp>
#include <boost/assert.hpp>
namespace boost {
namespace mysql {
namespace detail {
struct per_resultset_data
{
std::size_t num_columns{}; // Number of columns this resultset has
std::size_t meta_offset{}; // Offset into the vector of metadata
std::size_t field_offset; // Offset into the vector of fields (append mode only)
std::size_t num_rows{}; // Number of rows this resultset has (append mode only)
std::uint64_t affected_rows{}; // OK packet data
std::uint64_t last_insert_id{}; // OK packet data
std::uint16_t warnings{}; // OK packet data
std::size_t info_offset{}; // Offset into the vector of info characters
std::size_t info_size{}; // Number of characters that this resultset's info string has
bool has_ok_packet_data{false}; // The OK packet information is default constructed, or actual data?
bool is_out_params{false}; // Does this resultset contain OUT param information?
};
// A container similar to a vector with SBO. To avoid depending on Boost.Container
class resultset_container
{
bool first_has_data_{false};
per_resultset_data first_;
std::vector<per_resultset_data> rest_;
public:
resultset_container() = default;
std::size_t size() const noexcept { return !first_has_data_ ? 0 : rest_.size() + 1; }
bool empty() const noexcept { return !first_has_data_; }
void clear() noexcept
{
first_has_data_ = false;
rest_.clear();
}
per_resultset_data& operator[](std::size_t i) noexcept
{
return const_cast<per_resultset_data&>(const_cast<const resultset_container&>(*this)[i]);
}
const per_resultset_data& operator[](std::size_t i) const noexcept
{
BOOST_ASSERT(i < size());
return i == 0 ? first_ : rest_[i - 1];
}
per_resultset_data& back() noexcept
{
return const_cast<per_resultset_data&>(const_cast<const resultset_container&>(*this).back());
}
const per_resultset_data& back() const noexcept
{
BOOST_ASSERT(first_has_data_);
return rest_.empty() ? first_ : rest_.back();
}
BOOST_MYSQL_DECL per_resultset_data& emplace_back();
};
// Rows for all resultsets are stored in a single rows_impl object.
// - When a row batch is started, we record how many fields we had before the batch.
// - When rows are read, fields are allocated in the rows_impl object, then deserialized against
// the allocated storage. At this point, strings/blobs point into the connection read buffer.
// - When a row batch is finished, we copy strings/blobs into the rows_impl, then transform them
// into offsets to allow rows_impl to grow.
// - When the final OK packet is received, offsets are transformed back into views.
class results_impl final : public execution_processor
{
public:
results_impl() = default;
BOOST_MYSQL_DECL
row_view get_out_params() const noexcept;
std::size_t num_resultsets() const noexcept { return per_result_.size(); }
rows_view get_rows(std::size_t index) const noexcept
{
const auto& resultset_data = per_result_[index];
return access::construct<rows_view>(
rows_.fields().data() + resultset_data.field_offset,
resultset_data.num_rows * resultset_data.num_columns,
resultset_data.num_columns
);
}
metadata_collection_view get_meta(std::size_t index) const noexcept
{
const auto& resultset_data = get_resultset(index);
return metadata_collection_view(
meta_.data() + resultset_data.meta_offset,
resultset_data.num_columns
);
}
std::uint64_t get_affected_rows(std::size_t index) const noexcept
{
return get_resultset(index).affected_rows;
}
std::uint64_t get_last_insert_id(std::size_t index) const noexcept
{
return get_resultset(index).last_insert_id;
}
unsigned get_warning_count(std::size_t index) const noexcept { return get_resultset(index).warnings; }
string_view get_info(std::size_t index) const noexcept
{
const auto& resultset_data = get_resultset(index);
return string_view(info_.data() + resultset_data.info_offset, resultset_data.info_size);
}
bool get_is_out_params(std::size_t index) const noexcept { return get_resultset(index).is_out_params; }
results_impl& get_interface() noexcept { return *this; }
private:
// Virtual impls
BOOST_MYSQL_DECL
void reset_impl() noexcept override final;
BOOST_MYSQL_DECL
void on_num_meta_impl(std::size_t num_columns) override final;
BOOST_MYSQL_DECL
error_code on_head_ok_packet_impl(const ok_view& pack, diagnostics&) override final;
BOOST_MYSQL_DECL
error_code on_meta_impl(const coldef_view&, bool, diagnostics&) override final;
BOOST_MYSQL_DECL
error_code on_row_impl(span<const std::uint8_t> msg, const output_ref&, std::vector<field_view>&)
override final;
BOOST_MYSQL_DECL
error_code on_row_ok_packet_impl(const ok_view& pack) override final;
BOOST_MYSQL_DECL
void on_row_batch_start_impl() override final;
BOOST_MYSQL_DECL
void on_row_batch_finish_impl() override final;
// Data
std::vector<metadata> meta_;
resultset_container per_result_;
std::vector<char> info_;
row_impl rows_;
std::size_t num_fields_at_batch_start_{no_batch};
// Auxiliar
static constexpr std::size_t no_batch = std::size_t(-1);
bool has_active_batch() const noexcept { return num_fields_at_batch_start_ != no_batch; }
BOOST_MYSQL_DECL
void finish_batch();
per_resultset_data& current_resultset() noexcept
{
BOOST_ASSERT(!per_result_.empty());
return per_result_.back();
}
const per_resultset_data& current_resultset() const noexcept
{
BOOST_ASSERT(!per_result_.empty());
return per_result_.back();
}
BOOST_MYSQL_DECL
per_resultset_data& add_resultset();
BOOST_MYSQL_DECL
void on_ok_packet_impl(const ok_view& pack);
const per_resultset_data& get_resultset(std::size_t index) const noexcept
{
BOOST_ASSERT(index < per_result_.size());
return per_result_[index];
}
metadata_collection_view current_resultset_meta() const noexcept
{
return get_meta(per_result_.size() - 1);
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/results_impl.ipp>
#endif
#endif
@@ -0,0 +1,295 @@
//
// 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_EXECUTION_PROCESSOR_STATIC_EXECUTION_STATE_IMPL_HPP
#define BOOST_MYSQL_DETAIL_EXECUTION_PROCESSOR_STATIC_EXECUTION_STATE_IMPL_HPP
#include <boost/mysql/detail/config.hpp>
#ifdef BOOST_MYSQL_CXX14
#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/execution_processor/execution_processor.hpp>
#include <boost/mysql/detail/typing/get_type_index.hpp>
#include <boost/mysql/detail/typing/row_traits.hpp>
#include <boost/assert.hpp>
#include <array>
#include <cstddef>
#include <vector>
namespace boost {
namespace mysql {
namespace detail {
using execst_parse_fn_t =
error_code (*)(span<const std::size_t> pos_map, span<const field_view> from, const output_ref& ref);
struct execst_resultset_descriptor
{
std::size_t num_columns;
name_table_t name_table;
meta_check_fn_t meta_check;
execst_parse_fn_t parse_fn;
std::size_t type_index;
};
class execst_external_data
{
public:
struct ptr_data
{
std::size_t* pos_map;
};
execst_external_data(span<const execst_resultset_descriptor> desc, ptr_data ptr) noexcept
: desc_(desc), ptr_(ptr)
{
}
std::size_t num_resultsets() const noexcept { return desc_.size(); }
std::size_t num_columns(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].num_columns;
}
name_table_t name_table(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].name_table;
}
meta_check_fn_t meta_check_fn(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].meta_check;
}
execst_parse_fn_t parse_fn(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].parse_fn;
}
std::size_t type_index(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].type_index;
}
span<std::size_t> pos_map(std::size_t idx) const noexcept
{
return span<std::size_t>(ptr_.pos_map, num_columns(idx));
}
void set_pointers(ptr_data ptr) noexcept { ptr_ = ptr; }
private:
span<const execst_resultset_descriptor> desc_;
ptr_data ptr_;
};
class static_execution_state_erased_impl final : public execution_processor
{
public:
static_execution_state_erased_impl(execst_external_data ext) noexcept : ext_(ext) {}
execst_external_data& ext_data() noexcept { return ext_; }
metadata_collection_view meta() const noexcept { return meta_; }
std::uint64_t get_affected_rows() const noexcept
{
BOOST_ASSERT(ok_data_.has_value);
return ok_data_.affected_rows;
}
std::uint64_t get_last_insert_id() const noexcept
{
BOOST_ASSERT(ok_data_.has_value);
return ok_data_.last_insert_id;
}
unsigned get_warning_count() const noexcept
{
BOOST_ASSERT(ok_data_.has_value);
return ok_data_.warnings;
}
string_view get_info() const noexcept
{
BOOST_ASSERT(ok_data_.has_value);
return string_view(info_.data(), info_.size());
}
bool get_is_out_params() const noexcept
{
BOOST_ASSERT(ok_data_.has_value);
return ok_data_.is_out_params;
}
private:
// Data
struct ok_packet_data
{
bool has_value{false}; // The OK packet information is default constructed, or actual data?
std::uint64_t affected_rows{}; // OK packet data
std::uint64_t last_insert_id{}; // OK packet data
std::uint16_t warnings{}; // OK packet data
bool is_out_params{false}; // Does this resultset contain OUT param information?
};
execst_external_data ext_;
std::size_t resultset_index_{};
ok_packet_data ok_data_;
std::vector<char> info_;
std::vector<metadata> meta_;
// Virtual impls
BOOST_MYSQL_DECL
void reset_impl() noexcept override final;
BOOST_MYSQL_DECL
error_code on_head_ok_packet_impl(const ok_view& pack, diagnostics& diag) override final;
BOOST_MYSQL_DECL
void on_num_meta_impl(std::size_t num_columns) override final;
BOOST_MYSQL_DECL
error_code on_meta_impl(const coldef_view& coldef, bool is_last, diagnostics& diag) override final;
BOOST_MYSQL_DECL
error_code on_row_impl(
span<const std::uint8_t> msg,
const output_ref& ref,
std::vector<field_view>& fields
) override final;
BOOST_MYSQL_DECL
error_code on_row_ok_packet_impl(const ok_view& pack) override final;
void on_row_batch_start_impl() noexcept override final {}
void on_row_batch_finish_impl() noexcept override final {}
// Auxiliar
name_table_t current_name_table() const noexcept { return ext_.name_table(resultset_index_ - 1); }
span<std::size_t> current_pos_map() noexcept { return ext_.pos_map(resultset_index_ - 1); }
span<const std::size_t> current_pos_map() const noexcept { return ext_.pos_map(resultset_index_ - 1); }
error_code meta_check(diagnostics& diag) const
{
return ext_.meta_check_fn(resultset_index_ - 1)(current_pos_map(), meta_, diag);
}
BOOST_MYSQL_DECL
void on_new_resultset() noexcept;
BOOST_MYSQL_DECL
error_code on_ok_packet_impl(const ok_view& pack);
};
template <class StaticRow>
static error_code execst_parse_fn(
span<const std::size_t> pos_map,
span<const field_view> from,
const output_ref& ref
)
{
return parse(pos_map, from, ref.span_element<StaticRow>());
}
template <class... StaticRow>
constexpr std::array<execst_resultset_descriptor, sizeof...(StaticRow)> create_execst_resultset_descriptors()
{
return {{{
get_row_size<StaticRow>(),
get_row_name_table<StaticRow>(),
&meta_check<StaticRow>,
&execst_parse_fn<StaticRow>,
get_type_index<StaticRow, StaticRow...>(),
}...}};
}
template <class... StaticRow>
constexpr std::array<execst_resultset_descriptor, sizeof...(StaticRow)>
execst_resultset_descriptor_table = create_execst_resultset_descriptors<StaticRow...>();
template <BOOST_MYSQL_STATIC_ROW... StaticRow>
class static_execution_state_impl
{
// Storage for our data, which requires knowing the template args
struct
{
std::array<std::size_t, max_num_columns<StaticRow...>> pos_map{};
} data_;
// The type-erased impl, that will use pointers to the above storage
static_execution_state_erased_impl impl_;
execst_external_data::ptr_data ptr_data() noexcept
{
return {
data_.pos_map.data(),
};
}
void set_pointers() noexcept { impl_.ext_data().set_pointers(ptr_data()); }
public:
static_execution_state_impl() noexcept
: impl_({execst_resultset_descriptor_table<StaticRow...>, ptr_data()})
{
}
static_execution_state_impl(const static_execution_state_impl& rhs) : data_(rhs.data_), impl_(rhs.impl_)
{
set_pointers();
}
static_execution_state_impl(static_execution_state_impl&& rhs) noexcept
: data_(std::move(rhs.data_)), impl_(std::move(rhs.impl_))
{
set_pointers();
}
static_execution_state_impl& operator=(const static_execution_state_impl& rhs)
{
data_ = rhs.data_;
impl_ = rhs.impl_;
set_pointers();
return *this;
}
static_execution_state_impl& operator=(static_execution_state_impl&& rhs)
{
data_ = std::move(rhs.data_);
impl_ = std::move(rhs.impl_);
set_pointers();
return *this;
}
~static_execution_state_impl() = default;
const static_execution_state_erased_impl& get_interface() const noexcept { return impl_; }
static_execution_state_erased_impl& get_interface() noexcept { return impl_; }
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/static_execution_state_impl.ipp>
#endif
#endif // BOOST_MYSQL_CXX14
#endif
@@ -0,0 +1,349 @@
//
// 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_EXECUTION_PROCESSOR_STATIC_RESULTS_IMPL_HPP
#define BOOST_MYSQL_DETAIL_EXECUTION_PROCESSOR_STATIC_RESULTS_IMPL_HPP
#include <boost/mysql/detail/config.hpp>
#ifdef BOOST_MYSQL_CXX14
#include <boost/mysql/diagnostics.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/execution_processor/execution_processor.hpp>
#include <boost/mysql/detail/typing/readable_field_traits.hpp>
#include <boost/mysql/detail/typing/row_traits.hpp>
#include <boost/assert.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <array>
#include <cstddef>
namespace boost {
namespace mysql {
namespace detail {
using results_reset_fn_t = void (*)(void*);
using results_parse_fn_t =
error_code (*)(span<const std::size_t> pos_map, span<const field_view> from, void* to);
struct results_resultset_descriptor
{
std::size_t num_columns;
name_table_t name_table;
meta_check_fn_t meta_check;
results_parse_fn_t parse_fn;
};
struct static_per_resultset_data
{
std::size_t meta_offset{};
std::size_t meta_size{};
std::size_t info_offset{};
std::size_t info_size{};
bool has_ok_packet_data{false}; // The OK packet information is default constructed, or actual data?
std::uint64_t affected_rows{}; // OK packet data
std::uint64_t last_insert_id{}; // OK packet data
std::uint16_t warnings{}; // OK packet data
bool is_out_params{false}; // Does this resultset contain OUT param information?
};
class results_external_data
{
public:
struct ptr_data
{
void* rows;
std::size_t* pos_map;
static_per_resultset_data* per_resultset;
};
results_external_data(
span<const results_resultset_descriptor> desc,
results_reset_fn_t reset,
ptr_data ptr
) noexcept
: desc_(desc), reset_(reset), ptr_(ptr)
{
}
void set_pointers(ptr_data ptr) noexcept { ptr_ = ptr; }
std::size_t num_resultsets() const noexcept { return desc_.size(); }
std::size_t num_columns(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].num_columns;
}
name_table_t name_table(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].name_table;
}
meta_check_fn_t meta_check_fn(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].meta_check;
}
results_parse_fn_t parse_fn(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return desc_[idx].parse_fn;
}
results_reset_fn_t reset_fn() const noexcept { return reset_; }
void* rows() const noexcept { return ptr_.rows; }
span<std::size_t> pos_map(std::size_t idx) const noexcept
{
return span<std::size_t>(ptr_.pos_map, num_columns(idx));
}
static_per_resultset_data& per_result(std::size_t idx) const noexcept
{
BOOST_ASSERT(idx < num_resultsets());
return ptr_.per_resultset[idx];
}
private:
span<const results_resultset_descriptor> desc_;
results_reset_fn_t reset_;
ptr_data ptr_;
};
class static_results_erased_impl final : public execution_processor
{
public:
static_results_erased_impl(results_external_data ext) noexcept : ext_(ext) {}
results_external_data& ext_data() noexcept { return ext_; }
metadata_collection_view get_meta(std::size_t index) const noexcept
{
const auto& resultset_data = ext_.per_result(index);
return metadata_collection_view(meta_.data() + resultset_data.meta_offset, resultset_data.meta_size);
}
std::uint64_t get_affected_rows(std::size_t index) const noexcept
{
return ext_.per_result(index).affected_rows;
}
std::uint64_t get_last_insert_id(std::size_t index) const noexcept
{
return ext_.per_result(index).last_insert_id;
}
unsigned get_warning_count(std::size_t index) const noexcept { return ext_.per_result(index).warnings; }
string_view get_info(std::size_t index) const noexcept
{
const auto& resultset_data = ext_.per_result(index);
return string_view(info_.data() + resultset_data.info_offset, resultset_data.info_size);
}
bool get_is_out_params(std::size_t index) const noexcept { return ext_.per_result(index).is_out_params; }
private:
// Virtual implementations
BOOST_MYSQL_DECL
void reset_impl() noexcept override final;
BOOST_MYSQL_DECL
error_code on_head_ok_packet_impl(const ok_view& pack, diagnostics& diag) override final;
BOOST_MYSQL_DECL
void on_num_meta_impl(std::size_t num_columns) override final;
BOOST_MYSQL_DECL
error_code on_meta_impl(const coldef_view& coldef, bool is_last, diagnostics& diag) override final;
BOOST_MYSQL_DECL
error_code on_row_impl(span<const std::uint8_t> msg, const output_ref&, std::vector<field_view>& fields)
override final;
BOOST_MYSQL_DECL
error_code on_row_ok_packet_impl(const ok_view& pack) override final;
void on_row_batch_start_impl() override final {}
void on_row_batch_finish_impl() override final {}
// Data
results_external_data ext_;
std::vector<metadata> meta_;
std::vector<char> info_;
std::size_t resultset_index_{0};
// Helpers
span<std::size_t> current_pos_map() noexcept { return ext_.pos_map(resultset_index_ - 1); }
span<const std::size_t> current_pos_map() const noexcept { return ext_.pos_map(resultset_index_ - 1); }
name_table_t current_name_table() const noexcept { return ext_.name_table(resultset_index_ - 1); }
static_per_resultset_data& current_resultset() noexcept { return ext_.per_result(resultset_index_ - 1); }
metadata_collection_view current_resultset_meta() const noexcept
{
return get_meta(resultset_index_ - 1);
}
BOOST_MYSQL_DECL
static_per_resultset_data& add_resultset();
BOOST_MYSQL_DECL
error_code on_ok_packet_impl(const ok_view& pack);
error_code meta_check(diagnostics& diag) const
{
return ext_.meta_check_fn(resultset_index_ - 1)(current_pos_map(), current_resultset_meta(), diag);
}
};
template <class... StaticRow>
using results_rows_t = std::tuple<std::vector<StaticRow>...>;
template <class... StaticRow>
struct results_fns
{
using rows_t = results_rows_t<StaticRow...>;
struct reset_fn
{
rows_t& obj;
template <std::size_t I>
void operator()(boost::mp11::mp_size_t<I>) const noexcept
{
std::get<I>(obj).clear();
}
};
static void reset(void* rows_ptr) noexcept
{
auto& rows = *static_cast<rows_t*>(rows_ptr);
boost::mp11::mp_for_each<boost::mp11::mp_iota_c<sizeof...(StaticRow)>>(reset_fn{rows});
}
template <std::size_t I>
static error_code do_parse(span<const std::size_t> pos_map, span<const field_view> from, void* to)
{
auto& v = std::get<I>(*static_cast<rows_t*>(to));
v.emplace_back();
return parse(pos_map, from, v.back());
}
template <std::size_t I>
static constexpr results_resultset_descriptor create_descriptor()
{
using T = mp11::mp_at_c<mp11::mp_list<StaticRow...>, I>;
return {
get_row_size<T>(),
get_row_name_table<T>(),
&meta_check<T>,
&do_parse<I>,
};
}
template <std::size_t... I>
static constexpr std::array<results_resultset_descriptor, sizeof...(StaticRow)> create_descriptors(mp11::index_sequence<
I...>)
{
return {{create_descriptor<I>()...}};
}
};
template <class... StaticRow>
constexpr std::array<results_resultset_descriptor, sizeof...(StaticRow)>
results_resultset_descriptor_table = results_fns<StaticRow...>::create_descriptors(
mp11::make_index_sequence<sizeof...(StaticRow)>()
);
template <BOOST_MYSQL_STATIC_ROW... StaticRow>
class static_results_impl
{
// Data that requires knowing template params
struct
{
results_rows_t<StaticRow...> rows;
std::array<std::size_t, max_num_columns<StaticRow...>> pos_map{};
std::array<static_per_resultset_data, sizeof...(StaticRow)> per_resultset{};
} data_;
// The type-erased impl, that will use pointers to the above storage
static_results_erased_impl impl_;
results_external_data::ptr_data ptr_data() noexcept
{
return {
&data_.rows,
data_.pos_map.data(),
data_.per_resultset.data(),
};
}
void set_pointers() noexcept { impl_.ext_data().set_pointers(ptr_data()); }
public:
static_results_impl() noexcept
: impl_(results_external_data(
results_resultset_descriptor_table<StaticRow...>,
&results_fns<StaticRow...>::reset,
ptr_data()
))
{
}
static_results_impl(const static_results_impl& rhs) : data_(rhs.data_), impl_(rhs.impl_)
{
set_pointers();
}
static_results_impl(static_results_impl&& rhs) noexcept
: data_(std::move(rhs.data_)), impl_(std::move(rhs.impl_))
{
set_pointers();
}
static_results_impl& operator=(const static_results_impl& rhs)
{
data_ = rhs.data_;
impl_ = rhs.impl_;
set_pointers();
return *this;
}
static_results_impl& operator=(static_results_impl&& rhs)
{
data_ = std::move(rhs.data_);
impl_ = std::move(rhs.impl_);
set_pointers();
return *this;
}
// User facing
template <std::size_t I>
boost::span<const typename std::tuple_element<I, std::tuple<StaticRow...>>::type> get_rows(
) const noexcept
{
return std::get<I>(data_.rows);
}
const static_results_erased_impl& get_interface() const noexcept { return impl_; }
static_results_erased_impl& get_interface() noexcept { return impl_; }
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/static_results_impl.ipp>
#endif
#endif // BOOST_MYSQL_CXX14
#endif
+97
View File
@@ -0,0 +1,97 @@
//
// 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_FIELD_IMPL_HPP
#define BOOST_MYSQL_DETAIL_FIELD_IMPL_HPP
#include <boost/mysql/bad_field_access.hpp>
#include <boost/mysql/blob.hpp>
#include <boost/mysql/date.hpp>
#include <boost/mysql/datetime.hpp>
#include <boost/mysql/field_kind.hpp>
#include <boost/mysql/time.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/throw_exception.hpp>
#include <boost/variant2/variant.hpp>
#include <string>
#include <type_traits>
namespace boost {
namespace mysql {
namespace detail {
// Breaks a circular dependency between field_view and field
struct field_impl
{
using null_t = boost::variant2::monostate;
using variant_type = boost::variant2::variant<
null_t, // Any of the below when the value is NULL
std::int64_t, // signed TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT
std::uint64_t, // unsigned TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT, YEAR, BIT
std::string, // CHAR, VARCHAR, TEXT (all sizes), , ENUM,
// SET, DECIMAL
blob, // BINARY, VARBINARY, BLOB (all sizes), GEOMETRY
float, // FLOAT
double, // DOUBLE
date, // DATE
datetime, // DATETIME, TIMESTAMP
time // TIME
>;
variant_type data;
field_impl() = default;
template <typename... Args>
field_impl(Args&&... args) noexcept(std::is_nothrow_constructible<variant_type, Args...>::value)
: data(std::forward<Args>(args)...)
{
}
field_kind kind() const noexcept { return static_cast<field_kind>(data.index()); }
template <typename T>
const T& as() const
{
const T* res = boost::variant2::get_if<T>(&data);
if (!res)
BOOST_THROW_EXCEPTION(bad_field_access());
return *res;
}
template <typename T>
T& as()
{
T* res = boost::variant2::get_if<T>(&data);
if (!res)
BOOST_THROW_EXCEPTION(bad_field_access());
return *res;
}
template <typename T>
const T& get() const noexcept
{
constexpr auto I = mp11::mp_find<variant_type, T>::value;
return boost::variant2::unsafe_get<I>(data);
}
template <typename T>
T& get() noexcept
{
constexpr auto I = mp11::mp_find<variant_type, T>::value;
return boost::variant2::unsafe_get<I>(data);
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+49
View File
@@ -0,0 +1,49 @@
//
// 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_FLAGS_HPP
#define BOOST_MYSQL_DETAIL_FLAGS_HPP
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
namespace column_flags {
constexpr std::uint16_t not_null = 1; // Field can't be NULL.
constexpr std::uint16_t pri_key = 2; // Field is part of a primary key.
constexpr std::uint16_t unique_key = 4; // Field is part of a unique key.
constexpr std::uint16_t multiple_key = 8; // Field is part of a key.
constexpr std::uint16_t blob = 16; // Field is a blob.
constexpr std::uint16_t unsigned_ = 32; // Field is unsigned.
constexpr std::uint16_t zerofill = 64; // Field is zerofill.
constexpr std::uint16_t binary = 128; // Field is binary.
constexpr std::uint16_t enum_ = 256; // field is an enum
constexpr std::uint16_t auto_increment = 512; // field is a autoincrement field
constexpr std::uint16_t timestamp = 1024; // Field is a timestamp.
constexpr std::uint16_t set = 2048; // field is a set
constexpr std::uint16_t no_default_value = 4096; // Field doesn't have default value.
constexpr std::uint16_t on_update_now = 8192; // Field is set to NOW on UPDATE.
constexpr std::uint16_t part_key = 16384; // Intern; Part of some key.
constexpr std::uint16_t num = 32768; // Field is num (for clients)
} // namespace column_flags
namespace status_flags {
constexpr std::uint32_t more_results = 8;
constexpr std::uint32_t out_params = 4096;
} // namespace status_flags
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+801
View File
@@ -0,0 +1,801 @@
//
// 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_NETWORK_ALGORITHMS_HPP
#define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/execution_state.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/handshake_params.hpp>
#include <boost/mysql/rows_view.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/any_execution_request.hpp>
#include <boost/mysql/detail/channel_ptr.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/detail/typing/get_type_index.hpp>
#include <boost/asio/any_completion_handler.hpp>
#include <boost/mp11/integer_sequence.hpp>
#include <array>
#include <cstddef>
namespace boost {
namespace mysql {
template <class... StaticRow>
class static_execution_state;
namespace detail {
class channel;
template <class T>
using any_handler = asio::any_completion_handler<void(error_code, T)>;
using any_void_handler = asio::any_completion_handler<void(error_code)>;
// execution helpers
template <class... T, std::size_t... I>
std::array<field_view, sizeof...(T)> tuple_to_array_impl(const std::tuple<T...>& t, mp11::index_sequence<I...>) noexcept
{
return std::array<field_view, sizeof...(T)>{{to_field(std::get<I>(t))...}};
}
template <class... T>
std::array<field_view, sizeof...(T)> tuple_to_array(const std::tuple<T...>& t) noexcept
{
return tuple_to_array_impl(t, mp11::make_index_sequence<sizeof...(T)>());
}
struct query_request_getter
{
any_execution_request value;
any_execution_request get() const noexcept { return value; }
};
inline query_request_getter make_request_getter(string_view q, channel&) noexcept
{
return query_request_getter{q};
}
struct stmt_it_request_getter
{
statement stmt;
span<const field_view> params; // Points into channel shared_fields()
any_execution_request get() const noexcept { return any_execution_request(stmt, params); }
};
template <class FieldViewFwdIterator>
inline stmt_it_request_getter make_request_getter(
const bound_statement_iterator_range<FieldViewFwdIterator>& req,
channel& chan
)
{
auto& impl = access::get_impl(req);
auto& shared_fields = get_shared_fields(chan);
shared_fields.assign(impl.first, impl.last);
return {impl.stmt, shared_fields};
}
template <std::size_t N>
struct stmt_tuple_request_getter
{
statement stmt;
std::array<field_view, N> params;
any_execution_request get() const noexcept { return any_execution_request(stmt, params); }
};
template <class WritableFieldTuple>
stmt_tuple_request_getter<std::tuple_size<WritableFieldTuple>::value>
make_request_getter(const bound_statement_tuple<WritableFieldTuple>& req, channel&)
{
auto& impl = access::get_impl(req);
return {impl.stmt, tuple_to_array(impl.params)};
}
//
// connect
//
BOOST_MYSQL_DECL
void connect_erased(
channel& chan,
const void* endpoint,
const handshake_params& params,
error_code& err,
diagnostics& diag
);
BOOST_MYSQL_DECL
void async_connect_erased(
channel& chan,
const void* endpoint,
const handshake_params& params,
diagnostics& diag,
any_void_handler handler
);
// Handles casting from the generic EndpointType we've got in the interface to the concrete endpoint type
template <class Stream>
void connect_interface(
channel& chan,
const typename Stream::lowest_layer_type::endpoint_type& ep,
const handshake_params& params,
error_code& err,
diagnostics& diag
)
{
connect_erased(chan, &ep, params, err, diag);
}
template <class Stream>
struct connect_initiation
{
template <class Handler>
void operator()(
Handler&& handler,
channel* chan,
const typename Stream::lowest_layer_type::endpoint_type& endpoint,
handshake_params params,
diagnostics* diag
)
{
async_connect_erased(*chan, &endpoint, params, *diag, std::forward<Handler>(handler));
}
};
template <class Stream, class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_connect_interface(
channel& chan,
const typename Stream::lowest_layer_type::endpoint_type& endpoint,
const handshake_params& params,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
connect_initiation<Stream>(),
token,
&chan,
endpoint,
params,
&diag
);
}
//
// handshake
//
BOOST_MYSQL_DECL
void handshake_erased(channel& channel, const handshake_params& params, error_code& err, diagnostics& diag);
BOOST_MYSQL_DECL
void async_handshake_erased(
channel& chan,
const handshake_params& params,
diagnostics& diag,
any_void_handler
);
inline void handshake_interface(
channel& channel,
const handshake_params& params,
error_code& err,
diagnostics& diag
)
{
handshake_erased(channel, params, err, diag);
}
struct handshake_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, handshake_params params, diagnostics* diag)
{
async_handshake_erased(*chan, params, *diag, std::forward<Handler>(handler));
}
};
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_handshake_interface(
channel& chan,
const handshake_params& params,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
handshake_initiation(),
token,
&chan,
params,
&diag
);
}
//
// execute
//
BOOST_MYSQL_DECL
void execute_erased(
channel& channel,
const any_execution_request& req,
execution_processor& output,
error_code& err,
diagnostics& diag
);
BOOST_MYSQL_DECL void async_execute_erased(
channel& chan,
const any_execution_request& req,
execution_processor& output,
diagnostics& diag,
any_void_handler handler
);
struct initiate_execute
{
template <class Handler, class ExecutionRequest>
void operator()(
Handler&& handler,
channel& chan,
const ExecutionRequest& req,
execution_processor& proc,
diagnostics& diag
)
{
auto getter = make_request_getter(req, chan);
async_execute_erased(chan, getter.get(), proc, diag, std::forward<Handler>(handler));
}
};
template <class ExecutionRequest, class ResultsType>
void execute_interface(
channel& channel,
const ExecutionRequest& req,
ResultsType& result,
error_code& err,
diagnostics& diag
)
{
auto getter = make_request_getter(req, channel);
execute_erased(channel, getter.get(), access::get_impl(result).get_interface(), err, diag);
}
template <class ExecutionRequest, class ResultsType, class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_execute_interface(
channel& chan,
ExecutionRequest&& req,
ResultsType& result,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
initiate_execute(),
token,
std::ref(chan),
std::forward<ExecutionRequest>(req),
std::ref(access::get_impl(result).get_interface()),
std::ref(diag)
);
}
//
// start_execution
//
BOOST_MYSQL_DECL
void start_execution_erased(
channel& channel,
const any_execution_request& req,
execution_processor& proc,
error_code& err,
diagnostics& diag
);
BOOST_MYSQL_DECL
void async_start_execution_erased(
channel& channel,
const any_execution_request& req,
execution_processor& proc,
diagnostics& diag,
any_void_handler handler
);
struct initiate_start_execution
{
template <class Handler, class ExecutionRequest>
void operator()(
Handler&& handler,
channel& chan,
const ExecutionRequest& req,
execution_processor& proc,
diagnostics& diag
)
{
auto getter = make_request_getter(req, chan);
async_start_execution_erased(chan, getter.get(), proc, diag, std::forward<Handler>(handler));
}
};
template <class ExecutionRequest, class ExecutionStateType>
void start_execution_interface(
channel& channel,
const ExecutionRequest& req,
ExecutionStateType& st,
error_code& err,
diagnostics& diag
)
{
auto getter = make_request_getter(req, channel);
start_execution_erased(channel, getter.get(), access::get_impl(st).get_interface(), err, diag);
}
template <class ExecutionRequest, class ExecutionStateType, class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_start_execution_interface(
channel& chan,
ExecutionRequest&& req,
ExecutionStateType& st,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
initiate_start_execution(),
token,
std::ref(chan),
std::forward<ExecutionRequest>(req),
std::ref(access::get_impl(st).get_interface()),
std::ref(diag)
);
}
//
// prepare_statement
//
BOOST_MYSQL_DECL
statement prepare_statement_erased(channel& chan, string_view stmt, error_code& err, diagnostics& diag);
BOOST_MYSQL_DECL void async_prepare_statement_erased(
channel& chan,
string_view stmt,
diagnostics& diag,
any_handler<statement> handler
);
struct prepare_statement_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, string_view stmt_sql, diagnostics* diag)
{
async_prepare_statement_erased(*chan, stmt_sql, *diag, std::forward<Handler>(handler));
}
};
inline statement prepare_statement_interface(
channel& chan,
string_view stmt,
error_code& err,
diagnostics& diag
)
{
return prepare_statement_erased(chan, stmt, err, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code, boost::mysql::statement))
async_prepare_statement_interface(channel& chan, string_view stmt, diagnostics& diag, CompletionToken&& token)
{
return asio::async_initiate<CompletionToken, void(error_code, statement)>(
prepare_statement_initiation(),
token,
&chan,
stmt,
&diag
);
}
//
// close_statement
//
BOOST_MYSQL_DECL
void close_statement_erased(channel& chan, const statement& stmt, error_code& err, diagnostics& diag);
BOOST_MYSQL_DECL
void async_close_statement_erased(
channel& chan,
const statement& stmt,
diagnostics& diag,
any_void_handler handler
);
struct close_statement_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, statement stmt, diagnostics* diag)
{
async_close_statement_erased(*chan, stmt, *diag, std::forward<Handler>(handler));
}
};
inline void close_statement_interface(
channel& chan,
const statement& stmt,
error_code& err,
diagnostics& diag
)
{
close_statement_erased(chan, stmt, err, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_close_statement_interface(
channel& chan,
const statement& stmt,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
close_statement_initiation(),
token,
&chan,
stmt,
&diag
);
}
//
// read_some_rows (dynamic)
//
BOOST_MYSQL_DECL
rows_view read_some_rows_dynamic_erased(
channel& chan,
execution_state_impl& st,
error_code& err,
diagnostics& diag
);
BOOST_MYSQL_DECL void async_read_some_rows_dynamic_erased(
channel& chan,
execution_state_impl& st,
diagnostics& diag,
any_handler<rows_view> handler
);
struct read_some_rows_dynamic_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, execution_state_impl* st, diagnostics* diag)
{
async_read_some_rows_dynamic_erased(*chan, *st, *diag, std::forward<Handler>(handler));
}
};
inline rows_view read_some_rows_dynamic_interface(
channel& chan,
execution_state& st,
error_code& err,
diagnostics& diag
)
{
return read_some_rows_dynamic_erased(chan, access::get_impl(st), err, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
async_read_some_rows_dynamic_interface(
channel& chan,
execution_state& st,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code, rows_view)>(
read_some_rows_dynamic_initiation(),
token,
&chan,
&access::get_impl(st).get_interface(),
&diag
);
}
//
// read_some_rows (static)
//
BOOST_MYSQL_DECL
std::size_t read_some_rows_static_erased(
channel& chan,
execution_processor& proc,
const output_ref& output,
error_code& err,
diagnostics& diag
);
BOOST_MYSQL_DECL
void async_read_some_rows_erased(
channel& chan,
execution_processor& proc,
const output_ref& output,
diagnostics& diag,
any_handler<std::size_t> handler
);
template <class SpanRowType, class... RowType>
std::size_t read_some_rows_static_interface(
channel& chan,
static_execution_state<RowType...>& st,
span<SpanRowType> output,
error_code& err,
diagnostics& diag
)
{
constexpr std::size_t index = get_type_index<SpanRowType, RowType...>();
static_assert(index != index_not_found, "SpanRowType must be one of the types returned by the query");
return read_some_rows_static_erased(
chan,
access::get_impl(st).get_interface(),
output_ref(output, index),
err,
diag
);
}
struct read_some_rows_static_initiation
{
template <class Handler>
void operator()(
Handler&& handler,
channel* chan,
execution_processor* proc,
const output_ref& output,
diagnostics* diag
)
{
async_read_some_rows_erased(*chan, *proc, output, *diag, std::forward<Handler>(handler));
}
};
template <
class SpanRowType,
class... RowType,
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code, std::size_t)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
async_read_some_rows_static_interface(
channel& chan,
static_execution_state<RowType...>& st,
span<SpanRowType> output,
diagnostics& diag,
CompletionToken&& token
)
{
constexpr std::size_t index = get_type_index<SpanRowType, RowType...>();
static_assert(index != index_not_found, "SpanRowType must be one of the types returned by the query");
return asio::async_initiate<CompletionToken, void(error_code, std::size_t)>(
read_some_rows_static_initiation(),
token,
&chan,
&access::get_impl(st).get_interface(),
output_ref(output, index),
&diag
);
}
//
// read_resultset_head
//
BOOST_MYSQL_DECL
void read_resultset_head_erased(
channel& channel,
execution_processor& proc,
error_code& err,
diagnostics& diag
);
BOOST_MYSQL_DECL
void async_read_resultset_head_erased(
channel& chan,
execution_processor& proc,
diagnostics& diag,
any_void_handler handler
);
template <class ExecutionStateType>
void read_resultset_head_interface(
channel& channel,
ExecutionStateType& st,
error_code& err,
diagnostics& diag
)
{
read_resultset_head_erased(channel, access::get_impl(st).get_interface(), err, diag);
}
struct read_resultset_head_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, execution_processor* proc, diagnostics* diag)
{
async_read_resultset_head_erased(*chan, *proc, *diag, std::forward<Handler>(handler));
}
};
template <class CompletionToken, class ExecutionStateType>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_read_resultset_head_interface(
channel& chan,
ExecutionStateType& st,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
read_resultset_head_initiation(),
token,
&chan,
&access::get_impl(st).get_interface(),
&diag
);
}
//
// ping
//
BOOST_MYSQL_DECL
void ping_erased(channel& chan, error_code& code, diagnostics& diag);
BOOST_MYSQL_DECL
void async_ping_erased(channel& chan, diagnostics& diag, any_void_handler handler);
struct ping_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, diagnostics* diag)
{
async_ping_erased(*chan, *diag, std::forward<Handler>(handler));
}
};
inline void ping_interface(channel& chan, error_code& code, diagnostics& diag)
{
ping_erased(chan, code, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_ping_interface(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_initiate<CompletionToken, void(error_code)>(ping_initiation(), token, &chan, &diag);
}
//
// reset_connection
//
BOOST_MYSQL_DECL
void reset_connection_erased(channel& chan, error_code& code, diagnostics& diag);
BOOST_MYSQL_DECL
void async_reset_connection_erased(channel& chan, diagnostics& diag, any_void_handler handler);
struct reset_connection_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, diagnostics* diag)
{
async_reset_connection_erased(*chan, *diag, std::forward<Handler>(handler));
}
};
inline void reset_connection_interface(channel& chan, error_code& code, diagnostics& diag)
{
reset_connection_erased(chan, code, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_reset_connection_interface(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
reset_connection_initiation(),
token,
&chan,
&diag
);
}
//
// close connection
//
BOOST_MYSQL_DECL
void close_connection_erased(channel& chan, error_code& code, diagnostics& diag);
BOOST_MYSQL_DECL
void async_close_connection_erased(channel& chan, diagnostics& diag, any_void_handler handler);
struct close_connection_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, diagnostics* diag)
{
async_close_connection_erased(*chan, *diag, std::forward<Handler>(handler));
}
};
inline void close_connection_interface(channel& chan, error_code& code, diagnostics& diag)
{
close_connection_erased(chan, code, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_close_connection_interface(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
close_connection_initiation(),
token,
&chan,
&diag
);
}
//
// quit connection
//
BOOST_MYSQL_DECL
void quit_connection_erased(channel& chan, error_code& err, diagnostics& diag);
BOOST_MYSQL_DECL
void async_quit_connection_erased(channel& chan, diagnostics& diag, any_void_handler handler);
struct quit_connection_initiation
{
template <class Handler>
void operator()(Handler&& handler, channel* chan, diagnostics* diag)
{
async_quit_connection_erased(*chan, *diag, std::forward<Handler>(handler));
}
};
inline void quit_connection_interface(channel& chan, error_code& err, diagnostics& diag)
{
quit_connection_erased(chan, err, diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_quit_connection_interface(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_initiate<CompletionToken, void(error_code)>(
quit_connection_initiation(),
token,
&chan,
&diag
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/network_algorithms.ipp>
#endif
#endif
+37
View File
@@ -0,0 +1,37 @@
//
// 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_OK_VIEW_HPP
#define BOOST_MYSQL_DETAIL_OK_VIEW_HPP
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/flags.hpp>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
struct ok_view
{
std::uint64_t affected_rows;
std::uint64_t last_insert_id;
std::uint16_t status_flags;
std::uint16_t warnings;
string_view info;
bool more_results() const noexcept { return status_flags & status_flags::more_results; }
bool is_out_params() const noexcept { return status_flags & status_flags::out_params; }
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+34
View File
@@ -0,0 +1,34 @@
//
// 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_REBIND_EXECUTOR_HPP
#define BOOST_MYSQL_DETAIL_REBIND_EXECUTOR_HPP
#include <boost/asio/ssl/stream.hpp>
namespace boost {
namespace mysql {
namespace detail {
// This is required because ssl::stream doesn't have a rebind_executor member type
template <class Stream, class Executor>
struct rebind_executor
{
using type = typename Stream::template rebind_executor<Executor>::other;
};
template <class Stream, class Executor>
struct rebind_executor<boost::asio::ssl::stream<Stream>, Executor>
{
using type = boost::asio::ssl::stream<typename rebind_executor<Stream, Executor>::type>;
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+99
View File
@@ -0,0 +1,99 @@
//
// 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_RESULTS_ITERATOR_HPP
#define BOOST_MYSQL_DETAIL_RESULTS_ITERATOR_HPP
#include <boost/mysql/resultset.hpp>
#include <boost/mysql/resultset_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/execution_processor/results_impl.hpp>
namespace boost {
namespace mysql {
namespace detail {
class results_iterator
{
const results_impl* self_{};
std::size_t index_{};
public:
using value_type = resultset;
using reference = resultset_view;
using pointer = resultset_view;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
results_iterator() = default;
results_iterator(const results_impl* self, std::size_t index) noexcept : self_(self), index_(index) {}
results_iterator& operator++() noexcept
{
++index_;
return *this;
}
results_iterator operator++(int) noexcept
{
auto res = *this;
++(*this);
return res;
}
results_iterator& operator--() noexcept
{
--index_;
return *this;
}
results_iterator operator--(int) noexcept
{
auto res = *this;
--(*this);
return res;
}
results_iterator& operator+=(std::ptrdiff_t n) noexcept
{
index_ += n;
return *this;
}
results_iterator& operator-=(std::ptrdiff_t n) noexcept
{
index_ -= n;
return *this;
}
results_iterator operator+(std::ptrdiff_t n) const noexcept
{
return results_iterator(self_, index_ + n);
}
results_iterator operator-(std::ptrdiff_t n) const noexcept { return *this + (-n); }
std::ptrdiff_t operator-(results_iterator rhs) const noexcept { return index_ - rhs.index_; }
pointer operator->() const noexcept { return **this; }
reference operator*() const noexcept { return (*this)[0]; }
reference operator[](std::ptrdiff_t i) const noexcept
{
return access::construct<resultset_view>(*self_, index_ + i);
}
bool operator==(results_iterator rhs) const noexcept { return index_ == rhs.index_; }
bool operator!=(results_iterator rhs) const noexcept { return !(*this == rhs); }
bool operator<(results_iterator rhs) const noexcept { return index_ < rhs.index_; }
bool operator<=(results_iterator rhs) const noexcept { return index_ <= rhs.index_; }
bool operator>(results_iterator rhs) const noexcept { return index_ > rhs.index_; }
bool operator>=(results_iterator rhs) const noexcept { return index_ >= rhs.index_; }
std::size_t index() const noexcept { return index_; }
const results_impl* obj() const noexcept { return self_; }
};
inline results_iterator operator+(std::ptrdiff_t n, results_iterator it) noexcept { return it + n; }
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+25
View File
@@ -0,0 +1,25 @@
//
// 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_RESULTSET_ENCODING_HPP
#define BOOST_MYSQL_DETAIL_RESULTSET_ENCODING_HPP
namespace boost {
namespace mysql {
namespace detail {
enum class resultset_encoding
{
text,
binary
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_COMMON_HPP_ */
+95
View File
@@ -0,0 +1,95 @@
//
// 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_ROW_IMPL_HPP
#define BOOST_MYSQL_DETAIL_ROW_IMPL_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/core/span.hpp>
#include <cstddef>
#include <vector>
namespace boost {
namespace mysql {
namespace detail {
// Adds num_fields default-constructed fields to the vector, return pointer to the first
// allocated value. Used to allocate fields before deserialization
inline span<field_view> add_fields(std::vector<field_view>& storage, std::size_t num_fields)
{
std::size_t old_size = storage.size();
storage.resize(old_size + num_fields);
return span<field_view>(storage.data() + old_size, num_fields);
}
// A field_view vector with strings pointing into a
// single character buffer. Used to implement owning row types
class row_impl
{
public:
row_impl() = default;
BOOST_MYSQL_DECL
row_impl(const row_impl&);
row_impl(row_impl&&) = default;
BOOST_MYSQL_DECL
row_impl& operator=(const row_impl&);
row_impl& operator=(row_impl&&) = default;
~row_impl() = default;
// Copies the given span into *this
BOOST_MYSQL_DECL
row_impl(const field_view* fields, std::size_t size);
// Copies the given span into *this, used by row/rows in assignment from view
BOOST_MYSQL_DECL
void assign(const field_view* fields, std::size_t size);
// Adds new default constructed fields to provide storage to deserialization
span<field_view> add_fields(std::size_t num_fields)
{
return ::boost::mysql::detail::add_fields(fields_, num_fields);
}
// Saves strings in the [first, first+num_fields) range into the string buffer, used by execute
BOOST_MYSQL_DECL
void copy_strings_as_offsets(std::size_t first, std::size_t num_fields);
// Restores any offsets into string views, used by execute
BOOST_MYSQL_DECL
void offsets_to_string_views();
const std::vector<field_view>& fields() const noexcept { return fields_; }
void clear() noexcept
{
fields_.clear();
string_buffer_.clear();
}
private:
std::vector<field_view> fields_;
std::vector<unsigned char> string_buffer_;
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/row_impl.ipp>
#endif
#endif
+112
View File
@@ -0,0 +1,112 @@
//
// 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_ROWS_ITERATOR_HPP
#define BOOST_MYSQL_DETAIL_ROWS_ITERATOR_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/row.hpp>
#include <boost/mysql/row_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <cstddef>
#include <cstdint>
#include <iterator>
namespace boost {
namespace mysql {
namespace detail {
inline row_view row_slice(const field_view* fields, std::size_t num_columns, std::size_t offset) noexcept
{
return access::construct<row_view>(fields + num_columns * offset, num_columns);
}
class rows_iterator
{
const field_view* fields_{nullptr};
std::size_t num_columns_{0};
std::size_t row_num_{0};
public:
using value_type = row;
using reference = row_view;
using pointer = row_view;
using difference_type = std::ptrdiff_t;
using iterator_category = std::random_access_iterator_tag;
rows_iterator() = default;
rows_iterator(const field_view* fields, std::size_t num_columns, std::size_t rownum) noexcept
: fields_(fields), num_columns_(num_columns), row_num_(rownum)
{
}
rows_iterator& operator++() noexcept
{
++row_num_;
return *this;
}
rows_iterator operator++(int) noexcept
{
auto res = *this;
++(*this);
return res;
}
rows_iterator& operator--() noexcept
{
--row_num_;
return *this;
}
rows_iterator operator--(int) noexcept
{
auto res = *this;
--(*this);
return res;
}
rows_iterator& operator+=(std::ptrdiff_t n) noexcept
{
row_num_ += n;
return *this;
}
rows_iterator& operator-=(std::ptrdiff_t n) noexcept
{
row_num_ -= n;
return *this;
}
rows_iterator operator+(std::ptrdiff_t n) const noexcept
{
return rows_iterator(fields_, num_columns_, row_num_ + n);
}
rows_iterator operator-(std::ptrdiff_t n) const noexcept
{
return rows_iterator(fields_, num_columns_, row_num_ - n);
}
std::ptrdiff_t operator-(rows_iterator rhs) const noexcept { return row_num_ - rhs.row_num_; }
pointer operator->() const noexcept { return **this; }
reference operator*() const noexcept { return (*this)[0]; }
reference operator[](std::ptrdiff_t i) const noexcept
{
return row_slice(fields_, num_columns_, row_num_ + i);
}
bool operator==(rows_iterator rhs) const noexcept { return row_num_ == rhs.row_num_; }
bool operator!=(rows_iterator rhs) const noexcept { return !(*this == rhs); }
bool operator<(rows_iterator rhs) const noexcept { return row_num_ < rhs.row_num_; }
bool operator<=(rows_iterator rhs) const noexcept { return row_num_ <= rhs.row_num_; }
bool operator>(rows_iterator rhs) const noexcept { return row_num_ > rhs.row_num_; }
bool operator>=(rows_iterator rhs) const noexcept { return row_num_ >= rhs.row_num_; }
};
inline rows_iterator operator+(std::ptrdiff_t n, rows_iterator it) noexcept { return it + n; }
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+52
View File
@@ -0,0 +1,52 @@
//
// 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_SOCKET_STREAM_HPP
#define BOOST_MYSQL_DETAIL_SOCKET_STREAM_HPP
#include <boost/asio/basic_socket.hpp>
#include <boost/asio/basic_stream_socket.hpp>
#include <type_traits>
namespace boost {
namespace mysql {
namespace detail {
template <class T>
struct is_socket : std::false_type
{
};
// typename basic_stream_socket::lowest_layer_type is basic_socket, so we accept basic_socket and
// basic_stream_socket here
template <class Protocol, class Executor>
struct is_socket<asio::basic_socket<Protocol, Executor>> : std::true_type
{
};
template <class Protocol, class Executor>
struct is_socket<asio::basic_stream_socket<Protocol, Executor>> : std::true_type
{
};
template <class T, class = void>
struct is_socket_stream : std::false_type
{
};
template <class T>
struct is_socket_stream<T, typename std::enable_if<is_socket<typename T::lowest_layer_type>::value>::type>
: std::true_type
{
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+35
View File
@@ -0,0 +1,35 @@
//
// 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_STRING_VIEW_OFFSET_HPP
#define BOOST_MYSQL_DETAIL_STRING_VIEW_OFFSET_HPP
#include <cstddef>
namespace boost {
namespace mysql {
namespace detail {
// Represents a string_view using offsets into a buffer.
// Useful during deserialization, for buffers that may reallocate.
struct string_view_offset
{
std::size_t offset;
std::size_t size;
constexpr bool operator==(string_view_offset rhs) const noexcept
{
return offset == rhs.offset && size == rhs.size;
}
constexpr bool operator!=(string_view_offset rhs) const noexcept { return !(*this == rhs); }
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+33
View File
@@ -0,0 +1,33 @@
//
// 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_THROW_ON_ERROR_LOC_HPP
#define BOOST_MYSQL_DETAIL_THROW_ON_ERROR_LOC_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/error_with_diagnostics.hpp>
#include <boost/assert/source_location.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline void throw_on_error_loc(error_code err, const diagnostics& diag, const boost::source_location& loc)
{
if (err)
{
::boost::throw_exception(error_with_diagnostics(err, diag), loc);
}
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+32
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+22
View File
@@ -0,0 +1,22 @@
//
// 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_VOID_T_HPP
#define BOOST_MYSQL_DETAIL_VOID_T_HPP
namespace boost {
namespace mysql {
namespace detail {
template <typename...>
using void_t = void;
}
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_SERIALIZATION_HPP_ */
+146
View File
@@ -0,0 +1,146 @@
//
// 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_WRITABLE_FIELD_TRAITS_HPP
#define BOOST_MYSQL_DETAIL_WRITABLE_FIELD_TRAITS_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <type_traits>
namespace boost {
namespace mysql {
namespace detail {
template <class T, class En1 = void, class En2 = void>
struct writable_field_traits
{
static constexpr bool is_supported = false;
};
template <>
struct writable_field_traits<bool, void, void>
{
static constexpr bool is_supported = true;
static field_view to_field(bool value) noexcept { return field_view(value ? 1 : 0); }
};
template <class T>
struct writable_field_traits<
T,
typename std::enable_if<std::is_constructible<field_view, const T&>::value>::type,
void>
{
static constexpr bool is_supported = true;
static field_view to_field(const T& value) noexcept { return field_view(value); }
};
// Optionals. To avoid dependencies, we use a "concept".
// We consider a type an optional if has a `bool has_value() const` and
// `const value_type& value() const`
template <class T>
struct writable_field_traits<
T,
void,
typename std::enable_if<
std::is_same<decltype(std::declval<const T&>().has_value()), bool>::value &&
std::is_same<decltype(std::declval<const T&>().value()), const typename T::value_type&>::value>::type>
{
using value_traits = writable_field_traits<typename T::value_type>;
static constexpr bool is_supported = value_traits::is_supported;
static field_view to_field(const T& value) noexcept
{
return value.has_value() ? value_traits::to_field(value.value()) : field_view();
}
};
template <class T>
field_view to_field(const T& value) noexcept
{
return writable_field_traits<T>::to_field(value);
}
template <class T>
struct is_writable_field
{
static constexpr bool value = writable_field_traits<T>::is_supported;
};
// field_view_forward_iterator
template <typename T, typename = void>
struct is_field_view_forward_iterator : std::false_type
{
};
// clang-format off
template <typename T>
struct is_field_view_forward_iterator<
T,
typename std::enable_if<
std::is_convertible<
typename std::iterator_traits<T>::reference,
field_view
>::value
&&
std::is_base_of<
std::forward_iterator_tag,
typename std::iterator_traits<T>::iterator_category
>::value
>::type
> : std::true_type { };
// clang-format on
#ifdef BOOST_MYSQL_HAS_CONCEPTS
template <class T>
concept field_view_forward_iterator = is_field_view_forward_iterator<T>::value;
#define BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR ::boost::mysql::detail::field_view_forward_iterator
#else // BOOST_MYSQL_HAS_CONCEPTS
#define BOOST_MYSQL_FIELD_VIEW_FORWARD_ITERATOR class
#endif // BOOST_MYSQL_HAS_CONCEPTS
// writable_field_tuple
template <class... T>
struct is_writable_field_tuple_impl : std::false_type
{
};
template <class... T>
struct is_writable_field_tuple_impl<std::tuple<T...>>
: mp11::mp_all_of<mp11::mp_list<T...>, is_writable_field>
{
};
template <class Tuple>
struct is_writable_field_tuple : is_writable_field_tuple_impl<typename std::decay<Tuple>::type>
{
};
#ifdef BOOST_MYSQL_HAS_CONCEPTS
template <class T>
concept writable_field_tuple = is_writable_field_tuple<T>::value;
#define BOOST_MYSQL_WRITABLE_FIELD_TUPLE ::boost::mysql::detail::writable_field_tuple
#else // BOOST_MYSQL_HAS_CONCEPTS
#define BOOST_MYSQL_WRITABLE_FIELD_TUPLE class
#endif // BOOST_MYSQL_HAS_CONCEPTS
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+132
View File
@@ -0,0 +1,132 @@
//
// 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_DIAGNOSTICS_HPP
#define BOOST_MYSQL_DIAGNOSTICS_HPP
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <string>
namespace boost {
namespace mysql {
/**
* \brief Contains additional information about errors.
* \details
* This class is a container for additional diagnostics about an operation that
* failed. It can contain server-generated messages (\ref server_message) or client-side messages
* (\ref client_message). More members may be added in the future.
*/
class diagnostics
{
public:
/**
* \brief Constructs a diagnostics object with empty error messages.
* \par Exception safety
* No-throw guarantee.
*/
diagnostics() = default;
/**
* \brief Gets the client-generated error message.
* \details
* Contrary to \ref server_message, the client message never contains any string data
* returned by the server, and is always ASCII-encoded. If you're using the static interface,
* it may contain C++ type identifiers, too.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned view is valid as long as `*this` is alive, hasn't been assigned-to
* or moved-from, and \ref clear hasn't been called. Moving `*this` invalidates the view.
*/
string_view client_message() const noexcept
{
return impl_.is_server ? string_view() : string_view(impl_.msg);
}
/**
* \brief Gets the server-generated error message.
* \details
* It's encoded according to `character_set_results` character set, which
* usually matches the connection's character set. It may potentially contain user input.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned view is valid as long as `*this` is alive, hasn't been assigned-to
* or moved-from, and \ref clear hasn't been called. Moving `*this` invalidates the view.
*/
string_view server_message() const noexcept
{
return impl_.is_server ? string_view(impl_.msg) : string_view();
}
/**
* \brief Clears the error messages.
* \par Exception safety
* No-throw guarantee.
*/
void clear() noexcept
{
impl_.is_server = false;
impl_.msg.clear();
}
private:
#ifndef BOOST_MYSQL_DOXYGEN
struct
{
bool is_server{};
std::string msg;
void assign_client(std::string from)
{
msg = std::move(from);
is_server = false;
}
void assign_server(std::string from)
{
msg = std::move(from);
is_server = true;
}
} impl_;
friend bool operator==(const diagnostics& lhs, const diagnostics& rhs) noexcept;
friend struct detail::access;
#endif
};
/**
* \relates diagnostics
* \brief Compares two diagnostics objects.
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator==(const diagnostics& lhs, const diagnostics& rhs) noexcept
{
return lhs.impl_.is_server == rhs.impl_.is_server && lhs.impl_.msg == rhs.impl_.msg;
}
/**
* \relates diagnostics
* \brief Compares two diagnostics objects.
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator!=(const diagnostics& lhs, const diagnostics& rhs) noexcept { return !(lhs == rhs); }
} // namespace mysql
} // namespace boost
#endif
+85
View File
@@ -0,0 +1,85 @@
//
// 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_ERROR_CATEGORIES_HPP
#define BOOST_MYSQL_ERROR_CATEGORIES_HPP
#include <boost/mysql/detail/config.hpp>
#include <boost/system/error_category.hpp>
namespace boost {
namespace mysql {
/**
* \brief Returns the error_category associated to \ref client_errc.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is always valid (points to a singleton).
*
* \par Thread safety
* This function is thread-safe.
*/
BOOST_MYSQL_DECL
const boost::system::error_category& get_client_category() noexcept;
/**
* \brief Returns the error_category associated to \ref common_server_errc.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is always valid (points to a singleton).
*
* \par Thread safety
* This function is thread-safe.
*/
BOOST_MYSQL_DECL
const boost::system::error_category& get_common_server_category() noexcept;
/**
* \brief Returns the error_category associated to errors specific to MySQL.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is always valid (points to a singleton).
*
* \par Thread safety
* This function is thread-safe.
*/
BOOST_MYSQL_DECL
const boost::system::error_category& get_mysql_server_category() noexcept;
/**
* \brief Returns the error_category associated to errors specific to MariaDB.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is always valid (points to a singleton).
*
* \par Thread safety
* This function is thread-safe.
*/
BOOST_MYSQL_DECL
const boost::system::error_category& get_mariadb_server_category() noexcept;
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/error_categories.ipp>
#endif
#endif
+22
View File
@@ -0,0 +1,22 @@
//
// 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_ERROR_CODE_HPP
#define BOOST_MYSQL_ERROR_CODE_HPP
#include <boost/system/error_code.hpp>
namespace boost {
namespace mysql {
/// An alias for boost::system error codes.
using error_code = boost::system::error_code;
} // namespace mysql
} // namespace boost
#endif
+56
View File
@@ -0,0 +1,56 @@
//
// 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_ERROR_WITH_DIAGNOSTICS_HPP
#define BOOST_MYSQL_ERROR_WITH_DIAGNOSTICS_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/system/system_error.hpp>
namespace boost {
namespace mysql {
/**
* \brief A system_error with an embedded diagnostics object.
* \details
* Like `boost::system::system_error`, but adds a \ref diagnostics member
* containing additional information.
*/
class error_with_diagnostics : public boost::system::system_error
{
diagnostics diag_;
static boost::system::system_error create_base(const error_code& err, const diagnostics& diag)
{
return diag.client_message().empty() ? boost::system::system_error(err)
: boost::system::system_error(err, diag.client_message());
}
public:
/// Initializing constructor.
error_with_diagnostics(const error_code& err, const diagnostics& diag)
: boost::system::system_error(create_base(err, diag)), diag_(diag)
{
}
/**
* \brief Retrieves the server diagnostics embedded in this object.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive.
*/
const diagnostics& get_diagnostics() const noexcept { return diag_; }
};
} // namespace mysql
} // namespace boost
#endif
+221
View File
@@ -0,0 +1,221 @@
//
// 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_EXECUTION_STATE_HPP
#define BOOST_MYSQL_EXECUTION_STATE_HPP
#include <boost/mysql/metadata_collection_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/execution_processor/execution_state_impl.hpp>
#include <cstddef>
#include <cstdint>
namespace boost {
namespace mysql {
/**
* \brief Holds state for multi-function SQL execution operations (dynamic interface).
* \details
* This class behaves like a state machine. The current state can be accessed using
* \ref should_start_op, \ref should_read_rows, \ref should_read_head
* and \ref complete. They are mutually exclusive.
* More states may be added in the future as more protocol features are implemented.
*
* \par Thread safety
* Distinct objects: safe. \n
* Shared objects: unsafe.
*/
class execution_state
{
public:
/**
* \brief Default constructor.
* \details The constructed object is guaranteed to have
* `should_start_op() == true`.
*
* \par Exception safety
* No-throw guarantee.
*/
execution_state() = default;
/**
* \brief Copy constructor.
* \par Exception safety
* Strong guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* `*this` lifetime will be independent of `other`'s.
*/
execution_state(const execution_state& other) = default;
/**
* \brief Move constructor.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Views obtained from `other` remain valid.
*/
execution_state(execution_state&& other) = default;
/**
* \brief Copy assignment.
* \par Exception safety
* Basic guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* `*this` lifetime will be independent of `other`'s. Views obtained from `*this`
* are invalidated.
*/
execution_state& operator=(const execution_state& other) = default;
/**
* \brief Move assignment.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Views obtained from `*this` are invalidated. Views obtained from `other` remain valid.
*/
execution_state& operator=(execution_state&& other) = default;
/**
* \brief Returns whether `*this` is in the initial state.
* \details
* Call \ref connection::start_execution or \ref connection::async_start_execution to move
* forward. No data is available in this state.
*
* \par Exception safety
* No-throw guarantee.
*/
bool should_start_op() const noexcept { return impl_.is_reading_first(); }
/**
* \brief Returns whether the next operation should be read resultset head.
* \details
* Call \ref connection::read_resultset_head or its async counterpart to move forward.
* Metadata and OK data for the previous resultset is available in this state.
*
* \par Exception safety
* No-throw guarantee.
*/
bool should_read_head() const noexcept { return impl_.is_reading_first_subseq(); }
/**
* \brief Returns whether the next operation should be read some rows.
* \details
* Call \ref connection::read_some_rows or its async counterpart to move forward.
* Metadata for the current resultset is available in this state.
*
* \par Exception safety
* No-throw guarantee.
*/
bool should_read_rows() const noexcept { return impl_.is_reading_rows(); }
/**
* \brief Returns whether all the messages generated by this operation have been read.
* \details
* No further network calls are required to move forward. Metadata and OK data for the last
* resultset are available in this state.
*
* \par Exception safety
* No-throw guarantee.
*/
bool complete() const noexcept { return impl_.is_complete(); }
/**
* \brief Returns metadata about the columns in the query.
* \details
* The returned collection will have as many \ref metadata objects as columns retrieved by
* the SQL query, and in the same order.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* This function returns a view object, with reference semantics. The returned view points into
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
* from `*this` are alive.
*/
metadata_collection_view meta() const noexcept { return impl_.meta(); }
/**
* \brief Returns the number of rows affected by the SQL statement associated to this resultset.
* \par Exception safety
* No-throw guarantee.
*
* \par Preconditions
* `this->complete() == true || this->should_read_head() == true`
*/
std::uint64_t affected_rows() const noexcept { return impl_.get_affected_rows(); }
/**
* \brief Returns the last insert ID produced by the SQL statement associated to this resultset.
* \par Exception safety
* No-throw guarantee.
*
* \par Preconditions
* `this->complete() == true || this->should_read_head() == true`
*/
std::uint64_t last_insert_id() const noexcept { return impl_.get_last_insert_id(); }
/**
* \brief Returns the number of warnings produced by the SQL statement associated to this resultset.
* \par Exception safety
* No-throw guarantee.
*
* \par Preconditions
* `this->complete() == true || this->should_read_head() == true`
*/
unsigned warning_count() const noexcept { return impl_.get_warning_count(); }
/**
* \brief Returns additional text information about this resultset.
* \details
* The format of this information is documented by MySQL <a
* href="https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html">here</a>.
* \n
* The returned string always uses ASCII encoding, regardless of the connection's character set.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Preconditions
* `this->complete() == true || this->should_read_head() == true`
*
* \par Object lifetimes
* This function returns a view object, with reference semantics. The returned view points into
* memory owned by `*this`, and will be valid as long as `*this` or an object move-constructed
* from `*this` are alive.
*/
string_view info() const noexcept { return impl_.get_info(); }
/**
* \brief Returns whether the current resultset represents a procedure OUT params.
* \par Preconditions
* `this->complete() == true || this->should_read_head() == true`
*
* \par Exception safety
* No-throw guarantee.
*/
bool is_out_params() const noexcept { return impl_.get_is_out_params(); }
private:
detail::execution_state_impl impl_;
#ifndef BOOST_MYSQL_DOXYGEN
friend struct detail::access;
#endif
};
} // namespace mysql
} // namespace boost
#endif
+959
View File
@@ -0,0 +1,959 @@
//
// 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_FIELD_HPP
#define BOOST_MYSQL_FIELD_HPP
#include <boost/mysql/blob.hpp>
#include <boost/mysql/field_kind.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/field_impl.hpp>
#include <boost/variant2/variant.hpp>
#include <cstddef>
#include <iosfwd>
#include <string>
#ifdef __cpp_lib_string_view
#include <string_view>
#endif
namespace boost {
namespace mysql {
/**
* \brief Variant-like class that can represent of any of the allowed database types.
* \details
* This is a regular variant-like class that can represent any of the types that MySQL allows. It
* has value semantics (as opposed to \ref field_view). Instances of this class are not created
* by the library. They should be created by the user, when the reference semantics of
* \ref field_view are not appropriate.
* \n
* Like a variant, at any point, a `field` always contains a value of
* certain type. You can query the type using \ref kind and the `is_xxx` functions
* like \ref is_int64. Use `as_xxx` and `get_xxx` for checked and unchecked value
* access, respectively. You can mutate a `field` by calling the assignment operator,
* or using the lvalue references returned by `as_xxx` and `get_xxx`.
*/
class field
{
public:
/**
* \brief Constructs a `field` holding NULL.
* \par Exception safety
* No-throw guarantee.
*/
field() = default;
/**
* \brief Copy constructor.
* \par Exception safety
* Strong guarantee. Internal allocations may throw.
*/
field(const field&) = default;
/**
* \brief Move constructor.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* All references into `other` are invalidated, including the ones obtained by calling
* get_xxx, as_xxx and \ref field::operator field_view().
*/
field(field&& other) = default;
/**
* \brief Copy assignment.
* \par Exception safety
* Basic guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(const field&) = default;
/**
* \brief Move assignment.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references to `*this` obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view(). All references into `other`
* are invalidated, including the ones obtained by calling get_xxx, as_xxx and
* \ref field::operator field_view().
*/
field& operator=(field&& other) = default;
/// Destructor.
~field() = default;
/**
* \brief Constructs a `field` holding NULL.
* \details
* Caution: `field(NULL)` will __NOT__ match this overload. It will try to construct
* a `string_view` from a NULL C string, causing undefined behavior.
*
* \par Exception safety
* No-throw guarantee.
*/
explicit field(std::nullptr_t) noexcept {}
/**
* \brief Constructs a `field` holding an `int64`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(signed char v) noexcept : repr_(std::int64_t(v)) {}
/// \copydoc field(signed char)
explicit field(short v) noexcept : repr_(std::int64_t(v)) {}
/// \copydoc field(signed char)
explicit field(int v) noexcept : repr_(std::int64_t(v)) {}
/// \copydoc field(signed char)
explicit field(long v) noexcept : repr_(std::int64_t(v)) {}
/// \copydoc field(signed char)
explicit field(long long v) noexcept : repr_(std::int64_t(v)) {}
/**
* \brief Constructs a `field` holding an `uint64`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(unsigned char v) noexcept : repr_(std::uint64_t(v)) {}
/// \copydoc field(unsigned char)
explicit field(unsigned short v) noexcept : repr_(std::uint64_t(v)) {}
/// \copydoc field(unsigned char)
explicit field(unsigned int v) noexcept : repr_(std::uint64_t(v)) {}
/// \copydoc field(unsigned char)
explicit field(unsigned long v) noexcept : repr_(std::uint64_t(v)) {}
/// \copydoc field(unsigned char)
explicit field(unsigned long long v) noexcept : repr_(std::uint64_t(v)) {}
/**
* \brief Constructs a `field` holding a string.
* \par Exception safety
* Strong guarantee. Internal allocations may throw.
*/
explicit field(const std::string& v) : repr_(v) {}
/**
* \brief Constructs a `field` holding a string.
* \details v is moved into an internal `std::string` object.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(std::string&& v) noexcept : repr_(std::move(v)) {}
/// \copydoc field(const std::string&)
explicit field(const char* v) : repr_(boost::variant2::in_place_type_t<std::string>(), v) {}
/// \copydoc field(const std::string&)
explicit field(string_view v) : repr_(boost::variant2::in_place_type_t<std::string>(), v) {}
#if defined(__cpp_lib_string_view) || defined(BOOST_MYSQL_DOXYGEN)
/// \copydoc field(const std::string&)
explicit field(std::string_view v) noexcept : repr_(boost::variant2::in_place_type_t<std::string>(), v) {}
#endif
/**
* \brief Constructs a `field` holding a `blob`.
* \details v is moved into an internal `blob` object.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(blob v) noexcept : repr_(std::move(v)) {}
/**
* \brief Constructs a `field` holding a `float`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(float v) noexcept : repr_(v) {}
/**
* \brief Constructs a `field` holding a `double`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(double v) noexcept : repr_(v) {}
/**
* \brief Constructs a `field` holding a `date`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(const date& v) noexcept : repr_(v) {}
/**
* \brief Constructs a `field` holding a `datetime`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(const datetime& v) noexcept : repr_(v) {}
/**
* \brief Constructs a `field` holding a `time`.
* \par Exception safety
* No-throw guarantee.
*/
explicit field(const time& v) noexcept : repr_(v) {}
/**
* \brief Constructs a `field` from a \ref field_view.
* \details The resulting `field` has the same kind and value as the original `field_view`.
*
* \par Exception safety
* Strong guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* The resulting `field` is guaranteed to be valid even after `v` becomes invalid.
*/
field(const field_view& v) { from_view(v); }
/**
* \brief Replaces `*this` with a `NULL`, changing the kind to `null` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(std::nullptr_t) noexcept
{
repr_.data.emplace<detail::field_impl::null_t>();
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `int64` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(signed char v) noexcept
{
repr_.data.emplace<std::int64_t>(v);
return *this;
}
/// \copydoc operator=(signed char)
field& operator=(short v) noexcept
{
repr_.data.emplace<std::int64_t>(v);
return *this;
}
/// \copydoc operator=(signed char)
field& operator=(int v) noexcept
{
repr_.data.emplace<std::int64_t>(v);
return *this;
}
/// \copydoc operator=(signed char)
field& operator=(long v) noexcept
{
repr_.data.emplace<std::int64_t>(v);
return *this;
}
/// \copydoc operator=(signed char)
field& operator=(long long v) noexcept
{
repr_.data.emplace<std::int64_t>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `uint64` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(unsigned char v) noexcept
{
repr_.data.emplace<std::uint64_t>(v);
return *this;
}
/// \copydoc operator=(unsigned char)
field& operator=(unsigned short v) noexcept
{
repr_.data.emplace<std::uint64_t>(v);
return *this;
}
/// \copydoc operator=(unsigned char)
field& operator=(unsigned int v) noexcept
{
repr_.data.emplace<std::uint64_t>(v);
return *this;
}
/// \copydoc operator=(unsigned char)
field& operator=(unsigned long v) noexcept
{
repr_.data.emplace<std::uint64_t>(v);
return *this;
}
/// \copydoc operator=(unsigned char)
field& operator=(unsigned long long v) noexcept
{
repr_.data.emplace<std::uint64_t>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `string` and destroying any previous
* contents.
*
* \par Exception safety
* Basic guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(const std::string& v)
{
repr_.data.emplace<std::string>(v);
return *this;
}
/// \copydoc operator=(const std::string&)
field& operator=(std::string&& v)
{
repr_.data.emplace<std::string>(std::move(v));
return *this;
}
/// \copydoc operator=(const std::string&)
field& operator=(const char* v)
{
repr_.data.emplace<std::string>(v);
return *this;
}
/// \copydoc operator=(const std::string&)
field& operator=(string_view v)
{
repr_.data.emplace<std::string>(v);
return *this;
}
#if defined(__cpp_lib_string_view) || defined(BOOST_MYSQL_DOXYGEN)
/// \copydoc operator=(const std::string&)
field& operator=(std::string_view v)
{
repr_.data.emplace<std::string>(v);
return *this;
}
#endif
/**
* \brief Replaces `*this` with `v`, changing the kind to `blob` and destroying any
* previous contents.
*
* \par Exception safety
* Basic guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(blob v)
{
repr_.data.emplace<blob>(std::move(v));
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `float_` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(float v) noexcept
{
repr_.data.emplace<float>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `double` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(double v) noexcept
{
repr_.data.emplace<double>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `date` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(const date& v) noexcept
{
repr_.data.emplace<date>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `datetime` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions,
* but not the ones obtained by \ref field::operator field_view().
*/
field& operator=(const datetime& v) noexcept
{
repr_.data.emplace<datetime>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `time` and destroying any
* previous contents.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Invalidates references obtained by as_xxx and get_xxx functions, but not
*/
field& operator=(const time& v) noexcept
{
repr_.data.emplace<time>(v);
return *this;
}
/**
* \brief Replaces `*this` with `v`, changing the kind to `v.kind()` and destroying any previous
* contents.
*
* \par Exception safety
* Basic guarantee. Internal allocations may throw.
*
* \par Object lifetimes
* Invalidates references to `*this` obtained by as_xxx and get_xxx functions, but not
* the ones obtained by \ref field::operator field_view().
*\n
* `*this` is guaranteed to be valid even after `v` becomes invalid.
*/
field& operator=(const field_view& v)
{
from_view(v);
return *this;
}
/**
* \brief Returns the type of the value this `field` is holding.
* \par Exception safety
* No-throw guarantee.
*/
field_kind kind() const noexcept { return repr_.kind(); }
/**
* \brief Returns whether this `field` is holding a `NULL` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_null() const noexcept { return kind() == field_kind::null; }
/**
* \brief Returns whether this `field` is holding a `int64` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_int64() const noexcept { return kind() == field_kind::int64; }
/**
* \brief Returns whether this `field` is holding a `uint64` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_uint64() const noexcept { return kind() == field_kind::uint64; }
/**
* \brief Returns whether this `field` is holding a string value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_string() const noexcept { return kind() == field_kind::string; }
/**
* \brief Returns whether this `field` is holding a blob value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_blob() const noexcept { return kind() == field_kind::blob; }
/**
* \brief Returns whether this `field` is holding a `float` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_float() const noexcept { return kind() == field_kind::float_; }
/**
* \brief Returns whether this `field` is holding a `double` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_double() const noexcept { return kind() == field_kind::double_; }
/**
* \brief Returns whether this `field` is holding a `date` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_date() const noexcept { return kind() == field_kind::date; }
/**
* \brief Returns whether this `field` is holding a `datetime` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_datetime() const noexcept { return kind() == field_kind::datetime; }
/**
* \brief Returns whether this `field` is holding a `time` value.
* \par Exception safety
* No-throw guarantee.
*/
bool is_time() const noexcept { return kind() == field_kind::time; }
/**
* \brief Retrieves a reference to the underlying `std::int64_t` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_int64()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const std::int64_t& as_int64() const { return repr_.as<std::int64_t>(); }
/**
* \brief Retrieves a reference to the underlying `std::uint64_t` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_uint64()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const std::uint64_t& as_uint64() const { return repr_.as<std::uint64_t>(); }
/**
* \brief Retrieves a reference to the underlying `std::string` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_string()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const std::string& as_string() const { return repr_.as<std::string>(); }
/**
* \brief Retrieves a reference to the underlying `blob` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_blob()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const blob& as_blob() const { return repr_.as<blob>(); }
/**
* \brief Retrieves a reference to the underlying `float` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_float()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const float& as_float() const { return repr_.as<float>(); }
/**
* \brief Retrieves a reference to the underlying `double` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_double()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const double& as_double() const { return repr_.as<double>(); }
/**
* \brief Retrieves a reference to the underlying `date` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_date()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const date& as_date() const { return repr_.as<date>(); }
/**
* \brief Retrieves a reference to the underlying `datetime` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_datetime()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const datetime& as_datetime() const { return repr_.as<datetime>(); }
/**
* \brief Retrieves a reference to the underlying `time` value or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_time()`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const time& as_time() const { return repr_.as<time>(); }
/// \copydoc as_int64
std::int64_t& as_int64() { return repr_.as<std::int64_t>(); }
/// \copydoc as_uint64
std::uint64_t& as_uint64() { return repr_.as<std::uint64_t>(); }
/// \copydoc as_string
std::string& as_string() { return repr_.as<std::string>(); }
/// \copydoc as_blob
blob& as_blob() { return repr_.as<blob>(); }
/// \copydoc as_float
float& as_float() { return repr_.as<float>(); }
/// \copydoc as_double
double& as_double() { return repr_.as<double>(); }
/// \copydoc as_date
date& as_date() { return repr_.as<date>(); }
/// \copydoc as_datetime
datetime& as_datetime() { return repr_.as<datetime>(); }
/// \copydoc as_time
time& as_time() { return repr_.as<time>(); }
/**
* \brief Retrieves a reference to the underlying `std::int64_t` value (unchecked access).
* \par Preconditions
* `this->is_int64() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const std::int64_t& get_int64() const noexcept { return repr_.get<std::int64_t>(); }
/**
* \brief Retrieves a reference to the underlying `std::uint64_t` value (unchecked access).
* \par Preconditions
* `this->is_uint64() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const std::uint64_t& get_uint64() const noexcept { return repr_.get<std::uint64_t>(); }
/**
* \brief Retrieves a reference to the underlying `std::string` value (unchecked access).
* \par Preconditions
* `this->is_string() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const std::string& get_string() const noexcept { return repr_.get<std::string>(); }
/**
* \brief Retrieves a reference to the underlying `blob` value (unchecked access).
* \par Preconditions
* `this->is_blob() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const blob& get_blob() const noexcept { return repr_.get<blob>(); }
/**
* \brief Retrieves a reference to the underlying `float` value (unchecked access).
* \par Preconditions
* `this->is_float() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const float& get_float() const noexcept { return repr_.get<float>(); }
/**
* \brief Retrieves a reference to the underlying `double` value (unchecked access).
* \par Preconditions
* `this->is_double() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const double& get_double() const noexcept { return repr_.get<double>(); }
/**
* \brief Retrieves a reference to the underlying `date` value (unchecked access).
* \par Preconditions
* `this->is_date() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const date& get_date() const noexcept { return repr_.get<date>(); }
/**
* \brief Retrieves a reference to the underlying `datetime` value (unchecked access).
* \par Preconditions
* `this->is_datetime() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const datetime& get_datetime() const noexcept { return repr_.get<datetime>(); }
/**
* \brief Retrieves a reference to the underlying `time` value (unchecked access).
* \par Preconditions
* `this->is_time() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive and no function that invalidates
* references is called on `*this`.
*/
const time& get_time() const noexcept { return repr_.get<time>(); }
/// \copydoc get_int64
std::int64_t& get_int64() noexcept { return repr_.get<std::int64_t>(); }
/// \copydoc get_uint64
std::uint64_t& get_uint64() noexcept { return repr_.get<std::uint64_t>(); }
/// \copydoc get_string
std::string& get_string() noexcept { return repr_.get<std::string>(); }
/// \copydoc get_blob
blob& get_blob() noexcept { return repr_.get<blob>(); }
/// \copydoc get_float
float& get_float() noexcept { return repr_.get<float>(); }
/// \copydoc get_double
double& get_double() noexcept { return repr_.get<double>(); }
/// \copydoc get_date
date& get_date() noexcept { return repr_.get<date>(); }
/// \copydoc get_datetime
datetime& get_datetime() noexcept { return repr_.get<datetime>(); }
/// \copydoc get_time
time& get_time() noexcept { return repr_.get<time>(); }
/**
* \brief Constructs a \ref field_view pointing to `*this`.
* \details The resulting `field_view` has the same kind and value as `*this`.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned object acts as a
* reference to `*this`, and will be valid as long as `*this` is alive.
*/
inline operator field_view() const noexcept { return field_view(&repr_); }
private:
detail::field_impl repr_;
BOOST_MYSQL_DECL
void from_view(const field_view& v);
};
/**
* \relates field
* \brief Tests for equality.
* \details The same considerations as \ref field_view::operator== apply.
*
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator==(const field& lhs, const field& rhs) noexcept
{
return field_view(lhs) == field_view(rhs);
}
/**
* \relates field
* \brief Tests for inequality.
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator!=(const field& lhs, const field& rhs) noexcept { return !(lhs == rhs); }
/**
* \relates field
* \brief Tests for equality.
* \details The same considerations as \ref field_view::operator== apply.
*
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator==(const field_view& lhs, const field& rhs) noexcept { return lhs == field_view(rhs); }
/**
* \relates field
* \brief Tests for inequality.
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator!=(const field_view& lhs, const field& rhs) noexcept { return !(lhs == rhs); }
/**
* \relates field
* \brief Tests for equality.
* \details The same considerations as \ref field_view::operator== apply.
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator==(const field& lhs, const field_view& rhs) noexcept { return field_view(lhs) == rhs; }
/**
* \relates field
* \brief Tests for inequality.
* \par Exception safety
* No-throw guarantee.
*/
inline bool operator!=(const field& lhs, const field_view& rhs) noexcept { return !(lhs == rhs); }
/**
* \relates field
* \brief Streams a `field`.
*/
BOOST_MYSQL_DECL
std::ostream& operator<<(std::ostream& os, const field& v);
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/field.ipp>
#endif
#endif
+71
View File
@@ -0,0 +1,71 @@
//
// 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_FIELD_KIND_HPP
#define BOOST_MYSQL_FIELD_KIND_HPP
#include <boost/mysql/detail/config.hpp>
#include <iosfwd>
namespace boost {
namespace mysql {
/**
* \brief Represents the possible C++ types a `field` or `field_view` may have.
*/
enum class field_kind
{
// Order here is important
/// Any of the below when the value is NULL
null = 0,
/// The field contains a `std::int64_t`.
int64,
/// The field contains a `std::uint64_t`.
uint64,
/// The field contains a string (`std::string` for `field` and `string_view` for
/// `field_view`).
string,
/// The field contains a binary string (\ref blob for `field` and \ref blob_view for
/// `field_view`).
blob,
/// The field contains a `float`.
float_,
/// The field contains a `double`.
double_,
/// The field contains a \ref date.
date,
/// The field contains a \ref datetime.
datetime,
/// The field contains a \ref time.
time
};
/**
* \brief Streams a field_kind.
*/
BOOST_MYSQL_DECL
std::ostream& operator<<(std::ostream& os, field_kind v);
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/field_kind.ipp>
#endif
#endif
+612
View File
@@ -0,0 +1,612 @@
//
// 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_FIELD_VIEW_HPP
#define BOOST_MYSQL_FIELD_VIEW_HPP
#include <boost/mysql/blob_view.hpp>
#include <boost/mysql/date.hpp>
#include <boost/mysql/datetime.hpp>
#include <boost/mysql/field_kind.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/time.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/field_impl.hpp>
#include <boost/mysql/detail/string_view_offset.hpp>
#include <boost/config.hpp>
#include <cstddef>
#include <cstdint>
#include <iosfwd>
namespace boost {
namespace mysql {
/**
* \brief Non-owning variant-like class that can represent of any of the allowed database types.
* \details
* This is a variant-like class, similar to \ref field, but semi-owning and read-only. Values
* of this type are usually created by the library, not directly by the user. It's cheap to
* construct and copy, and it's the main library interface when reading values from MySQL.
* \n
* Like a variant, at any point, a `field_view` always points to a value of
* certain type. You can query the type using \ref field_view::kind and the `is_xxx` functions
* like \ref field_view::is_int64. Use `as_xxx` and `get_xxx` for checked and unchecked value
* access, respectively. As opposed to \ref field, these functions return values instead of
* references.
*
* \par Object lifetimes
* Depending on how it was constructed, `field_view` can have value or reference semantics:
* \n
* \li If it was created by the library, the `field_view` will have an associated \ref row,
* \ref rows or \ref results object holding memory to which the `field_view` points. It will be valid as
* long as the memory allocated by that object is valid.
* \li If it was created from a \ref field (by calling `operator field_view`), the
* `field_view` acts as a reference to that `field` object, and will be valid as long as the
* `field` is.
* \li If it was created from a scalar (null, integral, floating point, date, datetime or time), the
* `field_view` has value semnatics and will always be valid.
* \li If it was created from a string or blob type, the `field_view` acts as a `string_view` or `blob_view`,
* and will be valid as long as the original string/blob is.
* \n
* Calling any member function on a `field_view` that has been invalidated results in undefined
* behavior.
*/
class field_view
{
public:
/**
* \brief Constructs a `field_view` holding NULL.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR field_view() = default;
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding NULL.
* \details
* Caution: `field_view(NULL)` will <b>not</b> match this overload. It will try to construct
* a `string_view` from a NULL C string, causing undefined behavior.
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(std::nullptr_t) noexcept {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding an `int64`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(signed char v) noexcept : impl_{std::int64_t(v)} {}
/// \copydoc field_view(signed char)
BOOST_CXX14_CONSTEXPR explicit field_view(short v) noexcept : impl_{std::int64_t(v)} {}
/// \copydoc field_view(signed char)
BOOST_CXX14_CONSTEXPR explicit field_view(int v) noexcept : impl_{std::int64_t(v)} {}
/// \copydoc field_view(signed char)
BOOST_CXX14_CONSTEXPR explicit field_view(long v) noexcept : impl_{std::int64_t(v)} {}
/// \copydoc field_view(signed char)
BOOST_CXX14_CONSTEXPR explicit field_view(long long v) noexcept : impl_{std::int64_t(v)} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a `uint64`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(unsigned char v) noexcept : impl_{std::uint64_t(v)} {}
/// \copydoc field_view(unsigned char)
BOOST_CXX14_CONSTEXPR explicit field_view(unsigned short v) noexcept : impl_{std::uint64_t(v)} {}
/// \copydoc field_view(unsigned char)
BOOST_CXX14_CONSTEXPR explicit field_view(unsigned int v) noexcept : impl_{std::uint64_t(v)} {}
/// \copydoc field_view(unsigned char)
BOOST_CXX14_CONSTEXPR explicit field_view(unsigned long v) noexcept : impl_{std::uint64_t(v)} {}
/// \copydoc field_view(unsigned char)
BOOST_CXX14_CONSTEXPR explicit field_view(unsigned long long v) noexcept : impl_{std::uint64_t(v)} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a string.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with reference semantics. It will
* be valid as long as the character buffer the `string_view` points to is valid.
*/
BOOST_CXX14_CONSTEXPR explicit field_view(string_view v) noexcept : impl_{v} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a blob.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with reference semantics. It will
* be valid as long as the character buffer the `blob_view` points to is valid.
*/
BOOST_CXX14_CONSTEXPR explicit field_view(blob_view v) noexcept : impl_{v} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a `float`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(float v) noexcept : impl_{v} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a `double`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(double v) noexcept : impl_{v} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a `date`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(const date& v) noexcept : impl_{v} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a `datetime`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(const datetime& v) noexcept : impl_{v} {}
/**
* \brief (EXPERIMENTAL) Constructs a `field_view` holding a `time`.
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* Results in a `field_view` with value semantics (always valid).
*/
BOOST_CXX14_CONSTEXPR explicit field_view(const time& v) noexcept : impl_{v} {}
/**
* \brief Returns the type of the value this `field_view` is pointing to.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline field_kind kind() const noexcept;
/**
* \brief Returns whether this `field_view` points to a `NULL` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_null() const noexcept { return kind() == field_kind::null; }
/**
* \brief Returns whether this `field_view` points to a `int64` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_int64() const noexcept { return kind() == field_kind::int64; }
/**
* \brief Returns whether this `field_view` points to a `uint64` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_uint64() const noexcept { return kind() == field_kind::uint64; }
/**
* \brief Returns whether this `field_view` points to a string value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_string() const noexcept { return kind() == field_kind::string; }
/**
* \brief Returns whether this `field_view` points to a binary blob.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_blob() const noexcept { return kind() == field_kind::blob; }
/**
* \brief Returns whether this `field_view` points to a `float` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_float() const noexcept { return kind() == field_kind::float_; }
/**
* \brief Returns whether this `field_view` points to a `double` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_double() const noexcept { return kind() == field_kind::double_; }
/**
* \brief Returns whether this `field_view` points to a `date` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_date() const noexcept { return kind() == field_kind::date; }
/**
* \brief Returns whether this `field_view` points to a `datetime` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_datetime() const noexcept { return kind() == field_kind::datetime; }
/**
* \brief Returns whether this `field_view` points to a `time` value.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool is_time() const noexcept { return kind() == field_kind::time; }
/**
* \brief Retrieves the underlying value as an `int64` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_int64()`
*/
BOOST_CXX14_CONSTEXPR inline std::int64_t as_int64() const;
/**
* \brief Retrieves the underlying value as an `uint64` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_uint64()`
*/
BOOST_CXX14_CONSTEXPR inline std::uint64_t as_uint64() const;
/**
* \brief Retrieves the underlying value as a string or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_string()`
* \par Object lifetimes
* The returned view has the same lifetime rules as `*this` (it's valid as long as `*this` is valid).
*/
BOOST_CXX14_CONSTEXPR inline string_view as_string() const;
/**
* \brief Retrieves the underlying value as a blob or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_blob()`
* \par Object lifetimes
* The returned view has the same lifetime rules as `*this` (it's valid as long as `*this` is valid).
*/
BOOST_CXX14_CONSTEXPR inline blob_view as_blob() const;
/**
* \brief Retrieves the underlying value as a `float` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_float()`
*/
BOOST_CXX14_CONSTEXPR inline float as_float() const;
/**
* \brief Retrieves the underlying value as a `double` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_double()`
*/
BOOST_CXX14_CONSTEXPR inline double as_double() const;
/**
* \brief Retrieves the underlying value as a `date` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_date()`
*/
BOOST_CXX14_CONSTEXPR inline date as_date() const;
/**
* \brief Retrieves the underlying value as a `datetime` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_datetime()`
*/
BOOST_CXX14_CONSTEXPR inline datetime as_datetime() const;
/**
* \brief Retrieves the underlying value as a `time` or throws an exception.
* \par Exception safety
* Strong guarantee. Throws on type mismatch.
* \throws bad_field_access If `!this->is_time()`
*/
BOOST_CXX14_CONSTEXPR inline time as_time() const;
/**
* \brief Retrieves the underlying value as an `int64` (unchecked access).
* \par Preconditions
* `this->is_int64() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline std::int64_t get_int64() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<std::int64_t>() : impl_.repr.int64;
}
/**
* \brief Retrieves the underlying value as an `uint64` (unchecked access).
* \par Preconditions
* `this->is_uint64() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline std::uint64_t get_uint64() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<std::uint64_t>() : impl_.repr.uint64;
}
/**
* \brief Retrieves the underlying value as a string (unchecked access).
* \par Preconditions
* `this->is_string() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned view has the same lifetime rules as `*this` (it's valid as long as `*this` is valid).
*/
BOOST_CXX14_CONSTEXPR inline string_view get_string() const noexcept
{
return is_field_ptr() ? string_view(impl_.repr.field_ptr->get<std::string>()) : impl_.repr.string;
}
/**
* \brief Retrieves the underlying value as a blob (unchecked access).
* \par Preconditions
* `this->is_blob() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned view has the same lifetime rules as `*this` (it's valid as long as `*this` is valid).
*/
BOOST_CXX14_CONSTEXPR inline blob_view get_blob() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<blob>() : impl_.repr.blob;
}
/**
* \brief Retrieves the underlying value as a `float` (unchecked access).
* \par Preconditions
* `this->is_float() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline float get_float() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<float>() : impl_.repr.float_;
}
/**
* \brief Retrieves the underlying value as a `double` (unchecked access).
* \par Preconditions
* `this->is_double() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline double get_double() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<double>() : impl_.repr.double_;
}
/**
* \brief Retrieves the underlying value as a `date` (unchecked access).
* \par Preconditions
* `this->is_date() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline date get_date() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<date>() : impl_.repr.date_;
}
/**
* \brief Retrieves the underlying value as a `datetime` (unchecked access).
* \par Preconditions
* `this->is_datetime() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline datetime get_datetime() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<datetime>() : impl_.repr.datetime_;
}
/**
* \brief Retrieves the underlying value as a `time` (unchecked access).
* \par Preconditions
* `this->is_time() == true` (if violated, results in undefined behavior).
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline time get_time() const noexcept
{
return is_field_ptr() ? impl_.repr.field_ptr->get<time>() : impl_.repr.time_;
}
/**
* \brief Tests for equality.
* \details
* If one of the operands is a `uint64` and the other a
* `int64`, and the values are equal, returns `true`. Otherwise, if the types are
* different, returns always `false` (`float` and `double` values are considered to be
* different between them). `NULL` values are equal to other `NULL` values.
*
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR inline bool operator==(const field_view& rhs) const noexcept;
/**
* \brief Tests for inequality.
* \par Exception safety
* No-throw guarantee.
*/
BOOST_CXX14_CONSTEXPR bool operator!=(const field_view& rhs) const noexcept { return !(*this == rhs); }
private:
BOOST_CXX14_CONSTEXPR explicit field_view(detail::string_view_offset v, bool is_blob) noexcept
: impl_{v, is_blob}
{
}
BOOST_CXX14_CONSTEXPR explicit field_view(const detail::field_impl* v) noexcept : impl_{v} {}
enum class internal_kind
{
null = 0,
int64,
uint64,
string,
blob,
float_,
double_,
date,
datetime,
time,
sv_offset_string,
sv_offset_blob,
field_ptr
};
union repr_t
{
std::int64_t int64;
std::uint64_t uint64;
string_view string;
blob_view blob;
float float_;
double double_;
date date_;
datetime datetime_;
time time_;
detail::string_view_offset sv_offset_;
const detail::field_impl* field_ptr;
BOOST_CXX14_CONSTEXPR repr_t() noexcept : int64{} {}
BOOST_CXX14_CONSTEXPR repr_t(std::int64_t v) noexcept : int64(v) {}
BOOST_CXX14_CONSTEXPR repr_t(std::uint64_t v) noexcept : uint64(v) {}
BOOST_CXX14_CONSTEXPR repr_t(string_view v) noexcept : string{v} {}
BOOST_CXX14_CONSTEXPR repr_t(blob_view v) noexcept : blob{v} {}
BOOST_CXX14_CONSTEXPR repr_t(float v) noexcept : float_(v) {}
BOOST_CXX14_CONSTEXPR repr_t(double v) noexcept : double_(v) {}
BOOST_CXX14_CONSTEXPR repr_t(date v) noexcept : date_(v) {}
BOOST_CXX14_CONSTEXPR repr_t(datetime v) noexcept : datetime_(v) {}
BOOST_CXX14_CONSTEXPR repr_t(time v) noexcept : time_(v) {}
BOOST_CXX14_CONSTEXPR repr_t(detail::string_view_offset v) noexcept : sv_offset_(v) {}
BOOST_CXX14_CONSTEXPR repr_t(const detail::field_impl* v) noexcept : field_ptr(v) {}
};
struct impl_t
{
internal_kind ikind{internal_kind::null};
repr_t repr{};
// Required by lib internal functions
bool is_string_offset() const noexcept { return ikind == internal_kind::sv_offset_string; }
bool is_blob_offset() const noexcept { return ikind == internal_kind::sv_offset_blob; }
BOOST_CXX14_CONSTEXPR impl_t() = default;
BOOST_CXX14_CONSTEXPR impl_t(std::int64_t v) noexcept : ikind(internal_kind::int64), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(std::uint64_t v) noexcept : ikind(internal_kind::uint64), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(string_view v) noexcept : ikind(internal_kind::string), repr{v} {}
BOOST_CXX14_CONSTEXPR impl_t(blob_view v) noexcept : ikind(internal_kind::blob), repr{v} {}
BOOST_CXX14_CONSTEXPR impl_t(float v) noexcept : ikind(internal_kind::float_), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(double v) noexcept : ikind(internal_kind::double_), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(date v) noexcept : ikind(internal_kind::date), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(datetime v) noexcept : ikind(internal_kind::datetime), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(time v) noexcept : ikind(internal_kind::time), repr(v) {}
BOOST_CXX14_CONSTEXPR impl_t(detail::string_view_offset v, bool is_blob) noexcept
: ikind(is_blob ? internal_kind::sv_offset_blob : internal_kind::sv_offset_string), repr{v}
{
}
BOOST_CXX14_CONSTEXPR impl_t(const detail::field_impl* v) noexcept
: ikind(internal_kind::field_ptr), repr(v)
{
}
} impl_;
BOOST_CXX14_CONSTEXPR bool is_field_ptr() const noexcept
{
return impl_.ikind == internal_kind::field_ptr;
}
BOOST_CXX14_CONSTEXPR inline void check_kind(internal_kind expected) const;
#ifndef BOOST_MYSQL_DOXYGEN
friend class field;
friend struct detail::access;
BOOST_MYSQL_DECL
friend std::ostream& operator<<(std::ostream& os, const field_view& v);
#endif
};
/**
* \relates field_view
* \brief Streams a `field_view`.
*/
BOOST_MYSQL_DECL
std::ostream& operator<<(std::ostream& os, const field_view& v);
} // namespace mysql
} // namespace boost
#include <boost/mysql/impl/field_view.hpp>
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/field_view.ipp>
#endif
#endif
+160
View File
@@ -0,0 +1,160 @@
//
// 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_HANDSHAKE_PARAMS_HPP
#define BOOST_MYSQL_HANDSHAKE_PARAMS_HPP
#include <boost/mysql/buffer_params.hpp>
#include <boost/mysql/ssl_mode.hpp>
#include <boost/mysql/string_view.hpp>
#include <cstdint>
namespace boost {
namespace mysql {
/**
* \brief Parameters defining how to perform the handshake with a MySQL server.
* \par Object lifetimes
* This object stores references to strings (like username and password), performing
* no copy of these values. Users are resposible for keeping them alive until required.
*/
class handshake_params
{
string_view username_;
string_view password_;
string_view database_;
std::uint16_t connection_collation_;
ssl_mode ssl_;
bool multi_queries_;
public:
/// The default collation to use with the connection (`utf8mb4_general_ci` on both MySQL and MariaDB).
static constexpr std::uint16_t default_collation = 45;
/**
* \brief Initializing constructor.
* \par Exception safety
* No-throw guarantee.
*
* \param username User name to authenticate as.
* \param password Password for that username, possibly empty.
* \param db Database name to use, or empty string for no database (this is the default).
* \param connection_col The ID of the collation to use for the connection.
* Impacts how text queries and prepared statements are interpreted. Defaults to
* `utf8mb4_general_ci` (see \ref default_collation), which is compatible with MySQL 5.x, 8.x and MariaDB.
* \param mode The \ref ssl_mode to use with this connection; ignored if
* the connection's `Stream` does not support SSL.
* \param multi_queries Whether to enable support for executing semicolon-separated
* queries using \ref connection::execute and \ref connection::start_execution. Disabled by default.
*/
handshake_params(
string_view username,
string_view password,
string_view db = "",
std::uint16_t connection_col = default_collation,
ssl_mode mode = ssl_mode::require,
bool multi_queries = false
)
: username_(username),
password_(password),
database_(db),
connection_collation_(connection_col),
ssl_(mode),
multi_queries_(multi_queries)
{
}
/**
* \brief Retrieves the username.
* \par Exception safety
* No-throw guarantee.
*/
string_view username() const noexcept { return username_; }
/**
* \brief Sets the username.
* \par Exception safety
* No-throw guarantee.
*/
void set_username(string_view value) noexcept { username_ = value; }
/**
* \brief Retrieves the password.
* \par Exception safety
* No-throw guarantee.
*/
string_view password() const noexcept { return password_; }
/**
* \brief Sets the password.
* \par Exception safety
* No-throw guarantee.
*/
void set_password(string_view value) noexcept { password_ = value; }
/**
* \brief Retrieves the database name to use when connecting.
* \par Exception safety
* No-throw guarantee.
*/
string_view database() const noexcept { return database_; }
/**
* \brief Sets the database name to use when connecting.
* \par Exception safety
* No-throw guarantee.
*/
void set_database(string_view value) noexcept { database_ = value; }
/**
* \brief Retrieves the connection collation.
* \par Exception safety
* No-throw guarantee.
*/
std::uint16_t connection_collation() const noexcept { return connection_collation_; }
/**
* \brief Sets the connection collation.
* \par Exception safety
* No-throw guarantee.
*/
void set_connection_collation(std::uint16_t value) noexcept { connection_collation_ = value; }
/**
* \brief Retrieves the SSL mode.
* \par Exception safety
* No-throw guarantee.
*/
ssl_mode ssl() const noexcept { return ssl_; }
/**
* \brief Sets the SSL mode.
* \par Exception safety
* No-throw guarantee.
*/
void set_ssl(ssl_mode value) noexcept { ssl_ = value; }
/**
* \brief Retrieves whether multi-query support is enabled.
* \par Exception safety
* No-throw guarantee.
*/
bool multi_queries() const noexcept { return multi_queries_; }
/**
* \brief Enables or disables support for the multi-query feature.
* \par Exception safety
* No-throw guarantee.
*/
void set_multi_queries(bool v) noexcept { multi_queries_ = v; }
};
} // namespace mysql
} // namespace boost
#endif
+20
View File
@@ -0,0 +1,20 @@
//
// 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_IMPL_ANY_STREAM_IMPL_IPP
#define BOOST_MYSQL_IMPL_ANY_STREAM_IMPL_IPP
#pragma once
#include <boost/mysql/detail/any_stream_impl.hpp>
#ifdef BOOST_MYSQL_SEPARATE_COMPILATION
template class boost::mysql::detail::any_stream_impl<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>>;
template class boost::mysql::detail::any_stream_impl<boost::asio::ip::tcp::socket>;
#endif
#endif
+54
View File
@@ -0,0 +1,54 @@
//
// 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_IMPL_CHANNEL_PTR_IPP
#define BOOST_MYSQL_IMPL_CHANNEL_PTR_IPP
#pragma once
#include <boost/mysql/detail/channel_ptr.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
boost::mysql::detail::channel_ptr::channel_ptr(std::size_t read_buff_size, std::unique_ptr<any_stream> stream)
: chan_(new channel(read_buff_size, std::move(stream)))
{
}
boost::mysql::detail::channel_ptr::channel_ptr(channel_ptr&& rhs) noexcept : chan_(std::move(rhs.chan_)) {}
boost::mysql::detail::channel_ptr& boost::mysql::detail::channel_ptr::operator=(channel_ptr&& rhs) noexcept
{
chan_ = std::move(rhs.chan_);
return *this;
}
boost::mysql::detail::channel_ptr::~channel_ptr() {}
boost::mysql::detail::any_stream& boost::mysql::detail::channel_ptr::get_stream() const
{
return chan_->stream();
}
boost::mysql::metadata_mode boost::mysql::detail::channel_ptr::meta_mode() const noexcept
{
return chan_->meta_mode();
}
void boost::mysql::detail::channel_ptr::set_meta_mode(metadata_mode v) noexcept { chan_->set_meta_mode(v); }
boost::mysql::diagnostics& boost::mysql::detail::channel_ptr::shared_diag() noexcept
{
return chan_->shared_diag();
}
std::vector<boost::mysql::field_view>& boost::mysql::detail::get_shared_fields(channel& chan) noexcept
{
return chan.shared_fields();
}
#endif
+49
View File
@@ -0,0 +1,49 @@
//
// 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_IMPL_COLUMN_TYPE_IPP
#define BOOST_MYSQL_IMPL_COLUMN_TYPE_IPP
#pragma once
#include <boost/mysql/column_type.hpp>
#include <ostream>
std::ostream& boost::mysql::operator<<(std::ostream& os, column_type t)
{
switch (t)
{
case column_type::tinyint: return os << "tinyint";
case column_type::smallint: return os << "smallint";
case column_type::mediumint: return os << "mediumint";
case column_type::int_: return os << "int_";
case column_type::bigint: return os << "bigint";
case column_type::float_: return os << "float_";
case column_type::double_: return os << "double_";
case column_type::decimal: return os << "decimal";
case column_type::bit: return os << "bit";
case column_type::year: return os << "year";
case column_type::time: return os << "time";
case column_type::date: return os << "date";
case column_type::datetime: return os << "datetime";
case column_type::timestamp: return os << "timestamp";
case column_type::char_: return os << "char_";
case column_type::varchar: return os << "varchar";
case column_type::binary: return os << "binary";
case column_type::varbinary: return os << "varbinary";
case column_type::text: return os << "text";
case column_type::blob: return os << "blob";
case column_type::enum_: return os << "enum_";
case column_type::set: return os << "set";
case column_type::json: return os << "json";
case column_type::geometry: return os << "geometry";
default: return os << "<unknown column type>";
}
}
#endif
+34
View File
@@ -0,0 +1,34 @@
//
// 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_IMPL_DATE_IPP
#define BOOST_MYSQL_IMPL_DATE_IPP
#pragma once
#include <boost/mysql/date.hpp>
#include <cstdio>
#include <ostream>
std::ostream& boost::mysql::operator<<(std::ostream& os, const date& value)
{
// Worst-case output is 14 chars, extra space just in case
char buffer[32]{};
snprintf(
buffer,
sizeof(buffer),
"%04u-%02u-%02u",
static_cast<unsigned>(value.year()),
static_cast<unsigned>(value.month()),
static_cast<unsigned>(value.day())
);
os << buffer;
return os;
}
#endif
+38
View File
@@ -0,0 +1,38 @@
//
// 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_IMPL_DATETIME_IPP
#define BOOST_MYSQL_IMPL_DATETIME_IPP
#pragma once
#include <boost/mysql/datetime.hpp>
#include <cstdio>
#include <ostream>
std::ostream& boost::mysql::operator<<(std::ostream& os, const datetime& value)
{
// Worst-case output is 37 chars, extra space just in case
char buffer[64]{};
snprintf(
buffer,
sizeof(buffer),
"%04u-%02u-%02u %02d:%02u:%02u.%06u",
static_cast<unsigned>(value.year()),
static_cast<unsigned>(value.month()),
static_cast<unsigned>(value.day()),
static_cast<unsigned>(value.hour()),
static_cast<unsigned>(value.minute()),
static_cast<unsigned>(value.second()),
static_cast<unsigned>(value.microsecond())
);
os << buffer;
return os;
}
#endif
+143
View File
@@ -0,0 +1,143 @@
//
// 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_IMPL_ERROR_CATEGORIES_IPP
#define BOOST_MYSQL_IMPL_ERROR_CATEGORIES_IPP
#pragma once
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/common_server_errc.hpp>
#include <boost/mysql/error_categories.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/error/server_error_to_string.hpp>
namespace boost {
namespace mysql {
namespace detail {
BOOST_MYSQL_STATIC_OR_INLINE
const char* error_to_string(client_errc error) noexcept
{
switch (error)
{
case client_errc::incomplete_message: return "An incomplete message was received from the server";
case client_errc::extra_bytes: return "Unexpected extra bytes at the end of a message were received";
case client_errc::sequence_number_mismatch: return "Mismatched sequence numbers";
case client_errc::server_unsupported:
return "The server does not support the minimum required capabilities to establish the "
"connection";
case client_errc::protocol_value_error:
return "An unexpected value was found in a server-received message";
case client_errc::unknown_auth_plugin:
return "The user employs an authentication plugin not known to this library";
case client_errc::auth_plugin_requires_ssl:
return "The authentication plugin requires the connection to use SSL";
case client_errc::wrong_num_params:
return "The number of parameters passed to the prepared statement does not match the "
"number of actual parameters";
case boost::mysql::client_errc::server_doesnt_support_ssl:
return "The connection is configured to require SSL, but the server doesn't allow SSL connections. "
"Configure SSL on your server or change your connection to not require SSL";
case boost::mysql::client_errc::metadata_check_failed:
return "The static interface detected a type mismatch between your declared row type and what the "
"server returned. Verify your type definitions.";
case boost::mysql::client_errc::num_resultsets_mismatch:
return "The static interface detected a mismatch between the number of resultsets passed as template "
"arguments to static_results<T1, T2...>/static_execution_state<T1, T2...> and the number of "
"results returned by server";
case boost::mysql::client_errc::static_row_parsing_error:
return "The static interface encountered an error when parsing a field into a C++ data structure.";
case boost::mysql::client_errc::row_type_mismatch:
return "The StaticRow type passed to read_some_rows does not correspond to the resultset type being "
"read";
default: return "<unknown MySQL client error>";
}
}
BOOST_MYSQL_STATIC_OR_INLINE
const char* error_to_string(common_server_errc v) noexcept
{
const char* res = detail::common_error_to_string(static_cast<int>(v));
return res ? res : "<unknown server error>";
}
class client_category final : public boost::system::error_category
{
public:
const char* name() const noexcept final override { return "mysql.client"; }
std::string message(int ev) const final override { return error_to_string(static_cast<client_errc>(ev)); }
};
class common_server_category final : public boost::system::error_category
{
public:
const char* name() const noexcept final override { return "mysql.common-server"; }
std::string message(int ev) const final override
{
return error_to_string(static_cast<common_server_errc>(ev));
}
};
class mysql_server_category final : public boost::system::error_category
{
public:
const char* name() const noexcept final override { return "mysql.mysql-server"; }
std::string message(int ev) const final override { return detail::mysql_error_to_string(ev); }
};
class mariadb_server_category final : public boost::system::error_category
{
public:
const char* name() const noexcept final override { return "mysql.mariadb-server"; }
std::string message(int ev) const final override { return detail::mariadb_error_to_string(ev); }
};
// Optimization, so that static initialization happens only once (reduces C++11 thread-safe initialization
// overhead)
struct all_categories
{
client_category client;
common_server_category common_server;
mysql_server_category mysql_server;
mariadb_server_category mariadb_server;
static const all_categories& get() noexcept
{
static all_categories res;
return res;
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
const boost::system::error_category& boost::mysql::get_client_category() noexcept
{
return detail::all_categories::get().client;
}
const boost::system::error_category& boost::mysql::get_common_server_category() noexcept
{
return detail::all_categories::get().common_server;
}
const boost::system::error_category& boost::mysql::get_mysql_server_category() noexcept
{
return detail::all_categories::get().mysql_server;
}
const boost::system::error_category& boost::mysql::get_mariadb_server_category() noexcept
{
return detail::all_categories::get().mariadb_server;
}
#endif
+77
View File
@@ -0,0 +1,77 @@
//
// 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_IMPL_EXECUTION_STATE_IMPL_IPP
#define BOOST_MYSQL_IMPL_EXECUTION_STATE_IMPL_IPP
#pragma once
#include <boost/mysql/detail/execution_processor/execution_state_impl.hpp>
#include <boost/mysql/detail/row_impl.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
void boost::mysql::detail::execution_state_impl::on_ok_packet_impl(const ok_view& pack)
{
eof_data_.has_value = true;
eof_data_.affected_rows = pack.affected_rows;
eof_data_.last_insert_id = pack.last_insert_id;
eof_data_.warnings = pack.warnings;
eof_data_.is_out_params = pack.is_out_params();
info_.assign(pack.info.begin(), pack.info.end());
}
void boost::mysql::detail::execution_state_impl::reset_impl() noexcept
{
meta_.clear();
eof_data_ = ok_data();
info_.clear();
}
boost::mysql::error_code boost::mysql::detail::execution_state_impl::
on_head_ok_packet_impl(const ok_view& pack, diagnostics&)
{
on_new_resultset();
on_ok_packet_impl(pack);
return error_code();
}
void boost::mysql::detail::execution_state_impl::on_num_meta_impl(std::size_t num_columns)
{
on_new_resultset();
meta_.reserve(num_columns);
}
boost::mysql::error_code boost::mysql::detail::execution_state_impl::
on_meta_impl(const coldef_view& coldef, bool, diagnostics&)
{
meta_.push_back(create_meta(coldef));
return error_code();
}
boost::mysql::error_code boost::mysql::detail::execution_state_impl::on_row_impl(
span<const std::uint8_t> msg,
const output_ref&,
std::vector<field_view>& fields
)
{
// add row storage
span<field_view> storage = add_fields(fields, meta_.size());
// deserialize the row
return deserialize_row(encoding(), msg, meta_, storage);
}
boost::mysql::error_code boost::mysql::detail::execution_state_impl::on_row_ok_packet_impl(const ok_view& pack
)
{
on_ok_packet_impl(pack);
return error_code();
}
#endif
+49
View File
@@ -0,0 +1,49 @@
//
// 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_IMPL_FIELD_IPP
#define BOOST_MYSQL_IMPL_FIELD_IPP
#pragma once
#include <boost/mysql/field.hpp>
#include <ostream>
namespace boost {
namespace mysql {
namespace detail {
inline blob to_blob(blob_view v) { return blob(v.data(), v.data() + v.size()); }
} // namespace detail
} // namespace mysql
} // namespace boost
void boost::mysql::field::from_view(const field_view& fv)
{
switch (fv.kind())
{
case field_kind::null: repr_.data.emplace<detail::field_impl::null_t>(); break;
case field_kind::int64: repr_.data.emplace<std::int64_t>(fv.get_int64()); break;
case field_kind::uint64: repr_.data.emplace<std::uint64_t>(fv.get_uint64()); break;
case field_kind::string: repr_.data.emplace<std::string>(fv.get_string()); break;
case field_kind::blob: repr_.data.emplace<blob>(detail::to_blob(fv.get_blob())); break;
case field_kind::float_: repr_.data.emplace<float>(fv.get_float()); break;
case field_kind::double_: repr_.data.emplace<double>(fv.get_double()); break;
case field_kind::date: repr_.data.emplace<date>(fv.get_date()); break;
case field_kind::datetime: repr_.data.emplace<datetime>(fv.get_datetime()); break;
case field_kind::time: repr_.data.emplace<time>(fv.get_time()); break;
}
}
std::ostream& boost::mysql::operator<<(std::ostream& os, const field& value)
{
return os << field_view(value);
}
#endif
+34
View File
@@ -0,0 +1,34 @@
//
// 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_IMPL_FIELD_KIND_IPP
#define BOOST_MYSQL_IMPL_FIELD_KIND_IPP
#pragma once
#include <boost/mysql/field_kind.hpp>
#include <ostream>
std::ostream& boost::mysql::operator<<(std::ostream& os, boost::mysql::field_kind v)
{
switch (v)
{
case field_kind::null: return os << "null";
case field_kind::int64: return os << "int64";
case field_kind::uint64: return os << "uint64";
case field_kind::string: return os << "string";
case field_kind::float_: return os << "float_";
case field_kind::double_: return os << "double_";
case field_kind::date: return os << "date";
case field_kind::datetime: return os << "datetime";
case field_kind::time: return os << "time";
default: return os << "<invalid>";
}
}
#endif
+185
View File
@@ -0,0 +1,185 @@
//
// 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_IMPL_FIELD_VIEW_HPP
#define BOOST_MYSQL_IMPL_FIELD_VIEW_HPP
#pragma once
#include <boost/mysql/bad_field_access.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <cstring>
#include <limits>
namespace boost {
namespace mysql {
namespace detail {
inline bool blobs_equal(blob_view b1, blob_view b2)
{
if (b1.size() != b2.size())
return false;
return b1.empty() || std::memcmp(b1.data(), b2.data(), b2.size()) == 0;
}
} // namespace detail
} // namespace mysql
} // namespace boost
BOOST_CXX14_CONSTEXPR inline boost::mysql::field_kind boost::mysql::field_view::kind() const noexcept
{
switch (impl_.ikind)
{
case internal_kind::null: return field_kind::null;
case internal_kind::int64: return field_kind::int64;
case internal_kind::uint64: return field_kind::uint64;
case internal_kind::string: return field_kind::string;
case internal_kind::blob: return field_kind::blob;
case internal_kind::float_: return field_kind::float_;
case internal_kind::double_: return field_kind::double_;
case internal_kind::date: return field_kind::date;
case internal_kind::datetime: return field_kind::datetime;
case internal_kind::time: return field_kind::time;
case internal_kind::field_ptr: return impl_.repr.field_ptr->kind();
// sv_offset values must be converted via offset_to_string_view before calling any other fn
default: return field_kind::null;
}
}
BOOST_CXX14_CONSTEXPR std::int64_t boost::mysql::field_view::as_int64() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<std::int64_t>();
check_kind(internal_kind::int64);
return impl_.repr.int64;
}
BOOST_CXX14_CONSTEXPR std::uint64_t boost::mysql::field_view::as_uint64() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<std::uint64_t>();
check_kind(internal_kind::uint64);
return impl_.repr.uint64;
}
BOOST_CXX14_CONSTEXPR boost::mysql::string_view boost::mysql::field_view::as_string() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<std::string>();
check_kind(internal_kind::string);
return impl_.repr.string;
}
BOOST_CXX14_CONSTEXPR boost::mysql::blob_view boost::mysql::field_view::as_blob() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<blob>();
check_kind(internal_kind::blob);
return impl_.repr.blob;
}
BOOST_CXX14_CONSTEXPR float boost::mysql::field_view::as_float() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<float>();
check_kind(internal_kind::float_);
return impl_.repr.float_;
}
BOOST_CXX14_CONSTEXPR double boost::mysql::field_view::as_double() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<double>();
check_kind(internal_kind::double_);
return impl_.repr.double_;
}
BOOST_CXX14_CONSTEXPR boost::mysql::date boost::mysql::field_view::as_date() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<date>();
check_kind(internal_kind::date);
return impl_.repr.date_;
}
BOOST_CXX14_CONSTEXPR boost::mysql::datetime boost::mysql::field_view::as_datetime() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<datetime>();
check_kind(internal_kind::datetime);
return impl_.repr.datetime_;
}
BOOST_CXX14_CONSTEXPR boost::mysql::time boost::mysql::field_view::as_time() const
{
if (is_field_ptr())
return impl_.repr.field_ptr->as<time>();
check_kind(internal_kind::time);
return impl_.repr.time_;
}
BOOST_CXX14_CONSTEXPR void boost::mysql::field_view::check_kind(internal_kind expected) const
{
if (impl_.ikind != expected)
BOOST_THROW_EXCEPTION(bad_field_access());
}
BOOST_CXX14_CONSTEXPR bool boost::mysql::field_view::operator==(const field_view& rhs) const noexcept
{
// Make operator== work for types not representable by field_kind
if (impl_.ikind == internal_kind::sv_offset_string || impl_.ikind == internal_kind::sv_offset_blob)
{
return rhs.impl_.ikind == impl_.ikind && impl_.repr.sv_offset_ == rhs.impl_.repr.sv_offset_;
}
auto k = kind(), rhs_k = rhs.kind();
switch (k)
{
case field_kind::null: return rhs_k == field_kind::null;
case field_kind::int64:
if (rhs_k == field_kind::int64)
return get_int64() == rhs.get_int64();
else if (rhs_k == field_kind::uint64)
{
std::int64_t this_val = get_int64();
if (this_val < 0)
return false;
else
return static_cast<std::uint64_t>(this_val) == rhs.get_uint64();
}
else
return false;
case field_kind::uint64:
if (rhs_k == field_kind::uint64)
return get_uint64() == rhs.get_uint64();
else if (rhs_k == field_kind::int64)
{
std::int64_t rhs_val = rhs.get_int64();
if (rhs_val < 0)
return false;
else
return static_cast<std::uint64_t>(rhs_val) == get_uint64();
}
else
return false;
case field_kind::string: return rhs_k == field_kind::string && get_string() == rhs.get_string();
case field_kind::blob:
return rhs_k == field_kind::blob && detail::blobs_equal(get_blob(), rhs.get_blob());
case field_kind::float_: return rhs_k == field_kind::float_ && get_float() == rhs.get_float();
case field_kind::double_: return rhs_k == field_kind::double_ && get_double() == rhs.get_double();
case field_kind::date: return rhs_k == field_kind::date && get_date() == rhs.get_date();
case field_kind::datetime: return rhs_k == field_kind::datetime && get_datetime() == rhs.get_datetime();
case field_kind::time: return rhs_k == field_kind::time && get_time() == rhs.get_time();
default: BOOST_ASSERT(false); return false;
}
}
#endif
+100
View File
@@ -0,0 +1,100 @@
//
// 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_IMPL_FIELD_VIEW_IPP
#define BOOST_MYSQL_IMPL_FIELD_VIEW_IPP
#pragma once
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <ostream>
namespace boost {
namespace mysql {
namespace detail {
BOOST_MYSQL_STATIC_OR_INLINE
std::ostream& print_blob(std::ostream& os, blob_view value)
{
if (value.empty())
return os << "{}";
char buffer[16]{};
os << "{ ";
for (std::size_t i = 0; i < value.size(); ++i)
{
if (i != 0)
os << ", ";
unsigned byte = value[i];
std::snprintf(buffer, sizeof(buffer), "0x%02x", byte);
os << buffer;
}
os << " }";
return os;
}
BOOST_MYSQL_STATIC_OR_INLINE
std::ostream& print_time(std::ostream& os, const boost::mysql::time& value)
{
// Worst-case output is 26 chars, extra space just in case
char buffer[64]{};
using namespace std::chrono;
const char* sign = value < microseconds(0) ? "-" : "";
auto num_micros = value % seconds(1);
auto num_secs = duration_cast<seconds>(value % minutes(1) - num_micros);
auto num_mins = duration_cast<minutes>(value % hours(1) - num_secs);
auto num_hours = duration_cast<hours>(value - num_mins);
snprintf(
buffer,
sizeof(buffer),
"%s%02d:%02u:%02u.%06u",
sign,
static_cast<int>(std::abs(num_hours.count())),
static_cast<unsigned>(std::abs(num_mins.count())),
static_cast<unsigned>(std::abs(num_secs.count())),
static_cast<unsigned>(std::abs(num_micros.count()))
);
os << buffer;
return os;
}
} // namespace detail
} // namespace mysql
} // namespace boost
std::ostream& boost::mysql::operator<<(std::ostream& os, const field_view& value)
{
// Make operator<< work for detail::string_view_offset types
if (value.impl_.is_string_offset() || value.impl_.is_blob_offset())
{
return os << "<sv_offset>";
}
switch (value.kind())
{
case field_kind::null: return os << "<NULL>";
case field_kind::int64: return os << value.get_int64();
case field_kind::uint64: return os << value.get_uint64();
case field_kind::string: return os << value.get_string();
case field_kind::blob: return detail::print_blob(os, value.get_blob());
case field_kind::float_: return os << value.get_float();
case field_kind::double_: return os << value.get_double();
case field_kind::date: return os << value.get_date();
case field_kind::datetime: return os << value.get_datetime();
case field_kind::time: return detail::print_time(os, value.get_time());
default: BOOST_ASSERT(false); return os;
}
}
#endif
+48
View File
@@ -0,0 +1,48 @@
//
// 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_IMPL_INTERNAL_AUTH_AUTH_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_AUTH_AUTH_HPP
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/core/span.hpp>
#include <vector>
namespace boost {
namespace mysql {
namespace detail {
struct auth_response
{
std::vector<std::uint8_t> data;
string_view plugin_name;
};
BOOST_ATTRIBUTE_NODISCARD
BOOST_MYSQL_DECL
error_code compute_auth_response(
string_view plugin_name,
string_view password,
span<const std::uint8_t> challenge,
bool use_ssl,
auth_response& output
);
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/internal/auth/auth.ipp>
#endif
#endif
+235
View File
@@ -0,0 +1,235 @@
//
// 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_IMPL_INTERNAL_AUTH_AUTH_IPP
#define BOOST_MYSQL_IMPL_INTERNAL_AUTH_AUTH_IPP
#include "boost/mysql/detail/config.hpp"
#pragma once
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/impl/internal/auth/auth.hpp>
#include <boost/mysql/impl/internal/make_string_view.hpp>
#include <algorithm>
#include <cstring>
#include <openssl/sha.h>
namespace boost {
namespace mysql {
namespace detail {
// mysql_native_password
// Authorization for this plugin is always challenge (nonce) -> response
// (hashed password).
BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t mnp_challenge_length = 20;
BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t mnp_response_length = 20;
// challenge must point to challenge_length bytes of data
// output must point to response_length bytes of data
// SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
BOOST_MYSQL_STATIC_OR_INLINE
void mnp_compute_auth_string(string_view password, const void* challenge, void* output)
{
// SHA1 (password)
using sha1_buffer = unsigned char[SHA_DIGEST_LENGTH];
sha1_buffer password_sha1;
SHA1(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha1);
// Add server challenge (salt)
unsigned char salted_buffer[mnp_challenge_length + SHA_DIGEST_LENGTH];
memcpy(salted_buffer, challenge, mnp_challenge_length);
SHA1(password_sha1, sizeof(password_sha1), salted_buffer + 20);
sha1_buffer salted_sha1;
SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1);
// XOR
static_assert(mnp_response_length == SHA_DIGEST_LENGTH, "Buffer size mismatch");
for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
{
static_cast<std::uint8_t*>(output)[i] = password_sha1[i] ^ salted_sha1[i];
}
}
BOOST_MYSQL_STATIC_OR_INLINE
error_code mnp_compute_response(
string_view password,
boost::span<const std::uint8_t> challenge,
bool, // use_ssl
std::vector<std::uint8_t>& output
)
{
// Check challenge size
if (challenge.size() != mnp_challenge_length)
{
return make_error_code(client_errc::protocol_value_error);
}
// Do the calculation
output.resize(mnp_response_length);
mnp_compute_auth_string(password, challenge.data(), output.data());
return error_code();
}
// caching_sha2_password
// Authorization for this plugin may be cleartext password or challenge/response.
// The server has a cache that uses when employing challenge/response. When
// the server sends a challenge of challenge_length bytes, we should send
// the password hashed with the challenge. The server may send a challenge
// equals to perform_full_auth, meaning it could not use the cache to
// complete the auth. In this case, we should just send the cleartext password.
// Doing the latter requires a SSL connection. It is possible to perform full
// auth without an SSL connection, but that requires the server public key,
// and we do not implement that.
BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t csha2p_challenge_length = 20;
BOOST_MYSQL_STATIC_IF_COMPILED constexpr std::size_t csha2p_response_length = 32;
// challenge must point to challenge_length bytes of data
// output must point to response_length bytes of data
BOOST_MYSQL_STATIC_OR_INLINE
void csha2p_compute_auth_string(string_view password, const void* challenge, void* output)
{
static_assert(csha2p_response_length == SHA256_DIGEST_LENGTH, "Buffer size mismatch");
// SHA(SHA(password_sha) concat challenge) XOR password_sha
// hash1 = SHA(pass)
using sha_buffer = std::uint8_t[csha2p_response_length];
sha_buffer password_sha;
SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha);
// SHA(password_sha) concat challenge = buffer
std::uint8_t buffer[csha2p_response_length + csha2p_challenge_length];
SHA256(password_sha, csha2p_response_length, buffer);
std::memcpy(buffer + csha2p_response_length, challenge, csha2p_challenge_length);
// SHA(SHA(password_sha) concat challenge) = SHA(buffer) = salted_password
sha_buffer salted_password;
SHA256(buffer, sizeof(buffer), salted_password);
// salted_password XOR password_sha
for (unsigned i = 0; i < csha2p_response_length; ++i)
{
static_cast<std::uint8_t*>(output)[i] = salted_password[i] ^ password_sha[i];
}
}
BOOST_MYSQL_STATIC_OR_INLINE
bool should_perform_full_auth(boost::span<const std::uint8_t> challenge) noexcept
{
// A challenge of "\4" means "perform full auth"
return challenge.size() == 1u && challenge[0] == 4;
}
BOOST_MYSQL_STATIC_OR_INLINE
error_code csha2p_compute_response(
string_view password,
boost::span<const std::uint8_t> challenge,
bool use_ssl,
std::vector<std::uint8_t>& output
)
{
if (should_perform_full_auth(challenge))
{
if (!use_ssl)
{
return make_error_code(client_errc::auth_plugin_requires_ssl);
}
output.assign(password.begin(), password.end());
output.push_back(0);
return error_code();
}
else
{
// Check challenge size
if (challenge.size() != csha2p_challenge_length)
{
return make_error_code(client_errc::protocol_value_error);
}
// Do the calculation
output.resize(csha2p_response_length);
csha2p_compute_auth_string(password, challenge.data(), output.data());
return error_code();
}
}
// top-level API
struct authentication_plugin
{
using calculator_signature = error_code (*)(
string_view password,
boost::span<const std::uint8_t> challenge,
bool use_ssl,
std::vector<std::uint8_t>& output
);
string_view name;
calculator_signature calculator;
};
BOOST_MYSQL_STATIC_IF_COMPILED
constexpr authentication_plugin all_authentication_plugins[] = {
{
make_string_view("mysql_native_password"),
&mnp_compute_response,
},
{
make_string_view("caching_sha2_password"),
&csha2p_compute_response,
},
};
BOOST_MYSQL_STATIC_OR_INLINE
const authentication_plugin* find_plugin(string_view name)
{
auto it = std::find_if(
std::begin(all_authentication_plugins),
std::end(all_authentication_plugins),
[name](const authentication_plugin& plugin) { return plugin.name == name; }
);
return it == std::end(all_authentication_plugins) ? nullptr : it;
}
} // namespace detail
} // namespace mysql
} // namespace boost
boost::mysql::error_code boost::mysql::detail::compute_auth_response(
string_view plugin_name,
string_view password,
span<const std::uint8_t> challenge,
bool use_ssl,
auth_response& output
)
{
const auto* plugin = find_plugin(plugin_name);
if (plugin)
{
output.plugin_name = plugin->name;
if (password.empty())
{
// Blank password: we should just return an empty auth string
output.data.clear();
return error_code();
}
else
{
return plugin->calculator(password, challenge, use_ssl, output.data);
}
}
else
{
return client_errc::unknown_auth_plugin;
}
}
#endif
+150
View File
@@ -0,0 +1,150 @@
//
// 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_IMPL_INTERNAL_CHANNEL_CHANNEL_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_CHANNEL_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata_mode.hpp>
#include <boost/mysql/detail/any_stream.hpp>
#include <boost/mysql/impl/internal/channel/message_reader.hpp>
#include <boost/mysql/impl/internal/channel/message_writer.hpp>
#include <boost/mysql/impl/internal/channel/write_message.hpp>
#include <boost/mysql/impl/internal/protocol/capabilities.hpp>
#include <boost/mysql/impl/internal/protocol/db_flavor.hpp>
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/async_result.hpp>
#include <cstddef>
#include <memory>
#include <utility>
#include <vector>
namespace boost {
namespace mysql {
namespace detail {
// Implements the message layer of the MySQL protocol
class channel
{
db_flavor flavor_{db_flavor::mysql};
capabilities current_caps_;
std::uint8_t shared_sequence_number_{};
diagnostics shared_diag_; // for async ops
std::vector<field_view> shared_fields_;
metadata_mode meta_mode_{metadata_mode::minimal};
message_reader reader_;
message_writer writer_;
std::unique_ptr<any_stream> stream_;
public:
channel(std::size_t read_buffer_size, std::unique_ptr<any_stream> stream)
: reader_(read_buffer_size), stream_(std::move(stream))
{
}
// Executor
using executor_type = asio::any_io_executor;
executor_type get_executor() { return stream_->get_executor(); }
// Reading
bool has_read_messages() const noexcept { return reader_.has_message(); }
span<const std::uint8_t> next_read_message(std::uint8_t& seqnum, error_code& err) noexcept
{
return reader_.get_next_message(seqnum, err);
}
void read_some(error_code& code) { read_some_messages(*stream_, reader_, code); }
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_read_some(CompletionToken&& token)
{
return async_read_some_messages(*stream_, reader_, std::forward<CompletionToken>(token));
}
span<const std::uint8_t> read_one(std::uint8_t& seqnum, error_code& ec)
{
return read_one_message(*stream_, reader_, seqnum, ec);
}
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, span<const std::uint8_t>)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, span<const std::uint8_t>))
async_read_one(std::uint8_t& seqnum, CompletionToken&& token)
{
return async_read_one_message(*stream_, reader_, seqnum, std::forward<CompletionToken>(token));
}
// Exposed for the sake of testing
std::size_t read_buffer_size() const noexcept { return reader_.buffer().size(); }
// Writing. serialize() gets all the required data into the write buffers so it can be written
template <class Serializable>
void serialize(const Serializable& message, std::uint8_t& sequence_number)
{
std::size_t size = message.get_size();
auto buff = writer_.prepare_buffer(size, sequence_number);
message.serialize(buff);
}
// Writes what has been set up by serialize()
void write(error_code& code) { write_message(*stream_, writer_, code); }
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_write(CompletionToken&& token)
{
return async_write_message(*stream_, writer_, std::forward<CompletionToken>(token));
}
// Capabilities
capabilities current_capabilities() const noexcept { return current_caps_; }
void set_current_capabilities(capabilities value) noexcept { current_caps_ = value; }
// DB flavor
db_flavor flavor() const noexcept { return flavor_; }
void set_flavor(db_flavor v) noexcept { flavor_ = v; }
void reset()
{
flavor_ = db_flavor::mysql;
current_caps_ = capabilities();
reset_sequence_number();
stream_->reset_ssl_active();
// Metadata mode does not get reset on handshake
}
// Internal buffer, diagnostics and sequence_number to help async ops
diagnostics& shared_diag() noexcept { return shared_diag_; }
std::uint8_t& shared_sequence_number() noexcept { return shared_sequence_number_; }
std::uint8_t& reset_sequence_number() noexcept { return shared_sequence_number_ = 0; }
std::vector<field_view>& shared_fields() noexcept { return shared_fields_; }
const std::vector<field_view>& shared_fields() const noexcept { return shared_fields_; }
// Metadata mode
metadata_mode meta_mode() const noexcept { return meta_mode_; }
void set_meta_mode(metadata_mode v) noexcept { meta_mode_ = v; }
// SSL
bool ssl_active() const noexcept { return stream_->ssl_active(); }
// Getting the underlying stream
any_stream& stream() noexcept { return *stream_; }
const any_stream& stream() const noexcept { return *stream_; }
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+92
View File
@@ -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_IMPL_INTERNAL_CHANNEL_MESSAGE_PARSER_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_MESSAGE_PARSER_HPP
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/read_buffer.hpp>
#include <boost/mysql/impl/internal/protocol/constants.hpp>
#include <cstddef>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
class message_parser
{
struct state_t
{
bool is_first_frame{true};
std::uint8_t seqnum_first{};
std::uint8_t seqnum_last{};
bool reading_header{true};
std::size_t remaining_bytes{0};
bool more_frames_follow{false};
bool has_seqnum_mismatch{false};
};
std::size_t max_frame_size_;
state_t state_;
public:
struct result
{
// whether it has a message or not
bool has_message{false};
// if !has_message, number of bytes required to parse the current message
std::size_t required_size{0};
// if has_message, the actual parsed message
struct message_t
{
std::uint8_t seqnum_first;
std::uint8_t seqnum_last;
std::size_t size;
bool has_seqnum_mismatch; // for multi-frame messages, set to true if an error mismatch
// happened
} message{};
void set_required_size(std::size_t size) noexcept
{
has_message = false;
required_size = size;
}
void set_message(const message_t& msg) noexcept
{
has_message = true;
message = msg;
}
};
// max_frame_size is configurable so tests run faster
message_parser(std::size_t max_frame_size = MAX_PACKET_SIZE) noexcept : max_frame_size_(max_frame_size){};
// Attempts to process a message from buff and puts it into msg.
// If a message is read, res.has_message == true, and res.message will be populated.
// Otherwise, res.required_size will contain
// the number of bytes needed to complete the message part we're parsing.
// Doesn't cause buffer reallocations, and doesn't change the contents
// of buff's reserved area.
BOOST_MYSQL_DECL
void parse_message(read_buffer& buff, result& res) noexcept;
};
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/internal/channel/message_parser.ipp>
#endif
#endif
+110
View File
@@ -0,0 +1,110 @@
//
// 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_IMPL_INTERNAL_CHANNEL_MESSAGE_PARSER_IPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_MESSAGE_PARSER_IPP
#pragma once
#include <boost/mysql/impl/internal/channel/message_parser.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
void boost::mysql::detail::message_parser::parse_message(read_buffer& buff, result& res) noexcept
{
while (true)
{
if (state_.reading_header)
{
// If there are not enough bytes to process a header, request more
if (buff.pending_size() < HEADER_SIZE)
{
res.set_required_size(HEADER_SIZE - buff.pending_size());
return;
}
// Mark the header as belonging to the current message
buff.move_to_current_message(HEADER_SIZE);
// Deserialize the header
auto header = deserialize_frame_header(
span<const std::uint8_t, frame_header_size>(buff.pending_first() - HEADER_SIZE, HEADER_SIZE)
);
// Process the sequence number
if (state_.is_first_frame)
{
state_.seqnum_first = header.sequence_number;
state_.seqnum_last = header.sequence_number;
}
else
{
std::uint8_t expected_seqnum = state_.seqnum_last + 1;
if (header.sequence_number != expected_seqnum)
{
state_.has_seqnum_mismatch = true;
}
state_.seqnum_last = expected_seqnum;
}
// Process the packet size
state_.remaining_bytes = header.size;
state_.more_frames_follow = (state_.remaining_bytes == max_frame_size_);
// We are done with the header
if (state_.is_first_frame)
{
// If it's the 1st frame, we can just move the header bytes to the reserved
// area, avoiding a big memmove
buff.move_to_reserved(HEADER_SIZE);
}
else
{
buff.remove_current_message_last(HEADER_SIZE);
}
state_.is_first_frame = false;
state_.reading_header = false;
}
if (!state_.reading_header)
{
// Get the number of bytes belonging to this message
std::size_t new_bytes = (std::min)(buff.pending_size(), state_.remaining_bytes);
// Mark them as belonging to the current message in the buffer
buff.move_to_current_message(new_bytes);
// Update remaining bytes
state_.remaining_bytes -= new_bytes;
if (state_.remaining_bytes == 0)
{
state_.reading_header = true;
}
else
{
res.set_required_size(state_.remaining_bytes);
return;
}
// If we've fully read a message, we're done
if (!state_.remaining_bytes && !state_.more_frames_follow)
{
std::size_t message_size = buff.current_message_size();
buff.move_to_reserved(message_size);
res.set_message({
state_.seqnum_first,
state_.seqnum_last,
message_size,
state_.has_seqnum_mismatch,
});
state_ = state_t();
return;
}
}
}
}
#endif
+282
View File
@@ -0,0 +1,282 @@
//
// 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_IMPL_INTERNAL_CHANNEL_MESSAGE_READER_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_MESSAGE_READER_HPP
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/any_stream.hpp>
#include <boost/mysql/impl/internal/channel/message_parser.hpp>
#include <boost/mysql/impl/internal/channel/read_buffer.hpp>
#include <boost/mysql/impl/internal/channel/valgrind.hpp>
#include <boost/mysql/impl/internal/protocol/constants.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/post.hpp>
#include <boost/assert.hpp>
#include <cstddef>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
class message_reader
{
public:
message_reader(std::size_t initial_buffer_size, std::size_t max_frame_size = MAX_PACKET_SIZE)
: buffer_(initial_buffer_size), parser_(max_frame_size)
{
}
bool has_message() const noexcept { return result_.has_message; }
span<const std::uint8_t> get_next_message(std::uint8_t& seqnum, error_code& ec) noexcept
{
{
BOOST_ASSERT(has_message());
if (result_.message.has_seqnum_mismatch || seqnum != result_.message.seqnum_first)
{
ec = make_error_code(client_errc::sequence_number_mismatch);
return {};
}
seqnum = result_.message.seqnum_last + 1;
span<const std::uint8_t> res(
buffer_.current_message_first() - result_.message.size,
result_.message.size
);
parse_message();
ec = error_code();
return res;
}
}
// Reads some messages from stream, until there is at least one
// or an error happens. On success, has_message() returns true
// and get_next_message() returns the parsed message.
// May relocate the buffer, modifying buffer_first().
// The reserved area bytes will be removed before the actual read.
void read_some(any_stream& stream, error_code& ec)
{
// If we already have a message, complete immediately
if (has_message())
{
ec = error_code();
return;
}
// Remove processed messages
buffer_.remove_reserved();
while (!has_message())
{
// If any previous process_message indicated that we need more
// buffer space, resize the buffer now
maybe_resize_buffer();
// Actually read bytes
std::size_t bytes_read = stream.read_some(free_area(), ec);
if (ec)
break;
valgrind_make_mem_defined(buffer_.free_first(), bytes_read);
// Process them
on_read_bytes(bytes_read);
}
}
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_read_some(any_stream& stream, CompletionToken&& token);
// Exposed for the sake of testing
read_buffer& buffer() noexcept { return buffer_; }
const read_buffer& buffer() const noexcept { return buffer_; }
private:
struct read_some_op;
read_buffer buffer_;
message_parser parser_;
message_parser::result result_;
void parse_message() { parser_.parse_message(buffer_, result_); }
void maybe_resize_buffer()
{
if (!result_.has_message)
{
buffer_.grow_to_fit(result_.required_size);
}
}
void on_read_bytes(size_t num_bytes)
{
buffer_.move_to_pending(num_bytes);
parse_message();
}
asio::mutable_buffer free_area() noexcept
{
auto res = buffer_.free_area();
return asio::mutable_buffer(res.data(), res.size());
}
};
struct boost::mysql::detail::message_reader::read_some_op : boost::asio::coroutine
{
message_reader& reader_;
any_stream& stream_;
read_some_op(message_reader& reader, any_stream& stream) noexcept : reader_(reader), stream_(stream) {}
template <class Self>
void operator()(Self& self, error_code ec = {}, std::size_t bytes_read = 0)
{
// Error handling
if (ec)
{
self.complete(ec);
return;
}
// Non-error path
BOOST_ASIO_CORO_REENTER(*this)
{
// If we already have a message, complete immediately
if (reader_.has_message())
{
BOOST_ASIO_CORO_YIELD boost::asio::post(stream_.get_executor(), std::move(self));
self.complete(error_code());
BOOST_ASIO_CORO_YIELD break;
}
// Remove processed messages
reader_.buffer_.remove_reserved();
while (!reader_.has_message())
{
// If any previous process_message indicated that we need more
// buffer space, resize the buffer now
reader_.maybe_resize_buffer();
// Actually read bytes
BOOST_ASIO_CORO_YIELD stream_.async_read_some(reader_.free_area(), std::move(self));
valgrind_make_mem_defined(reader_.buffer_.free_first(), bytes_read);
// Process them
reader_.on_read_bytes(bytes_read);
}
self.complete(error_code());
}
}
};
// Public interface
inline void read_some_messages(any_stream& stream, message_reader& reader, error_code& ec)
{
return reader.read_some(stream, ec);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_read_some_messages(any_stream& stream, message_reader& reader, CompletionToken&& token)
{
return reader.async_read_some(stream, std::forward<CompletionToken>(token));
}
// Equivalent to read_some + get_next_message
inline span<const std::uint8_t> read_one_message(
any_stream& stream,
message_reader& reader,
std::uint8_t& seqnum,
error_code& ec
)
{
read_some_messages(stream, reader, ec);
if (ec)
return {};
else
return reader.get_next_message(seqnum, ec);
}
struct read_one_message_op : boost::asio::coroutine
{
message_reader& reader_;
any_stream& stream_;
std::uint8_t& seqnum_;
read_one_message_op(message_reader& reader, any_stream& stream, std::uint8_t& seqnum)
: reader_(reader), stream_(stream), seqnum_(seqnum)
{
}
template <class Self>
void operator()(Self& self, error_code code = {})
{
// Error handling
if (code)
{
self.complete(code, span<const std::uint8_t>());
return;
}
// Non-error path
BOOST_ASIO_CORO_REENTER(*this)
{
BOOST_ASIO_CORO_YIELD reader_.async_read_some(stream_, std::move(self));
{
auto b = reader_.get_next_message(seqnum_, code);
self.complete(code, b);
}
}
}
};
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
CompletionToken,
void(boost::mysql::error_code, ::boost::span<const std::uint8_t>)
)
async_read_one_message(
any_stream& stream,
message_reader& reader,
std::uint8_t& seqnum,
CompletionToken&& token
)
{
return boost::asio::async_compose<CompletionToken, void(error_code, span<const std::uint8_t>)>(
read_one_message_op(reader, stream, seqnum),
token,
stream
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(::boost::mysql::error_code))
boost::mysql::detail::message_reader::async_read_some(any_stream& stream, CompletionToken&& token)
{
return boost::asio::async_compose<CompletionToken, void(error_code)>(
read_some_op{*this, stream},
token,
stream
);
}
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_STATIC_STRING_HPP_ */
+142
View File
@@ -0,0 +1,142 @@
//
// 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_IMPL_INTERNAL_CHANNEL_MESSAGE_WRITER_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_MESSAGE_WRITER_HPP
#include <boost/mysql/impl/internal/protocol/constants.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <array>
#include <cstddef>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
class chunk_processor
{
std::size_t first_{};
std::size_t last_{};
std::size_t remaining() const noexcept { return last_ - first_; }
public:
chunk_processor() = default;
void reset() noexcept { reset(0, 0); }
void reset(std::size_t first, std::size_t last) noexcept
{
BOOST_ASSERT(last >= first);
first_ = first;
last_ = last;
}
void on_bytes_written(std::size_t n) noexcept
{
BOOST_ASSERT(remaining() >= n);
first_ += n;
}
bool done() const noexcept { return first_ == last_; }
span<const std::uint8_t> get_chunk(const std::vector<std::uint8_t>& buff) const noexcept
{
BOOST_ASSERT(buff.size() >= last_);
return {buff.data() + first_, remaining()};
}
};
class message_writer
{
std::vector<std::uint8_t> buffer_;
std::size_t max_frame_size_;
std::uint8_t* seqnum_{nullptr};
chunk_processor chunk_;
std::size_t total_bytes_{};
std::size_t total_bytes_written_{};
bool should_send_empty_frame_{};
void process_header_write(std::uint32_t size_to_write, std::uint8_t seqnum, std::size_t buff_offset)
{
serialize_frame_header(
frame_header{size_to_write, seqnum},
span<std::uint8_t, frame_header_size>(buffer_.data() + buff_offset, frame_header_size)
);
}
std::uint8_t next_seqnum() noexcept { return (*seqnum_)++; }
void prepare_next_chunk()
{
if (should_send_empty_frame_)
{
process_header_write(0, next_seqnum(), 0);
chunk_.reset(0, HEADER_SIZE);
should_send_empty_frame_ = false;
}
else if (total_bytes_written_ < total_bytes_)
{
std::size_t offset = total_bytes_written_;
std::size_t remaining = total_bytes_ - total_bytes_written_;
std::size_t size = (std::min)(max_frame_size_, remaining);
process_header_write(static_cast<std::uint32_t>(size), next_seqnum(), offset);
chunk_.reset(offset, offset + size + HEADER_SIZE);
if (remaining == max_frame_size_)
{
should_send_empty_frame_ = true;
}
total_bytes_written_ += size;
}
else
{
// We're done
chunk_.reset();
}
}
public:
message_writer(std::size_t max_frame_size = MAX_PACKET_SIZE) noexcept : max_frame_size_(max_frame_size) {}
span<std::uint8_t> prepare_buffer(std::size_t msg_size, std::uint8_t& seqnum)
{
buffer_.resize(msg_size + HEADER_SIZE);
total_bytes_ = msg_size;
total_bytes_written_ = 0;
should_send_empty_frame_ = msg_size == 0;
seqnum_ = &seqnum;
prepare_next_chunk();
return {buffer_.data() + HEADER_SIZE, msg_size};
}
bool done() const noexcept { return chunk_.done(); }
// This function returns an empty buffer to signal that we're done
span<const std::uint8_t> next_chunk() const
{
BOOST_ASSERT(!done());
return chunk_.get_chunk(buffer_);
}
void on_bytes_written(std::size_t n)
{
BOOST_ASSERT(!done());
// Acknowledge the written bytes
chunk_.on_bytes_written(n);
// Prepare the next chunk, if required
if (chunk_.done())
{
prepare_next_chunk();
}
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+133
View File
@@ -0,0 +1,133 @@
//
// 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_IMPL_INTERNAL_CHANNEL_READ_BUFFER_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_READ_BUFFER_HPP
#include <boost/assert.hpp>
#include <boost/core/span.hpp>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
namespace boost {
namespace mysql {
namespace detail {
// Custom buffer type optimized for read operations performed in the MySQL protocol.
// The buffer is a single, resizable chunk of memory with four areas:
// - Reserved area: messages that have already been read but are kept alive,
// either because we need them or because we haven't cleaned them yet.
// - Current message area: delimits the message we are currently parsing.
// - Pending bytes area: bytes we've read but haven't been parsed into a message yet.
// - Free area: free space for more bytes to be read.
class read_buffer
{
std::vector<std::uint8_t> buffer_;
std::size_t current_message_offset_{0};
std::size_t pending_offset_{0};
std::size_t free_offset_{0};
public:
read_buffer(std::size_t size) : buffer_(size, std::uint8_t(0)) { buffer_.resize(buffer_.capacity()); }
// Whole buffer accessors
const std::uint8_t* first() const noexcept { return buffer_.data(); }
std::size_t size() const noexcept { return buffer_.size(); }
// Area accessors
std::uint8_t* reserved_first() noexcept { return buffer_.data(); }
const std::uint8_t* reserved_first() const noexcept { return buffer_.data(); }
std::uint8_t* current_message_first() noexcept { return buffer_.data() + current_message_offset_; }
const std::uint8_t* current_message_first() const noexcept
{
return buffer_.data() + current_message_offset_;
}
std::uint8_t* pending_first() noexcept { return buffer_.data() + pending_offset_; }
const std::uint8_t* pending_first() const noexcept { return buffer_.data() + pending_offset_; }
std::uint8_t* free_first() noexcept { return buffer_.data() + free_offset_; }
const std::uint8_t* free_first() const noexcept { return buffer_.data() + free_offset_; }
std::size_t reserved_size() const noexcept { return current_message_offset_; }
std::size_t current_message_size() const noexcept { return pending_offset_ - current_message_offset_; }
std::size_t pending_size() const noexcept { return free_offset_ - pending_offset_; }
std::size_t free_size() const noexcept { return buffer_.size() - free_offset_; }
span<const std::uint8_t> reserved_area() const noexcept { return {reserved_first(), reserved_size()}; }
span<const std::uint8_t> current_message() const noexcept
{
return {current_message_first(), current_message_size()};
}
span<const std::uint8_t> pending_area() const noexcept { return {pending_first(), pending_size()}; }
span<std::uint8_t> free_area() noexcept { return {free_first(), free_size()}; }
// Moves n bytes from the free to the processing area (e.g. they've been read)
void move_to_pending(std::size_t length) noexcept
{
BOOST_ASSERT(length <= free_size());
free_offset_ += length;
}
// Moves n bytes from the processing to the current message area
void move_to_current_message(std::size_t length) noexcept
{
BOOST_ASSERT(length <= pending_size());
pending_offset_ += length;
}
// Removes the last length bytes from the current message area,
// effectively memmove'ing all subsequent bytes backwards.
// Used to remove intermediate headers. length must be > 0
void remove_current_message_last(std::size_t length) noexcept
{
BOOST_ASSERT(length <= current_message_size());
BOOST_ASSERT(length > 0);
std::memmove(pending_first() - length, pending_first(), pending_size());
pending_offset_ -= length;
free_offset_ -= length;
}
// Moves length bytes from the current message area to the reserved area
// Used to move entire parsed messages or message headers
void move_to_reserved(std::size_t length) noexcept
{
BOOST_ASSERT(length <= current_message_size());
current_message_offset_ += length;
}
// Removes the reserved area, effectively memmove'ing evth backwards
void remove_reserved() noexcept
{
if (reserved_size() > 0)
{
std::size_t currmsg_size = current_message_size();
std::size_t pend_size = pending_size();
std::memmove(buffer_.data(), current_message_first(), currmsg_size + pend_size);
current_message_offset_ = 0;
pending_offset_ = currmsg_size;
free_offset_ = currmsg_size + pend_size;
}
}
// Makes sure the free size is at least n bytes long; resizes the buffer if required
void grow_to_fit(std::size_t n)
{
if (free_size() < n)
{
buffer_.resize(buffer_.size() + n - free_size());
buffer_.resize(buffer_.capacity());
}
}
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_STATIC_STRING_HPP_ */
+38
View File
@@ -0,0 +1,38 @@
//
// 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_IMPL_INTERNAL_CHANNEL_VALGRIND_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_VALGRIND_HPP
#include <cstddef>
#ifdef BOOST_MYSQL_VALGRIND_TESTS
#include <valgrind/memcheck.h>
#endif
namespace boost {
namespace mysql {
namespace detail {
#ifdef BOOST_MYSQL_VALGRIND_TESTS
inline void valgrind_make_mem_defined(const void* data, std::size_t size)
{
VALGRIND_MAKE_MEM_DEFINED(data, size);
}
#else
inline void valgrind_make_mem_defined(const void*, std::size_t) noexcept {}
#endif
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_VALGRIND_HPP_ */
+97
View File
@@ -0,0 +1,97 @@
//
// 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_IMPL_INTERNAL_CHANNEL_WRITE_MESSAGE_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_CHANNEL_WRITE_MESSAGE_HPP
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/any_stream.hpp>
#include <boost/mysql/impl/internal/channel/message_writer.hpp>
#include <boost/mysql/impl/internal/protocol/constants.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/compose.hpp>
#include <boost/asio/coroutine.hpp>
#include <cstddef>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
// Writes an entire message to stream; partitions the message into
// chunks and adds the required headers
inline void write_message(any_stream& stream, message_writer& processor, error_code& ec)
{
while (!processor.done())
{
std::size_t bytes_written = stream.write_some(asio::buffer(processor.next_chunk()), ec);
if (ec)
break;
processor.on_bytes_written(bytes_written);
}
}
struct write_message_op : boost::asio::coroutine
{
any_stream& stream_;
message_writer& processor_;
write_message_op(any_stream& stream, message_writer& processor) noexcept
: stream_(stream), processor_(processor)
{
}
template <class Self>
void operator()(Self& self, error_code ec = {}, std::size_t bytes_written = 0)
{
// Error handling
if (ec)
{
self.complete(ec);
return;
}
// Non-error path
BOOST_ASIO_CORO_REENTER(*this)
{
// done() never returns false after a call to prepare_buffer(), so no post() needed
BOOST_ASSERT(!processor_.done());
while (!processor_.done())
{
BOOST_ASIO_CORO_YIELD stream_.async_write_some(
asio::buffer(processor_.next_chunk()),
std::move(self)
);
processor_.on_bytes_written(bytes_written);
};
self.complete(error_code());
}
}
};
template <BOOST_ASIO_COMPLETION_TOKEN_FOR(void(::boost::mysql::error_code)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
async_write_message(any_stream& stream, message_writer& processor, CompletionToken&& token)
{
return boost::asio::async_compose<CompletionToken, void(error_code)>(
write_message_op(stream, processor),
token,
stream
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,36 @@
//
// 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_IMPL_INTERNAL_ERROR_SERVER_ERROR_TO_STRING_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_ERROR_SERVER_ERROR_TO_STRING_HPP
#include <boost/mysql/detail/config.hpp>
namespace boost {
namespace mysql {
namespace detail {
// Returns NULL if this is not a common error (not a member of common_server_errc)
BOOST_MYSQL_DECL
const char* common_error_to_string(int v) noexcept;
// These return a default string if the error is not known
BOOST_MYSQL_DECL
const char* mysql_error_to_string(int v) noexcept;
BOOST_MYSQL_DECL
const char* mariadb_error_to_string(int v) noexcept;
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/internal/error/server_error_to_string.ipp>
#endif
#endif
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
//
// 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_IMPL_INTERNAL_MAKE_STRING_VIEW_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_MAKE_STRING_VIEW_HPP
#include <boost/mysql/string_view.hpp>
namespace boost {
namespace mysql {
namespace detail {
template <std::size_t N>
constexpr string_view make_string_view(const char (&buff)[N]) noexcept
{
static_assert(N >= 1, "Expected a C-array literal");
return string_view(buff, N - 1); // discard null terminator
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -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_IMPL_INTERNAL_NETWORK_ALGORITHMS_CLOSE_CONNECTION_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CLOSE_CONNECTION_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/network_algorithms/quit_connection.hpp>
#include <boost/asio/post.hpp>
namespace boost {
namespace mysql {
namespace detail {
struct close_connection_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
close_connection_op(channel& chan, diagnostics& diag) : chan_(chan), diag_(diag) {}
template <class Self>
void operator()(Self& self, error_code err = {})
{
error_code close_err;
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
if (!chan_.stream().is_open())
{
BOOST_ASIO_CORO_YIELD boost::asio::post(chan_.get_executor(), std::move(self));
self.complete(error_code());
BOOST_ASIO_CORO_YIELD break;
}
BOOST_ASIO_CORO_YIELD async_quit_connection_impl(chan_, diag_, std::move(self));
// We call close regardless of the quit outcome
chan_.stream().close(close_err);
self.complete(err ? err : close_err);
}
}
};
// Interface
inline void close_connection_impl(channel& chan, error_code& err, diagnostics& diag)
{
err.clear();
diag.clear();
// Close = quit + close stream. We close the stream regardless of the quit failing or not
if (chan.stream().is_open())
{
// MySQL quit notification
quit_connection_impl(chan, err, diag);
error_code close_err;
chan.stream().close(close_err);
if (!err)
{
err = close_err;
}
}
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_close_connection_impl(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_compose<CompletionToken, void(error_code)>(
close_connection_op{chan, diag},
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CLOSE_CONNECTION_HPP_ */
@@ -0,0 +1,61 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_CLOSE_STATEMENT_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CLOSE_STATEMENT_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <boost/asio/async_result.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline void compose_close_statement(channel& chan, const statement& stmt)
{
chan.serialize(close_stmt_command{stmt.id()}, chan.reset_sequence_number());
}
inline void close_statement_impl(channel& chan, const statement& stmt, error_code& err, diagnostics& diag)
{
err.clear();
diag.clear();
// Serialize the close message
compose_close_statement(chan, stmt);
// Send it. No response is sent back
chan.write(err);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_close_statement_impl(channel& chan, const statement& stmt, diagnostics& diag, CompletionToken&& token)
{
// We can do this here because we know no deferred tokens reach this function (thanks to erasing)
diag.clear();
// Serialize the close message
compose_close_statement(chan, stmt);
// Send it. No response is sent back
return chan.async_write(std::forward<CompletionToken>(token));
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CLOSE_STATEMENT_HPP_ */
@@ -0,0 +1,113 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_CONNECT_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_CONNECT_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/handshake_params.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/network_algorithms/handshake.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace mysql {
namespace detail {
struct connect_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
const void* ep_;
handshake_params params_;
connect_op(channel& chan, diagnostics& diag, const void* ep, const handshake_params& params)
: chan_(chan), diag_(diag), ep_(ep), params_(params)
{
}
template <class Self>
void operator()(Self& self, error_code code = {})
{
error_code ignored;
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
// Physical connect
BOOST_ASIO_CORO_YIELD chan_.stream().async_connect(ep_, std::move(self));
if (code)
{
chan_.stream().close(ignored);
self.complete(code);
BOOST_ASIO_CORO_YIELD break;
}
// Handshake
BOOST_ASIO_CORO_YIELD async_handshake_impl(chan_, params_, diag_, std::move(self));
if (code)
{
chan_.stream().close(ignored);
}
self.complete(code);
}
}
};
// External interface
inline void connect_impl(
channel& chan,
const void* endpoint,
const handshake_params& params,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
error_code ignored;
chan.stream().connect(endpoint, err);
if (err)
{
chan.stream().close(ignored);
return;
}
handshake_impl(chan, params, err, diag);
if (err)
{
chan.stream().close(ignored);
}
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_connect_impl(
channel& chan,
const void* endpoint,
const handshake_params& params,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_compose<CompletionToken, void(error_code)>(
connect_op{chan, diag, endpoint, params},
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_CONNECT_HPP_ */
@@ -0,0 +1,137 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_EXECUTE_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_EXECUTE_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/network_algorithms/read_resultset_head.hpp>
#include <boost/mysql/impl/internal/network_algorithms/read_some_rows.hpp>
#include <boost/mysql/impl/internal/network_algorithms/start_execution.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace mysql {
namespace detail {
struct execute_impl_op : boost::asio::coroutine
{
channel& chan_;
any_execution_request req_;
execution_processor& output_;
diagnostics& diag_;
execute_impl_op(
channel& chan,
const any_execution_request& req,
execution_processor& output,
diagnostics& diag
) noexcept
: chan_(chan), req_(req), output_(output), diag_(diag)
{
}
template <class Self>
void operator()(Self& self, error_code err = {}, std::size_t = 0)
{
// Error checking
if (err)
{
self.complete(err);
return;
}
// Normal path
BOOST_ASIO_CORO_REENTER(*this)
{
// Send request and read the first response
BOOST_ASIO_CORO_YIELD async_start_execution_impl(chan_, req_, output_, diag_, std::move(self));
// Read anything else
while (!output_.is_complete())
{
if (output_.is_reading_head())
{
BOOST_ASIO_CORO_YIELD
async_read_resultset_head_impl(chan_, output_, diag_, std::move(self));
}
else if (output_.is_reading_rows())
{
BOOST_ASIO_CORO_YIELD
async_read_some_rows_impl(chan_, output_, output_ref(), diag_, std::move(self));
}
}
self.complete(error_code());
}
}
};
// External interface
inline void execute_impl(
channel& channel,
const any_execution_request& req,
execution_processor& output,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
// Send request and read the first response
start_execution_impl(channel, req, output, err, diag);
if (err)
return;
// Read rows and anything else
while (!output.is_complete())
{
if (output.is_reading_head())
{
read_resultset_head_impl(channel, output, err, diag);
if (err)
return;
}
else if (output.is_reading_rows())
{
read_some_rows_impl(channel, output, output_ref(), err, diag);
if (err)
return;
}
}
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_execute_impl(
channel& chan,
const any_execution_request& req,
execution_processor& output,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_compose<CompletionToken, void(error_code)>(
execute_impl_op(chan, req, output, diag),
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,391 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_HANDSHAKE_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_HANDSHAKE_HPP
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/handshake_params.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/auth/auth.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/capabilities.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline capabilities conditional_capability(bool condition, std::uint32_t cap)
{
return capabilities(condition ? cap : 0);
}
inline error_code process_capabilities(
const handshake_params& params,
const server_hello& hello,
bool is_ssl_stream,
capabilities& negotiated_caps
)
{
auto ssl = params.ssl();
capabilities server_caps = hello.server_capabilities;
capabilities required_caps = mandatory_capabilities |
conditional_capability(!params.database().empty(), CLIENT_CONNECT_WITH_DB) |
conditional_capability(params.multi_queries(), CLIENT_MULTI_STATEMENTS) |
conditional_capability(
ssl == ssl_mode::require && is_ssl_stream,
CLIENT_SSL
);
if (required_caps.has(CLIENT_SSL) && !server_caps.has(CLIENT_SSL))
{
// This happens if the server doesn't have SSL configured. This special
// error code helps users diagnosing their problem a lot (server_unsupported doesn't).
return make_error_code(client_errc::server_doesnt_support_ssl);
}
else if (!server_caps.has_all(required_caps))
{
return make_error_code(client_errc::server_unsupported);
}
negotiated_caps = server_caps &
(required_caps | optional_capabilities |
conditional_capability(ssl == ssl_mode::enable && is_ssl_stream, CLIENT_SSL));
return error_code();
}
// When receiving an auth response from the server, several things can happen:
// - An OK packet. It means we are done with the auth phase. auth_result::complete.
// - An auth switch response. It means we should change the auth plugin,
// recalculate the auth response and send it back. auth_result::send_more_data.
// - An auth more data. Same as auth switch response, but without changing
// the authentication plugin. Also auth_result::send_more_data.
// - An auth more data with a challenge equals to fast_auth_complete_challenge.
// This means auth is complete and we should wait for an OK packet (auth_result::wait_for_ok).
// I have no clue why the server sends this instead of just an OK packet. It
// happens just for caching_sha2_password.
enum class auth_state
{
complete,
send_more_data,
wait_for_ok,
invalid
};
class handshake_processor
{
handshake_params params_;
diagnostics& diag_;
channel& channel_;
auth_response auth_resp_;
auth_state auth_state_{auth_state::invalid};
public:
handshake_processor(const handshake_params& params, diagnostics& diag, channel& channel)
: params_(params), diag_(diag), channel_(channel){};
const handshake_params& params() const noexcept { return params_; }
channel& get_channel() noexcept { return channel_; }
void clear_diagnostics() noexcept { diag_.clear(); }
// Once the handshake is processed, the capabilities are stored in the channel
bool use_ssl() const noexcept { return channel_.current_capabilities().has(CLIENT_SSL); }
// Initial greeting processing
error_code process_handshake(span<const std::uint8_t> buffer, bool is_ssl_stream)
{
// Deserialize server hello
server_hello hello{};
auto err = deserialize_server_hello(buffer, hello, diag_);
if (err)
return err;
// Check capabilities
capabilities negotiated_caps;
err = process_capabilities(params_, hello, is_ssl_stream, negotiated_caps);
if (err)
return err;
// Set capabilities & db flavor
channel_.set_current_capabilities(negotiated_caps);
channel_.set_flavor(hello.server);
// Compute auth response
return compute_auth_response(
hello.auth_plugin_name,
params_.password(),
hello.auth_plugin_data.to_span(),
use_ssl(),
auth_resp_
);
}
// Response to that initial greeting
void compose_ssl_request()
{
ssl_request sslreq{
channel_.current_capabilities(),
static_cast<std::uint32_t>(MAX_PACKET_SIZE),
params_.connection_collation(),
};
channel_.serialize(sslreq, channel_.shared_sequence_number());
}
void compose_login_request()
{
// Compose login request
login_request response{
channel_.current_capabilities(),
static_cast<std::uint32_t>(MAX_PACKET_SIZE),
params_.connection_collation(),
params_.username(),
auth_resp_.data,
params_.database(),
auth_resp_.plugin_name,
};
// Serialize
channel_.serialize(response, channel_.shared_sequence_number());
}
// Server handshake response
error_code process_handshake_server_response(span<const std::uint8_t> msg)
{
error_code err;
auto response = deserialize_handshake_server_response(msg, channel_.flavor(), diag_);
switch (response.type)
{
case handhake_server_response::type_t::ok:
// Auth success
auth_state_ = auth_state::complete;
return error_code();
case handhake_server_response::type_t::error: return response.data.err;
case handhake_server_response::type_t::auth_switch:
// Compute response
err = compute_auth_response(
response.data.auth_sw.plugin_name,
params_.password(),
response.data.auth_sw.auth_data,
use_ssl(),
auth_resp_
);
if (err)
return err;
// Serialize
channel_.serialize(auth_switch_response{auth_resp_.data}, channel_.shared_sequence_number());
auth_state_ = auth_state::send_more_data;
return error_code();
case handhake_server_response::type_t::ok_follows:
// The next packet will be an OK packet
auth_state_ = auth_state::wait_for_ok;
return error_code();
case handhake_server_response::type_t::auth_more_data:
// Compute response
err = compute_auth_response(
auth_resp_.plugin_name,
params_.password(),
response.data.more_data,
use_ssl(),
auth_resp_
);
if (err)
return err;
channel_.serialize(auth_switch_response{auth_resp_.data}, channel_.shared_sequence_number());
auth_state_ = auth_state::send_more_data;
return error_code();
default: BOOST_ASSERT(false); return error_code();
}
}
bool should_send_auth_switch_response() const noexcept
{
return auth_state_ == auth_state::send_more_data;
}
bool auth_complete() const noexcept { return auth_state_ == auth_state::complete; }
};
struct handshake_op : boost::asio::coroutine
{
handshake_processor processor_;
handshake_op(const handshake_params& params, diagnostics& diag, channel& channel)
: processor_(params, diag, channel)
{
}
channel& get_channel() noexcept { return processor_.get_channel(); }
template <class Self>
void operator()(Self& self, error_code err = {}, span<const std::uint8_t> read_msg = {})
{
// Error checking
if (err)
{
self.complete(err);
return;
}
// Non-error path
BOOST_ASIO_CORO_REENTER(*this)
{
processor_.clear_diagnostics();
// Setup the channel
get_channel().reset();
// Read server greeting
BOOST_ASIO_CORO_YIELD get_channel().async_read_one(
get_channel().shared_sequence_number(),
std::move(self)
);
// Process server greeting
err = processor_.process_handshake(read_msg, get_channel().stream().supports_ssl());
if (err)
{
self.complete(err);
BOOST_ASIO_CORO_YIELD break;
}
// SSL
if (processor_.use_ssl())
{
// Send SSL request
processor_.compose_ssl_request();
BOOST_ASIO_CORO_YIELD get_channel().async_write(std::move(self));
// SSL handshake
BOOST_ASIO_CORO_YIELD get_channel().stream().async_handshake(std::move(self));
}
// Compose and send handshake response
processor_.compose_login_request();
BOOST_ASIO_CORO_YIELD get_channel().async_write(std::move(self));
while (!processor_.auth_complete())
{
// Receive response
BOOST_ASIO_CORO_YIELD get_channel().async_read_one(
get_channel().shared_sequence_number(),
std::move(self)
);
// Process it
err = processor_.process_handshake_server_response(read_msg);
if (err)
{
self.complete(err);
BOOST_ASIO_CORO_YIELD break;
}
// We received an auth switch response and we have the response ready to be sent
if (processor_.should_send_auth_switch_response())
{
BOOST_ASIO_CORO_YIELD get_channel().async_write(std::move(self));
}
}
self.complete(error_code());
}
}
};
// External interface
inline void handshake_impl(
channel& channel,
const handshake_params& params,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
channel.reset();
// Set up processor
handshake_processor processor(params, diag, channel);
// Read server greeting
auto read_message = channel.read_one(channel.shared_sequence_number(), err);
if (err)
return;
// Process server greeting (handshake)
err = processor.process_handshake(read_message, channel.stream().supports_ssl());
if (err)
return;
// SSL
if (processor.use_ssl())
{
// Send SSL request
processor.compose_ssl_request();
channel.write(err);
if (err)
return;
// SSL handshake
channel.stream().handshake(err);
if (err)
return;
}
// Handshake response
processor.compose_login_request();
channel.write(err);
if (err)
return;
while (!processor.auth_complete())
{
// Receive response
read_message = channel.read_one(channel.shared_sequence_number(), err);
if (err)
return;
// Process it
err = processor.process_handshake_server_response(read_message);
if (err)
return;
if (processor.should_send_auth_switch_response())
{
// We received an auth switch request and we have the response ready to be sent
channel.write(err);
if (err)
return;
}
};
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_handshake_impl(
channel& chan,
const handshake_params& params,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_compose<CompletionToken, void(error_code)>(
handshake_op(params, diag, chan),
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+103
View File
@@ -0,0 +1,103 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_PING_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_PING_HPP
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline void serialize_ping_message(channel& chan)
{
chan.serialize(ping_command(), chan.reset_sequence_number());
}
struct ping_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
ping_op(channel& chan, diagnostics& diag) noexcept : chan_(chan), diag_(diag) {}
template <class Self>
void operator()(Self& self, error_code err = {}, span<const std::uint8_t> buff = {})
{
// Error checking
if (err)
{
self.complete(err);
return;
}
// Regular coroutine body; if there has been an error, we don't get here
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
// Serialize the message
serialize_ping_message(chan_);
// Write message
BOOST_ASIO_CORO_YIELD chan_.async_write(std::move(self));
// Read response
BOOST_ASIO_CORO_YIELD chan_.async_read_one(chan_.shared_sequence_number(), std::move(self));
// Verify it's what we expected
self.complete(deserialize_ok_response(buff, chan_.flavor(), diag_));
}
}
};
// Interface
inline void ping_impl(channel& chan, error_code& err, diagnostics& diag)
{
err.clear();
diag.clear();
// Serialize the message
serialize_ping_message(chan);
// Send it
chan.write(err);
if (err)
return;
// Read response
auto response = chan.read_one(chan.shared_sequence_number(), err);
if (err)
return;
// Verify it's what we expected
err = deserialize_ok_response(response, chan.flavor(), diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_ping_impl(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_compose<CompletionToken, void(error_code)>(ping_op(chan, diag), token, chan);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,209 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_PREPARE_STATEMENT_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_PREPARE_STATEMENT_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/access.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
class prepare_statement_processor
{
channel& channel_;
string_view stmt_sql_;
diagnostics& diag_;
statement res_;
unsigned remaining_meta_{};
public:
prepare_statement_processor(channel& chan, string_view stmt_sql, diagnostics& diag) noexcept
: channel_(chan), stmt_sql_(stmt_sql), diag_(diag)
{
}
void clear_diag() noexcept { diag_.clear(); }
void process_request()
{
channel_.serialize(prepare_stmt_command{stmt_sql_}, channel_.reset_sequence_number());
}
void process_response(span<const std::uint8_t> message, error_code& err)
{
prepare_stmt_response response{};
err = deserialize_prepare_stmt_response(message, channel_.flavor(), response, diag_);
if (err)
return;
res_ = access::construct<statement>(response.id, response.num_params);
remaining_meta_ = response.num_columns + response.num_params;
}
bool has_remaining_meta() const noexcept { return remaining_meta_ != 0; }
void on_meta_received() noexcept { --remaining_meta_; }
const statement& result() const noexcept { return res_; }
channel& get_channel() noexcept { return channel_; }
};
struct prepare_statement_op : boost::asio::coroutine
{
prepare_statement_processor processor_;
prepare_statement_op(channel& chan, string_view stmt_sql, diagnostics& diag)
: processor_(chan, stmt_sql, diag)
{
}
channel& get_channel() noexcept { return processor_.get_channel(); }
template <class Self>
void operator()(Self& self, error_code err = {}, span<const std::uint8_t> read_message = {})
{
// Error checking
if (err)
{
self.complete(err, statement());
return;
}
// Regular coroutine body; if there has been an error, we don't get here
BOOST_ASIO_CORO_REENTER(*this)
{
processor_.clear_diag();
// Serialize request
processor_.process_request();
// Write message
BOOST_ASIO_CORO_YIELD get_channel().async_write(std::move(self));
// Read response
BOOST_ASIO_CORO_YIELD get_channel().async_read_one(
get_channel().shared_sequence_number(),
std::move(self)
);
// Process response
processor_.process_response(read_message, err);
if (err)
{
self.complete(err, statement());
BOOST_ASIO_CORO_YIELD break;
}
// Server sends now one packet per parameter and field.
// We ignore these for now.
while (processor_.has_remaining_meta())
{
// Read from the stream if necessary
if (!get_channel().has_read_messages())
{
BOOST_ASIO_CORO_YIELD get_channel().async_read_some(std::move(self));
}
// Read the message
read_message = get_channel().next_read_message(get_channel().shared_sequence_number(), err);
if (err)
{
self.complete(err, statement());
BOOST_ASIO_CORO_YIELD break;
}
// Note it as processed
processor_.on_meta_received();
}
// Complete
self.complete(error_code(), processor_.result());
}
}
};
// External interface
inline statement prepare_statement_impl(
channel& channel,
string_view stmt_sql,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
prepare_statement_processor processor(channel, stmt_sql, diag);
// Prepare message
processor.process_request();
// Write message
channel.write(err);
if (err)
return statement();
// Read response
auto read_buffer = channel.read_one(channel.shared_sequence_number(), err);
if (err)
return statement();
// Process response
processor.process_response(read_buffer, err);
if (err)
return statement();
// Server sends now one packet per parameter and field.
// We ignore these for now.
while (processor.has_remaining_meta())
{
// Read from the stream if necessary
if (!channel.has_read_messages())
{
channel.read_some(err);
if (err)
return statement();
}
// Discard the message
channel.next_read_message(channel.shared_sequence_number(), err);
if (err)
return statement();
// Update the processor state
processor.on_meta_received();
}
return processor.result();
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, statement))
async_prepare_statement_impl(channel& chan, string_view stmt_sql, diagnostics& diag, CompletionToken&& token)
{
return asio::async_compose<CompletionToken, void(error_code, statement)>(
prepare_statement_op(chan, stmt_sql, diag),
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_PREPARE_STATEMENT_HPP_ */
@@ -0,0 +1,95 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_QUIT_CONNECTION_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_QUIT_CONNECTION_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline void compose_quit(channel& chan) { chan.serialize(quit_command(), chan.reset_sequence_number()); }
struct quit_connection_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
quit_connection_op(channel& chan, diagnostics& diag) noexcept : chan_(chan), diag_(diag) {}
template <class Self>
void operator()(Self& self, error_code err = {})
{
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
// Quit message
compose_quit(chan_);
BOOST_ASIO_CORO_YIELD chan_.async_write(std::move(self));
if (err)
{
self.complete(err);
}
// SSL shutdown error ignored, as MySQL doesn't always gracefully
// close SSL connections.
if (chan_.stream().ssl_active())
{
BOOST_ASIO_CORO_YIELD chan_.stream().async_shutdown(std::move(self));
}
self.complete(error_code());
}
}
};
// Interface
inline void quit_connection_impl(channel& chan, error_code& err, diagnostics& diag)
{
err.clear();
diag.clear();
compose_quit(chan);
chan.write(err);
if (err)
return;
if (chan.stream().ssl_active())
{
// SSL shutdown. Result ignored as MySQL does not always perform
// graceful SSL shutdowns
error_code ignored;
chan.stream().shutdown(ignored);
}
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_quit_connection_impl(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_compose<CompletionToken, void(error_code)>(
quit_connection_op(chan, diag),
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_QUIT_CONNECTION_HPP_ */
@@ -0,0 +1,204 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/detail/coldef_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline error_code process_execution_response(
channel& chan,
execution_processor& proc,
span<const std::uint8_t> msg,
diagnostics& diag
)
{
auto response = deserialize_execute_response(msg, chan.flavor(), diag);
error_code err;
switch (response.type)
{
case execute_response::type_t::error: err = response.data.err; break;
case execute_response::type_t::ok_packet:
err = proc.on_head_ok_packet(response.data.ok_pack, diag);
break;
case execute_response::type_t::num_fields: proc.on_num_meta(response.data.num_fields); break;
}
return err;
}
inline error_code process_field_definition(channel& chan, execution_processor& proc, diagnostics& diag)
{
// Read the field definition packet (it's cached at this point)
BOOST_ASSERT(chan.has_read_messages());
error_code err;
auto msg = chan.next_read_message(proc.sequence_number(), err);
if (err)
return err;
// Deserialize
coldef_view coldef{};
err = deserialize_column_definition(msg, coldef);
if (err)
return err;
// Notify the processor
return proc.on_meta(coldef, diag);
}
struct read_resultset_head_op : boost::asio::coroutine
{
channel& chan_;
execution_processor& proc_;
diagnostics& diag_;
read_resultset_head_op(channel& chan, execution_processor& proc, diagnostics& diag)
: chan_(chan), proc_(proc), diag_(diag)
{
}
template <class Self>
void operator()(Self& self, error_code err = {}, span<const std::uint8_t> read_message = {})
{
// Error checking
if (err)
{
self.complete(err);
return;
}
// Non-error path
BOOST_ASIO_CORO_REENTER(*this)
{
// Setup
diag_.clear();
// If we're not reading head, return
if (!proc_.is_reading_head())
{
BOOST_ASIO_CORO_YIELD boost::asio::post(chan_.get_executor(), std::move(self));
self.complete(error_code());
BOOST_ASIO_CORO_YIELD break;
}
// Read the response
BOOST_ASIO_CORO_YIELD chan_.async_read_one(proc_.sequence_number(), std::move(self));
// Response may be: ok_packet, err_packet, local infile request
// (not implemented), or response with fields
err = process_execution_response(chan_, proc_, read_message, diag_);
if (err)
{
self.complete(err);
BOOST_ASIO_CORO_YIELD break;
}
// Read all of the field definitions
while (proc_.is_reading_meta())
{
// Read from the stream if we need it
if (!chan_.has_read_messages())
{
BOOST_ASIO_CORO_YIELD chan_.async_read_some(std::move(self));
}
// Process the metadata packet
err = process_field_definition(chan_, proc_, diag_);
if (err)
{
self.complete(err);
BOOST_ASIO_CORO_YIELD break;
}
}
// No EOF packet is expected here, as we require deprecate EOF capabilities
self.complete(err);
}
}
};
// External interface
inline void read_resultset_head_impl(
channel& chan,
execution_processor& proc,
error_code& err,
diagnostics& diag
)
{
// Setup
err.clear();
diag.clear();
// If we're not reading head, return
if (!proc.is_reading_head())
return;
// Read the response
auto msg = chan.read_one(proc.sequence_number(), err);
if (err)
return;
// Response may be: ok_packet, err_packet, local infile request
// (not implemented), or response with fields
err = process_execution_response(chan, proc, msg, diag);
if (err)
return;
// Read all of the field definitions (zero if empty resultset)
while (proc.is_reading_meta())
{
// Read from the stream if required
if (!chan.has_read_messages())
{
chan.read_some(err);
if (err)
return;
}
// Process the packet
err = process_field_definition(chan, proc, diag);
if (err)
return;
}
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_read_resultset_head_impl(
channel& channel,
execution_processor& proc,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_compose<CompletionToken, void(error_code)>(
read_resultset_head_op(channel, proc, diag),
token,
channel
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,184 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_READ_SOME_ROWS_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_READ_SOME_ROWS_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/post.hpp>
#include <cstddef>
namespace boost {
namespace mysql {
namespace detail {
BOOST_ATTRIBUTE_NODISCARD inline error_code process_some_rows(
channel& chan,
execution_processor& proc,
output_ref output,
std::size_t& read_rows,
diagnostics& diag
)
{
// Process all read messages until they run out, an error happens
// or an EOF is received
read_rows = 0;
error_code err;
proc.on_row_batch_start();
while (chan.has_read_messages() && proc.is_reading_rows() && read_rows < output.max_size())
{
// Get the row message
auto buff = chan.next_read_message(proc.sequence_number(), err);
if (err)
return err;
// Deserialize it
auto res = deserialize_row_message(buff, chan.flavor(), diag);
if (res.type == row_message::type_t::error)
{
err = res.data.err;
}
else if (res.type == row_message::type_t::row)
{
output.set_offset(read_rows);
err = proc.on_row(res.data.row, output, chan.shared_fields());
if (!err)
++read_rows;
}
else
{
err = proc.on_row_ok_packet(res.data.ok_pack);
}
if (err)
return err;
}
proc.on_row_batch_finish();
return error_code();
}
struct read_some_rows_impl_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
execution_processor& proc_;
output_ref output_;
read_some_rows_impl_op(
channel& chan,
diagnostics& diag,
execution_processor& proc,
output_ref output
) noexcept
: chan_(chan), diag_(diag), proc_(proc), output_(output)
{
}
template <class Self>
void operator()(Self& self, error_code err = {})
{
// Error checking
if (err)
{
self.complete(err, 0);
return;
}
// Normal path
std::size_t read_rows = 0;
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
// If we are not reading rows, return
if (!proc_.is_reading_rows())
{
BOOST_ASIO_CORO_YIELD boost::asio::post(chan_.get_executor(), std::move(self));
self.complete(error_code(), 0);
BOOST_ASIO_CORO_YIELD break;
}
// Read at least one message
BOOST_ASIO_CORO_YIELD chan_.async_read_some(std::move(self));
// Process messages
err = process_some_rows(chan_, proc_, output_, read_rows, diag_);
if (err)
{
self.complete(err, 0);
BOOST_ASIO_CORO_YIELD break;
}
self.complete(error_code(), read_rows);
}
}
};
// External interface
inline std::size_t read_some_rows_impl(
channel& chan,
execution_processor& proc,
const output_ref& output,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
// If we are not reading rows, just return
if (!proc.is_reading_rows())
{
return 0;
}
// Read from the stream until there is at least one message
chan.read_some(err);
if (err)
return 0;
// Process read messages
std::size_t read_rows = 0;
err = process_some_rows(chan, proc, output, read_rows, diag);
if (err)
return 0;
return read_rows;
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::size_t))
async_read_some_rows_impl(
channel& chan,
execution_processor& proc,
const output_ref& output,
diagnostics& diag,
CompletionToken&& token
)
{
return asio::async_compose<CompletionToken, void(error_code, std::size_t)>(
read_some_rows_impl_op(chan, diag, proc, output),
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,105 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_READ_SOME_ROWS_DYNAMIC_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_READ_SOME_ROWS_DYNAMIC_HPP
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/rows_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_state_impl.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/network_algorithms/read_some_rows.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline rows_view get_some_rows(const channel& ch, const execution_state_impl& st)
{
return access::construct<rows_view>(
ch.shared_fields().data(),
ch.shared_fields().size(),
st.meta().size()
);
}
struct read_some_rows_dynamic_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
execution_state_impl& st_;
read_some_rows_dynamic_op(channel& chan, diagnostics& diag, execution_state_impl& st) noexcept
: chan_(chan), diag_(diag), st_(st)
{
}
template <class Self>
void operator()(Self& self, error_code err = {}, std::size_t = 0)
{
// Error checking
if (err)
{
self.complete(err, rows_view());
return;
}
// Normal path
BOOST_ASIO_CORO_REENTER(*this)
{
chan_.shared_fields().clear();
BOOST_ASIO_CORO_YIELD async_read_some_rows_impl(chan_, st_, output_ref(), diag_, std::move(self));
self.complete(error_code(), get_some_rows(chan_, st_));
}
}
};
// External interface
inline rows_view read_some_rows_dynamic_impl(
channel& channel,
execution_state_impl& st,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
channel.shared_fields().clear();
read_some_rows_impl(channel, st, output_ref(), err, diag);
if (err)
return rows_view();
return get_some_rows(channel, st);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, rows_view))
async_read_some_rows_dynamic_impl(
channel& channel,
execution_state_impl& st,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_compose<CompletionToken, void(error_code, rows_view)>(
read_some_rows_dynamic_op(channel, diag, st),
token,
channel
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,109 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_RESET_CONNECTION_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_RESET_CONNECTION_HPP
#pragma once
#include <boost/mysql/client_errc.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline void serialize_reset_connection_message(channel& chan)
{
chan.serialize(reset_connection_command(), chan.reset_sequence_number());
}
struct reset_connection_op : boost::asio::coroutine
{
channel& chan_;
diagnostics& diag_;
reset_connection_op(channel& chan, diagnostics& diag) noexcept : chan_(chan), diag_(diag) {}
template <class Self>
void operator()(Self& self, error_code err = {}, span<const std::uint8_t> buff = {})
{
// Error checking
if (err)
{
self.complete(err);
return;
}
// Regular coroutine body; if there has been an error, we don't get here
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
// Serialize the message
serialize_reset_connection_message(chan_);
// Write message
BOOST_ASIO_CORO_YIELD chan_.async_write(std::move(self));
// Read response
BOOST_ASIO_CORO_YIELD chan_.async_read_one(chan_.shared_sequence_number(), std::move(self));
// Verify it's what we expected
self.complete(deserialize_ok_response(buff, chan_.flavor(), diag_));
}
}
};
// Interface
inline void reset_connection_impl(channel& chan, error_code& err, diagnostics& diag)
{
err.clear();
diag.clear();
// Serialize the message
serialize_reset_connection_message(chan);
// Send it
chan.write(err);
if (err)
return;
// Read response
auto response = chan.read_one(chan.shared_sequence_number(), err);
if (err)
return;
// Verify it's what we expected
err = deserialize_ok_response(response, chan.flavor(), diag);
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_reset_connection_impl(channel& chan, diagnostics& diag, CompletionToken&& token)
{
return asio::async_compose<CompletionToken, void(error_code)>(
reset_connection_op(chan, diag),
token,
chan
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_IMPL_CLOSE_STATEMENT_HPP_ */
@@ -0,0 +1,183 @@
//
// 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_IMPL_INTERNAL_NETWORK_ALGORITHMS_START_EXECUTION_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_NETWORK_ALGORITHMS_START_EXECUTION_HPP
#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/statement.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/detail/any_execution_request.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/detail/resultset_encoding.hpp>
#include <boost/mysql/impl/internal/channel/channel.hpp>
#include <boost/mysql/impl/internal/network_algorithms/read_resultset_head.hpp>
#include <boost/mysql/impl/internal/protocol/protocol.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/asio/post.hpp>
namespace boost {
namespace mysql {
namespace detail {
inline error_code check_client_errors(const any_execution_request& req)
{
if (req.is_query)
return error_code();
return req.data.stmt.stmt.num_params() == req.data.stmt.params.size() ? error_code()
: client_errc::wrong_num_params;
}
inline resultset_encoding get_encoding(const any_execution_request& req)
{
return req.is_query ? resultset_encoding::text : resultset_encoding::binary;
}
inline void serialize_execution_request(
const any_execution_request& req,
channel& chan,
std::uint8_t& sequence_number
)
{
if (req.is_query)
{
chan.serialize(query_command{req.data.query}, sequence_number);
}
else
{
chan.serialize(execute_stmt_command{req.data.stmt.stmt.id(), req.data.stmt.params}, sequence_number);
}
}
inline void execution_setup(const any_execution_request& req, channel& chan, execution_processor& proc)
{
// Reeset the processor
proc.reset(get_encoding(req), chan.meta_mode());
// Serialize the execution request
serialize_execution_request(req, chan, proc.sequence_number());
}
struct start_execution_impl_op : boost::asio::coroutine
{
channel& chan_;
any_execution_request req_;
execution_processor& proc_;
diagnostics& diag_;
error_code client_err_; // keep it across posts
start_execution_impl_op(
channel& chan,
const any_execution_request& req,
execution_processor& proc,
diagnostics& diag
)
: chan_(chan), req_(req), proc_(proc), diag_(diag)
{
}
template <class Self>
void operator()(Self& self, error_code err = {})
{
// Error checking
if (err)
{
self.complete(err);
return;
}
// Non-error path
BOOST_ASIO_CORO_REENTER(*this)
{
diag_.clear();
// Check for errors
err = check_client_errors(req_);
if (err)
{
client_err_ = err;
BOOST_ASIO_CORO_YIELD boost::asio::post(chan_.get_executor(), std::move(self));
self.complete(client_err_);
BOOST_ASIO_CORO_YIELD break;
}
// Setup
execution_setup(req_, chan_, proc_);
// Send the execution request (serialized by setup)
BOOST_ASIO_CORO_YIELD chan_.async_write(std::move(self));
// Read the first resultset's head
BOOST_ASIO_CORO_YIELD
async_read_resultset_head_impl(chan_, proc_, diag_, std::move(self));
self.complete(error_code());
}
}
};
// External interface
inline void start_execution_impl(
channel& channel,
const any_execution_request& req,
execution_processor& proc,
error_code& err,
diagnostics& diag
)
{
err.clear();
diag.clear();
// Check for errors
err = check_client_errors(req);
if (err)
return;
// Setup
execution_setup(req, channel, proc);
// Send the execution request (serialized by setup)
channel.write(err);
if (err)
return;
// Read the first resultset's head
read_resultset_head_impl(channel, proc, err, diag);
if (err)
return;
}
template <class CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code))
async_start_execution_impl(
channel& channel,
const any_execution_request& req,
execution_processor& proc,
diagnostics& diag,
CompletionToken&& token
)
{
return boost::asio::async_compose<CompletionToken, void(error_code)>(
start_execution_impl_op(channel, req, proc, diag),
token,
channel
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif /* INCLUDE_MYSQL_IMPL_NETWORK_ALGORITHMS_READ_RESULTSET_HEAD_HPP_ */
+55
View File
@@ -0,0 +1,55 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_BASIC_TYPES_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_BASIC_TYPES_HPP
#include <boost/mysql/string_view.hpp>
#include <array>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
struct int3
{
std::uint32_t value;
};
struct int_lenenc
{
std::uint64_t value;
};
struct string_null
{
string_view value;
};
struct string_eof
{
string_view value;
};
struct string_lenenc
{
string_view value;
};
template <std::size_t N>
struct string_fixed
{
std::array<char, N> value;
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,35 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_BINARY_SERIALIZATION_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_BINARY_SERIALIZATION_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/protocol/serialization.hpp>
namespace boost {
namespace mysql {
namespace detail {
BOOST_MYSQL_DECL
std::size_t get_size(field_view input) noexcept;
BOOST_MYSQL_DECL
void serialize(serialization_context& ctx, field_view input) noexcept;
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/internal/protocol/binary_serialization.ipp>
#endif
#endif
@@ -0,0 +1,133 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_BINARY_SERIALIZATION_IPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_BINARY_SERIALIZATION_IPP
#pragma once
#include <boost/mysql/days.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/protocol/binary_serialization.hpp>
#include <boost/mysql/impl/internal/protocol/constants.hpp>
#include <boost/mysql/impl/internal/protocol/serialization.hpp>
#include <chrono>
namespace boost {
namespace mysql {
namespace detail {
// Binary serialization
template <class T>
BOOST_MYSQL_STATIC_OR_INLINE void serialize_binary_float(serialization_context& ctx, T input)
{
boost::endian::endian_store<T, sizeof(T), boost::endian::order::little>(ctx.first(), input);
ctx.advance(sizeof(T));
}
BOOST_MYSQL_STATIC_OR_INLINE
void serialize_binary_date(serialization_context& ctx, const date& input)
{
using namespace binc;
serialize(ctx, static_cast<std::uint8_t>(date_sz), input.year(), input.month(), input.day());
}
BOOST_MYSQL_STATIC_OR_INLINE
void serialize_binary_datetime(serialization_context& ctx, const datetime& input)
{
using namespace binc;
// Serialize
serialize(
ctx,
static_cast<std::uint8_t>(datetime_dhmsu_sz),
input.year(),
input.month(),
input.day(),
input.hour(),
input.minute(),
input.second(),
input.microsecond()
);
}
BOOST_MYSQL_STATIC_OR_INLINE
void serialize_binary_time(serialization_context& ctx, const boost::mysql::time& input)
{
using namespace binc;
using boost::mysql::days;
using std::chrono::duration_cast;
using std::chrono::hours;
using std::chrono::microseconds;
using std::chrono::minutes;
using std::chrono::seconds;
// Break time
auto num_micros = duration_cast<microseconds>(input % seconds(1));
auto num_secs = duration_cast<seconds>(input % minutes(1) - num_micros);
auto num_mins = duration_cast<minutes>(input % hours(1) - num_secs);
auto num_hours = duration_cast<hours>(input % days(1) - num_mins);
auto num_days = duration_cast<days>(input - num_hours);
std::uint8_t is_negative = (input.count() < 0) ? 1 : 0;
// Serialize
serialize(
ctx,
static_cast<std::uint8_t>(time_dhmsu_sz),
is_negative,
static_cast<std::uint32_t>(std::abs(num_days.count())),
static_cast<std::uint8_t>(std::abs(num_hours.count())),
static_cast<std::uint8_t>(std::abs(num_mins.count())),
static_cast<std::uint8_t>(std::abs(num_secs.count())),
static_cast<std::uint32_t>(std::abs(num_micros.count()))
);
}
} // namespace detail
} // namespace mysql
} // namespace boost
std::size_t boost::mysql::detail::get_size(field_view input) noexcept
{
switch (input.kind())
{
case field_kind::null: return 0;
case field_kind::int64: return 8;
case field_kind::uint64: return 8;
case field_kind::string: return get_size(string_lenenc{input.get_string()});
case field_kind::blob: return get_size(string_lenenc{to_string(input.get_blob())});
case field_kind::float_: return 4;
case field_kind::double_: return 8;
case field_kind::date: return binc::date_sz + binc::length_sz;
case field_kind::datetime: return binc::datetime_dhmsu_sz + binc::length_sz;
case field_kind::time: return binc::time_dhmsu_sz + binc::length_sz;
default: BOOST_ASSERT(false); return 0;
}
}
void boost::mysql::detail::serialize(serialization_context& ctx, field_view input) noexcept
{
switch (input.kind())
{
case field_kind::null: break;
case field_kind::int64: serialize(ctx, input.get_int64()); break;
case field_kind::uint64: serialize(ctx, input.get_uint64()); break;
case field_kind::string: serialize(ctx, string_lenenc{input.get_string()}); break;
case field_kind::blob: serialize(ctx, string_lenenc{to_string(input.get_blob())}); break;
case field_kind::float_: serialize_binary_float(ctx, input.get_float()); break;
case field_kind::double_: serialize_binary_float(ctx, input.get_double()); break;
case field_kind::date: serialize_binary_date(ctx, input.get_date()); break;
case field_kind::datetime: serialize_binary_datetime(ctx, input.get_datetime()); break;
case field_kind::time: serialize_binary_time(ctx, input.get_time()); break;
default: BOOST_ASSERT(false); break;
}
}
#endif
@@ -0,0 +1,47 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_BIT_DESERIALIZATION_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_BIT_DESERIALIZATION_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/string_view.hpp>
#include <boost/mysql/impl/internal/protocol/serialization.hpp>
#include <boost/endian/conversion.hpp>
#include <cstring>
namespace boost {
namespace mysql {
namespace detail {
// All BIT values come as binary values between 1 and 8 bytes length packed in string_lenenc's,
// for both the text and the binary protocols. As the text protocol already unpacks the
// string_lenenc layer, this function is in charge of just parsing the binary payload. The length of
// the BIT value depends on how the type was defined in the table (e.g. BIT(14) will send a 2 byte
// value; BIT(54) will send a 7 byte one). Values are sent as big-endian.
inline deserialize_errc deserialize_bit(string_view from, field_view& to) noexcept
{
std::size_t num_bytes = from.size();
if (num_bytes < 1 || num_bytes > 8)
{
return deserialize_errc::protocol_value_error;
}
unsigned char temp[8]{};
unsigned char* dest = temp + sizeof(temp) - num_bytes;
std::memcpy(dest, from.data(), num_bytes);
to = field_view(endian::load_big_u64(temp));
return deserialize_errc::ok;
}
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+130
View File
@@ -0,0 +1,130 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_CAPABILITIES_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_CAPABILITIES_HPP
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
// Server/client capabilities
// clang-format off
constexpr std::uint32_t CLIENT_LONG_PASSWORD = 1; // Use the improved version of Old Password Authentication
constexpr std::uint32_t CLIENT_FOUND_ROWS = 2; // Send found rows instead of affected rows in EOF_Packet
constexpr std::uint32_t CLIENT_LONG_FLAG = 4; // Get all column flags
constexpr std::uint32_t CLIENT_CONNECT_WITH_DB = 8; // Database (schema) name can be specified on connect in Handshake Response Packet
constexpr std::uint32_t CLIENT_NO_SCHEMA = 16; // Don't allow database.table.column
constexpr std::uint32_t CLIENT_COMPRESS = 32; // Compression protocol supported
constexpr std::uint32_t CLIENT_ODBC = 64; // Special handling of ODBC behavior
constexpr std::uint32_t CLIENT_LOCAL_FILES = 128; // Can use LOAD DATA LOCAL
constexpr std::uint32_t CLIENT_IGNORE_SPACE = 256; // Ignore spaces before '('
constexpr std::uint32_t CLIENT_PROTOCOL_41 = 512; // New 4.1 protocol
constexpr std::uint32_t CLIENT_INTERACTIVE = 1024; // This is an interactive client
constexpr std::uint32_t CLIENT_SSL = 2048; // Use SSL encryption for the session
constexpr std::uint32_t CLIENT_IGNORE_SIGPIPE = 4096; // Client only flag
constexpr std::uint32_t CLIENT_TRANSACTIONS = 8192; // Client knows about transactions
constexpr std::uint32_t CLIENT_RESERVED = 16384; // DEPRECATED: Old flag for 4.1 protocol
constexpr std::uint32_t CLIENT_SECURE_CONNECTION = 32768; // DEPRECATED: Old flag for 4.1 authentication, required by MariaDB
constexpr std::uint32_t CLIENT_MULTI_STATEMENTS = (1UL << 16); // Enable/disable multi-stmt support
constexpr std::uint32_t CLIENT_MULTI_RESULTS = (1UL << 17); // Enable/disable multi-results
constexpr std::uint32_t CLIENT_PS_MULTI_RESULTS = (1UL << 18); // Multi-results and OUT parameters in PS-protocol
constexpr std::uint32_t CLIENT_PLUGIN_AUTH = (1UL << 19); // Client supports plugin authentication
constexpr std::uint32_t CLIENT_CONNECT_ATTRS = (1UL << 20); // Client supports connection attributes
constexpr std::uint32_t CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA = (1UL << 21); // Enable authentication response packet to be larger than 255 bytes
constexpr std::uint32_t CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS = (1UL << 22); // Don't close the connection for a user account with expired password
constexpr std::uint32_t CLIENT_SESSION_TRACK = (1UL << 23); // Capable of handling server state change information
constexpr std::uint32_t CLIENT_DEPRECATE_EOF = (1UL << 24); // Client no longer needs EOF_Packet and will use OK_Packet instead
constexpr std::uint32_t CLIENT_SSL_VERIFY_SERVER_CERT = (1UL << 30); // Verify server certificate
constexpr std::uint32_t CLIENT_OPTIONAL_RESULTSET_METADATA = (1UL << 25); // The client can handle optional metadata information in the resultset
constexpr std::uint32_t CLIENT_REMEMBER_OPTIONS = (1UL << 31); // Don't reset the options after an unsuccessful connect
// clang-format on
class capabilities
{
std::uint32_t value_;
public:
constexpr explicit capabilities(std::uint32_t value = 0) noexcept : value_(value){};
constexpr std::uint32_t get() const noexcept { return value_; }
void set(std::uint32_t value) noexcept { value_ = value; }
constexpr bool has(std::uint32_t cap) const noexcept { return value_ & cap; }
constexpr bool has_all(capabilities other) const noexcept
{
return (value_ & other.get()) == other.get();
}
constexpr capabilities operator|(capabilities rhs) const noexcept
{
return capabilities(value_ | rhs.value_);
}
constexpr capabilities operator&(capabilities rhs) const noexcept
{
return capabilities(value_ & rhs.value_);
}
constexpr bool operator==(const capabilities& rhs) const noexcept { return value_ == rhs.value_; }
constexpr bool operator!=(const capabilities& rhs) const noexcept { return value_ != rhs.value_; }
};
/*
* CLIENT_LONG_PASSWORD: unset // Use the improved version of Old Password Authentication
* CLIENT_FOUND_ROWS: unset // Send found rows instead of affected rows in EOF_Packet
* CLIENT_LONG_FLAG: unset // Get all column flags
* CLIENT_CONNECT_WITH_DB: optional // Database (schema) name can be specified on connect in
* Handshake Response Packet CLIENT_NO_SCHEMA: unset // Don't allow database.table.column
* CLIENT_COMPRESS: unset // Compression protocol supported
* CLIENT_ODBC: unset // Special handling of ODBC behavior
* CLIENT_LOCAL_FILES: unset // Can use LOAD DATA LOCAL
* CLIENT_IGNORE_SPACE: unset // Ignore spaces before '('
* CLIENT_PROTOCOL_41: mandatory // New 4.1 protocol
* CLIENT_INTERACTIVE: unset // This is an interactive client
* CLIENT_SSL: unset // Use SSL encryption for the session
* CLIENT_IGNORE_SIGPIPE: unset // Client only flag
* CLIENT_TRANSACTIONS: unset // Client knows about transactions
* CLIENT_RESERVED: unset // DEPRECATED: Old flag for 4.1 protocol
* CLIENT_RESERVED2: unset // DEPRECATED: Old flag for 4.1 authentication
* \ CLIENT_SECURE_CONNECTION CLIENT_MULTI_STATEMENTS: unset // Enable/disable multi-stmt support
* CLIENT_MULTI_RESULTS: unset // Enable/disable multi-results
* CLIENT_PS_MULTI_RESULTS: unset // Multi-results and OUT parameters in PS-protocol
* CLIENT_PLUGIN_AUTH: mandatory // Client supports plugin authentication
* CLIENT_CONNECT_ATTRS: unset // Client supports connection attributes
* CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA: mandatory // Enable authentication response packet to be
* larger than 255 bytes CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS: unset // Don't close the connection
* for a user account with expired password CLIENT_SESSION_TRACK: unset // Capable of handling
* server state change information CLIENT_DEPRECATE_EOF: mandatory // Client no longer needs
* EOF_Packet and will use OK_Packet instead CLIENT_SSL_VERIFY_SERVER_CERT: unset // Verify server
* certificate CLIENT_OPTIONAL_RESULTSET_METADATA: unset // The client can handle optional metadata
* information in the resultset CLIENT_REMEMBER_OPTIONS: unset // Don't reset the options after an
* unsuccessful connect
*
* We pay attention to:
* CLIENT_CONNECT_WITH_DB: optional // Database (schema) name can be specified on connect in
* Handshake Response Packet CLIENT_PROTOCOL_41: mandatory // New 4.1 protocol CLIENT_PLUGIN_AUTH:
* mandatory // Client supports plugin authentication CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA:
* mandatory // Enable authentication response packet to be larger than 255 bytes
* CLIENT_DEPRECATE_EOF: mandatory // Client no longer needs EOF_Packet and will use OK_Packet
* instead
*/
// clang-format off
constexpr capabilities mandatory_capabilities{
CLIENT_PROTOCOL_41 |
CLIENT_PLUGIN_AUTH |
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
CLIENT_DEPRECATE_EOF |
CLIENT_SECURE_CONNECTION
};
// clang-format on
constexpr capabilities optional_capabilities{CLIENT_MULTI_RESULTS | CLIENT_PS_MULTI_RESULTS};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+65
View File
@@ -0,0 +1,65 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_CONSTANTS_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_CONSTANTS_HPP
#include <cstddef>
#include <cstdint>
namespace boost {
namespace mysql {
namespace detail {
constexpr std::size_t MAX_PACKET_SIZE = 0xffffff;
constexpr std::size_t HEADER_SIZE = 4;
// The binary collation number, used to distinguish blobs from strings
constexpr std::uint16_t binary_collation = 63;
// Prepared statements
namespace cursor_types {
constexpr std::uint8_t no_cursor = 0;
constexpr std::uint8_t read_only = 1;
constexpr std::uint8_t for_update = 2;
constexpr std::uint8_t scrollable = 4;
} // namespace cursor_types
// Binary protocol (de)serialization constants
namespace binc {
constexpr std::size_t length_sz = 1; // length byte, for date, datetime and time
constexpr std::size_t year_sz = 2;
constexpr std::size_t month_sz = 1;
constexpr std::size_t date_day_sz = 1;
constexpr std::size_t time_days_sz = 4;
constexpr std::size_t hours_sz = 1;
constexpr std::size_t mins_sz = 1;
constexpr std::size_t secs_sz = 1;
constexpr std::size_t micros_sz = 4;
constexpr std::size_t time_sign_sz = 1;
constexpr std::size_t date_sz = year_sz + month_sz + date_day_sz; // does not include length
constexpr std::size_t datetime_d_sz = date_sz;
constexpr std::size_t datetime_dhms_sz = datetime_d_sz + hours_sz + mins_sz + secs_sz;
constexpr std::size_t datetime_dhmsu_sz = datetime_dhms_sz + micros_sz;
constexpr std::size_t time_dhms_sz = time_sign_sz + time_days_sz + hours_sz + mins_sz + secs_sz;
constexpr std::size_t time_dhmsu_sz = time_dhms_sz + micros_sz;
constexpr std::size_t time_max_days = 34; // equivalent to the 839 hours, in the broken format
} // namespace binc
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
+25
View File
@@ -0,0 +1,25 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_DB_FLAVOR_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DB_FLAVOR_HPP
namespace boost {
namespace mysql {
namespace detail {
enum class db_flavor
{
mysql,
mariadb
};
} // namespace detail
} // namespace mysql
} // namespace boost
#endif
@@ -0,0 +1,37 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_BINARY_FIELD_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_BINARY_FIELD_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/protocol/serialization.hpp>
namespace boost {
namespace mysql {
namespace detail {
BOOST_MYSQL_DECL
deserialize_errc deserialize_binary_field(
deserialization_context& ctx,
const metadata& meta,
field_view& output
);
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/internal/protocol/deserialize_binary_field.ipp>
#endif
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_BINARY_DESERIALIZATION_HPP_ */
@@ -0,0 +1,319 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_BINARY_FIELD_IPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_BINARY_FIELD_IPP
#pragma once
#include <boost/mysql/field_kind.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/detail/datetime.hpp>
#include <boost/mysql/impl/internal/protocol/bit_deserialization.hpp>
#include <boost/mysql/impl/internal/protocol/constants.hpp>
#include <boost/mysql/impl/internal/protocol/deserialize_binary_field.hpp>
#include <boost/mysql/impl/internal/protocol/serialization.hpp>
#include <cmath>
#include <cstddef>
namespace boost {
namespace mysql {
namespace detail {
// strings
BOOST_MYSQL_STATIC_OR_INLINE
deserialize_errc deserialize_binary_field_string(
deserialization_context& ctx,
field_view& output,
bool is_blob
) noexcept
{
string_lenenc deser;
auto err = deserialize(ctx, deser);
if (err != deserialize_errc::ok)
return err;
if (is_blob)
{
output = field_view(
blob_view(reinterpret_cast<const unsigned char*>(deser.value.data()), deser.value.size())
);
}
else
{
output = field_view(deser.value);
}
return deserialize_errc::ok;
}
// ints
template <class TargetType, class DeserializableType>
BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
deserialize_binary_field_int_impl(deserialization_context& ctx, field_view& output) noexcept
{
DeserializableType deser;
auto err = deserialize(ctx, deser);
if (err != deserialize_errc::ok)
return err;
output = field_view(static_cast<TargetType>(deser));
return deserialize_errc::ok;
}
template <class DeserializableTypeUnsigned, class DeserializableTypeSigned>
BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
deserialize_binary_field_int(const metadata& meta, deserialization_context& ctx, field_view& output) noexcept
{
return meta.is_unsigned()
? deserialize_binary_field_int_impl<std::uint64_t, DeserializableTypeUnsigned>(ctx, output)
: deserialize_binary_field_int_impl<std::int64_t, DeserializableTypeSigned>(ctx, output);
}
// Bits. These come as a binary value between 1 and 8 bytes,
// packed in a string
BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
deserialize_binary_field_bit(deserialization_context& ctx, field_view& output) noexcept
{
string_lenenc buffer;
auto err = deserialize(ctx, buffer);
if (err != deserialize_errc::ok)
return err;
return boost::mysql::detail::deserialize_bit(buffer.value, output);
}
// Floats
template <class T>
BOOST_MYSQL_STATIC_OR_INLINE deserialize_errc
deserialize_binary_field_float(deserialization_context& ctx, field_view& output) noexcept
{
// Size check
if (!ctx.enough_size(sizeof(T)))
return deserialize_errc::incomplete_message;
// Endianness conversion. Boost.Endian support for floats start at 1.71
T v = boost::endian::endian_load<T, sizeof(T), boost::endian::order::little>(ctx.first());
// Nans and infs not allowed in SQL
if (std::isnan(v) || std::isinf(v))
return deserialize_errc::protocol_value_error;
// Done
ctx.advance(sizeof(T));
output = field_view(v);
return deserialize_errc::ok;
}
// Time types
BOOST_MYSQL_STATIC_OR_INLINE
deserialize_errc deserialize_binary_ymd(deserialization_context& ctx, boost::mysql::date& output)
{
using namespace boost::mysql::detail;
std::uint16_t year;
std::uint8_t month;
std::uint8_t day;
// Deserialize
auto err = deserialize(ctx, year, month, day);
if (err != deserialize_errc::ok)
return err;
// Range check
if (year > max_year || month > max_month || day > max_day)
{
return deserialize_errc::protocol_value_error;
}
output = boost::mysql::date(year, month, day);
return deserialize_errc::ok;
}
BOOST_MYSQL_STATIC_OR_INLINE
deserialize_errc deserialize_binary_field_date(deserialization_context& ctx, field_view& output) noexcept
{
using namespace boost::mysql::detail::binc;
// Deserialize length
std::uint8_t length;
auto err = deserialize(ctx, length);
if (err != deserialize_errc::ok)
return err;
// Check for zero dates
if (length < date_sz)
{
output = field_view(boost::mysql::date());
return deserialize_errc::ok;
}
// Deserialize rest of fields
boost::mysql::date d;
err = deserialize_binary_ymd(ctx, d);
if (err != deserialize_errc::ok)
return err;
output = field_view(d);
return deserialize_errc::ok;
}
BOOST_MYSQL_STATIC_OR_INLINE
deserialize_errc deserialize_binary_field_datetime(deserialization_context& ctx, field_view& output) noexcept
{
using namespace binc;
// Deserialize length
std::uint8_t length;
auto err = deserialize(ctx, length);
if (err != deserialize_errc::ok)
return err;
// If the DATETIME does not contain some of the values below,
// they are supposed to be zero
boost::mysql::date d{};
std::uint8_t hours = 0;
std::uint8_t minutes = 0;
std::uint8_t seconds = 0;
std::uint32_t micros = 0;
// Date part
if (length >= datetime_d_sz)
{
err = deserialize_binary_ymd(ctx, d);
if (err != deserialize_errc::ok)
return err;
}
// Hours, minutes, seconds
if (length >= datetime_dhms_sz)
{
err = deserialize(ctx, hours, minutes, seconds);
if (err != deserialize_errc::ok)
return err;
}
// Microseconds
if (length >= datetime_dhmsu_sz)
{
err = deserialize(ctx, micros);
if (err != deserialize_errc::ok)
return err;
}
// Validity check. deserialize_binary_ymd already does it for date
if (hours > max_hour || minutes > max_min || seconds > max_sec || micros > max_micro)
{
return deserialize_errc::protocol_value_error;
}
// Compose the final datetime
boost::mysql::datetime dt(d.year(), d.month(), d.day(), hours, minutes, seconds, micros);
output = field_view(dt);
return deserialize_errc::ok;
}
BOOST_MYSQL_STATIC_OR_INLINE
deserialize_errc deserialize_binary_field_time(deserialization_context& ctx, field_view& output) noexcept
{
using namespace boost::mysql::detail;
using namespace boost::mysql::detail::binc;
// Deserialize length
std::uint8_t length;
auto err = deserialize(ctx, length);
if (err != deserialize_errc::ok)
return err;
// If the TIME contains no value for these fields, they are zero
std::uint8_t is_negative = 0;
std::uint32_t num_days = 0;
std::uint8_t hours = 0;
std::uint8_t minutes = 0;
std::uint8_t seconds = 0;
std::uint32_t microseconds = 0;
// Sign, days, hours, minutes, seconds
if (length >= time_dhms_sz)
{
err = deserialize(ctx, is_negative, num_days, hours, minutes, seconds);
if (err != deserialize_errc::ok)
return err;
}
// Microseconds
if (length >= time_dhmsu_sz)
{
err = deserialize(ctx, microseconds);
if (err != deserialize_errc::ok)
return err;
}
// Range check
if (num_days > time_max_days || hours > max_hour || minutes > max_min || seconds > max_sec ||
microseconds > max_micro)
{
return deserialize_errc::protocol_value_error;
}
// Compose the final time
output = field_view(boost::mysql::time(
(is_negative ? -1 : 1) *
(boost::mysql::days(num_days) + std::chrono::hours(hours) + std::chrono::minutes(minutes) +
std::chrono::seconds(seconds) + std::chrono::microseconds(microseconds))
));
return deserialize_errc::ok;
}
} // namespace detail
} // namespace mysql
} // namespace boost
boost::mysql::detail::deserialize_errc boost::mysql::detail::deserialize_binary_field(
deserialization_context& ctx,
const metadata& meta,
field_view& output
)
{
switch (meta.type())
{
case column_type::tinyint:
return deserialize_binary_field_int<std::uint8_t, std::int8_t>(meta, ctx, output);
case column_type::smallint:
case column_type::year:
return deserialize_binary_field_int<std::uint16_t, std::int16_t>(meta, ctx, output);
case column_type::mediumint:
case column_type::int_:
return deserialize_binary_field_int<std::uint32_t, std::int32_t>(meta, ctx, output);
case column_type::bigint:
return deserialize_binary_field_int<std::uint64_t, std::int64_t>(meta, ctx, output);
case column_type::bit: return deserialize_binary_field_bit(ctx, output);
case column_type::float_: return deserialize_binary_field_float<float>(ctx, output);
case column_type::double_: return deserialize_binary_field_float<double>(ctx, output);
case column_type::timestamp:
case column_type::datetime: return deserialize_binary_field_datetime(ctx, output);
case column_type::date: return deserialize_binary_field_date(ctx, output);
case column_type::time: return deserialize_binary_field_time(ctx, output);
// True string types
case column_type::char_:
case column_type::varchar:
case column_type::text:
case column_type::enum_:
case column_type::set:
case column_type::decimal:
case column_type::json: return deserialize_binary_field_string(ctx, output, false);
// Blobs and anything else
case column_type::binary:
case column_type::varbinary:
case column_type::blob:
case column_type::geometry:
default: return deserialize_binary_field_string(ctx, output, true);
}
}
#endif
@@ -0,0 +1,33 @@
//
// 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_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_TEXT_FIELD_HPP
#define BOOST_MYSQL_IMPL_INTERNAL_PROTOCOL_DESERIALIZE_TEXT_FIELD_HPP
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/metadata.hpp>
#include <boost/mysql/detail/config.hpp>
#include <boost/mysql/impl/internal/protocol/serialization.hpp>
namespace boost {
namespace mysql {
namespace detail {
BOOST_MYSQL_DECL
deserialize_errc deserialize_text_field(string_view from, const metadata& meta, field_view& output);
} // namespace detail
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/internal/protocol/deserialize_text_field.ipp>
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More