connection_impl.hpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  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_DETAIL_CONNECTION_IMPL_HPP
  8. #define BOOST_MYSQL_DETAIL_CONNECTION_IMPL_HPP
  9. #include <boost/mysql/any_address.hpp>
  10. #include <boost/mysql/connect_params.hpp>
  11. #include <boost/mysql/diagnostics.hpp>
  12. #include <boost/mysql/error_code.hpp>
  13. #include <boost/mysql/execution_state.hpp>
  14. #include <boost/mysql/field_view.hpp>
  15. #include <boost/mysql/handshake_params.hpp>
  16. #include <boost/mysql/metadata_mode.hpp>
  17. #include <boost/mysql/rows_view.hpp>
  18. #include <boost/mysql/statement.hpp>
  19. #include <boost/mysql/string_view.hpp>
  20. #include <boost/mysql/detail/access.hpp>
  21. #include <boost/mysql/detail/algo_params.hpp>
  22. #include <boost/mysql/detail/config.hpp>
  23. #include <boost/mysql/detail/connect_params_helpers.hpp>
  24. #include <boost/mysql/detail/engine.hpp>
  25. #include <boost/mysql/detail/execution_processor/execution_processor.hpp>
  26. #include <boost/mysql/detail/initiation_base.hpp>
  27. #include <boost/mysql/detail/intermediate_handler.hpp>
  28. #include <boost/asio/any_io_executor.hpp>
  29. #include <boost/optional/optional.hpp>
  30. #include <boost/system/result.hpp>
  31. #include <cstddef>
  32. #include <cstdint>
  33. #include <cstring>
  34. #include <memory>
  35. #include <type_traits>
  36. #include <utility>
  37. #include <vector>
  38. namespace boost {
  39. namespace mysql {
  40. // Forward decl
  41. template <class... StaticRow>
  42. class static_execution_state;
  43. struct character_set;
  44. class pipeline_request;
  45. namespace detail {
  46. //
  47. // Helpers to interact with connection_state, without including its definition
  48. //
  49. class connection_state;
  50. struct connection_state_deleter
  51. {
  52. BOOST_MYSQL_DECL void operator()(connection_state*) const;
  53. };
  54. BOOST_MYSQL_DECL std::vector<field_view>& get_shared_fields(connection_state&);
  55. template <class AlgoParams>
  56. any_resumable_ref setup(connection_state&, diagnostics&, const AlgoParams&);
  57. // Note: AlgoParams should have !is_void_result
  58. template <class AlgoParams>
  59. typename AlgoParams::result_type get_result(const connection_state&);
  60. //
  61. // helpers to run algos
  62. //
  63. template <class AlgoParams>
  64. using has_void_result = std::is_same<typename AlgoParams::result_type, void>;
  65. template <class AlgoParams, bool is_void>
  66. struct completion_signature_impl;
  67. template <class AlgoParams>
  68. struct completion_signature_impl<AlgoParams, true>
  69. {
  70. // Using typedef to workaround a msvc 14.1 bug
  71. typedef void(type)(error_code);
  72. };
  73. template <class AlgoParams>
  74. struct completion_signature_impl<AlgoParams, false>
  75. {
  76. // Using typedef to workaround a msvc 14.1 bug
  77. typedef void(type)(error_code, typename AlgoParams::result_type);
  78. };
  79. template <class AlgoParams>
  80. using completion_signature_t = typename completion_signature_impl<
  81. AlgoParams,
  82. has_void_result<AlgoParams>::value>::type;
  83. // Intermediate handler
  84. template <class AlgoParams>
  85. struct generic_algo_fn
  86. {
  87. static_assert(!has_void_result<AlgoParams>::value, "Internal error: result_type should be non-void");
  88. using result_t = typename AlgoParams::result_type;
  89. template <class Handler>
  90. void operator()(Handler&& handler, error_code ec)
  91. {
  92. std::move(handler)(ec, ec ? result_t{} : get_result<AlgoParams>(*st));
  93. }
  94. connection_state* st;
  95. };
  96. // Note: the 1st async_initiate arg should be a diagnostics*,
  97. // so completion tokens knowing how Boost.MySQL work can operate
  98. class connection_impl
  99. {
  100. std::unique_ptr<engine> engine_;
  101. std::unique_ptr<connection_state, connection_state_deleter> st_;
  102. asio::any_io_executor get_executor() const { return engine_->get_executor(); }
  103. // Helper for execution requests
  104. template <class T>
  105. static auto make_request(T&& input, connection_state& st)
  106. -> decltype(execution_request_traits<typename std::decay<T>::type>::make_request(
  107. std::forward<T>(input),
  108. get_shared_fields(st)
  109. ))
  110. {
  111. return execution_request_traits<typename std::decay<T>::type>::make_request(
  112. std::forward<T>(input),
  113. get_shared_fields(st)
  114. );
  115. }
  116. // Generic algorithm
  117. template <class AlgoParams>
  118. typename AlgoParams::result_type run_impl(
  119. AlgoParams params,
  120. error_code& ec,
  121. diagnostics& diag,
  122. std::true_type /* has_void_result */
  123. )
  124. {
  125. engine_->run(setup(*st_, diag, params), ec);
  126. }
  127. template <class AlgoParams>
  128. typename AlgoParams::result_type run_impl(
  129. AlgoParams params,
  130. error_code& ec,
  131. diagnostics& diag,
  132. std::false_type /* has_void_result */
  133. )
  134. {
  135. engine_->run(setup(*st_, diag, params), ec);
  136. return get_result<AlgoParams>(*st_);
  137. }
  138. template <class AlgoParams, class Handler>
  139. static void async_run_impl(
  140. engine& eng,
  141. connection_state& st,
  142. AlgoParams params,
  143. diagnostics& diag,
  144. Handler&& handler,
  145. std::true_type /* has_void_result */
  146. )
  147. {
  148. eng.async_run(setup(st, diag, params), std::forward<Handler>(handler));
  149. }
  150. template <class AlgoParams, class Handler>
  151. static void async_run_impl(
  152. engine& eng,
  153. connection_state& st,
  154. AlgoParams params,
  155. diagnostics& diag,
  156. Handler&& handler,
  157. std::false_type /* has_void_result */
  158. )
  159. {
  160. eng.async_run(
  161. setup(st, diag, params),
  162. make_intermediate_handler(generic_algo_fn<AlgoParams>{&st}, std::forward<Handler>(handler))
  163. );
  164. }
  165. template <class AlgoParams, class Handler>
  166. static void async_run_impl(
  167. engine& eng,
  168. connection_state& st,
  169. AlgoParams params,
  170. diagnostics& diag,
  171. Handler&& handler
  172. )
  173. {
  174. async_run_impl(eng, st, params, diag, std::forward<Handler>(handler), has_void_result<AlgoParams>{});
  175. }
  176. struct run_algo_initiation : initiation_base
  177. {
  178. using initiation_base::initiation_base;
  179. template <class Handler, class AlgoParams>
  180. void operator()(
  181. Handler&& handler,
  182. diagnostics* diag,
  183. engine* eng,
  184. connection_state* st,
  185. AlgoParams params
  186. )
  187. {
  188. async_run_impl(*eng, *st, params, *diag, std::forward<Handler>(handler));
  189. }
  190. };
  191. // Connect
  192. static connect_algo_params make_params_connect(const void* server_address, const handshake_params& params)
  193. {
  194. return connect_algo_params{server_address, params, false};
  195. }
  196. static connect_algo_params make_params_connect_v2(const connect_params& params)
  197. {
  198. return connect_algo_params{
  199. &params.server_address,
  200. make_hparams(params),
  201. params.server_address.type() == address_type::unix_path
  202. };
  203. }
  204. template <class EndpointType>
  205. struct initiate_connect : initiation_base
  206. {
  207. using initiation_base::initiation_base;
  208. template <class Handler>
  209. void operator()(
  210. Handler&& handler,
  211. diagnostics* diag,
  212. engine* eng,
  213. connection_state* st,
  214. const EndpointType& endpoint,
  215. handshake_params params
  216. )
  217. {
  218. async_run_impl(
  219. *eng,
  220. *st,
  221. make_params_connect(&endpoint, params),
  222. *diag,
  223. std::forward<Handler>(handler)
  224. );
  225. }
  226. };
  227. struct initiate_connect_v2 : initiation_base
  228. {
  229. using initiation_base::initiation_base;
  230. template <class Handler>
  231. void operator()(
  232. Handler&& handler,
  233. diagnostics* diag,
  234. engine* eng,
  235. connection_state* st,
  236. const connect_params* params
  237. )
  238. {
  239. async_run_impl(*eng, *st, make_params_connect_v2(*params), *diag, std::forward<Handler>(handler));
  240. }
  241. };
  242. // execute
  243. struct initiate_execute : initiation_base
  244. {
  245. using initiation_base::initiation_base;
  246. template <class Handler, class ExecutionRequest>
  247. void operator()(
  248. Handler&& handler,
  249. diagnostics* diag,
  250. engine* eng,
  251. connection_state* st,
  252. ExecutionRequest&& req,
  253. execution_processor* proc
  254. )
  255. {
  256. async_run_impl(
  257. *eng,
  258. *st,
  259. execute_algo_params{make_request(std::forward<ExecutionRequest>(req), *st), proc},
  260. *diag,
  261. std::forward<Handler>(handler)
  262. );
  263. }
  264. };
  265. // start execution
  266. struct initiate_start_execution : initiation_base
  267. {
  268. using initiation_base::initiation_base;
  269. template <class Handler, class ExecutionRequest>
  270. void operator()(
  271. Handler&& handler,
  272. diagnostics* diag,
  273. engine* eng,
  274. connection_state* st,
  275. ExecutionRequest&& req,
  276. execution_processor* proc
  277. )
  278. {
  279. async_run_impl(
  280. *eng,
  281. *st,
  282. start_execution_algo_params{make_request(std::forward<ExecutionRequest>(req), *st), proc},
  283. *diag,
  284. std::forward<Handler>(handler)
  285. );
  286. }
  287. };
  288. public:
  289. BOOST_MYSQL_DECL connection_impl(
  290. std::size_t read_buff_size,
  291. std::size_t max_buffer_size,
  292. std::unique_ptr<engine> eng
  293. );
  294. BOOST_MYSQL_DECL metadata_mode meta_mode() const;
  295. BOOST_MYSQL_DECL void set_meta_mode(metadata_mode m);
  296. BOOST_MYSQL_DECL bool ssl_active() const;
  297. BOOST_MYSQL_DECL bool backslash_escapes() const;
  298. BOOST_MYSQL_DECL system::result<character_set> current_character_set() const;
  299. BOOST_MYSQL_DECL boost::optional<std::uint32_t> connection_id() const;
  300. BOOST_MYSQL_DECL diagnostics& shared_diag();
  301. engine& get_engine()
  302. {
  303. BOOST_ASSERT(engine_);
  304. return *engine_;
  305. }
  306. const engine& get_engine() const
  307. {
  308. BOOST_ASSERT(engine_);
  309. return *engine_;
  310. }
  311. // Generic algorithm
  312. template <class AlgoParams>
  313. typename AlgoParams::result_type run(AlgoParams params, error_code& ec, diagnostics& diag)
  314. {
  315. return run_impl(params, ec, diag, has_void_result<AlgoParams>{});
  316. }
  317. template <class AlgoParams, class CompletionToken>
  318. auto async_run(AlgoParams params, diagnostics& diag, CompletionToken&& token)
  319. -> decltype(asio::async_initiate<CompletionToken, completion_signature_t<AlgoParams>>(
  320. run_algo_initiation(get_executor()),
  321. token,
  322. &diag,
  323. engine_.get(),
  324. st_.get(),
  325. params
  326. ))
  327. {
  328. return asio::async_initiate<CompletionToken, completion_signature_t<AlgoParams>>(
  329. run_algo_initiation(get_executor()),
  330. token,
  331. &diag,
  332. engine_.get(),
  333. st_.get(),
  334. params
  335. );
  336. }
  337. // Connect
  338. template <class EndpointType>
  339. void connect(
  340. const EndpointType& endpoint,
  341. const handshake_params& params,
  342. error_code& err,
  343. diagnostics& diag
  344. )
  345. {
  346. run(make_params_connect(&endpoint, params), err, diag);
  347. }
  348. void connect_v2(const connect_params& params, error_code& err, diagnostics& diag)
  349. {
  350. run(make_params_connect_v2(params), err, diag);
  351. }
  352. template <class EndpointType, class CompletionToken>
  353. auto async_connect(
  354. const EndpointType& endpoint,
  355. const handshake_params& params,
  356. diagnostics& diag,
  357. CompletionToken&& token
  358. )
  359. -> decltype(asio::async_initiate<CompletionToken, void(error_code)>(
  360. initiate_connect<EndpointType>(get_executor()),
  361. token,
  362. &diag,
  363. engine_.get(),
  364. st_.get(),
  365. endpoint,
  366. params
  367. ))
  368. {
  369. return asio::async_initiate<CompletionToken, void(error_code)>(
  370. initiate_connect<EndpointType>(get_executor()),
  371. token,
  372. &diag,
  373. engine_.get(),
  374. st_.get(),
  375. endpoint,
  376. params
  377. );
  378. }
  379. template <class CompletionToken>
  380. auto async_connect_v2(const connect_params& params, diagnostics& diag, CompletionToken&& token)
  381. -> decltype(asio::async_initiate<CompletionToken, void(error_code)>(
  382. initiate_connect_v2(get_executor()),
  383. token,
  384. &diag,
  385. engine_.get(),
  386. st_.get(),
  387. &params
  388. ))
  389. {
  390. return asio::async_initiate<CompletionToken, void(error_code)>(
  391. initiate_connect_v2(get_executor()),
  392. token,
  393. &diag,
  394. engine_.get(),
  395. st_.get(),
  396. &params
  397. );
  398. }
  399. // Handshake
  400. handshake_algo_params make_params_handshake(const handshake_params& params) const
  401. {
  402. return {params, false};
  403. }
  404. // Execute
  405. template <class ExecutionRequest, class ResultsType>
  406. void execute(ExecutionRequest&& req, ResultsType& result, error_code& err, diagnostics& diag)
  407. {
  408. run(
  409. execute_algo_params{
  410. make_request(std::forward<ExecutionRequest>(req), *st_),
  411. &access::get_impl(result).get_interface()
  412. },
  413. err,
  414. diag
  415. );
  416. }
  417. template <class ExecutionRequest, class ResultsType, class CompletionToken>
  418. auto async_execute(
  419. ExecutionRequest&& req,
  420. ResultsType& result,
  421. diagnostics& diag,
  422. CompletionToken&& token
  423. )
  424. -> decltype(asio::async_initiate<CompletionToken, void(error_code)>(
  425. initiate_execute(get_executor()),
  426. token,
  427. &diag,
  428. engine_.get(),
  429. st_.get(),
  430. std::forward<ExecutionRequest>(req),
  431. &access::get_impl(result).get_interface()
  432. ))
  433. {
  434. return asio::async_initiate<CompletionToken, void(error_code)>(
  435. initiate_execute(get_executor()),
  436. token,
  437. &diag,
  438. engine_.get(),
  439. st_.get(),
  440. std::forward<ExecutionRequest>(req),
  441. &access::get_impl(result).get_interface()
  442. );
  443. }
  444. // Start execution
  445. template <class ExecutionRequest, class ExecutionStateType>
  446. void start_execution(
  447. ExecutionRequest&& req,
  448. ExecutionStateType& exec_st,
  449. error_code& err,
  450. diagnostics& diag
  451. )
  452. {
  453. run(
  454. start_execution_algo_params{
  455. make_request(std::forward<ExecutionRequest>(req), *st_),
  456. &access::get_impl(exec_st).get_interface()
  457. },
  458. err,
  459. diag
  460. );
  461. }
  462. template <class ExecutionRequest, class ExecutionStateType, class CompletionToken>
  463. auto async_start_execution(
  464. ExecutionRequest&& req,
  465. ExecutionStateType& exec_st,
  466. diagnostics& diag,
  467. CompletionToken&& token
  468. )
  469. -> decltype(asio::async_initiate<CompletionToken, void(error_code)>(
  470. initiate_start_execution(get_executor()),
  471. token,
  472. &diag,
  473. engine_.get(),
  474. st_.get(),
  475. std::forward<ExecutionRequest>(req),
  476. &access::get_impl(exec_st).get_interface()
  477. ))
  478. {
  479. return asio::async_initiate<CompletionToken, void(error_code)>(
  480. initiate_start_execution(get_executor()),
  481. token,
  482. &diag,
  483. engine_.get(),
  484. st_.get(),
  485. std::forward<ExecutionRequest>(req),
  486. &access::get_impl(exec_st).get_interface()
  487. );
  488. }
  489. // Read some rows (dynamic)
  490. read_some_rows_dynamic_algo_params make_params_read_some_rows(execution_state& st) const
  491. {
  492. return {&access::get_impl(st).get_interface()};
  493. }
  494. // Read some rows (static)
  495. template <class SpanElementType, class ExecutionState>
  496. read_some_rows_algo_params make_params_read_some_rows_static(
  497. ExecutionState& exec_st,
  498. span<SpanElementType> output
  499. ) const
  500. {
  501. return {
  502. &access::get_impl(exec_st).get_interface(),
  503. access::get_impl(exec_st).make_output_ref(output)
  504. };
  505. }
  506. // Read resultset head
  507. template <class ExecutionStateType>
  508. read_resultset_head_algo_params make_params_read_resultset_head(ExecutionStateType& st) const
  509. {
  510. return {&detail::access::get_impl(st).get_interface()};
  511. }
  512. // Close statement
  513. close_statement_algo_params make_params_close_statement(statement stmt) const { return {stmt.id()}; }
  514. // Run pipeline. Separately compiled to avoid including the pipeline header here
  515. BOOST_MYSQL_DECL
  516. static run_pipeline_algo_params make_params_pipeline(
  517. const pipeline_request& req,
  518. std::vector<stage_response>& response
  519. );
  520. // Exposed for testing
  521. connection_state& get_state() { return *st_; }
  522. };
  523. // To use some completion tokens, like deferred, in C++11, the old macros
  524. // BOOST_ASIO_INITFN_AUTO_RESULT_TYPE are no longer enough.
  525. // Helper typedefs to reduce duplication
  526. template <class AlgoParams, class CompletionToken>
  527. using async_run_t = decltype(std::declval<connection_impl&>().async_run(
  528. std::declval<AlgoParams>(),
  529. std::declval<diagnostics&>(),
  530. std::declval<CompletionToken>()
  531. ));
  532. template <class EndpointType, class CompletionToken>
  533. using async_connect_t = decltype(std::declval<connection_impl&>().async_connect(
  534. std::declval<const EndpointType&>(),
  535. std::declval<const handshake_params&>(),
  536. std::declval<diagnostics&>(),
  537. std::declval<CompletionToken>()
  538. ));
  539. template <class CompletionToken>
  540. using async_connect_v2_t = decltype(std::declval<connection_impl&>().async_connect_v2(
  541. std::declval<const connect_params&>(),
  542. std::declval<diagnostics&>(),
  543. std::declval<CompletionToken>()
  544. ));
  545. template <class ExecutionRequest, class ResultsType, class CompletionToken>
  546. using async_execute_t = decltype(std::declval<connection_impl&>().async_execute(
  547. std::declval<ExecutionRequest>(),
  548. std::declval<ResultsType&>(),
  549. std::declval<diagnostics&>(),
  550. std::declval<CompletionToken>()
  551. ));
  552. template <class ExecutionRequest, class ExecutionStateType, class CompletionToken>
  553. using async_start_execution_t = decltype(std::declval<connection_impl&>().async_start_execution(
  554. std::declval<ExecutionRequest>(),
  555. std::declval<ExecutionStateType&>(),
  556. std::declval<diagnostics&>(),
  557. std::declval<CompletionToken>()
  558. ));
  559. template <class CompletionToken>
  560. using async_handshake_t = async_run_t<handshake_algo_params, CompletionToken>;
  561. template <class CompletionToken>
  562. using async_read_resultset_head_t = async_run_t<read_resultset_head_algo_params, CompletionToken>;
  563. template <class CompletionToken>
  564. using async_read_some_rows_dynamic_t = async_run_t<read_some_rows_dynamic_algo_params, CompletionToken>;
  565. template <class CompletionToken>
  566. using async_prepare_statement_t = async_run_t<prepare_statement_algo_params, CompletionToken>;
  567. template <class CompletionToken>
  568. using async_close_statement_t = async_run_t<close_statement_algo_params, CompletionToken>;
  569. template <class CompletionToken>
  570. using async_set_character_set_t = async_run_t<set_character_set_algo_params, CompletionToken>;
  571. template <class CompletionToken>
  572. using async_ping_t = async_run_t<ping_algo_params, CompletionToken>;
  573. template <class CompletionToken>
  574. using async_reset_connection_t = async_run_t<reset_connection_algo_params, CompletionToken>;
  575. template <class CompletionToken>
  576. using async_quit_connection_t = async_run_t<quit_connection_algo_params, CompletionToken>;
  577. template <class CompletionToken>
  578. using async_close_connection_t = async_run_t<close_connection_algo_params, CompletionToken>;
  579. template <class CompletionToken>
  580. using async_run_pipeline_t = async_run_t<run_pipeline_algo_params, CompletionToken>;
  581. } // namespace detail
  582. } // namespace mysql
  583. } // namespace boost
  584. #ifdef BOOST_MYSQL_HEADER_ONLY
  585. #include <boost/mysql/impl/connection_impl.ipp>
  586. #endif
  587. #endif