start_execution.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. //
  2. // Copyright (c) 2019-2025 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. #ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_START_EXECUTION_HPP
  8. #define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_START_EXECUTION_HPP
  9. #include <boost/mysql/character_set.hpp>
  10. #include <boost/mysql/client_errc.hpp>
  11. #include <boost/mysql/constant_string_view.hpp>
  12. #include <boost/mysql/diagnostics.hpp>
  13. #include <boost/mysql/error_code.hpp>
  14. #include <boost/mysql/format_sql.hpp>
  15. #include <boost/mysql/detail/algo_params.hpp>
  16. #include <boost/mysql/detail/any_execution_request.hpp>
  17. #include <boost/mysql/detail/execution_processor/execution_processor.hpp>
  18. #include <boost/mysql/detail/next_action.hpp>
  19. #include <boost/mysql/detail/output_string.hpp>
  20. #include <boost/mysql/detail/resultset_encoding.hpp>
  21. #include <boost/mysql/impl/internal/coroutine.hpp>
  22. #include <boost/mysql/impl/internal/protocol/impl/serialization_context.hpp>
  23. #include <boost/mysql/impl/internal/protocol/serialization.hpp>
  24. #include <boost/mysql/impl/internal/sansio/connection_state_data.hpp>
  25. #include <boost/mysql/impl/internal/sansio/read_resultset_head.hpp>
  26. #include <boost/core/span.hpp>
  27. namespace boost {
  28. namespace mysql {
  29. namespace detail {
  30. // A serializable to generate the query in the write buffer without copies
  31. struct query_with_params
  32. {
  33. constant_string_view query;
  34. span<const format_arg> args;
  35. format_options opts;
  36. void serialize(serialization_context& ctx) const
  37. {
  38. // Create a format context
  39. auto fmt_ctx = access::construct<format_context_base>(output_string_ref::create(ctx), opts);
  40. // Serialize the query header
  41. ctx.add(0x03);
  42. // Serialize the actual query
  43. vformat_sql_to(fmt_ctx, query, args);
  44. // Check for errors
  45. ctx.add_error(fmt_ctx.error_state());
  46. }
  47. };
  48. class start_execution_algo
  49. {
  50. int resume_point_{0};
  51. read_resultset_head_algo read_head_st_;
  52. any_execution_request req_;
  53. bool is_top_level_;
  54. std::uint8_t& seqnum() { return processor().sequence_number(); }
  55. execution_processor& processor() { return read_head_st_.processor(); }
  56. static resultset_encoding get_encoding(any_execution_request::type_t type)
  57. {
  58. switch (type)
  59. {
  60. case any_execution_request::type_t::query:
  61. case any_execution_request::type_t::query_with_params: return resultset_encoding::text;
  62. case any_execution_request::type_t::stmt: return resultset_encoding::binary;
  63. default: BOOST_ASSERT(false); return resultset_encoding::text; // LCOV_EXCL_LINE
  64. }
  65. }
  66. next_action write_query_with_params(
  67. connection_state_data& st,
  68. any_execution_request::data_t::query_with_params_t data
  69. )
  70. {
  71. // Determine format options
  72. if (st.current_charset.name == nullptr)
  73. {
  74. return error_code(client_errc::unknown_character_set);
  75. }
  76. format_options opts{st.current_charset, st.backslash_escapes};
  77. // Write the request
  78. return st.write(query_with_params{data.query, data.args, opts}, seqnum());
  79. }
  80. next_action write_stmt(connection_state_data& st, any_execution_request::data_t::stmt_t data)
  81. {
  82. if (data.num_params != data.params.size())
  83. return error_code(client_errc::wrong_num_params);
  84. return st.write(execute_stmt_command{data.stmt_id, data.params}, seqnum());
  85. }
  86. next_action compose_request(connection_state_data& st)
  87. {
  88. switch (req_.type)
  89. {
  90. case any_execution_request::type_t::query: return st.write(query_command{req_.data.query}, seqnum());
  91. case any_execution_request::type_t::query_with_params:
  92. return write_query_with_params(st, req_.data.query_with_params);
  93. case any_execution_request::type_t::stmt: return write_stmt(st, req_.data.stmt);
  94. default: BOOST_ASSERT(false); return next_action(); // LCOV_EXCL_LINE
  95. }
  96. }
  97. public:
  98. // We pass false to the read head algo's constructor because it's a subordinate algos.
  99. // This suppresses state checks.
  100. start_execution_algo(start_execution_algo_params params, bool is_top_level = true) noexcept
  101. : read_head_st_({params.proc}, false), req_(params.req), is_top_level_(is_top_level)
  102. {
  103. }
  104. next_action resume(connection_state_data& st, diagnostics& diag, error_code ec)
  105. {
  106. next_action act;
  107. switch (resume_point_)
  108. {
  109. case 0:
  110. // Check connection status. The check is only correct if we're the top-level algorithm
  111. if (is_top_level_)
  112. {
  113. ec = st.check_status_ready();
  114. if (ec)
  115. return ec;
  116. }
  117. // Reset the processor
  118. processor().reset(get_encoding(req_.type), st.meta_mode);
  119. // Send the execution request
  120. BOOST_MYSQL_YIELD(resume_point_, 1, compose_request(st))
  121. if (ec)
  122. return ec;
  123. // If the request was sent successfully, and we're the top-level algorithm,
  124. // we're now running a multi-function operation. The status flag is cleared
  125. // by the other algorithms on error or when an OK packet is received
  126. if (is_top_level_)
  127. st.status = connection_status::engaged_in_multi_function;
  128. // Read the first resultset's head and return its result
  129. while (!(act = read_head_st_.resume(st, diag, ec)).is_done())
  130. BOOST_MYSQL_YIELD(resume_point_, 2, act)
  131. // If there was an error, we're no longer running a multi-function operation
  132. if (act.error())
  133. {
  134. if (is_top_level_)
  135. st.status = connection_status::ready;
  136. return act;
  137. }
  138. // If we received the final OK packet, we're no longer running a multi-function operation
  139. if (processor().is_complete() && is_top_level_)
  140. st.status = connection_status::ready;
  141. }
  142. return next_action();
  143. }
  144. };
  145. } // namespace detail
  146. } // namespace mysql
  147. } // namespace boost
  148. #endif