set_character_set.hpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. //
  2. // Copyright (c) 2019-2024 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_SET_CHARACTER_SET_HPP
  8. #define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_SET_CHARACTER_SET_HPP
  9. #include <boost/mysql/character_set.hpp>
  10. #include <boost/mysql/client_errc.hpp>
  11. #include <boost/mysql/diagnostics.hpp>
  12. #include <boost/mysql/format_sql.hpp>
  13. #include <boost/mysql/detail/algo_params.hpp>
  14. #include <boost/mysql/detail/next_action.hpp>
  15. #include <boost/mysql/impl/internal/coroutine.hpp>
  16. #include <boost/mysql/impl/internal/protocol/deserialization.hpp>
  17. #include <boost/mysql/impl/internal/protocol/serialization.hpp>
  18. #include <boost/mysql/impl/internal/sansio/connection_state_data.hpp>
  19. #include <boost/system/result.hpp>
  20. #include <cstdint>
  21. namespace boost {
  22. namespace mysql {
  23. namespace detail {
  24. // Securely compose a SET NAMES statement
  25. inline system::result<std::string> compose_set_names(character_set charset)
  26. {
  27. // The character set should not be default-constructed
  28. BOOST_ASSERT(charset.name != nullptr);
  29. // For security, if the character set has non-ascii characters in it name, reject it.
  30. format_context ctx(format_options{ascii_charset, true});
  31. ctx.append_raw("SET NAMES ").append_value(charset.name);
  32. return std::move(ctx).get();
  33. }
  34. class read_set_character_set_response_algo
  35. {
  36. int resume_point_{0};
  37. diagnostics* diag_;
  38. character_set charset_;
  39. std::uint8_t seqnum_{0};
  40. public:
  41. read_set_character_set_response_algo(diagnostics* diag, character_set charset, std::uint8_t seqnum)
  42. : diag_(diag), charset_(charset), seqnum_(seqnum)
  43. {
  44. }
  45. character_set charset() const { return charset_; }
  46. diagnostics& diag() { return *diag_; }
  47. std::uint8_t& sequence_number() { return seqnum_; }
  48. next_action resume(connection_state_data& st, error_code ec)
  49. {
  50. // SET NAMES never returns rows. Using execute requires us to allocate
  51. // a results object, which we can avoid by simply sending the query and reading the OK response.
  52. switch (resume_point_)
  53. {
  54. case 0:
  55. // Read the response
  56. BOOST_MYSQL_YIELD(resume_point_, 1, st.read(seqnum_))
  57. if (ec)
  58. return ec;
  59. // Verify it's what we expected
  60. ec = st.deserialize_ok(*diag_);
  61. if (ec)
  62. return ec;
  63. // If we were successful, update the character set
  64. st.current_charset = charset_;
  65. }
  66. return next_action();
  67. }
  68. };
  69. class set_character_set_algo
  70. {
  71. int resume_point_{0};
  72. read_set_character_set_response_algo read_response_st_;
  73. next_action compose_request(connection_state_data& st)
  74. {
  75. auto q = compose_set_names(read_response_st_.charset());
  76. if (q.has_error())
  77. return q.error();
  78. return st.write(query_command{q.value()}, read_response_st_.sequence_number());
  79. }
  80. public:
  81. set_character_set_algo(set_character_set_algo_params params) noexcept
  82. : read_response_st_(params.diag, params.charset, 0u)
  83. {
  84. }
  85. next_action resume(connection_state_data& st, error_code ec)
  86. {
  87. next_action act;
  88. // SET NAMES never returns rows. Using execute requires us to allocate
  89. // a results object, which we can avoid by simply sending the query and reading the OK response.
  90. switch (resume_point_)
  91. {
  92. case 0:
  93. // Setup
  94. read_response_st_.diag().clear();
  95. // Send the execution request
  96. BOOST_MYSQL_YIELD(resume_point_, 1, compose_request(st))
  97. if (ec)
  98. return ec;
  99. // Read the response
  100. while (!(act = read_response_st_.resume(st, ec)).is_done())
  101. BOOST_MYSQL_YIELD(resume_point_, 2, act)
  102. return act;
  103. }
  104. return next_action();
  105. }
  106. };
  107. } // namespace detail
  108. } // namespace mysql
  109. } // namespace boost
  110. #endif