gnss-sim/3rdparty/boost/mysql/pipeline.hpp

437 lines
14 KiB
C++
Raw Normal View History

2024-12-24 16:15:51 +00:00
//
// Copyright (c) 2019-2024 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_PIPELINE_HPP
#define BOOST_MYSQL_PIPELINE_HPP
#include <boost/mysql/character_set.hpp>
#include <boost/mysql/diagnostics.hpp>
#include <boost/mysql/error_code.hpp>
#include <boost/mysql/field_view.hpp>
#include <boost/mysql/results.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/detail/execution_processor/execution_processor.hpp>
#include <boost/mysql/detail/pipeline.hpp>
#include <boost/mysql/detail/writable_field_traits.hpp>
#include <boost/assert.hpp>
#include <boost/core/span.hpp>
#include <boost/variant2/variant.hpp>
#include <array>
#include <cstdint>
#include <utility>
#include <vector>
namespace boost {
namespace mysql {
/**
* \brief (EXPERIMENTAL) A variant-like type holding the response of a single pipeline stage.
* \details
* This is a variant-like type, similar to `boost::system::result`. At any point in time,
* it can contain: \n
* \li A \ref statement. Will happen if the stage was a prepare statement that succeeded.
* \li A \ref results. Will happen if the stage was a query or statement execution that succeeded.
* \li An \ref error_code, \ref diagnostics pair. Will happen if the stage failed, or if it succeeded but
* it doesn't yield a value (as in close statement, reset connection and set character set).
*
* \par Experimental
* This part of the API is experimental, and may change in successive
* releases without previous notice.
*/
class stage_response
{
#ifndef BOOST_MYSQL_DOXYGEN
struct errcode_with_diagnostics
{
error_code ec;
diagnostics diag;
};
struct
{
variant2::variant<errcode_with_diagnostics, statement, results> value;
void emplace_results() { value.emplace<results>(); }
void emplace_error() { value.emplace<errcode_with_diagnostics>(); }
detail::execution_processor& get_processor()
{
return detail::access::get_impl(variant2::unsafe_get<2>(value));
}
void set_result(statement s) { value = s; }
void set_error(error_code ec, diagnostics&& diag)
{
value.emplace<0>(errcode_with_diagnostics{ec, std::move(diag)});
}
} impl_;
friend struct detail::access;
#endif
bool has_error() const { return impl_.value.index() == 0u; }
BOOST_MYSQL_DECL
void check_has_results() const;
public:
/**
* \brief Default constructor.
* \details
* Constructs an object containing an empty error code and diagnostics.
*
* \par Exception safety
* No-throw guarantee.
*/
stage_response() = default;
/**
* \brief Returns true if the object contains a statement.
*
* \par Exception safety
* No-throw guarantee.
*/
bool has_statement() const noexcept { return impl_.value.index() == 1u; }
/**
* \brief Returns true if the object contains a results.
*
* \par Exception safety
* No-throw guarantee.
*/
bool has_results() const noexcept { return impl_.value.index() == 2u; }
/**
* \brief Retrieves the contained error code.
* \details
* If `*this` contains an error, retrieves it.
* Otherwise (if `this->has_statement() || this->has_results()`),
* returns an empty (default-constructed) error code.
*
* \par Exception safety
* No-throw guarantee.
*/
error_code error() const noexcept
{
return has_error() ? variant2::unsafe_get<0>(impl_.value).ec : error_code();
}
/**
* \brief Retrieves the contained diagnostics (lvalue reference accessor).
* \details
* If `*this` contains an error, retrieves the associated diagnostic information
* by copying it.
* Otherwise (if `this->has_statement() || this->has_results()`),
* returns an empty diagnostics object.
*
* \par Exception safety
* Strong guarantee: memory allocations may throw.
*/
diagnostics diag() const&
{
return has_error() ? variant2::unsafe_get<0>(impl_.value).diag : diagnostics();
}
/**
* \brief Retrieves the contained diagnostics (rvalue reference accessor).
* \details
* If `*this` contains an error, retrieves the associated diagnostic information
* by moving it.
* Otherwise (if `this->has_statement() || this->has_results()`),
* returns an empty (default-constructed) error.
*
* \par Exception safety
* No-throw guarantee.
*/
diagnostics diag() && noexcept
{
return has_error() ? variant2::unsafe_get<0>(std::move(impl_.value)).diag : diagnostics();
}
/**
* \brief Retrieves the contained statement or throws an exception.
* \details
* If `*this` contains an statement (`this->has_statement() == true`),
* retrieves it. Otherwise, throws an exception.
*
* \par Exception safety
* Strong guarantee. Throws on invalid input.
* \throws std::invalid_argument If `*this` does not contain a statement.
*/
BOOST_MYSQL_DECL
statement as_statement() const;
/**
* \brief Retrieves the contained statement (unchecked accessor).
* \details
* If `*this` contains an statement,
* retrieves it. Otherwise, the behavior is undefined.
*
* \par Preconditions
* `this->has_statement() == true`
*
* \par Exception safety
* No-throw guarantee.
*/
statement get_statement() const noexcept
{
BOOST_ASSERT(has_statement());
return variant2::unsafe_get<1>(impl_.value);
}
/**
* \brief Retrieves the contained results or throws an exception.
* \details
* If `*this` contains a `results` object (`this->has_results() == true`),
* retrieves a reference to it. Otherwise, throws an exception.
*
* \par Exception safety
* Strong guarantee. Throws on invalid input.
* \throws std::invalid_argument If `this->has_results() == false`
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive
* and hasn't been assigned to.
*/
const results& as_results() const&
{
check_has_results();
return variant2::unsafe_get<2>(impl_.value);
}
/// \copydoc as_results
results&& as_results() &&
{
check_has_results();
return variant2::unsafe_get<2>(std::move(impl_.value));
}
/**
* \brief Retrieves the contained results (unchecked accessor).
* \details
* If `*this` contains a `results` object, retrieves a reference to it.
* Otherwise, the behavior is undefined.
*
* \par Preconditions
* `this->has_results() == true`
*
* \par Exception safety
* No-throw guarantee.
*
* \par Object lifetimes
* The returned reference is valid as long as `*this` is alive
* and hasn't been assigned to.
*/
const results& get_results() const& noexcept
{
BOOST_ASSERT(has_results());
return variant2::unsafe_get<2>(impl_.value);
}
/// \copydoc get_results
results&& get_results() && noexcept
{
BOOST_ASSERT(has_results());
return variant2::unsafe_get<2>(std::move(impl_.value));
}
};
/**
* \brief (EXPERIMENTAL) A pipeline request.
* \details
* Contains a collection of pipeline stages, fully describing the work to be performed
* by a pipeline operation.
* Call any of the `add_xxx` functions to append new stages to the request.
*
* \par Experimental
* This part of the API is experimental, and may change in successive
* releases without previous notice.
*/
class pipeline_request
{
#ifndef BOOST_MYSQL_DOXYGEN
struct impl_t
{
std::vector<std::uint8_t> buffer_;
std::vector<detail::pipeline_request_stage> stages_;
} impl_;
friend struct detail::access;
#endif
public:
/**
* \brief Default constructor.
* \details Constructs an empty pipeline request, with no stages.
*
* \par Exception safety
* No-throw guarantee.
*/
pipeline_request() = default;
/**
* \brief Adds a stage that executes a text query.
* \details
* Creates a stage that will run `query` as a SQL query,
* like \ref any_connection::execute.
*
* \par Exception safety
* Strong guarantee. Memory allocations may throw.
*
* \par Object lifetimes
* query is copied into the request and need not be kept alive after this function returns.
*/
BOOST_MYSQL_DECL
pipeline_request& add_execute(string_view query);
/**
* \brief Adds a stage that executes a prepared statement.
* \details
* Creates a stage that runs
* `stmt` bound to any parameters passed in `params`, like \ref any_connection::execute
* and \ref statement::bind. For example, `add_execute(stmt, 42, "John")` has
* effects equivalent to `conn.execute(stmt.bind(42, "John"))`.
*
* \par Exception safety
* Strong guarantee. Throws if the supplied number of parameters doesn't match the number
* of parameters expected by the statement. Additionally, memory allocations may throw.
* \throws std::invalid_argument If `sizeof...(params) != stmt.num_params()`
*
* \par Preconditions
* The passed statement should be valid (`stmt.valid() == true`).
*
* \par Object lifetimes
* Any objects pointed to by `params` are copied into the request and
* need not be kept alive after this function returns.
*
* \par Type requirements
* Any type satisfying `WritableField` can be used as a parameter.
* This includes all types that can be used with \ref statement::bind,
* including scalar types, strings, blobs and optionals.
*/
template <BOOST_MYSQL_WRITABLE_FIELD... WritableField>
pipeline_request& add_execute(statement stmt, const WritableField&... params)
{
std::array<field_view, sizeof...(WritableField)> params_arr{{detail::to_field(params)...}};
return add_execute_range(stmt, params_arr);
}
/**
* \brief Adds a stage that executes a prepared statement.
* \details
* Creates a stage that runs
* `stmt` bound to any parameters passed in `params`, like \ref any_connection::execute
* and \ref statement::bind. For example, `add_execute_range(stmt, params)` has
* effects equivalent to `conn.execute(stmt.bind(params.begin(), params.end()))`.
* \n
* This function can be used instead of \ref add_execute when the number of actual parameters
* of a statement is not known at compile time.
*
* \par Exception safety
* Strong guarantee. Throws if the supplied number of parameters doesn't match the number
* of parameters expected by the statement. Additionally, memory allocations may throw.
* \throws std::invalid_argument If `params.size() != stmt.num_params()`
*
* \par Preconditions
* The passed statement should be valid (`stmt.valid() == true`).
*
* \par Object lifetimes
* The `params` range is copied into the request and
* needs not be kept alive after this function returns.
*/
BOOST_MYSQL_DECL
pipeline_request& add_execute_range(statement stmt, span<const field_view> params);
/**
* \brief Adds a prepare statement stage.
* \details
* Creates a stage that prepares a statement server-side. The resulting
* stage has effects equivalent to `conn.prepare_statement(stmt_sql)`.
*
* \par Exception safety
* Strong guarantee. Memory allocations may throw.
*
* \par Object lifetimes
* stmt_sql is copied into the request and need not be kept alive after this function returns.
*/
BOOST_MYSQL_DECL
pipeline_request& add_prepare_statement(string_view stmt_sql);
/**
* \brief Adds a close statement stage.
* \details
* Creates a stage that closes a prepared statement. The resulting
* stage has effects equivalent to `conn.close_statement(stmt)`.
*
* \par Exception safety
* Strong guarantee. Memory allocations may throw.
*
* \par Preconditions
* The passed statement should be valid (`stmt.valid() == true`).
*/
BOOST_MYSQL_DECL pipeline_request& add_close_statement(statement stmt);
/**
* \brief Adds a reset connection stage.
* \details
* Creates a stage that resets server-side session state. The resulting
* stage has effects equivalent to `conn.reset_connection()`.
*
* \par Exception safety
* Strong guarantee. Memory allocations may throw.
*/
BOOST_MYSQL_DECL pipeline_request& add_reset_connection();
/**
* \brief Adds a set character set stage.
* \details
* Creates a stage that sets the connection's character set.
* The resulting stage has effects equivalent to `conn.set_character_set(charset)`.
*
* \par Exception safety
* Strong guarantee. Throws if the supplied character set name is not valid
* (i.e. `charset.name` contains non-ASCII characters).
* The check is performed as a hardening measure, and never happens with
* the character sets provided by this library.
*
* Additionally, memory allocations may throw.
* \throws std::invalid_argument If `charset.name` contains non-ASCII characters.
*
* \par Preconditions
* The passed character set should not be default-constructed
* (`charset.name != nullptr`).
*/
BOOST_MYSQL_DECL pipeline_request& add_set_character_set(character_set charset);
/**
* \brief Removes all stages in the pipeline request, making the object empty again.
* \details
* Can be used to re-use a single request object for multiple pipeline operations.
*
* \par Exception safety
* No-throw guarantee.
*/
void clear() noexcept
{
impl_.buffer_.clear();
impl_.stages_.clear();
}
};
} // namespace mysql
} // namespace boost
#ifdef BOOST_MYSQL_HEADER_ONLY
#include <boost/mysql/impl/pipeline.ipp>
#endif
#endif