Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-04-17 08:39:00

0001 //
0002 // Copyright (c) 2019-2024 Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #ifndef BOOST_MYSQL_IMPL_INTERNAL_SANSIO_SET_CHARACTER_SET_HPP
0009 #define BOOST_MYSQL_IMPL_INTERNAL_SANSIO_SET_CHARACTER_SET_HPP
0010 
0011 #include <boost/mysql/character_set.hpp>
0012 #include <boost/mysql/client_errc.hpp>
0013 #include <boost/mysql/diagnostics.hpp>
0014 #include <boost/mysql/format_sql.hpp>
0015 
0016 #include <boost/mysql/detail/algo_params.hpp>
0017 #include <boost/mysql/detail/next_action.hpp>
0018 
0019 #include <boost/mysql/impl/internal/coroutine.hpp>
0020 #include <boost/mysql/impl/internal/protocol/deserialization.hpp>
0021 #include <boost/mysql/impl/internal/protocol/serialization.hpp>
0022 #include <boost/mysql/impl/internal/sansio/connection_state_data.hpp>
0023 
0024 #include <boost/system/result.hpp>
0025 
0026 #include <cstdint>
0027 
0028 namespace boost {
0029 namespace mysql {
0030 namespace detail {
0031 
0032 // Securely compose a SET NAMES statement
0033 inline system::result<std::string> compose_set_names(character_set charset)
0034 {
0035     // The character set should not be default-constructed
0036     BOOST_ASSERT(charset.name != nullptr);
0037 
0038     // For security, if the character set has non-ascii characters in it name, reject it.
0039     format_context ctx(format_options{ascii_charset, true});
0040     ctx.append_raw("SET NAMES ").append_value(charset.name);
0041     return std::move(ctx).get();
0042 }
0043 
0044 class read_set_character_set_response_algo
0045 {
0046     int resume_point_{0};
0047     diagnostics* diag_;
0048     character_set charset_;
0049     std::uint8_t seqnum_{0};
0050 
0051 public:
0052     read_set_character_set_response_algo(diagnostics* diag, character_set charset, std::uint8_t seqnum)
0053         : diag_(diag), charset_(charset), seqnum_(seqnum)
0054     {
0055     }
0056     character_set charset() const { return charset_; }
0057     diagnostics& diag() { return *diag_; }
0058     std::uint8_t& sequence_number() { return seqnum_; }
0059 
0060     next_action resume(connection_state_data& st, error_code ec)
0061     {
0062         // SET NAMES never returns rows. Using execute requires us to allocate
0063         // a results object, which we can avoid by simply sending the query and reading the OK response.
0064         switch (resume_point_)
0065         {
0066         case 0:
0067 
0068             // Read the response
0069             BOOST_MYSQL_YIELD(resume_point_, 1, st.read(seqnum_))
0070             if (ec)
0071                 return ec;
0072 
0073             // Verify it's what we expected
0074             ec = st.deserialize_ok(*diag_);
0075             if (ec)
0076                 return ec;
0077 
0078             // If we were successful, update the character set
0079             st.current_charset = charset_;
0080         }
0081 
0082         return next_action();
0083     }
0084 };
0085 
0086 class set_character_set_algo
0087 {
0088     int resume_point_{0};
0089     read_set_character_set_response_algo read_response_st_;
0090 
0091     next_action compose_request(connection_state_data& st)
0092     {
0093         auto q = compose_set_names(read_response_st_.charset());
0094         if (q.has_error())
0095             return q.error();
0096         return st.write(query_command{q.value()}, read_response_st_.sequence_number());
0097     }
0098 
0099 public:
0100     set_character_set_algo(set_character_set_algo_params params) noexcept
0101         : read_response_st_(params.diag, params.charset, 0u)
0102     {
0103     }
0104 
0105     next_action resume(connection_state_data& st, error_code ec)
0106     {
0107         next_action act;
0108 
0109         // SET NAMES never returns rows. Using execute requires us to allocate
0110         // a results object, which we can avoid by simply sending the query and reading the OK response.
0111         switch (resume_point_)
0112         {
0113         case 0:
0114 
0115             // Setup
0116             read_response_st_.diag().clear();
0117 
0118             // Send the execution request
0119             BOOST_MYSQL_YIELD(resume_point_, 1, compose_request(st))
0120             if (ec)
0121                 return ec;
0122 
0123             // Read the response
0124             while (!(act = read_response_st_.resume(st, ec)).is_done())
0125                 BOOST_MYSQL_YIELD(resume_point_, 2, act)
0126             return act;
0127         }
0128 
0129         return next_action();
0130     }
0131 };
0132 
0133 }  // namespace detail
0134 }  // namespace mysql
0135 }  // namespace boost
0136 
0137 #endif