123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- //
- // 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
|