diff --git a/.appveyor.yml b/.appveyor.yml index 2e0c57a7..6778ff46 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -16,8 +16,8 @@ environment: global: B2_CI_VERSION: 1 B2_CXXFLAGS: -permissive- - B2_VARIANR: release,debug - B2_CXXSTD: 17 + B2_VARIANT: release,debug + B2_CXXSTD: 11,14,17 matrix: # CMake - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 diff --git a/.travis.yml b/.travis.yml index 59f9304b..3486ea37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,8 +34,84 @@ __osx_defaults: &__osx_defaults language: cpp script: bash -e -x tools/build_unix.sh +env: + global: + - CMAKE_CXX_STANDARD=17 + - DATABASE=mysql:8 + matrix: include: + # CMake OSX builds + - name: cmake_osx_clang_debug + <<: *__osx_defaults + env: + - CMAKE_BUILD_TYPE=Debug + - USE_COVERAGE=1 + - name: cmake_osx_clang_release + <<: *__osx_defaults + env: + - CMAKE_BUILD_TYPE=Release + + # B2 builds + - name: b2_linux_clang + <<: *__linux_defaults + env: + - B2_CXXSTD=11,14,17 + - B2_TOOLSET=clang + - B2_VARIANT=debug,release + - name: b2_osx_clang + <<: *__osx_defaults + env: + - B2_CXXSTD=11,14,17 + - B2_TOOLSET=clang + - B2_VARIANT=debug,release + # Linux CMake builds + - name: cmake_linux_clang10_debug + <<: *__linux_defaults + compiler: clang + env: + - CMAKE_BUILD_TYPE=Debug + - CMAKE_CXX_STANDARD=20 + - USE_COVERAGE=1 + - CMAKE_CXX_FLAGS="-stdlib=libc++ -DBOOST_ASIO_DISABLE_CONCEPTS" + - CMAKE_OPTIONS="-DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10" + install: + - sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" + - sudo apt install clang-10 libc++-10-dev libc++abi-10-dev + + - name: cmake_linux_gcc_debug + <<: *__linux_defaults + compiler: gcc + env: + - CMAKE_BUILD_TYPE=Debug + - USE_VALGRIND=1 + - USE_COVERAGE=1 + - name: cmake_linux_gcc_release + <<: *__linux_defaults + compiler: gcc + env: + - CMAKE_BUILD_TYPE=Release + - name: cmake_linux_clang_debug + <<: *__linux_defaults + compiler: clang + env: + - CMAKE_BUILD_TYPE=Debug + - USE_VALGRIND=1 + - USE_COVERAGE=1 + - name: cmake_linux_clang_release + <<: *__linux_defaults + compiler: clang + env: + - CMAKE_BUILD_TYPE=Release + - name: cmake_linux_clang_debug_cxx11 + <<: *__linux_defaults + compiler: clang + env: + - CMAKE_BUILD_TYPE=Debug + - CMAKE_CXX_STANDARD=11 + - USE_VALGRIND=1 + - USE_COVERAGE=1 + # Other database version builds (CMake Linux) - name: cmake_linux_clang_debug_mysql5 <<: *__linux_defaults @@ -45,6 +121,7 @@ matrix: - DATABASE=mysql:5 - USE_VALGRIND=1 - USE_COVERAGE=1 + - name: cmake_linux_clang_debug_mariadb <<: *__linux_defaults compiler: clang @@ -54,74 +131,5 @@ matrix: - USE_VALGRIND=1 - USE_COVERAGE=1 - # B2 builds - - name: b2_linux_clang_mysql8 - <<: *__linux_defaults - env: - - DATABASE=mysql:8 - - B2_CXXSTD=17 - - B2_TOOLSET=clang - - B2_VARIANT=debug,release - - name: b2_osx_clang_mysql8 - <<: *__osx_defaults - env: - - DATABASE=mysql:8 - - B2_CXXSTD=17 - - B2_TOOLSET=clang - - B2_VARIANT=debug,release - # Linux CMake builds - - name: cmake_linux_clang10_debug_mysql8 - <<: *__linux_defaults - compiler: clang - env: - - CMAKE_BUILD_TYPE=Debug - - DATABASE=mysql:8 - - USE_COVERAGE=1 - - CMAKE_CXX_FLAGS="-stdlib=libc++ -DBOOST_ASIO_DISABLE_CONCEPTS" - - CMAKE_OPTIONS="-DCMAKE_C_COMPILER=clang-10 -DCMAKE_CXX_COMPILER=clang++-10 -DCMAKE_CXX_STANDARD=20" - install: - - sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" - - sudo apt install clang-10 libc++-10-dev libc++abi-10-dev - - name: cmake_linux_gcc_debug_mysql8 - <<: *__linux_defaults - compiler: gcc - env: - - CMAKE_BUILD_TYPE=Debug - - DATABASE=mysql:8 - - USE_VALGRIND=1 - - USE_COVERAGE=1 - - name: cmake_linux_gcc_release_mysql8 - <<: *__linux_defaults - compiler: gcc - env: - - CMAKE_BUILD_TYPE=Release - - DATABASE=mysql:8 - - name: cmake_linux_clang_debug_mysql8 - <<: *__linux_defaults - compiler: clang - env: - - CMAKE_BUILD_TYPE=Debug - - DATABASE=mysql:8 - - USE_VALGRIND=1 - - USE_COVERAGE=1 - - name: cmake_linux_clang_release_mysql8 - <<: *__linux_defaults - compiler: clang - env: - - CMAKE_BUILD_TYPE=Release - - DATABASE=mysql:8 - - # CMake OSX builds - - name: cmake_osx_clang_debug_mysql8 - <<: *__osx_defaults - env: - - CMAKE_BUILD_TYPE=Debug - - DATABASE=mysql:8 - - USE_COVERAGE=1 - - name: cmake_osx_clang_release_mysql8 - <<: *__osx_defaults - env: - - CMAKE_BUILD_TYPE=Release - - DATABASE=mysql:8 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index b0a63332..ab1c198d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ target_include_directories( target_compile_features( boost_mysql INTERFACE - cxx_std_17 + cxx_std_11 ) # If we're on MSVC, the value of the __cplusplus macro is incorrect. diff --git a/README.md b/README.md index a994e9eb..30811ffd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ -----------|---------|---------- [![Build Status](https://travis-ci.com/anarthal/mysql-asio.png?branch=master)](https://github.com/anarthal/mysql-asio) | [![Build status](https://ci.appveyor.com/api/projects/status/slqnb8mt91v33p1y/branch/master?svg=true)](https://ci.appveyor.com/project/anarthal/mysql-asio/branch/master) | [![codecov](https://codecov.io/gh/anarthal/mysql-asio/branch/master/graph/badge.svg)](https://codecov.io/gh/anarthal/mysql-asio/branch/master) -Boost.MySQL (former MySQL.Asio) is a C++17 client for the MySQL database server, based on Boost.Asio. +Boost.MySQL (former MySQL.Asio) is a C++11 client for the MySQL database server, based on Boost.Asio. This library is in the process of being proposed for Boost. ## Why another MySQL C++ client? @@ -12,8 +12,8 @@ This library is in the process of being proposed for Boost. - It is fully compatible with Boost.Asio and integrates well with any other library in the Boost.Asio ecosystem (like Boost.Beast). - It supports Boost.Asio's universal asynchronous model, which means you can - go asyncrhonous using callbacks, futures or coroutines. -- It is written in modern C++ (C++17) and takes advantage of the latest language + go asyncrhonous using callbacks, futures or coroutines (including C++20 coroutines). +- It is written in modern C++ (C++11) and takes advantage of the latest language features and standard library additions. - It is header only. @@ -31,12 +31,8 @@ Finally, link your target against the **Boost::mysql** interface library, and yo ## Requirements -- C++17 capable compiler (tested with gcc 7.4, clang 7.0, Apple clang 11.0, MSVC 19.25). -- Boost 1.72 or higher. The following Boost libraries are used: - - Boost.Asio (and in consequence, Boost.System). - - Boost.Lexical_Cast. - - Boost.Endian. - - Boost.Coroutine (only if you are using boost::asio::yield_context). +- C++11 capable compiler (tested with gcc 7.4, clang 7.0, Apple clang 11.0, MSVC 19.25). +- Boost 1.72 or higher. - OpenSSL. - CMake 3.13.0 or higher, if using CMake to build against the library (this is the preferred way). - Howard Hinnant's date library (https://github.com/HowardHinnant/date) v2.4.1 or higher. diff --git a/TODO.txt b/TODO.txt index 4e4365f9..1c7401f1 100644 --- a/TODO.txt +++ b/TODO.txt @@ -5,6 +5,13 @@ Better docs BoostBook Automate the process Make undocumented enum values show (for collation) +License issues + date + gtest +CI tests for older compilers + GCCs + Clangs + MSVCs Multiresultset Text protocol Binary protocol (stored procedures) @@ -33,19 +40,11 @@ Usability Other possible features CLIENT_SESSION_TRACK CLIENT_OPTIONAL_RESULTSET_METADATA - Lower C++ std requirements Status flags accessors in resultset (for OK_Packet) Technical debt - Increase compiler test coverage + Increase int serialization test coverage Increase DB version test coverage Conan for dependencies - Move to most recent Boost - Remove int1_signed, int2_signed... - Remove int1, int2, int4, int8 - More coverage for serialization of int types - Use a proper float endianness reverse - Use boost::variant2 - Add constexpr to value::to_variant() Tests for field_metadata::character_set Refactor database type cases, and remove unit tests superseeded by them Random input tests diff --git a/doc/main_page.hpp b/doc/main_page.hpp index 614b5157..1178378d 100644 --- a/doc/main_page.hpp +++ b/doc/main_page.hpp @@ -8,15 +8,15 @@ /** * \mainpage User manual * - * Boost.MySQL is a C++17 client for the MySQL database server, based on Boost.Asio. + * Boost.MySQL is a C++11 client for the MySQL database server, based on Boost.Asio. * * \section why Why another MySQL C++ client? * * - It is fully compatible with Boost.Asio and integrates well with any other * library in the Boost.Asio ecosystem (like Boost.Beast). * - It supports Boost.Asio's universal asynchronous model, which means you can - * go asyncrhonous using callbacks, futures or coroutines. - * - It is written in modern C++ (C++17) and takes advantage of the latest language + * go asyncrhonous using callbacks, futures or coroutines (including C++20 coroutines). + * - It is written in modern C++ (C++11) and takes advantage of the latest language * features and standard library additions. * - It is header only. * @@ -34,11 +34,8 @@ * * \section Requirements * - * - C++17 capable compiler (tested with gcc 7.4, clang 7.0, Apple clang 11.0, MSVC 19.25). - * - Boost 1.70 or higher. The following Boost libraries are used: - * - Boost.Asio (and in consequence, Boost.System). - * - Boost.Lexical_Cast. - * - Boost.Endian. + * - C++11 capable compiler (tested with gcc 7.4, clang 7.0, Apple clang 11.0, MSVC 19.25). + * - Boost 1.72 or higher. * - OpenSSL. * - CMake 3.13.0 or higher, if using CMake to build against the library (this is the preferred way). * - Howard Hinnant's date library (https://github.com/HowardHinnant/date) v2.4.1 or higher. diff --git a/example/metadata.cpp b/example/metadata.cpp index 0fd91bff..a77258ed 100644 --- a/example/metadata.cpp +++ b/example/metadata.cpp @@ -22,6 +22,13 @@ * please have a look to the query_sync.cpp example. */ +#define ASSERT(expr) \ + if (!(expr)) \ + { \ + std::cerr << "Assertion failed: " #expr << std::endl; \ + exit(1); \ + } + void main_impl(int argc, char** argv) { if (argc != 3) @@ -54,29 +61,29 @@ void main_impl(int argc, char** argv) * You can retrieve the field name, type, number of decimals, * suggested display width, whether the field is part of a key... */ - assert(result.fields().size() == 2); + ASSERT(result.fields().size() == 2); - [[maybe_unused]] const boost::mysql::field_metadata& company_name = result.fields()[0]; - assert(company_name.database() == "boost_mysql_examples"); // database name - assert(company_name.table() == "comp"); // the alias we assigned to the table in the query - assert(company_name.original_table() == "company"); // the original table name - assert(company_name.field_name() == "company_name"); // the name of the field in the query - assert(company_name.original_field_name() == "name"); // the name of the physical field in the table - assert(company_name.type() == boost::mysql::field_type::varchar); // we created the field as a VARCHAR - assert(!company_name.is_primary_key()); // field is not a primary key - assert(!company_name.is_auto_increment()); // field is not AUTO_INCREMENT - assert(company_name.is_not_null()); // field may not be NULL + const boost::mysql::field_metadata& company_name = result.fields()[0]; + ASSERT(company_name.database() == "boost_mysql_examples"); // database name + ASSERT(company_name.table() == "comp"); // the alias we assigned to the table in the query + ASSERT(company_name.original_table() == "company"); // the original table name + ASSERT(company_name.field_name() == "company_name"); // the name of the field in the query + ASSERT(company_name.original_field_name() == "name"); // the name of the physical field in the table + ASSERT(company_name.type() == boost::mysql::field_type::varchar); // we created the field as a VARCHAR + ASSERT(!company_name.is_primary_key()); // field is not a primary key + ASSERT(!company_name.is_auto_increment()); // field is not AUTO_INCREMENT + ASSERT(company_name.is_not_null()); // field may not be NULL - [[maybe_unused]] const boost::mysql::field_metadata& employee_id = result.fields()[1]; - assert(employee_id.database() == "boost_mysql_examples"); // database name - assert(employee_id.table() == "emp"); // the alias we assigned to the table in the query - assert(employee_id.original_table() == "employee"); // the original table name - assert(employee_id.field_name() == "employee_id"); // the name of the field in the query - assert(employee_id.original_field_name() == "id"); // the name of the physical field in the table - assert(employee_id.type() == boost::mysql::field_type::int_); // we created the field as INT - assert(employee_id.is_primary_key()); // field is a primary key - assert(employee_id.is_auto_increment()); // we declared the field as AUTO_INCREMENT - assert(employee_id.is_not_null()); // field cannot be NULL + const boost::mysql::field_metadata& employee_id = result.fields()[1]; + ASSERT(employee_id.database() == "boost_mysql_examples"); // database name + ASSERT(employee_id.table() == "emp"); // the alias we assigned to the table in the query + ASSERT(employee_id.original_table() == "employee"); // the original table name + ASSERT(employee_id.field_name() == "employee_id"); // the name of the field in the query + ASSERT(employee_id.original_field_name() == "id"); // the name of the physical field in the table + ASSERT(employee_id.type() == boost::mysql::field_type::int_); // we created the field as INT + ASSERT(employee_id.is_primary_key()); // field is a primary key + ASSERT(employee_id.is_auto_increment()); // we declared the field as AUTO_INCREMENT + ASSERT(employee_id.is_not_null()); // field cannot be NULL // Close the connection conn.close(); diff --git a/example/prepared_statements.cpp b/example/prepared_statements.cpp index cc7db267..a6b08338 100644 --- a/example/prepared_statements.cpp +++ b/example/prepared_statements.cpp @@ -22,6 +22,13 @@ * please have a look to the query_sync.cpp example. */ +#define ASSERT(expr) \ + if (!(expr)) \ + { \ + std::cerr << "Assertion failed: " #expr << std::endl; \ + exit(1); \ + } + void main_impl(int argc, char** argv) { if (argc != 3) @@ -63,11 +70,11 @@ void main_impl(int argc, char** argv) */ const char* salary_getter_sql = "SELECT salary FROM employee WHERE first_name = ?"; boost::mysql::tcp_prepared_statement salary_getter = conn.prepare_statement(salary_getter_sql); - assert(salary_getter.num_params() == 1); // num_params() returns the number of parameters (question marks) + ASSERT(salary_getter.num_params() == 1); // num_params() returns the number of parameters (question marks) const char* salary_updater_sql = "UPDATE employee SET salary = ? WHERE first_name = ?"; boost::mysql::tcp_prepared_statement salary_updater = conn.prepare_statement(salary_updater_sql); - assert(salary_updater.num_params() == 2); + ASSERT(salary_updater.num_params() == 2); /* * Once a statement has been prepared, it can be executed as many times as @@ -88,8 +95,8 @@ void main_impl(int argc, char** argv) */ boost::mysql::tcp_resultset result = salary_getter.execute(boost::mysql::make_values("Efficient")); std::vector salaries = result.fetch_all(); // Get all the results - assert(salaries.size() == 1); - [[maybe_unused]] double salary = salaries[0].values().at(0).get(); // First row, first column + ASSERT(salaries.size() == 1); + double salary = salaries[0].values().at(0).get(); // First row, first column std::cout << "The salary before the payrise was: " << salary << std::endl; /** @@ -109,9 +116,9 @@ void main_impl(int argc, char** argv) */ result = salary_getter.execute(boost::mysql::make_values("Efficient")); salaries = result.fetch_all(); - assert(salaries.size() == 1); + ASSERT(salaries.size() == 1); salary = salaries[0].values().at(0).get(); - assert(salary == 35000); // Our update took place, and the dev got his pay rise + ASSERT(salary == 35000); // Our update took place, and the dev got his pay rise std::cout << "The salary after the payrise was: " << salary << std::endl; /** diff --git a/example/query_async_callbacks.cpp b/example/query_async_callbacks.cpp index af9dbeaf..c0fc964b 100644 --- a/example/query_async_callbacks.cpp +++ b/example/query_async_callbacks.cpp @@ -47,11 +47,18 @@ using boost::mysql::owning_row; * of the handler signatures. */ +#define ASSERT(expr) \ + if (!(expr)) \ + { \ + std::cerr << "Assertion failed: " #expr << std::endl; \ + exit(1); \ + } + void print_employee(const boost::mysql::row& employee) { std::cout << "Employee '" - << employee.values()[0] << " " // first_name (type std::string_view) - << employee.values()[1] << "' earns " // last_name (type std::string_view) + << employee.values()[0] << " " // first_name (type boost::string_view) + << employee.values()[1] << "' earns " // last_name (type boost::string_view) << employee.values()[2] << " dollars yearly\n"; // salary (type double) } @@ -114,9 +121,9 @@ public: { const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = 'Slacker'"; connection.async_query(sql, additional_info, - [this](error_code err, [[maybe_unused]] tcp_resultset&& result) { + [this](error_code err, tcp_resultset&& result) { die_on_error(err, additional_info); - assert(result.fields().size() == 0); + ASSERT(result.fields().size() == 0); query_intern(); }); } @@ -129,9 +136,9 @@ public: resultset = std::move(result); resultset.async_fetch_all(additional_info, [this](error_code err, const std::vector& rows) { die_on_error(err, additional_info); - assert(rows.size() == 1); - [[maybe_unused]] auto salary = rows[0].values()[0].get(); - assert(salary == 15000); + ASSERT(rows.size() == 1); + auto salary = rows[0].values()[0].get(); + ASSERT(salary == 15000); close(); }); }); @@ -145,7 +152,7 @@ public: }); } - auto& context() { return ctx; } + void run() { ctx.run(); } }; void main_impl(int argc, char** argv) @@ -158,7 +165,7 @@ void main_impl(int argc, char** argv) application app (argv[1], argv[2]); app.start(); // starts the async chain - app.context().run(); // run the asio::io_context until the async chain finishes + app.run(); // run the asio::io_context until the async chain finishes } int main(int argc, char** argv) diff --git a/example/query_async_coroutines.cpp b/example/query_async_coroutines.cpp index b20dc281..93db2526 100644 --- a/example/query_async_coroutines.cpp +++ b/example/query_async_coroutines.cpp @@ -47,8 +47,8 @@ using boost::mysql::error_info; void print_employee(const boost::mysql::row& employee) { std::cout << "Employee '" - << employee.values()[0] << " " // first_name (type std::string_view) - << employee.values()[1] << "' earns " // last_name (type std::string_view) + << employee.values()[0] << " " // first_name (type boost::string_view) + << employee.values()[1] << "' earns " // last_name (type boost::string_view) << employee.values()[2] << " dollars yearly\n"; // salary (type double) } diff --git a/example/query_async_coroutinescpp20.cpp b/example/query_async_coroutinescpp20.cpp index 76e43f33..2306cd50 100644 --- a/example/query_async_coroutinescpp20.cpp +++ b/example/query_async_coroutinescpp20.cpp @@ -57,8 +57,8 @@ using boost::mysql::error_info; void print_employee(const boost::mysql::row& employee) { std::cout << "Employee '" - << employee.values()[0] << " " // first_name (type std::string_view) - << employee.values()[1] << "' earns " // last_name (type std::string_view) + << employee.values()[0] << " " // first_name (type boost::string_view) + << employee.values()[1] << "' earns " // last_name (type boost::string_view) << employee.values()[2] << " dollars yearly\n"; // salary (type double) } diff --git a/example/query_async_futures.cpp b/example/query_async_futures.cpp index b43ac31e..f71f73d2 100644 --- a/example/query_async_futures.cpp +++ b/example/query_async_futures.cpp @@ -48,8 +48,8 @@ using boost::asio::use_future; void print_employee(const boost::mysql::row& employee) { std::cout << "Employee '" - << employee.values()[0] << " " // first_name (type std::string_view) - << employee.values()[1] << "' earns " // last_name (type std::string_view) + << employee.values()[0] << " " // first_name (type boost::string_view) + << employee.values()[1] << "' earns " // last_name (type boost::string_view) << employee.values()[2] << " dollars yearly\n"; // salary (type double) } diff --git a/example/query_sync.cpp b/example/query_sync.cpp index 1fd4d885..9ad27f80 100644 --- a/example/query_sync.cpp +++ b/example/query_sync.cpp @@ -18,6 +18,13 @@ * This example uses synchronous functions and handles errors using exceptions. */ +#define ASSERT(expr) \ + if (!(expr)) \ + { \ + std::cerr << "Assertion failed: " #expr << std::endl; \ + exit(1); \ + } + /** * Prints an employee to std::cout. An employee here is a mysql::row, * which represents a row returned by a SQL query. You can access the values in @@ -32,8 +39,8 @@ void print_employee(const boost::mysql::row& employee) { std::cout << "Employee '" - << employee.values()[0] << " " // first_name (type std::string_view) - << employee.values()[1] << "' earns " // last_name (type std::string_view) + << employee.values()[0] << " " // first_name (type boost::string_view) + << employee.values()[1] << "' earns " // last_name (type boost::string_view) << employee.values()[2] << " dollars yearly\n"; // salary (type double) } @@ -102,14 +109,14 @@ void main_impl(int argc, char** argv) // resultset will have no fields and no rows sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'"; result = conn.query(sql); - assert(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields + ASSERT(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields // Check we have updated our poor intern salary result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'"); auto rows = result.fetch_all(); - assert(rows.size() == 1); - [[maybe_unused]] double salary = rows[0].values()[0].get(); - assert(salary == 10000); + ASSERT(rows.size() == 1); + double salary = rows[0].values()[0].get(); + ASSERT(salary == 10000); // Close the connection. This notifies the MySQL we want to log out // and then closes the underlying socket. This operation implies a network diff --git a/example/unix_socket.cpp b/example/unix_socket.cpp index 5220c398..5fbede84 100644 --- a/example/unix_socket.cpp +++ b/example/unix_socket.cpp @@ -23,11 +23,18 @@ void print_employee(const boost::mysql::row& employee) { std::cout << "Employee '" - << employee.values()[0] << " " // first_name (type std::string_view) - << employee.values()[1] << "' earns " // last_name (type std::string_view) + << employee.values()[0] << " " // first_name (type boost::string_view) + << employee.values()[1] << "' earns " // last_name (type boost::string_view) << employee.values()[2] << " dollars yearly\n"; // salary (type double) } +#define ASSERT(expr) \ + if (!(expr)) \ + { \ + std::cerr << "Assertion failed: " #expr << std::endl; \ + exit(1); \ + } + // UNIX sockets are only available in, er, UNIX systems. Typedefs for // UNIX socket-based connections are only available in UNIX systems. // Check for BOOST_ASIO_HAS_LOCAL_SOCKETS to know if UNIX socket @@ -85,14 +92,14 @@ void main_impl(int argc, char** argv) // resultset will have no fields and no rows sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'"; result = conn.query(sql); - assert(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields + ASSERT(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields // Check we have updated our poor intern salary result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'"); auto rows = result.fetch_all(); - assert(rows.size() == 1); - [[maybe_unused]] double salary = rows[0].values()[0].get(); - assert(salary == 10000); + ASSERT(rows.size() == 1); + double salary = rows[0].values()[0].get(); + ASSERT(salary == 10000); // Notify the MySQL server we want to quit, then close the underlying connection. conn.close(); diff --git a/include/boost/mysql/connection.hpp b/include/boost/mysql/connection.hpp index 15c75147..0de4a54c 100644 --- a/include/boost/mysql/connection.hpp +++ b/include/boost/mysql/connection.hpp @@ -186,10 +186,10 @@ public: * connection (like connection::query, connection::prepare_statement or * prepared_statement::execute). Otherwise, the results are undefined. */ - resultset query(std::string_view query_string, error_code&, error_info&); + resultset query(boost::string_view query_string, error_code&, error_info&); /// Executes a SQL text query (sync with exceptions version). - resultset query(std::string_view query_string); + resultset query(boost::string_view query_string); /** * \brief Executes a SQL text query (async version without error_info). @@ -203,7 +203,7 @@ public: > BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, resultset)) async_query( - std::string_view query_string, + boost::string_view query_string, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ) { @@ -218,7 +218,7 @@ public: > BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, resultset)) async_query( - std::string_view query_string, + boost::string_view query_string, error_info& output_info, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ); @@ -234,10 +234,10 @@ public: * prepare_statement was called is alive and open. See prepared_statement docs * for more info. */ - prepared_statement prepare_statement(std::string_view statement, error_code&, error_info&); + prepared_statement prepare_statement(boost::string_view statement, error_code&, error_info&); /// Prepares a statement (sync with exceptions version). - prepared_statement prepare_statement(std::string_view statement); + prepared_statement prepare_statement(boost::string_view statement); /** * \brief Prepares a statement (async version without error_info). @@ -251,7 +251,7 @@ public: > BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, prepared_statement)) async_prepare_statement( - std::string_view statement, + boost::string_view statement, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ) { @@ -266,7 +266,7 @@ public: > BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, prepared_statement)) async_prepare_statement( - std::string_view statement, + boost::string_view statement, error_info& output_info, CompletionToken&& token BOOST_ASIO_DEFAULT_COMPLETION_TOKEN(executor_type) ); diff --git a/include/boost/mysql/connection_params.hpp b/include/boost/mysql/connection_params.hpp index f577377c..80c5f165 100644 --- a/include/boost/mysql/connection_params.hpp +++ b/include/boost/mysql/connection_params.hpp @@ -8,7 +8,7 @@ #ifndef BOOST_MYSQL_CONNECTION_PARAMS_HPP #define BOOST_MYSQL_CONNECTION_PARAMS_HPP -#include +#include #include "boost/mysql/collation.hpp" /** @@ -60,17 +60,17 @@ public: */ class connection_params { - std::string_view username_; - std::string_view password_; - std::string_view database_; + boost::string_view username_; + boost::string_view password_; + boost::string_view database_; collation connection_collation_; ssl_options ssl_; public: /// Initializing constructor connection_params( - std::string_view username, ///< Username to authenticate as - std::string_view password, ///< Password for that username, possibly empty. - std::string_view db = "", ///< Database to use, or empty string for no database. + boost::string_view username, ///< Username to authenticate as + boost::string_view password, ///< Password for that username, possibly empty. + boost::string_view db = "", ///< Database to use, or empty string for no database. collation connection_col = collation::utf8_general_ci, ///< The default character set and collation for the connection. const ssl_options& opts = ssl_options() ///< The TLS options to use with this connection. ) : @@ -83,22 +83,22 @@ public: } /// Retrieves the username. - std::string_view username() const noexcept { return username_; } + boost::string_view username() const noexcept { return username_; } /// Sets the username. - void set_username(std::string_view value) noexcept { username_ = value; } + void set_username(boost::string_view value) noexcept { username_ = value; } /// Retrieves the password. - std::string_view password() const noexcept { return password_; } + boost::string_view password() const noexcept { return password_; } /// Sets the password - void set_password(std::string_view value) noexcept { password_ = value; } + void set_password(boost::string_view value) noexcept { password_ = value; } /// Retrieves the database. - std::string_view database() const noexcept { return database_; } + boost::string_view database() const noexcept { return database_; } /// Sets the database - void set_database(std::string_view value) noexcept { database_ = value; } + void set_database(boost::string_view value) noexcept { database_ = value; } /// Retrieves the connection collation. collation connection_collation() const noexcept { return connection_collation_; } diff --git a/include/boost/mysql/detail/auth/auth_calculator.hpp b/include/boost/mysql/detail/auth/auth_calculator.hpp index f8837cbf..34b7668a 100644 --- a/include/boost/mysql/detail/auth/auth_calculator.hpp +++ b/include/boost/mysql/detail/auth/auth_calculator.hpp @@ -9,9 +9,10 @@ #define BOOST_MYSQL_DETAIL_AUTH_AUTH_CALCULATOR_HPP #include "boost/mysql/error.hpp" +#include "boost/mysql/detail/auxiliar/bytestring.hpp" #include -#include -#include +#include +#include namespace boost { namespace mysql { @@ -20,31 +21,34 @@ namespace detail { struct authentication_plugin { using calculator_signature = error_code (*)( - std::string_view password, - std::string_view challenge, + boost::string_view password, + boost::string_view challenge, bool use_ssl, - std::string& output + bytestring& output ); - std::string_view name; + boost::string_view name; calculator_signature calculator; }; class auth_calculator { const authentication_plugin* plugin_ {nullptr}; - std::string response_; + bytestring response_; - inline static const authentication_plugin* find_plugin(std::string_view name); + inline static const authentication_plugin* find_plugin(boost::string_view name); public: inline error_code calculate( - std::string_view plugin_name, - std::string_view password, - std::string_view challenge, + boost::string_view plugin_name, + boost::string_view password, + boost::string_view challenge, bool use_ssl ); - std::string_view response() const noexcept { return response_; } - std::string_view plugin_name() const noexcept + boost::string_view response() const noexcept + { + return boost::string_view(reinterpret_cast(response_.data()), response_.size()); + } + boost::string_view plugin_name() const noexcept { assert(plugin_); return plugin_->name; diff --git a/include/boost/mysql/detail/auth/caching_sha2_password.hpp b/include/boost/mysql/detail/auth/caching_sha2_password.hpp index c2c1282b..e67a3074 100644 --- a/include/boost/mysql/detail/auth/caching_sha2_password.hpp +++ b/include/boost/mysql/detail/auth/caching_sha2_password.hpp @@ -9,7 +9,7 @@ #define BOOST_MYSQL_DETAIL_AUTH_CACHING_SHA2_PASSWORD_HPP #include -#include +#include #include "boost/mysql/error.hpp" namespace boost { @@ -27,10 +27,10 @@ namespace caching_sha2_password { // auth without an SSL connection, but that requires the server public key, // and we do not implement that. inline error_code compute_response( - std::string_view password, - std::string_view challenge, + boost::string_view password, + boost::string_view challenge, bool use_ssl, - std::string& output + bytestring& output ); } // caching_sha2_password diff --git a/include/boost/mysql/detail/auth/impl/auth_calculator.ipp b/include/boost/mysql/detail/auth/impl/auth_calculator.ipp index 878625cb..189a9c5d 100644 --- a/include/boost/mysql/detail/auth/impl/auth_calculator.ipp +++ b/include/boost/mysql/detail/auth/impl/auth_calculator.ipp @@ -10,18 +10,19 @@ #include "boost/mysql/detail/auth/mysql_native_password.hpp" #include "boost/mysql/detail/auth/caching_sha2_password.hpp" +#include "boost/mysql/detail/auxiliar/make_string_view.hpp" namespace boost { namespace mysql { namespace detail { constexpr authentication_plugin mysql_native_password_plugin { - "mysql_native_password", + make_string_view("mysql_native_password"), &mysql_native_password::compute_response }; constexpr authentication_plugin caching_sha2_password_plugin { - "caching_sha2_password", + make_string_view("caching_sha2_password"), &caching_sha2_password::compute_response }; @@ -36,7 +37,7 @@ constexpr std::array all_authentication_plugins inline const boost::mysql::detail::authentication_plugin* boost::mysql::detail::auth_calculator::find_plugin( - std::string_view name + boost::string_view name ) { auto it = std::find_if( @@ -49,9 +50,9 @@ boost::mysql::detail::auth_calculator::find_plugin( inline boost::mysql::error_code boost::mysql::detail::auth_calculator::calculate( - std::string_view plugin_name, - std::string_view password, - std::string_view challenge, + boost::string_view plugin_name, + boost::string_view password, + boost::string_view challenge, bool use_ssl ) { @@ -62,7 +63,7 @@ boost::mysql::detail::auth_calculator::calculate( // Blank password: we should just return an empty auth string if (password.empty()) { - response_ = ""; + response_.clear(); return error_code(); } else diff --git a/include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp b/include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp index c538aa82..3a651cef 100644 --- a/include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp +++ b/include/boost/mysql/detail/auth/impl/caching_sha2_password.ipp @@ -10,6 +10,7 @@ #include #include +#include "boost/mysql/detail/auxiliar/make_string_view.hpp" namespace boost { namespace mysql { @@ -18,17 +19,17 @@ namespace caching_sha2_password { constexpr std::size_t challenge_length = 20; constexpr std::size_t response_length = 32; -constexpr std::string_view perform_full_auth = "\4"; +constexpr boost::string_view perform_full_auth = make_string_view("\4"); // challenge must point to challenge_length bytes of data // output must point to response_length bytes of data inline void compute_auth_string( - std::string_view password, + boost::string_view password, const void* challenge, void* output ) { - static_assert(response_length == SHA256_DIGEST_LENGTH); + static_assert(response_length == SHA256_DIGEST_LENGTH, "Buffer size mismatch"); // SHA(SHA(password_sha) concat challenge) XOR password_sha // hash1 = SHA(pass) @@ -61,10 +62,10 @@ inline void compute_auth_string( inline boost::mysql::error_code boost::mysql::detail::caching_sha2_password::compute_response( - std::string_view password, - std::string_view challenge, + boost::string_view password, + boost::string_view challenge, bool use_ssl, - std::string& output + bytestring& output ) { if (challenge == perform_full_auth) @@ -73,7 +74,7 @@ boost::mysql::detail::caching_sha2_password::compute_response( { return make_error_code(errc::auth_plugin_requires_ssl); } - output = password; + output.assign(password.begin(), password.end()); output.push_back(0); return error_code(); } diff --git a/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp b/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp index 77598d86..cac6f78e 100644 --- a/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp +++ b/include/boost/mysql/detail/auth/impl/mysql_native_password.ipp @@ -23,7 +23,7 @@ constexpr std::size_t response_length = 20; // output must point to response_length bytes of data // SHA1( password ) XOR SHA1( "20-bytes random data from server" SHA1( SHA1( password ) ) ) inline void compute_auth_string( - std::string_view password, + boost::string_view password, const void* challenge, void* output ) @@ -41,7 +41,7 @@ inline void compute_auth_string( SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1); // XOR - static_assert(response_length == SHA_DIGEST_LENGTH); + static_assert(response_length == SHA_DIGEST_LENGTH, "Buffer size mismatch"); for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i) { static_cast(output)[i] = password_sha1[i] ^ salted_sha1[i]; @@ -56,10 +56,10 @@ inline void compute_auth_string( inline boost::mysql::error_code boost::mysql::detail::mysql_native_password::compute_response( - std::string_view password, - std::string_view challenge, + boost::string_view password, + boost::string_view challenge, bool, // use_ssl - std::string& output + bytestring& output ) { // Check challenge size diff --git a/include/boost/mysql/detail/auth/mysql_native_password.hpp b/include/boost/mysql/detail/auth/mysql_native_password.hpp index 65e330e9..f9e8ad6e 100644 --- a/include/boost/mysql/detail/auth/mysql_native_password.hpp +++ b/include/boost/mysql/detail/auth/mysql_native_password.hpp @@ -9,7 +9,7 @@ #define BOOST_MYSQL_DETAIL_AUTH_MYSQL_NATIVE_PASSWORD_HPP #include -#include +#include #include "boost/mysql/error.hpp" namespace boost { @@ -20,10 +20,10 @@ namespace mysql_native_password { // Authorization for this plugin is always challenge (nonce) -> response // (hashed password). inline error_code compute_response( - std::string_view password, - std::string_view challenge, + boost::string_view password, + boost::string_view challenge, bool use_ssl, - std::string& output + bytestring& output ); diff --git a/include/boost/mysql/detail/auxiliar/make_string_view.hpp b/include/boost/mysql/detail/auxiliar/make_string_view.hpp new file mode 100644 index 00000000..3fadb88e --- /dev/null +++ b/include/boost/mysql/detail/auxiliar/make_string_view.hpp @@ -0,0 +1,30 @@ +// +// Copyright (c) 2019-2020 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_AUXILIAR_MAKE_STRING_VIEW_HPP +#define BOOST_MYSQL_DETAIL_AUXILIAR_MAKE_STRING_VIEW_HPP + +#include + +namespace boost { +namespace mysql { +namespace detail { + +template +constexpr boost::string_view make_string_view(const char(&buff)[N]) noexcept +{ + static_assert(N>=1, "Expected a C-array literal"); + return boost::string_view(buff, N-1); // discard null terminator +} + +} // detail +} // mysql +} // boost + + + +#endif diff --git a/include/boost/mysql/detail/auxiliar/static_string.hpp b/include/boost/mysql/detail/auxiliar/static_string.hpp index 3b25cb93..cd73156e 100644 --- a/include/boost/mysql/detail/auxiliar/static_string.hpp +++ b/include/boost/mysql/detail/auxiliar/static_string.hpp @@ -11,7 +11,7 @@ // A very simplified variable-length string with fixed max-size #include -#include +#include #include #include #include @@ -25,22 +25,20 @@ class static_string { std::array buffer_; std::size_t size_; - - void size_check([[maybe_unused]] std::size_t sz) const noexcept { assert(sz <= max_size); } public: static_string() noexcept: size_(0) {} - static_string(std::string_view value) noexcept : size_(value.size()) + static_string(boost::string_view value) noexcept : size_(value.size()) { - size_check(value.size()); + assert(value.size() <= max_size); size_ = value.size(); std::memcpy(buffer_.data(), value.data(), value.size()); } std::size_t size() const noexcept { return size_; } - std::string_view value() const noexcept { return std::string_view(buffer_.data(), size_); } + boost::string_view value() const noexcept { return boost::string_view(buffer_.data(), size_); } void append(const void* arr, std::size_t sz) noexcept { std::size_t new_size = size_ + sz; - size_check(new_size); + assert(new_size <= max_size); std::memcpy(buffer_.data() + size_, arr, sz); size_ = new_size; } diff --git a/include/boost/mysql/detail/auxiliar/stringize.hpp b/include/boost/mysql/detail/auxiliar/stringize.hpp index 42eb910f..4835d982 100644 --- a/include/boost/mysql/detail/auxiliar/stringize.hpp +++ b/include/boost/mysql/detail/auxiliar/stringize.hpp @@ -15,11 +15,20 @@ namespace boost { namespace mysql { namespace detail { +inline void stringize_helper(std::ostream&) noexcept {} + +template +void stringize_helper(std::ostream& os, const T& input, const Types&... tail) +{ + os << input; + stringize_helper(os, tail...); +} + template std::string stringize(const Types&... inputs) { std::ostringstream ss; - (ss << ... << inputs); + stringize_helper(ss, inputs...); return ss.str(); } diff --git a/include/boost/mysql/detail/auxiliar/tmp.hpp b/include/boost/mysql/detail/auxiliar/tmp.hpp deleted file mode 100644 index df86b954..00000000 --- a/include/boost/mysql/detail/auxiliar/tmp.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) 2019-2020 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_AUXILIAR_TMP_HPP -#define BOOST_MYSQL_DETAIL_AUXILIAR_TMP_HPP - -#include - -namespace boost { -namespace mysql { -namespace detail { - -template -struct is_one_of -{ - static constexpr bool value = std::is_same_v || is_one_of::value; -}; - -template -struct is_one_of -{ - static constexpr bool value = std::is_same_v; -}; - - -template -constexpr bool is_one_of_v = is_one_of::value; - -} // detail -} // mysql -} // boost - - -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_TMP_HPP_ */ diff --git a/include/boost/mysql/detail/auxiliar/valgrind.hpp b/include/boost/mysql/detail/auxiliar/valgrind.hpp index 76c730e8..6c5fedfd 100644 --- a/include/boost/mysql/detail/auxiliar/valgrind.hpp +++ b/include/boost/mysql/detail/auxiliar/valgrind.hpp @@ -16,15 +16,25 @@ namespace boost { namespace mysql { namespace detail { +#ifdef BOOST_MYSQL_VALGRIND_TESTS + inline void valgrind_make_mem_defined( - [[maybe_unused]] boost::asio::const_buffer buff + boost::asio::const_buffer buff ) { -#ifdef BOOST_MYSQL_VALGRIND_TESTS VALGRIND_MAKE_MEM_DEFINED(buff.data(), buff.size()); -#endif } +#else + +inline void valgrind_make_mem_defined(boost::asio::const_buffer) {} + +#endif + + + + + } // detail } // mysql } // boost diff --git a/include/boost/mysql/detail/network_algorithms/execute_generic.hpp b/include/boost/mysql/detail/network_algorithms/execute_generic.hpp index 76970583..637c5ab7 100644 --- a/include/boost/mysql/detail/network_algorithms/execute_generic.hpp +++ b/include/boost/mysql/detail/network_algorithms/execute_generic.hpp @@ -10,7 +10,7 @@ #include "boost/mysql/detail/network_algorithms/common.hpp" #include "boost/mysql/resultset.hpp" -#include +#include namespace boost { namespace mysql { diff --git a/include/boost/mysql/detail/network_algorithms/execute_query.hpp b/include/boost/mysql/detail/network_algorithms/execute_query.hpp index 0136cee4..25bae36b 100644 --- a/include/boost/mysql/detail/network_algorithms/execute_query.hpp +++ b/include/boost/mysql/detail/network_algorithms/execute_query.hpp @@ -10,7 +10,7 @@ #include "boost/mysql/detail/network_algorithms/common.hpp" #include "boost/mysql/resultset.hpp" -#include +#include namespace boost { namespace mysql { @@ -19,7 +19,7 @@ namespace detail { template void execute_query( channel& channel, - std::string_view query, + boost::string_view query, resultset& output, error_code& err, error_info& info @@ -29,7 +29,7 @@ template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, resultset)) async_execute_query( channel& chan, - std::string_view query, + boost::string_view query, CompletionToken&& token, error_info& info ); diff --git a/include/boost/mysql/detail/network_algorithms/handshake.hpp b/include/boost/mysql/detail/network_algorithms/handshake.hpp index 015b1144..3f6a0c6b 100644 --- a/include/boost/mysql/detail/network_algorithms/handshake.hpp +++ b/include/boost/mysql/detail/network_algorithms/handshake.hpp @@ -8,7 +8,7 @@ #ifndef BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_HANDSHAKE_HPP #define BOOST_MYSQL_DETAIL_NETWORK_ALGORITHMS_HANDSHAKE_HPP -#include +#include #include "boost/mysql/detail/network_algorithms/common.hpp" #include "boost/mysql/detail/protocol/channel.hpp" #include "boost/mysql/detail/protocol/protocol_types.hpp" diff --git a/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp b/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp index 26e982e5..bb585e2e 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/close_statement.hpp @@ -19,7 +19,7 @@ void boost::mysql::detail::close_statement( ) { // Compose the close message - com_stmt_close_packet packet {int4(statement_id)}; + com_stmt_close_packet packet {statement_id}; // Serialize it serialize_message(packet, chan.current_capabilities(), chan.shared_buffer()); @@ -42,7 +42,7 @@ boost::mysql::detail::async_close_statement( ) { // Compose the close message - com_stmt_close_packet packet {int4(statement_id)}; + com_stmt_close_packet packet {statement_id}; // Serialize it serialize_message(packet, chan.current_capabilities(), chan.shared_buffer()); diff --git a/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp b/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp index c9076e1e..cb00a922 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_generic.hpp @@ -20,7 +20,7 @@ class execute_processor capabilities caps_; bytestring buffer_; std::size_t field_count_ {}; - ok_packet ok_packet_; + ok_packet ok_packet_ {}; std::vector fields_; std::vector field_buffers_; public: @@ -44,8 +44,8 @@ public: // If it is none of this, then the message type itself is the beginning of // a length-encoded int containing the field count deserialization_context ctx (boost::asio::buffer(buffer_), caps_); - std::uint8_t msg_type; - std::tie(err, msg_type) = deserialize_message_type(ctx); + std::uint8_t msg_type = 0; + err = make_error_code(deserialize(ctx, msg_type)); if (err) return; if (msg_type == ok_packet_header) diff --git a/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp b/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp index 3933049a..34622c20 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_query.hpp @@ -15,7 +15,7 @@ template void boost::mysql::detail::execute_query( channel& channel, - std::string_view query, + boost::string_view query, resultset& output, error_code& err, error_info& info @@ -40,7 +40,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( ) boost::mysql::detail::async_execute_query( channel& chan, - std::string_view query, + boost::string_view query, CompletionToken&& token, error_info& info ) diff --git a/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp b/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp index d7ec7d8d..0ec67a90 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/execute_statement.hpp @@ -24,10 +24,10 @@ com_stmt_execute_packet make_stmt_execute_packet( ) { return com_stmt_execute_packet { - int4(statement_id), - int1(0), // flags - int4(1), // iteration count - int1(1), // new params flag: set + statement_id, + std::uint8_t(0), // flags + std::uint32_t(1), // iteration count + std::uint8_t(1), // new params flag: set params_begin, params_end }; diff --git a/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp b/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp index 8cca5ea4..e85c26bb 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/handshake.hpp @@ -34,9 +34,10 @@ inline error_code deserialize_handshake( ) { deserialization_context ctx (boost::asio::buffer(buffer), capabilities()); - auto [err, msg_type] = deserialize_message_type(ctx); - if (err) - return err; + std::uint8_t msg_type = 0; + auto err = deserialize(ctx, msg_type); + if (err != errc::ok) + return make_error_code(err); if (msg_type == handshake_protocol_version_9) { return make_error_code(errc::server_unsupported); @@ -85,7 +86,7 @@ public: error_code process_capabilities(const handshake_packet& handshake) { auto ssl = params_.ssl().mode(); - capabilities server_caps (handshake.capability_falgs.value); + capabilities server_caps (handshake.capability_falgs); capabilities required_caps = mandatory_capabilities | conditional_capability(!params_.database().empty(), CLIENT_CONNECT_WITH_DB) | conditional_capability(ssl == ssl_mode::require, CLIENT_SSL); @@ -124,9 +125,9 @@ public: void compose_ssl_request(bytestring& buffer) { ssl_request sslreq { - int4(negotiated_capabilities().get()), - int4(static_cast(MAX_PACKET_SIZE)), - int1(get_collation_first_byte(params_.connection_collation())), + negotiated_capabilities().get(), + static_cast(MAX_PACKET_SIZE), + get_collation_first_byte(params_.connection_collation()), {} }; @@ -138,9 +139,9 @@ public: { // Compose response handshake_response_packet response { - int4(negotiated_caps_.get()), - int4(static_cast(MAX_PACKET_SIZE)), - int1(get_collation_first_byte(params_.connection_collation())), + negotiated_caps_.get(), + static_cast(MAX_PACKET_SIZE), + get_collation_first_byte(params_.connection_collation()), string_null(params_.username()), string_lenenc(auth_calc_.response()), string_null(params_.database()), @@ -159,7 +160,8 @@ public: ) { deserialization_context ctx (boost::asio::buffer(buffer), negotiated_caps_); - auto [err, msg_type] = deserialize_message_type(ctx); + std::uint8_t msg_type = 0; + auto err = make_error_code(deserialize(ctx, msg_type)); if (err) return err; if (msg_type == ok_packet_header) @@ -181,7 +183,6 @@ public: return err; // Compute response - auth_switch_response_packet auth_sw_res; err = auth_calc_.calculate( auth_sw.plugin_name.value, params_.password(), @@ -190,10 +191,13 @@ public: ); if (err) return err; - auth_sw_res.auth_plugin_data.value = auth_calc_.response(); // Serialize - serialize_message(auth_sw_res, negotiated_caps_, buffer); + serialize_message( + auth_switch_response_packet{string_eof(auth_calc_.response())}, + negotiated_caps_, + buffer + ); result = auth_result::send_more_data; return error_code(); @@ -206,7 +210,7 @@ public: if (err) return err; - std::string_view challenge = more_data.auth_plugin_data.value; + boost::string_view challenge = more_data.auth_plugin_data.value; if (challenge == fast_auth_complete_challenge) { result = auth_result::wait_for_ok; diff --git a/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp b/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp index afe2c700..b57457f6 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/prepare_statement.hpp @@ -16,10 +16,10 @@ template class prepare_statement_processor { channel& channel_; - com_stmt_prepare_ok_packet response_; + com_stmt_prepare_ok_packet response_ {}; public: prepare_statement_processor(channel& chan): channel_(chan) {} - void process_request(std::string_view statement) + void process_request(boost::string_view statement) { com_stmt_prepare_packet packet { string_eof(statement) }; serialize_message(packet, channel_.current_capabilities(), channel_.shared_buffer()); @@ -32,7 +32,7 @@ public: channel_.current_capabilities() ); std::uint8_t msg_type = 0; - std::tie(err, msg_type) = deserialize_message_type(ctx); + err = make_error_code(deserialize(ctx, msg_type)); if (err) return; @@ -49,13 +49,13 @@ public: err = deserialize_message(ctx, response_); } } - auto& get_buffer() noexcept { return channel_.shared_buffer(); } - auto& get_channel() noexcept { return channel_; } - const auto& get_response() const noexcept { return response_; } + bytestring& get_buffer() noexcept { return channel_.shared_buffer(); } + channel& get_channel() noexcept { return channel_; } + const com_stmt_prepare_ok_packet& get_response() const noexcept { return response_; } unsigned get_num_metadata_packets() const noexcept { - return response_.num_columns.value + response_.num_params.value; + return response_.num_columns + response_.num_params; } }; @@ -69,7 +69,7 @@ struct prepare_statement_op : boost::asio::coroutine prepare_statement_op( channel& chan, error_info& output_info, - std::string_view statement + boost::string_view statement ) : processor_(chan), output_info_(output_info) @@ -132,7 +132,7 @@ struct prepare_statement_op : boost::asio::coroutine template void boost::mysql::detail::prepare_statement( channel& channel, - std::string_view statement, + boost::string_view statement, error_code& err, error_info& info, prepared_statement& output @@ -177,7 +177,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( ) boost::mysql::detail::async_prepare_statement( channel& chan, - std::string_view statement, + boost::string_view statement, CompletionToken&& token, error_info& info ) diff --git a/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp b/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp index 56d98c42..3682f143 100644 --- a/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp +++ b/include/boost/mysql/detail/network_algorithms/impl/read_row.hpp @@ -28,9 +28,9 @@ inline read_row_result process_read_message( assert(deserializer); // Message type: row, error or eof? - std::uint8_t msg_type; + std::uint8_t msg_type = 0; deserialization_context ctx (boost::asio::buffer(buffer), current_capabilities); - std::tie(err, msg_type) = deserialize_message_type(ctx); + err = make_error_code(deserialize(ctx, msg_type)); if (err) return read_row_result::error; if (msg_type == eof_packet_header) diff --git a/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp b/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp index 9678b756..4bda46ed 100644 --- a/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp +++ b/include/boost/mysql/detail/network_algorithms/prepare_statement.hpp @@ -18,7 +18,7 @@ namespace detail { template void prepare_statement( channel& chan, - std::string_view statement, + boost::string_view statement, error_code& err, error_info& info, prepared_statement& output @@ -28,7 +28,7 @@ template BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, prepared_statement)) async_prepare_statement( channel& chan, - std::string_view statement, + boost::string_view statement, CompletionToken&& token, error_info& info ); diff --git a/include/boost/mysql/detail/network_algorithms/read_row.hpp b/include/boost/mysql/detail/network_algorithms/read_row.hpp index eae03aa0..87436f21 100644 --- a/include/boost/mysql/detail/network_algorithms/read_row.hpp +++ b/include/boost/mysql/detail/network_algorithms/read_row.hpp @@ -10,7 +10,7 @@ #include "boost/mysql/detail/network_algorithms/common.hpp" #include "boost/mysql/metadata.hpp" -#include +#include #include namespace boost { diff --git a/include/boost/mysql/detail/protocol/capabilities.hpp b/include/boost/mysql/detail/protocol/capabilities.hpp index 74b6b9dd..bdf4a5ce 100644 --- a/include/boost/mysql/detail/protocol/capabilities.hpp +++ b/include/boost/mysql/detail/protocol/capabilities.hpp @@ -50,7 +50,7 @@ class capabilities public: constexpr explicit capabilities(std::uint32_t value=0) noexcept : value_(value) {}; constexpr std::uint32_t get() const noexcept { return value_; } - constexpr void set(std::uint32_t value) noexcept { value_ = value; } + void set(std::uint32_t value) noexcept { value_ = value; } constexpr bool has(std::uint32_t cap) const noexcept { return value_ & cap; } constexpr bool has_all(capabilities other) const noexcept { return (value_ & other.get()) == other.get(); } constexpr capabilities operator|(capabilities rhs) const noexcept { return capabilities(value_ | rhs.value_); } diff --git a/include/boost/mysql/detail/protocol/channel.hpp b/include/boost/mysql/detail/protocol/channel.hpp index 312d95ac..44b5391b 100644 --- a/include/boost/mysql/detail/protocol/channel.hpp +++ b/include/boost/mysql/detail/protocol/channel.hpp @@ -16,8 +16,8 @@ #include #include #include +#include #include -#include namespace boost { namespace mysql { @@ -39,7 +39,7 @@ class channel }; Stream stream_; - std::optional ssl_block_; + boost::optional ssl_block_; std::uint8_t sequence_number_ {0}; std::array header_buffer_ {}; // for async ops bytestring shared_buff_; // for async ops @@ -61,10 +61,12 @@ class channel std::size_t write_impl(BufferSeq&& buff, error_code& ec); template - auto async_read_impl(BufferSeq&& buff, CompletionToken&& token); + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::size_t)) + async_read_impl(BufferSeq&& buff, CompletionToken&& token); template - auto async_write_impl(BufferSeq&& buff, CompletionToken&& token); + BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, std::size_t)) + async_write_impl(BufferSeq&& buff, CompletionToken&& token); struct read_op; struct write_op; diff --git a/include/boost/mysql/detail/protocol/common_messages.hpp b/include/boost/mysql/detail/protocol/common_messages.hpp index 61d5976a..74128924 100644 --- a/include/boost/mysql/detail/protocol/common_messages.hpp +++ b/include/boost/mysql/detail/protocol/common_messages.hpp @@ -21,16 +21,16 @@ namespace detail { struct packet_header { int3 packet_size; - int1 sequence_number; -}; + std::uint8_t sequence_number; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &packet_header::packet_size, - &packet_header::sequence_number - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.packet_size, + self.sequence_number + ); + } }; // ok packet @@ -39,22 +39,22 @@ struct ok_packet // header: int<1> header 0x00 or 0xFE the OK packet header int_lenenc affected_rows; int_lenenc last_insert_id; - int2 status_flags; // server_status_flags - int2 warnings; + std::uint16_t status_flags; // server_status_flags + std::uint16_t warnings; // CLIENT_SESSION_TRACK: not implemented string_lenenc info; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &ok_packet::affected_rows, - &ok_packet::last_insert_id, - &ok_packet::status_flags, - &ok_packet::warnings, - &ok_packet::info - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.affected_rows, + self.last_insert_id, + self.status_flags, + self.warnings, + self.info + ); + } }; template <> @@ -68,22 +68,24 @@ struct serialization_traits : struct err_packet { // int<1> header 0xFF ERR packet header - int2 error_code; + std::uint16_t error_code; string_fixed<1> sql_state_marker; string_fixed<5> sql_state; string_eof error_message; + + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.error_code, + self.sql_state_marker, + self.sql_state, + self.error_message + ); + } }; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &err_packet::error_code, - &err_packet::sql_state_marker, - &err_packet::sql_state, - &err_packet::error_message - ); -}; +static_assert(is_struct_with_fields(), "Bad!"); // col def struct column_definition_packet @@ -95,28 +97,28 @@ struct column_definition_packet string_lenenc name; // virtual column name string_lenenc org_name; // physical column name collation character_set; - int4 column_length; // maximum length of the field + std::uint32_t column_length; // maximum length of the field protocol_field_type type; // type of the column as defined in enum_field_types - int2 flags; // Flags as defined in Column Definition Flags - int1 decimals; // max shown decimal digits. 0x00 for int/static strings; 0x1f for dynamic strings, double, float -}; + std::uint16_t flags; // Flags as defined in Column Definition Flags + std::uint8_t decimals; // max shown decimal digits. 0x00 for int/static strings; 0x1f for dynamic strings, double, float -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &column_definition_packet::catalog, - &column_definition_packet::schema, - &column_definition_packet::table, - &column_definition_packet::org_table, - &column_definition_packet::name, - &column_definition_packet::org_name, - &column_definition_packet::character_set, - &column_definition_packet::column_length, - &column_definition_packet::type, - &column_definition_packet::flags, - &column_definition_packet::decimals - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.catalog, + self.schema, + self.table, + self.org_table, + self.name, + self.org_name, + self.character_set, + self.column_length, + self.type, + self.flags, + self.decimals + ); + } }; template <> @@ -130,12 +132,9 @@ struct serialization_traits -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple(); + template + static void apply(Self&, Callable&&) noexcept {} }; // aux diff --git a/include/boost/mysql/detail/protocol/constants.hpp b/include/boost/mysql/detail/protocol/constants.hpp index 889119ba..b9d89186 100644 --- a/include/boost/mysql/detail/protocol/constants.hpp +++ b/include/boost/mysql/detail/protocol/constants.hpp @@ -9,6 +9,7 @@ #define BOOST_MYSQL_DETAIL_PROTOCOL_CONSTANTS_HPP #include "boost/mysql/detail/protocol/protocol_types.hpp" +#include "boost/mysql/detail/auxiliar/make_string_view.hpp" namespace boost { namespace mysql { @@ -71,7 +72,7 @@ constexpr std::uint8_t ok_packet_header = 0x00; constexpr std::uint8_t eof_packet_header = 0xfe; constexpr std::uint8_t auth_switch_request_header = 0xfe; constexpr std::uint8_t auth_more_data_header = 0x01; -constexpr std::string_view fast_auth_complete_challenge = "\3"; +constexpr boost::string_view fast_auth_complete_challenge = make_string_view("\3"); // Column flags namespace column_flags { diff --git a/include/boost/mysql/detail/protocol/handshake_messages.hpp b/include/boost/mysql/detail/protocol/handshake_messages.hpp index c3ba694e..3077e7fc 100644 --- a/include/boost/mysql/detail/protocol/handshake_messages.hpp +++ b/include/boost/mysql/detail/protocol/handshake_messages.hpp @@ -22,26 +22,26 @@ struct handshake_packet // int<1> protocol version Always 10 string_null server_version; - int4 connection_id; + std::uint32_t connection_id; auth_buffer_type auth_plugin_data; // not an actual protocol field, the merge of two fields - int4 capability_falgs; // merge of the two parts - not an actual field - int1 character_set; // default server a_protocol_character_set, only the lower 8-bits - int2 status_flags; // server_status_flags + std::uint32_t capability_falgs; // merge of the two parts - not an actual field + std::uint8_t character_set; // default server a_protocol_character_set, only the lower 8-bits + std::uint16_t status_flags; // server_status_flags string_null auth_plugin_name; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &handshake_packet::server_version, - &handshake_packet::connection_id, - &handshake_packet::auth_plugin_data, - &handshake_packet::capability_falgs, - &handshake_packet::character_set, - &handshake_packet::status_flags, - &handshake_packet::auth_plugin_name - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.server_version, + self.connection_id, + self.auth_plugin_data, + self.capability_falgs, + self.character_set, + self.status_flags, + self.auth_plugin_name + ); + } }; template <> @@ -54,29 +54,29 @@ struct serialization_traits -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &handshake_response_packet::client_flag, - &handshake_response_packet::max_packet_size, - &handshake_response_packet::character_set, - &handshake_response_packet::username, - &handshake_response_packet::auth_response, - &handshake_response_packet::database, - &handshake_response_packet::client_plugin_name - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.client_flag, + self.max_packet_size, + self.character_set, + self.username, + self.auth_response, + self.database, + self.client_plugin_name + ); + } }; template <> @@ -92,21 +92,21 @@ struct serialization_traits filler {}; -}; + std::uint32_t client_flag; + std::uint32_t max_packet_size; + std::uint8_t character_set; + string_fixed<23> filler; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &ssl_request::client_flag, - &ssl_request::max_packet_size, - &ssl_request::character_set, - &ssl_request::filler - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.client_flag, + self.max_packet_size, + self.character_set, + self.filler + ); + } }; // auth switch request @@ -114,29 +114,29 @@ struct auth_switch_request_packet { string_null plugin_name; string_eof auth_plugin_data; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &auth_switch_request_packet::plugin_name, - &auth_switch_request_packet::auth_plugin_data - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.plugin_name, + self.auth_plugin_data + ); + } }; // response struct auth_switch_response_packet { string_eof auth_plugin_data; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &auth_switch_response_packet::auth_plugin_data - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.auth_plugin_data + ); + } }; template <> @@ -151,17 +151,16 @@ struct serialization_traits -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &auth_more_data_packet::auth_plugin_data - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.auth_plugin_data + ); + } }; - } // detail } // mysql } // boost diff --git a/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp b/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp index d29c3d7b..3a3bf7c5 100644 --- a/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/binary_deserialization.ipp @@ -8,19 +8,31 @@ #ifndef BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_BINARY_DESERIALIZATION_IPP #define BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_BINARY_DESERIALIZATION_IPP -#include #include "boost/mysql/detail/protocol/serialization.hpp" #include "boost/mysql/detail/protocol/null_bitmap_traits.hpp" #include "boost/mysql/detail/protocol/constants.hpp" -#include "boost/mysql/detail/auxiliar/tmp.hpp" namespace boost { namespace mysql { namespace detail { -// ints and strings -template -errc deserialize_binary_value_value_holder( +// strings +inline errc deserialize_binary_value_string( + deserialization_context& ctx, + value& output +) noexcept +{ + string_lenenc deser; + auto err = deserialize(ctx, deser); + if (err != errc::ok) + return err; + output = value(deser.value); + return errc::ok; +} + +// ints +template +errc deserialize_binary_value_int_impl( deserialization_context& ctx, value& output ) noexcept @@ -29,7 +41,7 @@ errc deserialize_binary_value_value_holder( auto err = deserialize(ctx, deser); if (err != errc::ok) return err; - output = value(static_cast(deser.value)); + output = value(static_cast(deser)); return errc::ok; } @@ -44,9 +56,9 @@ errc deserialize_binary_value_int( ) noexcept { return meta.is_unsigned() ? - deserialize_binary_value_value_holder< + deserialize_binary_value_int_impl< std::uint64_t, DeserializableTypeUnsigned>(ctx, output) : - deserialize_binary_value_value_holder< + deserialize_binary_value_int_impl< std::int64_t, DeserializableTypeSigned>(ctx, output); } @@ -62,15 +74,8 @@ errc deserialize_binary_value_float( return errc::incomplete_message; // Endianness conversion. Boost.Endian support for floats start at 1.71 - T v; -#if BOOST_ENDIAN_BIG_BYTE - char buf [sizeof(T)]; - std::memcpy(buf, ctx.first(), sizeof(T)); - std::reverse(buf, buf + sizeof(T)); - std::memcpy(&v, buf, sizeof(T)); -#else - std::memcpy(&v, ctx.first(), sizeof(T)); -#endif + T v = boost::endian::endian_load(ctx.first()); // Nans and infs not allowed in SQL if (std::isnan(v) || std::isinf(v)) @@ -88,9 +93,9 @@ inline errc deserialize_binary_ymd( ::date::year_month_day& output ) { - int2 year; - int1 month; - int1 day; + std::uint16_t year; + std::uint8_t month; + std::uint8_t day; // Deserialize auto err = deserialize(ctx, year, month, day); @@ -98,17 +103,17 @@ inline errc deserialize_binary_ymd( return err; // Range check - if (year.value > max_year || - month.value > max_month || - day.value > max_day) + if (year > max_year || + month > max_month || + day > max_day) { return errc::protocol_value_error; } output = ::date::year_month_day ( - ::date::year(year.value), - ::date::month(month.value), - ::date::day(day.value) + ::date::year(year), + ::date::month(month), + ::date::day(day) ); return errc::ok; @@ -120,13 +125,13 @@ inline errc deserialize_binary_value_date( ) noexcept { // Deserialize length - int1 length; + std::uint8_t length; auto err = deserialize(ctx, length); if (err != errc::ok) return err; // Check for zero dates, represented in C++ as a NULL - if (length.value < binc::date_sz) + if (length < binc::date_sz) { output = value(nullptr); return errc::ok; @@ -163,7 +168,7 @@ inline errc deserialize_binary_value_datetime( using namespace binc; // Deserialize length - int1 length; + std::uint8_t length; auto err = deserialize(ctx, length); if (err != errc::ok) return err; @@ -171,7 +176,7 @@ inline errc deserialize_binary_value_datetime( // Deserialize date. If the DATETIME does not contain these values, // they are supposed to be zero (invalid date) ::date::year_month_day ymd (::date::year(0), ::date::month(0), ::date::day(0)); - if (length.value >= datetime_d_sz) + if (length >= datetime_d_sz) { err = deserialize_binary_ymd(ctx, ymd); if (err != errc::ok) @@ -179,13 +184,13 @@ inline errc deserialize_binary_value_datetime( } // If the DATETIME contains no value for these fields, they are zero - int1 hours (0); - int1 minutes (0); - int1 seconds (0); - int4 micros (0); + std::uint8_t hours = 0; + std::uint8_t minutes = 0; + std::uint8_t seconds = 0; + std::uint32_t micros = 0; // Hours, minutes, seconds - if (length.value >= datetime_dhms_sz) + if (length >= datetime_dhms_sz) { err = deserialize(ctx, hours, minutes, seconds); if (err != errc::ok) @@ -193,7 +198,7 @@ inline errc deserialize_binary_value_datetime( } // Microseconds - if (length.value >= datetime_dhmsu_sz) + if (length >= datetime_dhmsu_sz) { err = deserialize(ctx, micros); if (err != errc::ok) @@ -203,10 +208,10 @@ inline errc deserialize_binary_value_datetime( // Validity check. We make this check before // the invalid date check to make invalid dates with incorrect // hours/mins/secs/micros fail - if (hours.value > max_hour || - minutes.value > max_min || - seconds.value > max_sec || - micros.value > max_micro) + if (hours > max_hour || + minutes > max_min || + seconds > max_sec || + micros > max_micro) { return errc::protocol_value_error; } @@ -229,10 +234,10 @@ inline errc deserialize_binary_value_datetime( // Compose the final datetime. Doing time of day and date separately to avoid overflow auto time_of_day = - std::chrono::hours(hours.value) + - std::chrono::minutes(minutes.value) + - std::chrono::seconds(seconds.value) + - std::chrono::microseconds(micros.value); + std::chrono::hours(hours) + + std::chrono::minutes(minutes) + + std::chrono::seconds(seconds) + + std::chrono::microseconds(micros); output = value(d + time_of_day); return errc::ok; } @@ -245,21 +250,21 @@ inline errc deserialize_binary_value_time( using namespace binc; // Deserialize length - int1 length; + std::uint8_t length; auto err = deserialize(ctx, length); if (err != errc::ok) return err; // If the TIME contains no value for these fields, they are zero - int1 is_negative (0); - int4 days (0); - int1 hours (0); - int1 minutes(0); - int1 seconds(0); - int4 microseconds(0); + std::uint8_t is_negative = 0; + std::uint32_t days = 0; + std::uint8_t hours = 0; + std::uint8_t minutes = 0; + std::uint8_t seconds = 0; + std::uint32_t microseconds = 0; // Sign, days, hours, minutes, seconds - if (length.value >= time_dhms_sz) + if (length >= time_dhms_sz) { err = deserialize( ctx, @@ -274,7 +279,7 @@ inline errc deserialize_binary_value_time( } // Microseconds - if (length.value >= time_dhmsu_sz) + if (length >= time_dhmsu_sz) { err = deserialize(ctx, microseconds); if (err != errc::ok) @@ -282,22 +287,22 @@ inline errc deserialize_binary_value_time( } // Range check - if (days.value > time_max_days || - hours.value > max_hour || - minutes.value > max_min || - seconds.value > max_sec || - microseconds.value > max_micro) + if (days > time_max_days || + hours > max_hour || + minutes > max_min || + seconds > max_sec || + microseconds > max_micro) { return errc::protocol_value_error; } // Compose the final time - output = value(time((is_negative.value ? -1 : 1) * ( - ::date::days(days.value) + - std::chrono::hours(hours.value) + - std::chrono::minutes(minutes.value) + - std::chrono::seconds(seconds.value) + - std::chrono::microseconds(microseconds.value) + output = value(time((is_negative ? -1 : 1) * ( + ::date::days(days) + + std::chrono::hours(hours) + + std::chrono::minutes(minutes) + + std::chrono::seconds(seconds) + + std::chrono::microseconds(microseconds) ))); return errc::ok; } @@ -316,15 +321,15 @@ inline boost::mysql::errc boost::mysql::detail::deserialize_binary_value( switch (meta.protocol_type()) { case protocol_field_type::tiny: - return deserialize_binary_value_int(meta, ctx, output); + return deserialize_binary_value_int(meta, ctx, output); case protocol_field_type::short_: case protocol_field_type::year: - return deserialize_binary_value_int(meta, ctx, output); + return deserialize_binary_value_int(meta, ctx, output); case protocol_field_type::int24: case protocol_field_type::long_: - return deserialize_binary_value_int(meta, ctx, output); + return deserialize_binary_value_int(meta, ctx, output); case protocol_field_type::longlong: - return deserialize_binary_value_int(meta, ctx, output); + return deserialize_binary_value_int(meta, ctx, output); case protocol_field_type::float_: return deserialize_binary_value_float(ctx, output); case protocol_field_type::double_: @@ -352,7 +357,7 @@ inline boost::mysql::errc boost::mysql::detail::deserialize_binary_value( case protocol_field_type::newdecimal: case protocol_field_type::geometry: default: - return deserialize_binary_value_value_holder(ctx, output); + return deserialize_binary_value_string(ctx, output); } } diff --git a/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp b/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp index 05b2e4ee..b8c397d0 100644 --- a/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/binary_serialization.ipp @@ -16,21 +16,15 @@ namespace detail { // Floating point types template -std::enable_if_t> +typename std::enable_if::value>::type serialize_binary_value_impl( serialization_context& ctx, T input ) { - // Endianness conversion -#if BOOST_ENDIAN_BIG_BYTE - char buf [sizeof(T)]; - std::memcpy(buf, &input, sizeof(T)); - std::reverse(buf, buf + sizeof(T)); - ctx.write(buf, sizeof(T)); -#else - ctx.write(&input, sizeof(T)); -#endif + boost::endian::endian_store(ctx.first(), input); + ctx.advance(sizeof(T)); } // Time types @@ -43,9 +37,9 @@ inline void serialize_binary_ymd( { serialize( ctx, - int2(static_cast(static_cast(ymd.year()))), - int1(static_cast(static_cast(ymd.month()))), - int1(static_cast(static_cast(ymd.day()))) + static_cast(static_cast(ymd.year())), + static_cast(static_cast(ymd.month())), + static_cast(static_cast(ymd.day())) ); } @@ -57,7 +51,7 @@ inline void serialize_binary_value_impl( ::date::year_month_day ymd (input); assert(ymd.ok()); - serialize(ctx, int1(static_cast(binc::date_sz))); + serialize(ctx, static_cast(binc::date_sz)); serialize_binary_ymd(ctx, ymd); } @@ -73,14 +67,14 @@ inline void serialize_binary_value_impl( assert(ymd.ok()); // Serialize - serialize(ctx, int1(static_cast(binc::datetime_dhmsu_sz))); + serialize(ctx, static_cast(binc::datetime_dhmsu_sz)); serialize_binary_ymd(ctx, ymd); serialize( ctx, - int1(static_cast(tod.hours().count())), - int1(static_cast(tod.minutes().count())), - int1(static_cast(tod.seconds().count())), - int4(static_cast(tod.subseconds().count())) + static_cast(tod.hours().count()), + static_cast(tod.minutes().count()), + static_cast(tod.seconds().count()), + static_cast(tod.subseconds().count()) ); } @@ -95,18 +89,18 @@ inline void serialize_binary_value_impl( auto minutes = std::chrono::duration_cast(input % std::chrono::hours(1)); auto seconds = std::chrono::duration_cast(input % std::chrono::minutes(1)); auto microseconds = input % std::chrono::seconds(1); - int1 is_negative (input.count() < 0 ? 1 : 0); + std::uint8_t is_negative = (input.count() < 0) ? 1 : 0; // Serialize serialize( ctx, - int1(static_cast(binc::time_dhmsu_sz)), + static_cast(binc::time_dhmsu_sz), is_negative, - int4(static_cast(std::abs(days.count()))), - int1(static_cast(std::abs(hours.count()))), - int1(static_cast(std::abs(minutes.count()))), - int1(static_cast(std::abs(seconds.count()))), - int4(static_cast(std::abs(microseconds.count()))) + static_cast(std::abs(days.count())), + static_cast(std::abs(hours.count())), + static_cast(std::abs(minutes.count())), + static_cast(std::abs(seconds.count())), + static_cast(std::abs(microseconds.count())) ); } @@ -121,7 +115,7 @@ struct size_visitor template std::size_t operator()(T) noexcept { return sizeof(T); } - std::size_t operator()(std::string_view v) noexcept { return get_size(ctx, string_lenenc(v)); } + std::size_t operator()(boost::string_view v) noexcept { return get_size(ctx, string_lenenc(v)); } std::size_t operator()(const date&) noexcept { return binc::date_sz + binc::length_sz; } std::size_t operator()(const datetime&) noexcept { return binc::datetime_dhmsu_sz + binc::length_sz; } std::size_t operator()(const time&) noexcept { return binc::time_dhmsu_sz + binc::length_sz; } @@ -137,9 +131,9 @@ struct serialize_visitor template void operator()(const T& v) noexcept { serialize_binary_value_impl(ctx, v); } - void operator()(std::int64_t v) noexcept { serialize(ctx, int8_signed(v)); } - void operator()(std::uint64_t v) noexcept { serialize(ctx, int8(v)); } - void operator()(std::string_view v) noexcept { serialize(ctx, string_lenenc(v)); } + void operator()(std::int64_t v) noexcept { serialize(ctx, v); } + void operator()(std::uint64_t v) noexcept { serialize(ctx, v); } + void operator()(boost::string_view v) noexcept { serialize(ctx, string_lenenc(v)); } void operator()(std::nullptr_t) noexcept {} }; @@ -153,7 +147,7 @@ inline std::size_t boost::mysql::detail::get_binary_value_size( const value& input ) noexcept { - return std::visit(size_visitor(ctx), input.to_variant()); + return boost::variant2::visit(size_visitor(ctx), input.to_variant()); } inline void boost::mysql::detail::serialize_binary_value( @@ -161,7 +155,7 @@ inline void boost::mysql::detail::serialize_binary_value( const value& input ) noexcept { - std::visit(serialize_visitor(ctx), input.to_variant()); + boost::variant2::visit(serialize_visitor(ctx), input.to_variant()); } diff --git a/include/boost/mysql/detail/protocol/impl/channel.hpp b/include/boost/mysql/detail/protocol/impl/channel.hpp index 010d55bb..3ff9e7d2 100644 --- a/include/boost/mysql/detail/protocol/impl/channel.hpp +++ b/include/boost/mysql/detail/protocol/impl/channel.hpp @@ -58,9 +58,12 @@ boost::mysql::error_code boost::mysql::detail::channel::process_header_r { packet_header header; deserialization_context ctx (boost::asio::buffer(header_buffer_), capabilities(0)); // unaffected by capabilities - [[maybe_unused]] errc err = deserialize(ctx, header); - assert(err == errc::ok); // this should always succeed - if (!process_sequence_number(header.sequence_number.value)) + errc err = deserialize(ctx, header); + if (err != errc::ok) + { + return make_error_code(err); + } + if (!process_sequence_number(header.sequence_number)) { return make_error_code(errc::sequence_number_mismatch); } @@ -75,7 +78,7 @@ void boost::mysql::detail::channel::process_header_write( { packet_header header; header.packet_size.value = size_to_write; - header.sequence_number.value = next_sequence_number(); + header.sequence_number = next_sequence_number(); serialization_context ctx (capabilities(0), header_buffer_.data()); // capabilities not relevant here serialize(ctx, header); } @@ -116,7 +119,8 @@ std::size_t boost::mysql::detail::channel::write_impl( template template -auto boost::mysql::detail::channel::async_read_impl( +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code, std::size_t)) +boost::mysql::detail::channel::async_read_impl( BufferSeq&& buff, CompletionToken&& token ) @@ -143,7 +147,8 @@ auto boost::mysql::detail::channel::async_read_impl( template template -auto boost::mysql::detail::channel::async_write_impl( +BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code, std::size_t)) +boost::mysql::detail::channel::async_write_impl( BufferSeq&& buff, CompletionToken&& token ) diff --git a/include/boost/mysql/detail/protocol/impl/common_messages.ipp b/include/boost/mysql/detail/protocol/impl/common_messages.ipp index 7a13ed55..98c1824c 100644 --- a/include/boost/mysql/detail/protocol/impl/common_messages.ipp +++ b/include/boost/mysql/detail/protocol/impl/common_messages.ipp @@ -43,7 +43,7 @@ boost::mysql::detail::serialization_traits< ) noexcept { int_lenenc length_of_fixed_fields; - int2 final_padding; + std::uint16_t final_padding = 0; return deserialize( ctx, output.catalog, @@ -71,8 +71,8 @@ inline boost::mysql::error_code boost::mysql::detail::process_error_packet( auto code = deserialize_message(ctx, error_packet); if (code) return code; - info.set_message(std::string(error_packet.error_message.value)); - return make_error_code(static_cast(error_packet.error_code.value)); + info.set_message(error_packet.error_message.value.to_string()); + return make_error_code(static_cast(error_packet.error_code)); } diff --git a/include/boost/mysql/detail/protocol/impl/handshake_messages.ipp b/include/boost/mysql/detail/protocol/impl/handshake_messages.ipp index e4acc611..1ca0cbb5 100644 --- a/include/boost/mysql/detail/protocol/impl/handshake_messages.ipp +++ b/include/boost/mysql/detail/protocol/impl/handshake_messages.ipp @@ -22,8 +22,8 @@ boost::mysql::detail::serialization_traits< string_fixed auth_plugin_data_part_1; string_fixed<2> capability_flags_low; string_fixed<2> capability_flags_high; - int1 filler; // should be 0 - int1 auth_plugin_data_len; + std::uint8_t filler; // should be 0 + std::uint8_t auth_plugin_data_len = 0; string_fixed<10> reserved; auto err = deserialize( @@ -41,13 +41,13 @@ boost::mysql::detail::serialization_traits< return err; // Compose capabilities - auto capabilities_begin = reinterpret_cast(&output.capability_falgs.value); + auto capabilities_begin = reinterpret_cast(&output.capability_falgs); memcpy(capabilities_begin, capability_flags_low.data(), 2); memcpy(capabilities_begin + 2, capability_flags_high.data(), 2); - boost::endian::little_to_native_inplace(output.capability_falgs.value); + boost::endian::little_to_native_inplace(output.capability_falgs); // Check minimum server capabilities to deserialize this frame - capabilities cap (output.capability_falgs.value); + capabilities cap (output.capability_falgs); if (!cap.has(CLIENT_PLUGIN_AUTH)) return errc::server_unsupported; @@ -62,7 +62,7 @@ boost::mysql::detail::serialization_traits< // Auth plugin data, second part auto auth2_length = static_cast( - std::max(13, auth_plugin_data_len.value - auth1_length)); + std::max(13, auth_plugin_data_len - auth1_length)); const void* auth2_data = ctx.first(); if (!ctx.enough_size(auth2_length)) return errc::incomplete_message; diff --git a/include/boost/mysql/detail/protocol/impl/prepared_statement_messages.hpp b/include/boost/mysql/detail/protocol/impl/prepared_statement_messages.hpp index 1341c10b..485fcd60 100644 --- a/include/boost/mysql/detail/protocol/impl/prepared_statement_messages.hpp +++ b/include/boost/mysql/detail/protocol/impl/prepared_statement_messages.hpp @@ -22,17 +22,17 @@ inline protocol_field_type get_protocol_field_type( { struct visitor { - constexpr auto operator()(std::int64_t) const noexcept { return protocol_field_type::longlong; } - constexpr auto operator()(std::uint64_t) const noexcept { return protocol_field_type::longlong; } - constexpr auto operator()(std::string_view) const noexcept { return protocol_field_type::varchar; } - constexpr auto operator()(float) const noexcept { return protocol_field_type::float_; } - constexpr auto operator()(double) const noexcept { return protocol_field_type::double_; } - constexpr auto operator()(date) const noexcept { return protocol_field_type::date; } - constexpr auto operator()(datetime) const noexcept { return protocol_field_type::datetime; } - constexpr auto operator()(time) const noexcept { return protocol_field_type::time; } - constexpr auto operator()(std::nullptr_t) const noexcept { return protocol_field_type::null; } + protocol_field_type operator()(std::int64_t) const noexcept { return protocol_field_type::longlong; } + protocol_field_type operator()(std::uint64_t) const noexcept { return protocol_field_type::longlong; } + protocol_field_type operator()(boost::string_view) const noexcept { return protocol_field_type::varchar; } + protocol_field_type operator()(float) const noexcept { return protocol_field_type::float_; } + protocol_field_type operator()(double) const noexcept { return protocol_field_type::double_; } + protocol_field_type operator()(date) const noexcept { return protocol_field_type::date; } + protocol_field_type operator()(datetime) const noexcept { return protocol_field_type::datetime; } + protocol_field_type operator()(time) const noexcept { return protocol_field_type::time; } + protocol_field_type operator()(std::nullptr_t) const noexcept { return protocol_field_type::null; } }; - return std::visit(visitor(), input.to_variant()); + return boost::variant2::visit(visitor(), input.to_variant()); } // Whether to include the unsigned flag in the statement execute message @@ -41,7 +41,7 @@ inline bool is_unsigned( const value& input ) noexcept { - return std::holds_alternative(input.to_variant()); + return input.is(); } } // detail @@ -57,7 +57,7 @@ boost::mysql::detail::serialization_traits< com_stmt_prepare_ok_packet& output ) noexcept { - int1 reserved; + std::uint8_t reserved; return deserialize( ctx, output.statement_id, @@ -102,9 +102,10 @@ boost::mysql::detail::serialization_traits< const com_stmt_execute_packet& input ) noexcept { + constexpr std::uint8_t command_id = com_stmt_execute_packet::command_id; serialize( ctx, - int1(com_stmt_execute_packet::command_id), + command_id, input.statement_id, input.flags, input.iteration_count @@ -135,7 +136,7 @@ boost::mysql::detail::serialization_traits< for (auto it = input.params_begin; it != input.params_end; ++it) { meta.type = get_protocol_field_type(*it); - meta.unsigned_flag.value = is_unsigned(*it) ? 0x80 : 0; + meta.unsigned_flag = is_unsigned(*it) ? 0x80 : 0; serialize(ctx, meta); } diff --git a/include/boost/mysql/detail/protocol/impl/serialization.hpp b/include/boost/mysql/detail/protocol/impl/serialization.hpp index 59f483b5..cb226966 100644 --- a/include/boost/mysql/detail/protocol/impl/serialization.hpp +++ b/include/boost/mysql/detail/protocol/impl/serialization.hpp @@ -14,15 +14,6 @@ namespace boost { namespace mysql { namespace detail { -// Helpers for fixed size ints -template -constexpr std::size_t get_int_size() -{ - static_assert(is_fixed_size_int()); - return std::is_same::value ? 3 : - std::is_same::value ? 6 : sizeof(T); -} - // Helpers for structs struct is_command_helper { @@ -38,49 +29,77 @@ struct is_command : decltype(is_command_helper::get(nullptr)) { }; +struct is_struct_with_fields_helper +{ + struct test_op + { + template + void operator()(const T&) {} + }; + + template + static constexpr std::true_type get(decltype(T::apply(std::declval(), test_op()))*); + + template + static constexpr std::false_type get(...); +}; + +struct struct_deserialize_op +{ + deserialization_context& ctx; + errc result; + + struct_deserialize_op(deserialization_context& ctx) noexcept : + ctx(ctx), result(errc::ok) {} + + template + void operator()(Types&... output) noexcept + { + result = deserialize(ctx, output...); + } +}; + +struct struct_get_size_op +{ + const serialization_context& ctx; + std::size_t result; + + struct_get_size_op(const serialization_context& ctx, std::size_t init_value) noexcept : + ctx(ctx), result(init_value) {} + + template + void operator()(const Types&... input) noexcept + { + result += get_size(ctx, input...); + } +}; + +struct struct_serialize_op +{ + serialization_context& ctx; + + struct_serialize_op(serialization_context& ctx) noexcept : ctx(ctx) {} + + template + void operator()(const Types&... input) noexcept + { + serialize(ctx, input...); + } +}; + +// Helpers to serialize the command id byte template -using struct_index_sequence = std::make_index_sequence::value) ->>; - -template -errc deserialize_struct( - deserialization_context& ctx, - T& output, - std::index_sequence -) noexcept -{ - return deserialize( - ctx, - (output.*(std::get(get_struct_fields::value)))... - ); -} - -template -void serialize_struct( +void serialize_command_id( serialization_context& ctx, - const T& input, - std::index_sequence + std::true_type ) noexcept { - serialize( - ctx, - (input.*(std::get(get_struct_fields::value)))... - ); + constexpr std::uint8_t command_id = T::command_id; + serialize(ctx, command_id); } -template -std::size_t get_size_struct( - const serialization_context& ctx, - const T& input, - std::index_sequence -) noexcept -{ - return get_size( - ctx, - (input.*(std::get(get_struct_fields::value)))... - ); -} +template +void serialize_command_id(serialization_context&, std::false_type) noexcept {} inline errc deserialize_helper(deserialization_context&) noexcept { @@ -163,121 +182,47 @@ std::size_t boost::mysql::detail::get_size( return get_size_helper(ctx, input...); } -// Fixed size ints -template -constexpr bool boost::mysql::detail::is_fixed_size_int() -{ - return - std::is_integral>::value && - std::is_base_of>, T>::value && - !std::is_same::value; -} - +// Plain ints template boost::mysql::errc boost::mysql::detail::serialization_traits< T, - boost::mysql::detail::serialization_tag::fixed_size_int + boost::mysql::detail::serialization_tag::plain_int >::deserialize_( deserialization_context& ctx, T& output ) noexcept { - static_assert(std::is_standard_layout_v); - - constexpr auto size = get_int_size(); - if (!ctx.enough_size(size)) + constexpr std::size_t sz = sizeof(T); + if (!ctx.enough_size(sz)) { return errc::incomplete_message; } - - memset(&output.value, 0, sizeof(output.value)); - memcpy(&output.value, ctx.first(), size); - boost::endian::little_to_native_inplace(output.value); - ctx.advance(size); - + output = boost::endian::endian_load< + T, sz, boost::endian::order::little>(ctx.first()); + ctx.advance(sz); return errc::ok; } template void boost::mysql::detail::serialization_traits< T, - boost::mysql::detail::serialization_tag::fixed_size_int + boost::mysql::detail::serialization_tag::plain_int >::serialize_( serialization_context& ctx, T input ) noexcept { - boost::endian::native_to_little_inplace(input.value); - ctx.write(&input.value, get_int_size()); -} - -template -constexpr std::size_t boost::mysql::detail::serialization_traits< - T, - boost::mysql::detail::serialization_tag::fixed_size_int ->::get_size_(const serialization_context&, T) noexcept -{ - return get_int_size(); -} - -// Fixed size strings -template -boost::mysql::errc boost::mysql::detail::serialization_traits< - boost::mysql::detail::string_fixed, - boost::mysql::detail::serialization_tag::none ->::deserialize_( - deserialization_context& ctx, - string_fixed& output -) noexcept -{ - if (!ctx.enough_size(N)) - { - return errc::incomplete_message; - } - memcpy(output.data(), ctx.first(), N); - ctx.advance(N); - return errc::ok; -} - -// Enums -template -boost::mysql::errc -boost::mysql::detail::serialization_traits< - T, - boost::mysql::detail::serialization_tag::enumeration ->::deserialize_( - deserialization_context& ctx, - T& output -) noexcept -{ - serializable_type value; - errc err = deserialize(ctx, value); - if (err != errc::ok) - { - return err; - } - output = static_cast(value.value); - return errc::ok; -} - -template -std::size_t boost::mysql::detail::serialization_traits< - T, - boost::mysql::detail::serialization_tag::enumeration ->::get_size_(const serialization_context&, T) noexcept -{ - return get_int_size(); + boost::endian::endian_store< + T, sizeof(T), boost::endian::order::little>(ctx.first(), input); + ctx.advance(sizeof(T)); } // Structs template constexpr bool boost::mysql::detail::is_struct_with_fields() { - return !std::is_same_v< - std::decay_t::value)>, - not_a_struct_with_fields - >; + return decltype(is_struct_with_fields_helper::get(nullptr))::value; } template @@ -290,7 +235,9 @@ boost::mysql::detail::serialization_traits< T& output ) noexcept { - return deserialize_struct(ctx, output, struct_index_sequence()); + struct_deserialize_op op (ctx); + T::apply(output, op); + return op.result; } template @@ -299,17 +246,14 @@ boost::mysql::detail::serialization_traits< T, boost::mysql::detail::serialization_tag::struct_with_fields >::serialize_( - [[maybe_unused]] serialization_context& ctx, - [[maybe_unused]] const T& input + serialization_context& ctx, + const T& input ) noexcept { // For commands, add the command ID. Commands are only sent by the client, // so this is not considered in the deserialization functions. - if constexpr (is_command::value) - { - serialize(ctx, int1(T::command_id)); - } - serialize_struct(ctx, input, struct_index_sequence()); + serialize_command_id(ctx, is_command()); + T::apply(input, struct_serialize_op(ctx)); } template @@ -319,9 +263,9 @@ boost::mysql::detail::serialization_traits< boost::mysql::detail::serialization_tag::struct_with_fields >::get_size_(const serialization_context& ctx, const T& input) noexcept { - std::size_t res = is_command::value ? 1 : 0; - res += get_size_struct(ctx, input, struct_index_sequence()); - return res; + struct_get_size_op op (ctx, is_command::value ? 1 : 0); + T::apply(input, op); + return op.result; } // Miscellanea @@ -368,7 +312,7 @@ constexpr boost::mysql::detail::serialization_tag boost::mysql::detail::get_serialization_tag() { return - is_fixed_size_int() ? serialization_tag::fixed_size_int : + std::is_integral::value ? serialization_tag::plain_int : std::is_enum::value ? serialization_tag::enumeration : is_struct_with_fields() ? serialization_tag::struct_with_fields : serialization_tag::none; diff --git a/include/boost/mysql/detail/protocol/impl/serialization.ipp b/include/boost/mysql/detail/protocol/impl/serialization.ipp index b7b4600c..eb6fa3e8 100644 --- a/include/boost/mysql/detail/protocol/impl/serialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/serialization.ipp @@ -14,12 +14,12 @@ namespace boost { namespace mysql { namespace detail { -inline std::string_view get_string( +inline boost::string_view get_string( const std::uint8_t* from, std::size_t size ) { - return std::string_view(reinterpret_cast(from), size); + return boost::string_view(reinterpret_cast(from), size); } } // detail @@ -35,35 +35,35 @@ boost::mysql::detail::serialization_traits< int_lenenc& output ) noexcept { - int1 first_byte; + std::uint8_t first_byte = 0; errc err = deserialize(ctx, first_byte); if (err != errc::ok) { return err; } - if (first_byte.value == 0xFC) + if (first_byte == 0xFC) { - int2 value; + std::uint16_t value = 0; err = deserialize(ctx, value); - output.value = value.value; + output.value = value; } - else if (first_byte.value == 0xFD) + else if (first_byte == 0xFD) { int3 value; err = deserialize(ctx, value); output.value = value.value; } - else if (first_byte.value == 0xFE) + else if (first_byte == 0xFE) { - int8 value; + std::uint64_t value = 0; err = deserialize(ctx, value); - output.value = value.value; + output.value = value; } else { err = errc::ok; - output.value = first_byte.value; + output.value = first_byte; } return err; } @@ -80,12 +80,12 @@ boost::mysql::detail::serialization_traits< { if (input.value < 251) { - serialize(ctx, int1(static_cast(input.value))); + serialize(ctx, static_cast(input.value)); } else if (input.value < 0x10000) { ctx.write(0xfc); - serialize(ctx, int2(static_cast(input.value))); + serialize(ctx, static_cast(input.value)); } else if (input.value < 0x1000000) { @@ -95,7 +95,7 @@ boost::mysql::detail::serialization_traits< else { ctx.write(0xfe); - serialize(ctx, int8(static_cast(input.value))); + serialize(ctx, static_cast(input.value)); } } @@ -181,24 +181,5 @@ boost::mysql::detail::serialization_traits< return errc::ok; } -inline std::pair -boost::mysql::detail::deserialize_message_type( - deserialization_context& ctx -) -{ - int1 msg_type; - std::pair res {}; - auto err = deserialize(ctx, msg_type); - if (err == errc::ok) - { - res.second = msg_type.value; - } - else - { - res.first = make_error_code(err); - } - return res; -} - #endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_IMPL_SERIALIZATION_IPP_ */ diff --git a/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp b/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp index 368d193d..5a66f166 100644 --- a/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp +++ b/include/boost/mysql/detail/protocol/impl/text_deserialization.ipp @@ -27,7 +27,7 @@ namespace detail { // Integers template errc deserialize_text_value_int_impl( - std::string_view from, + boost::string_view from, value& to ) noexcept { @@ -40,7 +40,7 @@ errc deserialize_text_value_int_impl( } inline errc deserialize_text_value_int( - std::string_view from, + boost::string_view from, value& to, const field_metadata& meta ) noexcept @@ -53,7 +53,7 @@ inline errc deserialize_text_value_int( // Floating points template errc deserialize_text_value_float( - std::string_view from, + boost::string_view from, value& to ) noexcept { @@ -67,7 +67,7 @@ errc deserialize_text_value_float( // Strings inline errc deserialize_text_value_string( - std::string_view from, + boost::string_view from, value& to ) noexcept { @@ -89,7 +89,7 @@ inline unsigned compute_micros(unsigned parsed_micros, unsigned decimals) noexce } inline errc deserialize_text_ymd( - std::string_view from, + boost::string_view from, ::date::year_month_day& to ) { @@ -123,7 +123,7 @@ inline errc deserialize_text_ymd( } inline errc deserialize_text_value_date( - std::string_view from, + boost::string_view from, value& to ) noexcept { @@ -152,7 +152,7 @@ inline errc deserialize_text_value_date( } inline errc deserialize_text_value_datetime( - std::string_view from, + boost::string_view from, value& to, const field_metadata& meta ) noexcept @@ -233,7 +233,7 @@ inline errc deserialize_text_value_datetime( } inline errc deserialize_text_value_time( - std::string_view from, + boost::string_view from, value& to, const field_metadata& meta ) noexcept @@ -315,7 +315,7 @@ inline bool is_next_field_null( } // boost inline boost::mysql::errc boost::mysql::detail::deserialize_text_value( - std::string_view from, + boost::string_view from, const field_metadata& meta, value& output ) diff --git a/include/boost/mysql/detail/protocol/prepared_statement_messages.hpp b/include/boost/mysql/detail/protocol/prepared_statement_messages.hpp index da79c3c3..89d081f9 100644 --- a/include/boost/mysql/detail/protocol/prepared_statement_messages.hpp +++ b/include/boost/mysql/detail/protocol/prepared_statement_messages.hpp @@ -22,37 +22,37 @@ struct com_stmt_prepare_packet string_eof statement; static constexpr std::uint8_t command_id = 0x16; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &com_stmt_prepare_packet::statement - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.statement + ); + } }; // response struct com_stmt_prepare_ok_packet { - // int1 status: must be 0 - int4 statement_id; - int2 num_columns; - int2 num_params; - // int1 reserved_1: must be 0 - int2 warning_count; - // int1 metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA: not implemented -}; + // std::uint8_t status: must be 0 + std::uint32_t statement_id; + std::uint16_t num_columns; + std::uint16_t num_params; + // std::uint8_t reserved_1: must be 0 + std::uint16_t warning_count; + // std::uint8_t metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA: not implemented -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &com_stmt_prepare_ok_packet::statement_id, - &com_stmt_prepare_ok_packet::num_columns, - &com_stmt_prepare_ok_packet::num_params, - &com_stmt_prepare_ok_packet::warning_count - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.statement_id, + self.num_columns, + self.num_params, + self.warning_count + ); + } }; template <> @@ -67,26 +67,26 @@ struct serialization_traits struct com_stmt_execute_packet { - int4 statement_id; - int1 flags; - int4 iteration_count; + std::uint32_t statement_id; + std::uint8_t flags; + std::uint32_t iteration_count; // if num_params > 0: NULL bitmap - int1 new_params_bind_flag; + std::uint8_t new_params_bind_flag; ForwardIterator params_begin; ForwardIterator params_end; static constexpr std::uint8_t command_id = 0x17; -}; -template -struct get_struct_fields> -{ - static constexpr auto value = std::make_tuple( - &com_stmt_execute_packet::statement_id, - &com_stmt_execute_packet::flags, - &com_stmt_execute_packet::iteration_count, - &com_stmt_execute_packet::new_params_bind_flag - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.statement_id, + self.flags, + self.iteration_count, + self.new_params_bind_flag + ); + } }; template @@ -104,32 +104,32 @@ struct serialization_traits< struct com_stmt_execute_param_meta_packet { protocol_field_type type; - int1 unsigned_flag; -}; + std::uint8_t unsigned_flag; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &com_stmt_execute_param_meta_packet::type, - &com_stmt_execute_param_meta_packet::unsigned_flag - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.type, + self.unsigned_flag + ); + } }; // close struct com_stmt_close_packet { - int4 statement_id; + std::uint32_t statement_id; static constexpr std::uint8_t command_id = 0x19; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &com_stmt_close_packet::statement_id - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.statement_id + ); + } }; diff --git a/include/boost/mysql/detail/protocol/protocol_types.hpp b/include/boost/mysql/detail/protocol/protocol_types.hpp index 96286e39..250dd415 100644 --- a/include/boost/mysql/detail/protocol/protocol_types.hpp +++ b/include/boost/mysql/detail/protocol/protocol_types.hpp @@ -8,36 +8,47 @@ #ifndef BOOST_MYSQL_DETAIL_PROTOCOL_PROTOCOL_TYPES_HPP #define BOOST_MYSQL_DETAIL_PROTOCOL_PROTOCOL_TYPES_HPP -#include "boost/mysql/detail/protocol/value_holder.hpp" +#include #include -#include #include #include #include +#include namespace boost { namespace mysql { namespace detail { -struct int1 : value_holder { using value_holder::value_holder; }; -struct int2 : value_holder { using value_holder::value_holder; }; +template +struct value_holder +{ + using value_type = T; + + static_assert(std::is_nothrow_default_constructible::value, + "value_holder should be nothrow default constructible"); + static_assert(std::is_nothrow_move_constructible::value, + "value_holder should be nothrow move constructible"); + + value_type value; + + value_holder() noexcept : value{} {}; + + explicit constexpr value_holder(value_type v) noexcept : value(v) {} + + constexpr bool operator==(const value_holder& rhs) const { return value == rhs.value; } + constexpr bool operator!=(const value_holder& rhs) const { return value != rhs.value; } +}; + struct int3 : value_holder { using value_holder::value_holder; }; -struct int4 : value_holder { using value_holder::value_holder; }; -struct int6 : value_holder { using value_holder::value_holder; }; -struct int8 : value_holder { using value_holder::value_holder; }; -struct int1_signed : value_holder { using value_holder::value_holder; }; -struct int2_signed : value_holder { using value_holder::value_holder; }; -struct int4_signed : value_holder { using value_holder::value_holder; }; -struct int8_signed : value_holder { using value_holder::value_holder; }; struct int_lenenc : value_holder { using value_holder::value_holder; }; +struct string_null : value_holder { using value_holder::value_holder; }; +struct string_eof : value_holder { using value_holder::value_holder; }; +struct string_lenenc : value_holder { using value_holder::value_holder; }; + template using string_fixed = std::array; -struct string_null : value_holder { using value_holder::value_holder; }; -struct string_eof : value_holder { using value_holder::value_holder; }; -struct string_lenenc : value_holder { using value_holder::value_holder; }; - } // detail } // mysql diff --git a/include/boost/mysql/detail/protocol/query_messages.hpp b/include/boost/mysql/detail/protocol/query_messages.hpp index 40897d5d..32e40066 100644 --- a/include/boost/mysql/detail/protocol/query_messages.hpp +++ b/include/boost/mysql/detail/protocol/query_messages.hpp @@ -20,14 +20,14 @@ struct com_query_packet string_eof query; static constexpr std::uint8_t command_id = 3; -}; -template <> -struct get_struct_fields -{ - static constexpr auto value = std::make_tuple( - &com_query_packet::query - ); + template + static void apply(Self& self, Callable&& cb) + { + std::forward(cb)( + self.query + ); + } }; } diff --git a/include/boost/mysql/detail/protocol/serialization.hpp b/include/boost/mysql/detail/protocol/serialization.hpp index ec1d3c9b..3448a1a3 100644 --- a/include/boost/mysql/detail/protocol/serialization.hpp +++ b/include/boost/mysql/detail/protocol/serialization.hpp @@ -25,7 +25,7 @@ namespace detail { enum class serialization_tag { none, - fixed_size_int, + plain_int, enumeration, struct_with_fields }; @@ -45,16 +45,33 @@ void serialize(serialization_context& ctx, const Types&... input) noexcept; template std::size_t get_size(const serialization_context& ctx, const Types&... input) noexcept; -// Fixed-size integers +// Plain integers template -constexpr bool is_fixed_size_int(); - -template -struct serialization_traits +struct serialization_traits { static errc deserialize_(deserialization_context& ctx, T& output) noexcept; static void serialize_(serialization_context& ctx, T input) noexcept; - static constexpr std::size_t get_size_(const serialization_context&, T) noexcept; + static constexpr std::size_t get_size_(const serialization_context&, T) noexcept { return sizeof(T); } +}; + +// int3 +template <> +struct serialization_traits +{ + static inline errc deserialize_(deserialization_context& ctx, int3& output) noexcept + { + if (!ctx.enough_size(3)) + return errc::incomplete_message; + output.value = boost::endian::load_little_u24(ctx.first()); + ctx.advance(3); + return errc::ok; + } + static inline void serialize_(serialization_context& ctx, int3 input) noexcept + { + boost::endian::store_little_u24(ctx.first(), input.value); + ctx.advance(3); + } + static inline std::size_t get_size_(const serialization_context&, int3) noexcept { return 3; } }; // int_lenenc @@ -71,7 +88,14 @@ struct serialization_traits template struct serialization_traits, serialization_tag::none> { - static errc deserialize_(deserialization_context& ctx, string_fixed& output) noexcept; + static errc deserialize_(deserialization_context& ctx, string_fixed& output) noexcept + { + if (!ctx.enough_size(N)) + return errc::incomplete_message; + memcpy(output.data(), ctx.first(), N); + ctx.advance(N); + return errc::ok; + } static void serialize_(serialization_context& ctx, const string_fixed& input) noexcept { ctx.write(input.data(), N); @@ -134,30 +158,31 @@ struct serialization_traits template struct serialization_traits { - using underlying_type = std::underlying_type_t; - using serializable_type = value_holder; + using underlying_type = typename std::underlying_type::type; - static errc deserialize_(deserialization_context& ctx, T& output) noexcept; + static errc deserialize_(deserialization_context& ctx, T& output) noexcept + { + underlying_type value = 0; + errc err = deserialize(ctx, value); + output = static_cast(value); + return err; + } static void serialize_(serialization_context& ctx, T input) noexcept { - serialize(ctx, serializable_type(static_cast(input))); + serialize(ctx, static_cast(input)); } - static std::size_t get_size_(const serialization_context&, T) noexcept; + static std::size_t get_size_(const serialization_context&, T) noexcept { return sizeof(underlying_type); } }; -// Structs and commands (messages) +// Structs and commands // To allow a limited way of reflection, structs should -// specialize get_struct_fields with a tuple of pointers to members, -// thus defining which fields should be (de)serialized in the struct -// and in which order -struct not_a_struct_with_fields {}; // Tag indicating a type is not a struct with fields - -template -struct get_struct_fields -{ - static constexpr not_a_struct_with_fields value {}; -}; - +// provide a static member function with signature: +// void apply(Self&, Callable&&), which will invoke Callable +// with each field in the struct as argument (similar to std::apply). +// Making the member function static with a Self template parameter +// allows calling apply with const and non-const objects as self. +// Types fulfilling this requirement will have the below function +// return true template constexpr bool is_struct_with_fields(); @@ -203,11 +228,6 @@ error_code deserialize_message( template void serialize_fields(serialization_context& ctx, const Types&... fields) noexcept; -inline std::pair deserialize_message_type( - deserialization_context& ctx -); - - } // detail } // mysql } // boost diff --git a/include/boost/mysql/detail/protocol/text_deserialization.hpp b/include/boost/mysql/detail/protocol/text_deserialization.hpp index a7b7c7e2..c15b3954 100644 --- a/include/boost/mysql/detail/protocol/text_deserialization.hpp +++ b/include/boost/mysql/detail/protocol/text_deserialization.hpp @@ -19,7 +19,7 @@ namespace mysql { namespace detail { inline errc deserialize_text_value( - std::string_view from, + boost::string_view from, const field_metadata& meta, value& output ); diff --git a/include/boost/mysql/detail/protocol/value_holder.hpp b/include/boost/mysql/detail/protocol/value_holder.hpp deleted file mode 100644 index 96f6453d..00000000 --- a/include/boost/mysql/detail/protocol/value_holder.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (c) 2019-2020 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_PROTOCOL_VALUE_HOLDER_HPP -#define BOOST_MYSQL_DETAIL_PROTOCOL_VALUE_HOLDER_HPP - -#include -#include - -namespace boost { -namespace mysql { -namespace detail { - -template -struct value_holder -{ - using value_type = T; - static_assert(std::is_nothrow_default_constructible_v); - - value_type value; - - value_holder() noexcept: value{} {}; - - template - explicit constexpr value_holder(U&& u) - noexcept(std::is_nothrow_constructible_v): - value(std::forward(u)) {} - - constexpr bool operator==(const value_holder& rhs) const { return value == rhs.value; } - constexpr bool operator!=(const value_holder& rhs) const { return value != rhs.value; } -}; - -// Operations on value holders -struct get_value_type_helper -{ - struct no_value_type {}; - - template - static constexpr typename T::value_type get(typename T::value_type*); - - template - static constexpr no_value_type get(...); -}; - -template -struct get_value_type -{ - using type = decltype(get_value_type_helper().get(nullptr)); - using no_value_type = get_value_type_helper::no_value_type; -}; - -template -using get_value_type_t = typename get_value_type::type; - -template -constexpr bool is_value_holder_v = std::is_base_of_v>, T>; - -} -} -} - - - -#endif /* INCLUDE_BOOST_MYSQL_DETAIL_PROTOCOL_VALUE_HOLDER_HPP_ */ diff --git a/include/boost/mysql/impl/connection.hpp b/include/boost/mysql/impl/connection.hpp index dd2123ca..aa4be82f 100644 --- a/include/boost/mysql/impl/connection.hpp +++ b/include/boost/mysql/impl/connection.hpp @@ -61,7 +61,7 @@ boost::mysql::connection::async_handshake( // Query template boost::mysql::resultset boost::mysql::connection::query( - std::string_view query_string, + boost::string_view query_string, error_code& err, error_info& info ) @@ -74,7 +74,7 @@ boost::mysql::resultset boost::mysql::connection::query( template boost::mysql::resultset boost::mysql::connection::query( - std::string_view query_string + boost::string_view query_string ) { resultset res; @@ -92,7 +92,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( void(boost::mysql::error_code, boost::mysql::resultset) ) boost::mysql::connection::async_query( - std::string_view query_string, + boost::string_view query_string, error_info& output_info, CompletionToken&& token ) @@ -108,7 +108,7 @@ boost::mysql::connection::async_query( template boost::mysql::prepared_statement boost::mysql::connection::prepare_statement( - std::string_view statement, + boost::string_view statement, error_code& err, error_info& info ) @@ -121,7 +121,7 @@ boost::mysql::prepared_statement boost::mysql::connection::prepa template boost::mysql::prepared_statement boost::mysql::connection::prepare_statement( - std::string_view statement + boost::string_view statement ) { mysql::prepared_statement res; @@ -139,7 +139,7 @@ BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( void(boost::mysql::error_code, boost::mysql::prepared_statement) ) boost::mysql::connection::async_prepare_statement( - std::string_view statement, + boost::string_view statement, error_info& output_info, CompletionToken&& token ) diff --git a/include/boost/mysql/impl/error.hpp b/include/boost/mysql/impl/error.hpp index a692aa96..ece88cc8 100644 --- a/include/boost/mysql/impl/error.hpp +++ b/include/boost/mysql/impl/error.hpp @@ -9,6 +9,7 @@ #define BOOST_MYSQL_IMPL_ERROR_HPP #include +#include #include #include "boost/mysql/impl/error_descriptions.hpp" @@ -45,11 +46,29 @@ public: return error_to_string(static_cast(ev)); } }; + +#ifdef BOOST_NO_CXX17_INLINE_VARIABLES + +inline mysql_error_category_t& get_mysql_error_category() noexcept +{ + static mysql_error_category_t res; + return res; +} + +#else + inline mysql_error_category_t mysql_error_category; +inline mysql_error_category_t& get_mysql_error_category() noexcept +{ + return mysql_error_category; +} + +#endif + inline boost::system::error_code make_error_code(errc error) { - return boost::system::error_code(static_cast(error), mysql_error_category); + return boost::system::error_code(static_cast(error), get_mysql_error_category()); } inline void check_error_code(const error_code& code, const error_info& info) diff --git a/include/boost/mysql/impl/metadata.ipp b/include/boost/mysql/impl/metadata.ipp index e2773e32..f4446bcd 100644 --- a/include/boost/mysql/impl/metadata.ipp +++ b/include/boost/mysql/impl/metadata.ipp @@ -85,7 +85,7 @@ inline boost::mysql::field_type boost::mysql::field_metadata::type() const noexc { if (field_type_ == field_type::_not_computed) { - field_type_ = detail::compute_field_type(msg_.type, msg_.flags.value); + field_type_ = detail::compute_field_type(msg_.type, msg_.flags); assert(field_type_ != field_type::_not_computed); } return field_type_; diff --git a/include/boost/mysql/impl/prepared_statement.hpp b/include/boost/mysql/impl/prepared_statement.hpp index d6058480..9da36c50 100644 --- a/include/boost/mysql/impl/prepared_statement.hpp +++ b/include/boost/mysql/impl/prepared_statement.hpp @@ -52,7 +52,7 @@ boost::mysql::resultset boost::mysql::prepared_statement::execut { detail::execute_statement( *channel_, - stmt_msg_.statement_id.value, + stmt_msg_.statement_id, params_first, params_last, res, @@ -77,6 +77,59 @@ boost::mysql::resultset boost::mysql::prepared_statement::execut return res; } +// Helper for async_execute +template +struct boost::mysql::prepared_statement::async_execute_initiation +{ + template + struct error_handler + { + error_code err; + HandlerType h; + + void operator()() + { + std::forward(h)(err, resultset()); + } + }; + + template + void operator()( + HandlerType&& handler, + error_code err, + error_info& info, + prepared_statement& stmt, + ForwardIterator params_first, + ForwardIterator params_last + ) const + { + if (err) + { + auto executor = boost::asio::get_associated_executor( + handler, + stmt.next_layer().get_executor() + ); + + boost::asio::post(boost::asio::bind_executor( + executor, + error_handler{err, std::forward(handler)} + )); + } + else + { + // Actually execute the statement + detail::async_execute_statement( + *stmt.channel_, + stmt.stmt_msg_.statement_id, + params_first, + params_last, + std::forward(handler), + info + ); + } + } +}; + template template )) CompletionToken> @@ -98,39 +151,8 @@ boost::mysql::prepared_statement::async_execute( error_code err; check_num_params(params_first, params_last, err, output_info); - auto initiation = [](auto&& handler, error_code err, error_info& info, - prepared_statement& stmt, ForwardIterator params_first, - ForwardIterator params_last) { - if (err) - { - auto executor = boost::asio::get_associated_executor( - handler, - stmt.next_layer().get_executor() - ); - - boost::asio::post(boost::asio::bind_executor( - executor, - [handler = std::forward(handler), err] () mutable { - std::forward(handler)(err, resultset()); - } - )); - } - else - { - // Actually execute the statement - detail::async_execute_statement( - *stmt.channel_, - stmt.stmt_msg_.statement_id.value, - params_first, - params_last, - std::forward(handler), - info - ); - } - }; - return boost::asio::async_initiate)>( - initiation, + async_execute_initiation(), token, err, std::ref(output_info), diff --git a/include/boost/mysql/impl/resultset.hpp b/include/boost/mysql/impl/resultset.hpp index c51e1383..73fac590 100644 --- a/include/boost/mysql/impl/resultset.hpp +++ b/include/boost/mysql/impl/resultset.hpp @@ -238,7 +238,11 @@ struct boost::mysql::resultset::fetch_many_op }; template - auto bind_handler(Self& self, error_code err) + auto bind_handler(Self& self, error_code err) -> + boost::asio::executor_binder< + decltype(std::bind(std::move(self), err, detail::read_row_result::eof)), + decltype(boost::asio::get_associated_executor(self, impl_->parent_resultset.get_executor())) + > { auto executor = boost::asio::get_associated_executor( self, diff --git a/include/boost/mysql/impl/value.hpp b/include/boost/mysql/impl/value.hpp index f4fca5c0..769e9ccc 100644 --- a/include/boost/mysql/impl/value.hpp +++ b/include/boost/mysql/impl/value.hpp @@ -22,12 +22,14 @@ inline bool is_out_of_range( } // Range checks -static_assert(date::min() <= min_date); -static_assert(date::max() >= max_date); -static_assert(datetime::min() <= min_datetime); -static_assert(datetime::max() >= max_datetime); -static_assert(time::min() <= min_time); -static_assert(time::max() >= max_time); +#ifndef BOOST_NO_CXX14_CONSTEXPR +static_assert(date::min() <= min_date, "Range check failed"); +static_assert(date::max() >= max_date, "Range check failed"); +static_assert(datetime::min() <= min_datetime, "Range check failed"); +static_assert(datetime::max() >= max_datetime, "Range check failed"); +static_assert(time::min() <= min_time, "Range check failed"); +static_assert(time::max() >= max_time, "Range check failed"); +#endif struct print_visitor { @@ -62,31 +64,62 @@ struct print_visitor void operator()(std::nullptr_t) const { os << ""; } }; -template -constexpr std::optional get_optional_noconv( - const value::variant_type& v +template class Optional> +struct value_converter +{ + static BOOST_CXX14_CONSTEXPR Optional convert(const value::variant_type&) noexcept + { + return {}; + } +}; + +template