// // 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_DETAIL_CONNECTION_IMPL_HPP #define BOOST_MYSQL_DETAIL_CONNECTION_IMPL_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace mysql { // Forward decl template class static_execution_state; struct character_set; class pipeline_request; namespace detail { // Forward decl class connection_state; // // Helpers to interact with connection_state, without including its definition // struct connection_state_deleter { BOOST_MYSQL_DECL void operator()(connection_state*) const; }; BOOST_MYSQL_DECL std::vector& get_shared_fields(connection_state&); template any_resumable_ref setup(connection_state&, const AlgoParams&); // Note: AlgoParams should have !is_void_result template typename AlgoParams::result_type get_result(const connection_state&); // // execution helpers // template std::array tuple_to_array_impl(const std::tuple& t, mp11::index_sequence) noexcept { boost::ignore_unused(t); // MSVC gets confused if sizeof...(T) == 0 return std::array{{to_field(std::get(t))...}}; } template std::array tuple_to_array(const std::tuple& t) noexcept { return tuple_to_array_impl(t, mp11::make_index_sequence()); } 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, std::vector&) noexcept { return query_request_getter{q}; } struct stmt_it_request_getter { statement stmt; span params; // Points into the connection state's shared fields any_execution_request get() const noexcept { return any_execution_request(stmt, params); } }; template inline stmt_it_request_getter make_request_getter( const bound_statement_iterator_range& req, std::vector& shared_fields ) { auto& impl = access::get_impl(req); shared_fields.assign(impl.first, impl.last); return {impl.stmt, shared_fields}; } template struct stmt_tuple_request_getter { statement stmt; std::array params; any_execution_request get() const noexcept { return any_execution_request(stmt, params); } }; template stmt_tuple_request_getter::value> make_request_getter(const bound_statement_tuple& req, std::vector&) { auto& impl = access::get_impl(req); return {impl.stmt, tuple_to_array(impl.params)}; } // // helpers to run algos // template using has_void_result = std::is_same; template struct completion_signature_impl; template struct completion_signature_impl { // Using typedef to workaround a msvc 14.1 bug typedef void(type)(error_code); }; template struct completion_signature_impl { // Using typedef to workaround a msvc 14.1 bug typedef void(type)(error_code, typename AlgoParams::result_type); }; template using completion_signature_t = typename completion_signature_impl< AlgoParams, has_void_result::value>::type; // Intermediate handler template struct generic_algo_handler { static_assert(!has_void_result::value, "Internal error: result_type should be non-void"); using result_t = typename AlgoParams::result_type; template generic_algo_handler(DeducedHandler&& h, connection_state& st) : final_handler(std::forward(h)), st(&st) { } void operator()(error_code ec) { std::move(final_handler)(ec, ec ? result_t{} : get_result(*st)); } Handler final_handler; // needs to be accessed by associator connection_state* st; }; class connection_impl { std::unique_ptr engine_; std::unique_ptr st_; // Generic algorithm template typename AlgoParams::result_type run_impl( AlgoParams params, error_code& ec, std::true_type /* has_void_result */ ) { engine_->run(setup(*st_, params), ec); } template typename AlgoParams::result_type run_impl( AlgoParams params, error_code& ec, std::false_type /* has_void_result */ ) { engine_->run(setup(*st_, params), ec); return get_result(*st_); } template static void async_run_impl( engine& eng, connection_state& st, AlgoParams params, Handler&& handler, std::true_type /* has_void_result */ ) { eng.async_run(setup(st, params), std::forward(handler)); } template static void async_run_impl( engine& eng, connection_state& st, AlgoParams params, Handler&& handler, std::false_type /* has_void_result */ ) { using intermediate_handler_t = generic_algo_handler::type>; eng.async_run(setup(st, params), intermediate_handler_t(std::forward(handler), st)); } template static void async_run_impl(engine& eng, connection_state& st, AlgoParams params, Handler&& handler) { async_run_impl(eng, st, params, std::forward(handler), has_void_result{}); } struct run_algo_initiation { template void operator()(Handler&& handler, engine* eng, connection_state* st, AlgoParams params) { async_run_impl(*eng, *st, params, std::forward(handler)); } }; // Connect static connect_algo_params make_params_connect(diagnostics& diag, const handshake_params& params) { return connect_algo_params{&diag, params, false}; } static connect_algo_params make_params_connect_v2(diagnostics& diag, const connect_params& params) { return connect_algo_params{ &diag, make_hparams(params), params.server_address.type() == address_type::unix_path }; } template struct initiate_connect { template void operator()( Handler&& handler, engine* eng, connection_state* st, const EndpointType& endpoint, handshake_params params, diagnostics* diag ) { eng->set_endpoint(&endpoint); async_run_impl(*eng, *st, make_params_connect(*diag, params), std::forward(handler)); } }; struct initiate_connect_v2 { template void operator()( Handler&& handler, engine* eng, connection_state* st, const connect_params* params, diagnostics* diag ) { eng->set_endpoint(¶ms->server_address); async_run_impl(*eng, *st, make_params_connect_v2(*diag, *params), std::forward(handler)); } }; // execute struct initiate_execute { template void operator()( Handler&& handler, engine* eng, connection_state* st, const ExecutionRequest& req, execution_processor* proc, diagnostics* diag ) { auto getter = make_request_getter(req, get_shared_fields(*st)); async_run_impl( *eng, *st, execute_algo_params{diag, getter.get(), proc}, std::forward(handler) ); } }; // start execution struct initiate_start_execution { template void operator()( Handler&& handler, engine* eng, connection_state* st, const ExecutionRequest& req, execution_processor* proc, diagnostics* diag ) { auto getter = make_request_getter(req, get_shared_fields(*st)); async_run_impl( *eng, *st, start_execution_algo_params{diag, getter.get(), proc}, std::forward(handler) ); } }; public: BOOST_MYSQL_DECL connection_impl( std::size_t read_buff_size, std::size_t max_buffer_size, std::unique_ptr eng ); BOOST_MYSQL_DECL metadata_mode meta_mode() const; BOOST_MYSQL_DECL void set_meta_mode(metadata_mode m); BOOST_MYSQL_DECL bool ssl_active() const; BOOST_MYSQL_DECL bool backslash_escapes() const; BOOST_MYSQL_DECL system::result current_character_set() const; BOOST_MYSQL_DECL diagnostics& shared_diag(); // TODO: get rid of this engine& get_engine() { BOOST_ASSERT(engine_); return *engine_; } const engine& get_engine() const { BOOST_ASSERT(engine_); return *engine_; } // Generic algorithm template typename AlgoParams::result_type run(AlgoParams params, error_code& ec) { return run_impl(params, ec, has_void_result{}); } template auto async_run(AlgoParams params, CompletionToken&& token) -> decltype(asio::async_initiate>( run_algo_initiation(), token, engine_.get(), st_.get(), params )) { return asio::async_initiate>( run_algo_initiation(), token, engine_.get(), st_.get(), params ); } // Connect template void connect( const EndpointType& endpoint, const handshake_params& params, error_code& err, diagnostics& diag ) { engine_->set_endpoint(&endpoint); run(make_params_connect(diag, params), err); } void connect_v2(const connect_params& params, error_code& err, diagnostics& diag) { engine_->set_endpoint(¶ms.server_address); run(make_params_connect_v2(diag, params), err); } template auto async_connect( const EndpointType& endpoint, const handshake_params& params, diagnostics& diag, CompletionToken&& token ) -> decltype(asio::async_initiate( initiate_connect(), token, engine_.get(), st_.get(), endpoint, params, &diag )) { return asio::async_initiate( initiate_connect(), token, engine_.get(), st_.get(), endpoint, params, &diag ); } template auto async_connect_v2(const connect_params& params, diagnostics& diag, CompletionToken&& token) -> decltype(asio::async_initiate( initiate_connect_v2(), token, engine_.get(), st_.get(), ¶ms, &diag )) { return asio::async_initiate( initiate_connect_v2(), token, engine_.get(), st_.get(), ¶ms, &diag ); } // Handshake handshake_algo_params make_params_handshake(const handshake_params& params, diagnostics& diag) const { return {&diag, params, false}; } // Execute template void execute(const ExecutionRequest& req, ResultsType& result, error_code& err, diagnostics& diag) { auto getter = make_request_getter(req, get_shared_fields(*st_)); run(execute_algo_params{&diag, getter.get(), &access::get_impl(result).get_interface()}, err); } template auto async_execute( ExecutionRequest&& req, ResultsType& result, diagnostics& diag, CompletionToken&& token ) -> decltype(asio::async_initiate( initiate_execute(), token, engine_.get(), st_.get(), std::forward(req), &access::get_impl(result).get_interface(), &diag )) { return asio::async_initiate( initiate_execute(), token, engine_.get(), st_.get(), std::forward(req), &access::get_impl(result).get_interface(), &diag ); } // Start execution template void start_execution( const ExecutionRequest& req, ExecutionStateType& exec_st, error_code& err, diagnostics& diag ) { auto getter = make_request_getter(req, get_shared_fields(*st_)); run(start_execution_algo_params{&diag, getter.get(), &access::get_impl(exec_st).get_interface()}, err); } template auto async_start_execution( ExecutionRequest&& req, ExecutionStateType& exec_st, diagnostics& diag, CompletionToken&& token ) -> decltype(asio::async_initiate( initiate_start_execution(), token, engine_.get(), st_.get(), std::forward(req), &access::get_impl(exec_st).get_interface(), &diag )) { return asio::async_initiate( initiate_start_execution(), token, engine_.get(), st_.get(), std::forward(req), &access::get_impl(exec_st).get_interface(), &diag ); } // Read some rows (dynamic) read_some_rows_dynamic_algo_params make_params_read_some_rows(execution_state& st, diagnostics& diag) const { return {&diag, &access::get_impl(st).get_interface()}; } // Read some rows (static) template read_some_rows_algo_params make_params_read_some_rows_static( ExecutionState& exec_st, span output, diagnostics& diag ) const { return { &diag, &access::get_impl(exec_st).get_interface(), access::get_impl(exec_st).make_output_ref(output) }; } // Read resultset head template read_resultset_head_algo_params make_params_read_resultset_head(ExecutionStateType& st, diagnostics& diag) const { return {&diag, &detail::access::get_impl(st).get_interface()}; } // Close statement close_statement_algo_params make_params_close_statement(statement stmt, diagnostics& diag) const { return {&diag, stmt.id()}; } // Set character set set_character_set_algo_params make_params_set_character_set( const character_set& charset, diagnostics& diag ) const { return {&diag, charset}; } // Ping ping_algo_params make_params_ping(diagnostics& diag) const { return {&diag}; } // Reset connection reset_connection_algo_params make_params_reset_connection(diagnostics& diag) const { return {&diag}; } // Quit connection quit_connection_algo_params make_params_quit(diagnostics& diag) const { return {&diag}; } // Close connection close_connection_algo_params make_params_close(diagnostics& diag) const { return {&diag}; } // Run pipeline. Separately compiled to avoid including the pipeline header here BOOST_MYSQL_DECL static run_pipeline_algo_params make_params_pipeline( const pipeline_request& req, std::vector& response, diagnostics& diag ); }; // To use some completion tokens, like deferred, in C++11, the old macros // BOOST_ASIO_INITFN_AUTO_RESULT_TYPE are no longer enough. // Helper typedefs to reduce duplication template using async_run_t = decltype(std::declval() .async_run(std::declval(), std::declval())); template using async_connect_t = decltype(std::declval().async_connect( std::declval(), std::declval(), std::declval(), std::declval() )); template using async_connect_v2_t = decltype(std::declval().async_connect_v2( std::declval(), std::declval(), std::declval() )); template using async_execute_t = decltype(std::declval().async_execute( std::declval(), std::declval(), std::declval(), std::declval() )); template using async_start_execution_t = decltype(std::declval().async_start_execution( std::declval(), std::declval(), std::declval(), std::declval() )); template using async_handshake_t = async_run_t; template using async_read_resultset_head_t = async_run_t; template using async_read_some_rows_dynamic_t = async_run_t; template using async_prepare_statement_t = async_run_t; template using async_close_statement_t = async_run_t; template using async_set_character_set_t = async_run_t; template using async_ping_t = async_run_t; template using async_reset_connection_t = async_run_t; template using async_quit_connection_t = async_run_t; template using async_close_connection_t = async_run_t; template using async_run_pipeline_t = async_run_t; } // namespace detail } // namespace mysql } // namespace boost // Propagate associated properties namespace boost { namespace asio { template