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