Replaced tabs for spaces

This commit is contained in:
ruben 2020-04-20 11:36:17 +01:00
parent 16ef83b136
commit a54c83ef6a
113 changed files with 9058 additions and 9059 deletions

View File

@ -15,7 +15,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
if(BUILD_TESTING)
set(_MYSQL_TESTING_ENABLED ON)
set(_MYSQL_TESTING_ENABLED ON)
endif()
endif()
@ -34,7 +34,7 @@ find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)
if (BOOST_MYSQL_VALGRIND_TESTS)
find_package(Mysqlvalgrind REQUIRED)
find_package(Mysqlvalgrind REQUIRED)
endif()
# Date
@ -58,35 +58,35 @@ add_library(
target_link_libraries(
mysql_asio
INTERFACE
Boost::system
Threads::Threads
OpenSSL::Crypto
OpenSSL::SSL
INTERFACE
Boost::system
Threads::Threads
OpenSSL::Crypto
OpenSSL::SSL
)
target_include_directories(
mysql_asio
INTERFACE
include
${date_SOURCE_DIR}/include
mysql_asio
INTERFACE
include
${date_SOURCE_DIR}/include
)
target_compile_features(
mysql_asio
INTERFACE
cxx_std_17
mysql_asio
INTERFACE
cxx_std_17
)
if (BOOST_MYSQL_VALGRIND_TESTS)
target_link_libraries(
mysql_asio
INTERFACE
Mysqlvalgrind::Mysqlvalgrind
)
target_link_libraries(
mysql_asio
INTERFACE
Mysqlvalgrind::Mysqlvalgrind
)
endif()
# Examples and tests
if(_MYSQL_TESTING_ENABLED)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/test_utils.cmake)
add_subdirectory(example)
add_subdirectory(test)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/test_utils.cmake)
add_subdirectory(example)
add_subdirectory(test)
endif()

View File

@ -1,5 +1,4 @@
Sanitize
Tabs to spaces
Bad serialize(value.value) somewhere
Copy operations for handshake
Test zero dates

View File

@ -12,31 +12,31 @@
cp ci/*.pem /tmp # Copy SSL certs/keys to a known location
if [ $TRAVIS_OS_NAME == "osx" ]; then
brew update
brew install $DATABASE
cp ci/unix-ci.cnf ~/.my.cnf # This location is checked by both MySQL and MariaDB
sudo mkdir -p /var/run/mysqld/
sudo chmod 777 /var/run/mysqld/
mysql.server start # Note that running this with sudo fails
if [ $DATABASE == "mariadb" ]; then
sudo mysql -u root < ci/root_user_setup.sql
else
export BOOST_MYSQL_HAS_SHA256=1
fi
brew update
brew install $DATABASE
cp ci/unix-ci.cnf ~/.my.cnf # This location is checked by both MySQL and MariaDB
sudo mkdir -p /var/run/mysqld/
sudo chmod 777 /var/run/mysqld/
mysql.server start # Note that running this with sudo fails
if [ $DATABASE == "mariadb" ]; then
sudo mysql -u root < ci/root_user_setup.sql
else
export BOOST_MYSQL_HAS_SHA256=1
fi
else
sudo cp ci/unix-ci.cnf /etc/mysql/conf.d/
sudo service mysql restart
wget https://github.com/Kitware/CMake/releases/download/v3.17.0/cmake-3.17.0-Linux-x86_64.sh -q -O cmake-latest.sh
mkdir -p /tmp/cmake-latest
bash cmake-latest.sh --prefix=/tmp/cmake-latest --skip-license
export PATH=/tmp/cmake-latest/bin:$PATH
sudo cp ci/unix-ci.cnf /etc/mysql/conf.d/
sudo service mysql restart
wget https://github.com/Kitware/CMake/releases/download/v3.17.0/cmake-3.17.0-Linux-x86_64.sh -q -O cmake-latest.sh
mkdir -p /tmp/cmake-latest
bash cmake-latest.sh --prefix=/tmp/cmake-latest --skip-license
export PATH=/tmp/cmake-latest/bin:$PATH
fi
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
$(if [ $USE_VALGRIND ]; then echo -DBOOST_MYSQL_VALGRIND_TESTS=ON; fi) \
$CMAKE_OPTIONS \
..
$(if [ $USE_VALGRIND ]; then echo -DBOOST_MYSQL_VALGRIND_TESTS=ON; fi) \
$CMAKE_OPTIONS \
..
make -j6 CTEST_OUTPUT_ON_FAILURE=1 all test

View File

@ -26,29 +26,29 @@ if(Mysqlvalgrind_FOUND AND NOT TARGET Mysqlvalgrind::Mysqlvalgrind)
endif()
if (Mysqlvalgrind_FOUND AND NOT COMMAND MysqlValgrind_AddTest)
function(Mysqlvalgrind_AddMemcheckTest)
set(options "")
set(oneValueArgs NAME)
set(multiValueArgs COMMAND)
cmake_parse_arguments(
AddMemcheckTest
"${options}"
"${oneValueArgs}"
function(Mysqlvalgrind_AddMemcheckTest)
set(options "")
set(oneValueArgs NAME)
set(multiValueArgs COMMAND)
cmake_parse_arguments(
AddMemcheckTest
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
add_test(
NAME ${AddMemcheckTest_NAME}
COMMAND
${Mysqlvalgrind_EXECUTABLE}
--leak-check=full
--error-limit=yes
--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions.txt
--error-exitcode=1
--gen-suppressions=all
${AddMemcheckTest_COMMAND}
)
endfunction()
add_test(
NAME ${AddMemcheckTest_NAME}
COMMAND
${Mysqlvalgrind_EXECUTABLE}
--leak-check=full
--error-limit=yes
--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions.txt
--error-exitcode=1
--gen-suppressions=all
${AddMemcheckTest_COMMAND}
)
endfunction()
endif()

View File

@ -9,31 +9,31 @@ find_package(Python3 REQUIRED)
# Adds a test fixture that consists of running a SQL file in the MySQL server
function (_mysql_sql_setup_fixture)
set(options "")
set(options "")
set(oneValueArgs TEST_NAME FIXTURE_NAME SQL_FILE SKIP_VAR)
set(multiValueArgs "")
cmake_parse_arguments(
SQLFIXT
"${options}"
"${oneValueArgs}"
SQLFIXT
"${options}"
"${oneValueArgs}"
"${multiValueArgs}"
${ARGN}
)
# If this env var is defined we will skip setup
# (just for development) (done in the Python script)
if (SQLFIXT_SKIP_VAR)
set(ADDITIONAL_OPTIONS -s ${SQLFIXT_SKIP_VAR})
endif()
# Actual test
add_test(
NAME ${SQLFIXT_TEST_NAME}
COMMAND
${Python3_EXECUTABLE}
${CMAKE_SOURCE_DIR}/tools/run_sql.py
${SQLFIXT_SQL_FILE}
${ADDITIONAL_OPTIONS}
)
set_tests_properties(${SQLFIXT_TEST_NAME} PROPERTIES FIXTURES_SETUP ${SQLFIXT_FIXTURE_NAME})
# If this env var is defined we will skip setup
# (just for development) (done in the Python script)
if (SQLFIXT_SKIP_VAR)
set(ADDITIONAL_OPTIONS -s ${SQLFIXT_SKIP_VAR})
endif()
# Actual test
add_test(
NAME ${SQLFIXT_TEST_NAME}
COMMAND
${Python3_EXECUTABLE}
${CMAKE_SOURCE_DIR}/tools/run_sql.py
${SQLFIXT_SQL_FILE}
${ADDITIONAL_OPTIONS}
)
set_tests_properties(${SQLFIXT_TEST_NAME} PROPERTIES FIXTURES_SETUP ${SQLFIXT_FIXTURE_NAME})
endfunction()

View File

@ -8,26 +8,26 @@
# Utility function to set warnings and other compile properties of
# our test targets
function(_mysql_common_target_settings TARGET_NAME)
if(MSVC)
if (WIN32 AND CMAKE_SYSTEM_VERSION)
set(WINNT_VERSION ${CMAKE_SYSTEM_VERSION})
string(REPLACE "." "" WINNT_VERSION ${WINNT_VERSION})
string(REGEX REPLACE "([0-9])" "0\\1" WINNT_VERSION ${WINNT_VERSION})
set(WINNT_VERSION "0x${WINNT_VERSION}")
endif()
target_compile_definitions(
${TARGET_NAME}
PRIVATE
_CRT_SECURE_NO_WARNINGS
_WIN32_WINNT=${WINNT_VERSION} # Silence warnings in Windows
_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING # Warnings in C++17 for Asio
)
# For some reason, Appveyor setup needs this to link against coroutine
target_link_directories(${TARGET_NAME} PRIVATE ${Boost_LIBRARY_DIRS})
target_compile_options(${TARGET_NAME} PRIVATE /bigobj) # Prevent failures on Windows
else()
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
endif()
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF) # disable extensions
if(MSVC)
if (WIN32 AND CMAKE_SYSTEM_VERSION)
set(WINNT_VERSION ${CMAKE_SYSTEM_VERSION})
string(REPLACE "." "" WINNT_VERSION ${WINNT_VERSION})
string(REGEX REPLACE "([0-9])" "0\\1" WINNT_VERSION ${WINNT_VERSION})
set(WINNT_VERSION "0x${WINNT_VERSION}")
endif()
target_compile_definitions(
${TARGET_NAME}
PRIVATE
_CRT_SECURE_NO_WARNINGS
_WIN32_WINNT=${WINNT_VERSION} # Silence warnings in Windows
_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING # Warnings in C++17 for Asio
)
# For some reason, Appveyor setup needs this to link against coroutine
target_link_directories(${TARGET_NAME} PRIVATE ${Boost_LIBRARY_DIRS})
target_compile_options(${TARGET_NAME} PRIVATE /bigobj) # Prevent failures on Windows
else()
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
endif()
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF) # disable extensions
endfunction()

View File

@ -9,39 +9,39 @@ find_package(Boost REQUIRED COMPONENTS coroutine)
# Compile the example
function (_mysql_build_example EXECUTABLE_NAME CPPFILE)
add_executable(
${EXECUTABLE_NAME}
${CPPFILE}
)
target_link_libraries(
${EXECUTABLE_NAME}
PRIVATE
mysql_asio
Boost::coroutine
)
_mysql_common_target_settings(${EXECUTABLE_NAME})
add_executable(
${EXECUTABLE_NAME}
${CPPFILE}
)
target_link_libraries(
${EXECUTABLE_NAME}
PRIVATE
mysql_asio
Boost::coroutine
)
_mysql_common_target_settings(${EXECUTABLE_NAME})
endfunction()
# Run it as a test
function (_mysql_test_example EXECUTABLE_NAME)
set(EXECUTABLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE_NAME})
set(EXECUTABLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE_NAME})
set(TEST_NAME "mysql_${EXECUTABLE_NAME}")
add_test(
NAME ${TEST_NAME}
COMMAND ${EXECUTABLE_PATH} example_user example_password
)
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
add_test(
NAME ${TEST_NAME}
COMMAND ${EXECUTABLE_PATH} example_user example_password
)
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
endfunction()
# Run it as a test using Valgrind
function (_mysql_memcheck_example EXECUTABLE_NAME)
set(EXECUTABLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE_NAME})
set(EXECUTABLE_PATH ${CMAKE_CURRENT_BINARY_DIR}/${EXECUTABLE_NAME})
set(TEST_NAME "mysql_${EXECUTABLE_NAME}_memcheck")
Mysqlvalgrind_AddMemcheckTest(
NAME ${TEST_NAME}
COMMAND ${EXECUTABLE_PATH} example_user example_password
)
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
Mysqlvalgrind_AddMemcheckTest(
NAME ${TEST_NAME}
COMMAND ${EXECUTABLE_PATH} example_user example_password
)
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
endfunction()
# The list of all the examples we have
@ -62,9 +62,9 @@ set(MYSQL_EXAMPLES_NOMEMCHECK
# Run setup
_mysql_sql_setup_fixture(
TEST_NAME mysql_examples_setup
FIXTURE_NAME mysql_examples_fixture
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/db_setup.sql
TEST_NAME mysql_examples_setup
FIXTURE_NAME mysql_examples_fixture
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/db_setup.sql
)
# Build and run examples
@ -73,10 +73,10 @@ foreach(EXAMPLE_NAME ${MYSQL_EXAMPLES})
_mysql_build_example(${EXECUTABLE_NAME} "${EXAMPLE_NAME}.cpp")
# Don't run examples twice. Some of them would require re-running setup
# Don't run examples twice. Some of them would require re-running setup
if (BOOST_MYSQL_VALGRIND_TESTS AND NOT ${EXAMPLE_NAME} IN_LIST MYSQL_EXAMPLES_NOMEMCHECK)
_mysql_memcheck_example(${EXECUTABLE_NAME})
_mysql_memcheck_example(${EXECUTABLE_NAME})
else()
_mysql_test_example(${EXECUTABLE_NAME})
_mysql_test_example(${EXECUTABLE_NAME})
endif()
endforeach()

View File

@ -15,30 +15,30 @@ USE mysql_asio_examples;
-- Tables
CREATE TABLE company(
id CHAR(10) NOT NULL PRIMARY KEY,
name VARCHAR(100) NOT NULL
id CHAR(10) NOT NULL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE employee(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
salary DOUBLE,
company_id CHAR(10) NOT NULL,
FOREIGN KEY (company_id) REFERENCES company(id)
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
salary DOUBLE,
company_id CHAR(10) NOT NULL,
FOREIGN KEY (company_id) REFERENCES company(id)
);
INSERT INTO company (name, id) VALUES
("Award Winning Company, Inc.", "AWC"),
("Sector Global Leader Plc", "SGL"),
("High Growth Startup, Ltd", "HGS")
("Award Winning Company, Inc.", "AWC"),
("Sector Global Leader Plc", "SGL"),
("High Growth Startup, Ltd", "HGS")
;
INSERT INTO employee (first_name, last_name, salary, company_id) VALUES
("Efficient", "Developer", 30000, "AWC"),
("Lazy", "Manager", 80000, "AWC"),
("Good", "Team Player", 35000, "HGS"),
("Enormous", "Slacker", 45000, "SGL"),
("Coffee", "Drinker", 30000, "HGS"),
("Underpaid", "Intern", 15000, "AWC")
("Efficient", "Developer", 30000, "AWC"),
("Lazy", "Manager", 80000, "AWC"),
("Good", "Team Player", 35000, "HGS"),
("Enormous", "Slacker", 45000, "SGL"),
("Coffee", "Drinker", 30000, "HGS"),
("Underpaid", "Intern", 15000, "AWC")
;
-- User

View File

@ -24,78 +24,78 @@
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
// Connection params (host, port, user, password, database)
boost::asio::ip::tcp::endpoint ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port);
boost::mysql::connection_params params (argv[1], argv[2], "mysql_asio_examples");
// Connection params (host, port, user, password, database)
boost::asio::ip::tcp::endpoint ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port);
boost::mysql::connection_params params (argv[1], argv[2], "mysql_asio_examples");
// TCP and MySQL level connect
boost::asio::io_context ctx;
boost::mysql::tcp_connection conn (ctx);
conn.next_layer().connect(ep);
conn.handshake(params);
// TCP and MySQL level connect
boost::asio::io_context ctx;
boost::mysql::tcp_connection conn (ctx);
conn.next_layer().connect(ep);
conn.handshake(params);
// Issue the query
const char* sql = R"(
SELECT comp.name AS company_name, emp.id AS employee_id
FROM employee emp
JOIN company comp ON (comp.id = emp.company_id)
)";
boost::mysql::tcp_resultset result = conn.query(sql);
// Issue the query
const char* sql = R"(
SELECT comp.name AS company_name, emp.id AS employee_id
FROM employee emp
JOIN company comp ON (comp.id = emp.company_id)
)";
boost::mysql::tcp_resultset result = conn.query(sql);
/**
* Resultsets allow you to access metadata about the fields in the query
* using the fields() function, which returns a vector of field_metadata
* (one per field in the query, and in the same order as in the query).
* 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);
/**
* Resultsets allow you to access metadata about the fields in the query
* using the fields() function, which returns a vector of field_metadata
* (one per field in the query, and in the same order as in the query).
* 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);
[[maybe_unused]] const boost::mysql::field_metadata& company_name = result.fields()[0];
assert(company_name.database() == "mysql_asio_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& company_name = result.fields()[0];
assert(company_name.database() == "mysql_asio_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() == "mysql_asio_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
[[maybe_unused]] const boost::mysql::field_metadata& employee_id = result.fields()[1];
assert(employee_id.database() == "mysql_asio_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
}
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -24,128 +24,128 @@
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
// Connection parameters
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
// Connection parameters
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
boost::asio::io_context ctx;
boost::asio::io_context ctx;
// Declare the connection object and authenticate to the server
boost::mysql::tcp_connection conn (ctx);
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
conn.handshake(params); // Authenticates to the MySQL server
// Declare the connection object and authenticate to the server
boost::mysql::tcp_connection conn (ctx);
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
conn.handshake(params); // Authenticates to the MySQL server
/**
* We can tell MySQL to prepare a statement using connection::prepare_statement.
* We provide a string SQL statement, which can include any number of parameters,
* identified by question marks. Parameters are optional: you can prepare a statement
* with no parameters.
*
* Prepared statements are stored in the server on a per-connection basis.
* Once a connection is closed, all prepared statements for that connection are deallocated.
*
* The result of prepare_statement is a mysql::prepared_statement object, which is
* templatized on the stream type of the connection (tcp_prepared_statement in our case).
*
* We prepare two statements, a SELECT and an UPDATE.
*/
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)
/**
* We can tell MySQL to prepare a statement using connection::prepare_statement.
* We provide a string SQL statement, which can include any number of parameters,
* identified by question marks. Parameters are optional: you can prepare a statement
* with no parameters.
*
* Prepared statements are stored in the server on a per-connection basis.
* Once a connection is closed, all prepared statements for that connection are deallocated.
*
* The result of prepare_statement is a mysql::prepared_statement object, which is
* templatized on the stream type of the connection (tcp_prepared_statement in our case).
*
* We prepare two statements, a SELECT and an UPDATE.
*/
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)
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);
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);
/*
* Once a statement has been prepared, it can be executed as many times as
* desired, by calling prepared_statement::execute(). execute takes as input a
* (possibly empty) collection of mysql::value's and returns a resultset.
* The returned resultset works the same as the one returned by connection::query().
*
* The parameters passed to execute() are replaced in order of declaration:
* the first question mark will be replaced by the first passed parameter,
* the second question mark by the second parameter and so on. The number
* of passed parameters must match exactly the number of parameters for
* the prepared statement.
*
* Any collection providing member functions begin() and end() returning
* forward iterators to mysql::value's is acceptable. We use mysql::make_values(),
* which creates a std::array with the passed in values converted to mysql::value's.
* An iterator version of execute() is also available.
*/
boost::mysql::tcp_resultset result = salary_getter.execute(boost::mysql::make_values("Efficient"));
std::vector<boost::mysql::owning_row> salaries = result.fetch_all(); // Get all the results
assert(salaries.size() == 1);
[[maybe_unused]] auto salary = std::get<double>(salaries[0].values().at(0)); // First row, first column
assert(salary == 30000);
std::cout << "The salary before the payrise was: " << salary << std::endl;
/*
* Once a statement has been prepared, it can be executed as many times as
* desired, by calling prepared_statement::execute(). execute takes as input a
* (possibly empty) collection of mysql::value's and returns a resultset.
* The returned resultset works the same as the one returned by connection::query().
*
* The parameters passed to execute() are replaced in order of declaration:
* the first question mark will be replaced by the first passed parameter,
* the second question mark by the second parameter and so on. The number
* of passed parameters must match exactly the number of parameters for
* the prepared statement.
*
* Any collection providing member functions begin() and end() returning
* forward iterators to mysql::value's is acceptable. We use mysql::make_values(),
* which creates a std::array with the passed in values converted to mysql::value's.
* An iterator version of execute() is also available.
*/
boost::mysql::tcp_resultset result = salary_getter.execute(boost::mysql::make_values("Efficient"));
std::vector<boost::mysql::owning_row> salaries = result.fetch_all(); // Get all the results
assert(salaries.size() == 1);
[[maybe_unused]] auto salary = std::get<double>(salaries[0].values().at(0)); // First row, first column
assert(salary == 30000);
std::cout << "The salary before the payrise was: " << salary << std::endl;
/**
* Run the update. In this case, we must pass in two parameters.
* Note that MySQL is flexible in the types passed as parameters.
* In this case, we are sending the value 35000, which gets converted
* into a mysql::value with type std::int32_t, while the 'salary'
* column is declared as a DOUBLE. The MySQL server will do
* the right thing for us.
*/
salary_updater.execute(boost::mysql::make_values(35000, "Efficient"));
/**
* Run the update. In this case, we must pass in two parameters.
* Note that MySQL is flexible in the types passed as parameters.
* In this case, we are sending the value 35000, which gets converted
* into a mysql::value with type std::int32_t, while the 'salary'
* column is declared as a DOUBLE. The MySQL server will do
* the right thing for us.
*/
salary_updater.execute(boost::mysql::make_values(35000, "Efficient"));
/**
* Execute the select again. We can execute a prepared statement
* as many times as we want. We do NOT need to call
* connection::prepare_statement() again.
*/
result = salary_getter.execute(boost::mysql::make_values("Efficient"));
salaries = result.fetch_all();
assert(salaries.size() == 1);
salary = std::get<double>(salaries[0].values().at(0));
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;
/**
* Execute the select again. We can execute a prepared statement
* as many times as we want. We do NOT need to call
* connection::prepare_statement() again.
*/
result = salary_getter.execute(boost::mysql::make_values("Efficient"));
salaries = result.fetch_all();
assert(salaries.size() == 1);
salary = std::get<double>(salaries[0].values().at(0));
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;
/**
* Close the statements. Closing a statement deallocates it from the server.
* Once a statement is closed, trying to execute it will return an error.
*
* Closing statements implies communicating with the server and can thus fail.
*
* Statements are automatically deallocated once the connection is closed.
* If you are re-using connection objects and preparing statements over time,
* you should close() your statements to prevent excessive resource usage.
* If you are not re-using the connections, or are preparing your statements
* just once at application startup, there is no need to perform this step.
*/
salary_updater.close();
salary_getter.close();
/**
* Close the statements. Closing a statement deallocates it from the server.
* Once a statement is closed, trying to execute it will return an error.
*
* Closing statements implies communicating with the server and can thus fail.
*
* Statements are automatically deallocated once the connection is closed.
* If you are re-using connection objects and preparing statements over time,
* you should close() your statements to prevent excessive resource usage.
* If you are not re-using the connections, or are preparing your statements
* just once at application startup, there is no need to perform this step.
*/
salary_updater.close();
salary_getter.close();
}
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -48,126 +48,126 @@ using boost::mysql::owning_row;
void print_employee(const boost::mysql::row& employee)
{
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
void die_on_error(
const error_code& err,
const boost::mysql::error_info& info = boost::mysql::error_info()
const error_code& err,
const boost::mysql::error_info& info = boost::mysql::error_info()
)
{
if (err)
{
std::cerr << "Error: " << err << ": " << info.message() << std::endl;
exit(1);
}
if (err)
{
std::cerr << "Error: " << err << ": " << info.message() << std::endl;
exit(1);
}
}
class application
{
boost::asio::ip::tcp::endpoint ep; // Physical endpoint to connect to
boost::mysql::connection_params conn_params; // MySQL credentials and other connection config
boost::asio::io_context ctx; // boost::asio context
boost::mysql::tcp_connection connection; // Represents the connection to the MySQL server
boost::mysql::tcp_resultset resultset; // A result from a query
boost::mysql::error_info additional_info; // Will be populated with additional information about any errors
boost::asio::ip::tcp::endpoint ep; // Physical endpoint to connect to
boost::mysql::connection_params conn_params; // MySQL credentials and other connection config
boost::asio::io_context ctx; // boost::asio context
boost::mysql::tcp_connection connection; // Represents the connection to the MySQL server
boost::mysql::tcp_resultset resultset; // A result from a query
boost::mysql::error_info additional_info; // Will be populated with additional information about any errors
public:
application(const char* username, const char* password) :
ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port),
conn_params(username, password, "mysql_asio_examples"),
connection(ctx)
{
}
application(const char* username, const char* password) :
ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port),
conn_params(username, password, "mysql_asio_examples"),
connection(ctx)
{
}
void start() { connect(); }
void start() { connect(); }
void connect()
{
connection.next_layer().async_connect(ep, [this](error_code err) {
die_on_error(err);
connection.async_handshake(conn_params, [this](error_code err) {
die_on_error(err, additional_info);
query_employees();
}, &additional_info);
});
}
void connect()
{
connection.next_layer().async_connect(ep, [this](error_code err) {
die_on_error(err);
connection.async_handshake(conn_params, [this](error_code err) {
die_on_error(err, additional_info);
query_employees();
}, &additional_info);
});
}
void query_employees()
{
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
die_on_error(err, additional_info);
resultset = std::move(result);
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
die_on_error(err, additional_info);
for (const auto& employee: rows)
{
print_employee(employee);
}
update_slacker();
}, &additional_info);
}, &additional_info);
}
void query_employees()
{
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
die_on_error(err, additional_info);
resultset = std::move(result);
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
die_on_error(err, additional_info);
for (const auto& employee: rows)
{
print_employee(employee);
}
update_slacker();
}, &additional_info);
}, &additional_info);
}
void update_slacker()
{
const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = 'Slacker'";
connection.async_query(sql, [this](error_code err, [[maybe_unused]] tcp_resultset&& result) {
die_on_error(err, additional_info);
assert(result.fields().size() == 0);
query_intern();
}, &additional_info);
}
void update_slacker()
{
const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = 'Slacker'";
connection.async_query(sql, [this](error_code err, [[maybe_unused]] tcp_resultset&& result) {
die_on_error(err, additional_info);
assert(result.fields().size() == 0);
query_intern();
}, &additional_info);
}
void query_intern()
{
const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'";
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
die_on_error(err, additional_info);
resultset = std::move(result);
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
die_on_error(err, additional_info);
assert(rows.size() == 1);
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 15000);
}, &additional_info);
}, &additional_info);
}
void query_intern()
{
const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'";
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
die_on_error(err, additional_info);
resultset = std::move(result);
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
die_on_error(err, additional_info);
assert(rows.size() == 1);
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 15000);
}, &additional_info);
}, &additional_info);
}
auto& context() { return ctx; }
auto& context() { return ctx; }
};
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
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
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
}
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -45,117 +45,117 @@ using boost::mysql::error_info;
void print_employee(const boost::mysql::row& employee)
{
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
// Throws an exception if an operation failed
void check_error(
const error_code& err,
const error_info& info = {}
const error_code& err,
const error_info& info = {}
)
{
if (err)
{
throw boost::system::system_error(err, info.message());
}
if (err)
{
throw boost::system::system_error(err, info.message());
}
}
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
// Context and connections
boost::asio::io_context ctx;
boost::mysql::tcp_connection conn (ctx);
// Context and connections
boost::asio::io_context ctx;
boost::mysql::tcp_connection conn (ctx);
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
/**
* The entry point. We spawn a stackful coroutine using boost::asio::spawn
* (see https://www.boost.org/doc/libs/1_72_0/doc/html/boost_asio/reference/spawn.html).
*
* The coroutine will actually start running when we call io_context::run().
* It will suspend every time we call one of the asyncrhonous functions, saving
* all information it needs for resuming. When the asynchronous operation completes,
* the coroutine will resume in the point it was left.
*
* The return type of a coroutine is the second argument to the handler signature
* for the asynchronous operation. For example, connection::query has a handler
* signature of void(error_code, resultset<Stream>), so the coroutine return
* type is resultset<Stream>.
*
*/
boost::asio::spawn(ctx.get_executor(), [&conn, ep, params](boost::asio::yield_context yield) {
// This error_code and error_info will be filled if an
// operation fails. We will check them for every operation we perform.
boost::mysql::error_code ec;
boost::mysql::error_info additional_info;
/**
* The entry point. We spawn a stackful coroutine using boost::asio::spawn
* (see https://www.boost.org/doc/libs/1_72_0/doc/html/boost_asio/reference/spawn.html).
*
* The coroutine will actually start running when we call io_context::run().
* It will suspend every time we call one of the asyncrhonous functions, saving
* all information it needs for resuming. When the asynchronous operation completes,
* the coroutine will resume in the point it was left.
*
* The return type of a coroutine is the second argument to the handler signature
* for the asynchronous operation. For example, connection::query has a handler
* signature of void(error_code, resultset<Stream>), so the coroutine return
* type is resultset<Stream>.
*
*/
boost::asio::spawn(ctx.get_executor(), [&conn, ep, params](boost::asio::yield_context yield) {
// This error_code and error_info will be filled if an
// operation fails. We will check them for every operation we perform.
boost::mysql::error_code ec;
boost::mysql::error_info additional_info;
// TCP connect
conn.next_layer().async_connect(ep, yield[ec]);
check_error(ec);
// TCP connect
conn.next_layer().async_connect(ep, yield[ec]);
check_error(ec);
// MySQL handshake
conn.async_handshake(params, yield[ec], &additional_info);
check_error(ec, additional_info);
// MySQL handshake
conn.async_handshake(params, yield[ec], &additional_info);
check_error(ec, additional_info);
// Issue the query to the server
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::tcp_resultset result = conn.async_query(sql, yield[ec], &additional_info);
check_error(ec, additional_info);
// Issue the query to the server
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::tcp_resultset result = conn.async_query(sql, yield[ec], &additional_info);
check_error(ec, additional_info);
/**
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
* which returns a single row at every call. The returned row is a pointer
* to memory owned by the resultset, and is re-used for each row. Thus, returned
* rows remain valid until the next call to async_fetch_one(). When no more
* rows are available, async_fetch_one returns nullptr.
*/
while (true)
{
const boost::mysql::row* row = result.async_fetch_one(yield[ec], &additional_info);
check_error(ec, additional_info);
if (!row) break; // No more rows available
print_employee(*row);
}
});
/**
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
* which returns a single row at every call. The returned row is a pointer
* to memory owned by the resultset, and is re-used for each row. Thus, returned
* rows remain valid until the next call to async_fetch_one(). When no more
* rows are available, async_fetch_one returns nullptr.
*/
while (true)
{
const boost::mysql::row* row = result.async_fetch_one(yield[ec], &additional_info);
check_error(ec, additional_info);
if (!row) break; // No more rows available
print_employee(*row);
}
});
// Don't forget to call run()! Otherwise, your program
// will not spawn the coroutine and will do nothing.
ctx.run();
// Don't forget to call run()! Otherwise, your program
// will not spawn the coroutine and will do nothing.
ctx.run();
}
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -46,11 +46,11 @@ using boost::asio::use_future;
void print_employee(const boost::mysql::row& employee)
{
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
/**
@ -63,92 +63,92 @@ void print_employee(const boost::mysql::row& employee)
*/
class application
{
boost::asio::io_context ctx_;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard_;
std::thread runner_;
boost::asio::io_context ctx_;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard_;
std::thread runner_;
public:
application(): guard_(ctx_.get_executor()), runner_([this] { ctx_.run(); }) {}
application(const application&) = delete;
application(application&&) = delete;
application& operator=(const application&) = delete;
application& operator=(application&&) = delete;
~application()
{
guard_.reset();
runner_.join();
}
boost::asio::io_context& context() { return ctx_; }
application(): guard_(ctx_.get_executor()), runner_([this] { ctx_.run(); }) {}
application(const application&) = delete;
application(application&&) = delete;
application& operator=(const application&) = delete;
application& operator=(application&&) = delete;
~application()
{
guard_.reset();
runner_.join();
}
boost::asio::io_context& context() { return ctx_; }
};
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
// Context and connections
application app; // boost::asio::io_context and a thread that calls run()
boost::mysql::tcp_connection conn (app.context());
// Context and connections
application app; // boost::asio::io_context and a thread that calls run()
boost::mysql::tcp_connection conn (app.context());
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
// TCP connect
std::future<void> fut = conn.next_layer().async_connect(ep, use_future);
fut.get();
// TCP connect
std::future<void> fut = conn.next_layer().async_connect(ep, use_future);
fut.get();
/**
* Perform the MySQL handshake. Calling async_handshake triggers the
* operation, and calling future::get() blocks the current thread until
* it completes. get() will throw an exception if the operation fails.
*/
fut = conn.async_handshake(params, use_future);
fut.get();
/**
* Perform the MySQL handshake. Calling async_handshake triggers the
* operation, and calling future::get() blocks the current thread until
* it completes. get() will throw an exception if the operation fails.
*/
fut = conn.async_handshake(params, use_future);
fut.get();
// Issue the query to the server
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
std::future<boost::mysql::tcp_resultset> resultset_fut = conn.async_query(sql, use_future);
boost::mysql::tcp_resultset result = resultset_fut.get();
// Issue the query to the server
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
std::future<boost::mysql::tcp_resultset> resultset_fut = conn.async_query(sql, use_future);
boost::mysql::tcp_resultset result = resultset_fut.get();
/**
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
* which returns a single row at every call. The returned row is a pointer
* to memory owned by the resultset, and is re-used for each row. Thus, returned
* rows remain valid until the next call to async_fetch_one(). When no more
* rows are available, async_fetch_one returns nullptr.
*/
while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get())
{
print_employee(*current_row);
}
/**
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
* which returns a single row at every call. The returned row is a pointer
* to memory owned by the resultset, and is re-used for each row. Thus, returned
* rows remain valid until the next call to async_fetch_one(). When no more
* rows are available, async_fetch_one returns nullptr.
*/
while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get())
{
print_employee(*current_row);
}
// application dtor. stops io_context and then joins the thread
// application dtor. stops io_context and then joins the thread
}
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -31,102 +31,102 @@
*/
void print_employee(const boost::mysql::row& employee)
{
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
void main_impl(int argc, char** argv)
{
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
if (argc != 3)
{
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
exit(1);
}
/**
* Connection parameters that tell us where and how to connect to the MySQL server.
* There are two types of parameters:
* - TCP-level connection parameters, identifying the host and port to connect to.
* - MySQL level parameters: database credentials and schema to use.
*/
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
// Note: by default, SSL will be used if the server supports it.
// connection_params accepts an optional ssl_options argument
// determining whether to use SSL or not. See ssl_options and ssl_mode
// documentation for further details on SSL.
/**
* Connection parameters that tell us where and how to connect to the MySQL server.
* There are two types of parameters:
* - TCP-level connection parameters, identifying the host and port to connect to.
* - MySQL level parameters: database credentials and schema to use.
*/
boost::asio::ip::tcp::endpoint ep (
boost::asio::ip::address_v4::loopback(), // host
boost::mysql::default_port // port
);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
// Note: by default, SSL will be used if the server supports it.
// connection_params accepts an optional ssl_options argument
// determining whether to use SSL or not. See ssl_options and ssl_mode
// documentation for further details on SSL.
boost::asio::io_context ctx;
boost::asio::io_context ctx;
/**
* Represents a single connection over TCP to a MySQL server.
* Before being able to use it, you have to connect to the server by:
* - Establishing the TCP-level session.
* - Authenticating to the MySQL server.
*/
boost::mysql::tcp_connection conn (ctx);
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
conn.handshake(params); // Authenticates to the MySQL server
/**
* Represents a single connection over TCP to a MySQL server.
* Before being able to use it, you have to connect to the server by:
* - Establishing the TCP-level session.
* - Authenticating to the MySQL server.
*/
boost::mysql::tcp_connection conn (ctx);
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
conn.handshake(params); // Authenticates to the MySQL server
/**
* To issue a SQL query to the database server, use tcp_connection::query, which takes
* the SQL to be executed as parameter and returns a resultset object.
*
* Resultset objects represent the result of a query, in tabular format.
* They hold metadata describing the fields the resultset holds (in this case, first_name,
* last_name and salary). To get the actual data, use fetch_one, fetch_many or fetch_all.
* We will use fetch_all, which returns all the received rows as a std::vector.
*
* We will get all employees working for 'High Growth Startup'.
*/
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::tcp_resultset result = conn.query(sql);
/**
* To issue a SQL query to the database server, use tcp_connection::query, which takes
* the SQL to be executed as parameter and returns a resultset object.
*
* Resultset objects represent the result of a query, in tabular format.
* They hold metadata describing the fields the resultset holds (in this case, first_name,
* last_name and salary). To get the actual data, use fetch_one, fetch_many or fetch_all.
* We will use fetch_all, which returns all the received rows as a std::vector.
*
* We will get all employees working for 'High Growth Startup'.
*/
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::tcp_resultset result = conn.query(sql);
// Get all the rows in the resultset
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
for (const auto& employee: employees)
{
print_employee(employee);
}
// Get all the rows in the resultset
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
for (const auto& employee: employees)
{
print_employee(employee);
}
// We can issue any SQL statement, not only SELECTs. In this case, the returned
// 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
// We can issue any SQL statement, not only SELECTs. In this case, the returned
// 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
// 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]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 10000);
// 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]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 10000);
}
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -22,11 +22,11 @@
void print_employee(const boost::mysql::row& employee)
{
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
std::cout << "Employee '"
<< employee.values()[0] << " " // first_name (type std::string_view)
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
}
// UNIX sockets are only available in, er, UNIX systems. Typedefs for
@ -37,90 +37,90 @@ void print_employee(const boost::mysql::row& employee)
void main_impl(int argc, char** argv)
{
if (argc != 3 && argc != 4)
{
std::cerr << "Usage: " << argv[0] << " <username> <password> [<socket-path>]\n";
exit(1);
}
if (argc != 3 && argc != 4)
{
std::cerr << "Usage: " << argv[0] << " <username> <password> [<socket-path>]\n";
exit(1);
}
const char* socket_path = "/var/run/mysqld/mysqld.sock";
if (argc == 4)
{
socket_path = argv[3];
}
const char* socket_path = "/var/run/mysqld/mysqld.sock";
if (argc == 4)
{
socket_path = argv[3];
}
/**
* Connection parameters that tell us where and how to connect to the MySQL server.
* There are two types of parameters:
* - UNIX-level connection parameters, identifying the UNIX socket to connect to.
* - MySQL level parameters: database credentials and schema to use.
*/
boost::asio::local::stream_protocol::endpoint ep (socket_path);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
// Note: by default, SSL will be used if the server supports it.
// connection_params accepts an optional ssl_options argument
// determining whether to use SSL or not. See ssl_options and ssl_mode
// documentation for further details on SSL.
/**
* Connection parameters that tell us where and how to connect to the MySQL server.
* There are two types of parameters:
* - UNIX-level connection parameters, identifying the UNIX socket to connect to.
* - MySQL level parameters: database credentials and schema to use.
*/
boost::asio::local::stream_protocol::endpoint ep (socket_path);
boost::mysql::connection_params params (
argv[1], // username
argv[2], // password
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
);
// Note: by default, SSL will be used if the server supports it.
// connection_params accepts an optional ssl_options argument
// determining whether to use SSL or not. See ssl_options and ssl_mode
// documentation for further details on SSL.
boost::asio::io_context ctx;
boost::asio::io_context ctx;
// Connection to the MySQL server, over a UNIX socket
boost::mysql::unix_connection conn (ctx);
conn.next_layer().connect(ep); // next_level() returns a boost::asio::local::stream_protocol::socket
conn.handshake(params); // Authenticates to the MySQL server
// Connection to the MySQL server, over a UNIX socket
boost::mysql::unix_connection conn (ctx);
conn.next_layer().connect(ep); // next_level() returns a boost::asio::local::stream_protocol::socket
conn.handshake(params); // Authenticates to the MySQL server
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::unix_resultset result = conn.query(sql);
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
boost::mysql::unix_resultset result = conn.query(sql);
// Get all the rows in the resultset
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
for (const auto& employee: employees)
{
print_employee(employee);
}
// Get all the rows in the resultset
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
for (const auto& employee: employees)
{
print_employee(employee);
}
// We can issue any SQL statement, not only SELECTs. In this case, the returned
// 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
// We can issue any SQL statement, not only SELECTs. In this case, the returned
// 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
// 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]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 10000);
// 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]] auto salary = std::get<double>(rows[0].values()[0]);
assert(salary == 10000);
}
#else
void main_impl(int, char**)
{
std::cout << "Sorry, your system does not support UNIX sockets" << std::endl;
std::cout << "Sorry, your system does not support UNIX sockets" << std::endl;
}
#endif
int main(int argc, char** argv)
{
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
try
{
main_impl(argc, argv);
}
catch (const boost::system::system_error& err)
{
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
return 1;
}
catch (const std::exception& err)
{
std::cerr << "Error: " << err.what() << std::endl;
return 1;
}
}

View File

@ -57,112 +57,112 @@ namespace mysql {
* Otherwise, results are undefined.
*/
template <
typename Stream ///< The underlying stream to use; must satisfy Boost.Asio's SyncStream and AsyncStream concepts.
typename Stream ///< The underlying stream to use; must satisfy Boost.Asio's SyncStream and AsyncStream concepts.
>
class connection
{
using channel_type = detail::channel<Stream>;
using channel_type = detail::channel<Stream>;
Stream next_layer_;
channel_type channel_;
Stream next_layer_;
channel_type channel_;
public:
/**
* \brief Initializing constructor.
* \details Creates a Stream object by forwarding any passed in arguments to its constructor.
*/
template <typename... Args>
connection(Args&&... args) :
next_layer_(std::forward<Args>(args)...),
channel_(next_layer_)
{
}
/**
* \brief Initializing constructor.
* \details Creates a Stream object by forwarding any passed in arguments to its constructor.
*/
template <typename... Args>
connection(Args&&... args) :
next_layer_(std::forward<Args>(args)...),
channel_(next_layer_)
{
}
/// Retrieves the underlying Stream object.
Stream& next_layer() { return next_layer_; }
/// Retrieves the underlying Stream object.
Stream& next_layer() { return next_layer_; }
/// Retrieves the underlying Stream object.
const Stream& next_layer() const { return next_layer_; }
/// Retrieves the underlying Stream object.
const Stream& next_layer() const { return next_layer_; }
/**
* \brief Returns whether the connection uses SSL or not.
* \details Will always return false for connections that haven't been
* established yet (handshake not run yet). If the handshake fails,
* the return value is undefined.
*
* This function can be used to determine
* whether you are using a SSL connection or not when using
* optional SSL (ssl_mode::enable).
*/
bool uses_ssl() const noexcept { return channel_.ssl_active(); }
/**
* \brief Returns whether the connection uses SSL or not.
* \details Will always return false for connections that haven't been
* established yet (handshake not run yet). If the handshake fails,
* the return value is undefined.
*
* This function can be used to determine
* whether you are using a SSL connection or not when using
* optional SSL (ssl_mode::enable).
*/
bool uses_ssl() const noexcept { return channel_.ssl_active(); }
/// Performs the MySQL-level handshake (synchronous with error code version).
void handshake(const connection_params& params, error_code& ec, error_info& info);
/// Performs the MySQL-level handshake (synchronous with error code version).
void handshake(const connection_params& params, error_code& ec, error_info& info);
/// Performs the MySQL-level handshake (synchronous with exceptions version).
void handshake(const connection_params& params);
/// Performs the MySQL-level handshake (synchronous with exceptions version).
void handshake(const connection_params& params);
/// Handler signature for handshake.
using handshake_signature = void(error_code);
/// Handler signature for handshake.
using handshake_signature = void(error_code);
/**
* \brief Performs the MySQL-level handshake (asynchronous version).
* \details The strings pointed to by params should be kept alive by the caller
* until the operation completes, as no copy is made by the library.
*/
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
async_handshake(const connection_params& params, CompletionToken&& token, error_info* info = nullptr);
/**
* \brief Performs the MySQL-level handshake (asynchronous version).
* \details The strings pointed to by params should be kept alive by the caller
* until the operation completes, as no copy is made by the library.
*/
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
async_handshake(const connection_params& params, CompletionToken&& token, error_info* info = nullptr);
/**
* \brief Executes a SQL text query (sync with error code version).
* \details Does not perform the actual retrieval of the data; use the various
* fetch functions within resultset to achieve that.
* \see resultset
*
* Note that query_string may contain any valid SQL, not just SELECT statements.
* If your query does not return any data, then the resultset will be empty.
*
* \warning After query() has returned, you should read the entire resultset
* before calling any function that involves communication with the server over this
* connection (like connection::query, connection::prepare_statement or
* prepared_statement::execute). Otherwise, the results are undefined.
*/
resultset<Stream> query(std::string_view query_string, error_code&, error_info&);
/**
* \brief Executes a SQL text query (sync with error code version).
* \details Does not perform the actual retrieval of the data; use the various
* fetch functions within resultset to achieve that.
* \see resultset
*
* Note that query_string may contain any valid SQL, not just SELECT statements.
* If your query does not return any data, then the resultset will be empty.
*
* \warning After query() has returned, you should read the entire resultset
* before calling any function that involves communication with the server over this
* connection (like connection::query, connection::prepare_statement or
* prepared_statement::execute). Otherwise, the results are undefined.
*/
resultset<Stream> query(std::string_view query_string, error_code&, error_info&);
/// Executes a SQL text query (sync with exceptions version).
resultset<Stream> query(std::string_view query_string);
/// Executes a SQL text query (sync with exceptions version).
resultset<Stream> query(std::string_view query_string);
/// Handler signature for query.
using query_signature = void(error_code, resultset<Stream>);
/// Handler signature for query.
using query_signature = void(error_code, resultset<Stream>);
/// Executes a SQL text query (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, query_signature)
async_query(std::string_view query_string, CompletionToken&& token, error_info* info=nullptr);
/// Executes a SQL text query (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, query_signature)
async_query(std::string_view query_string, CompletionToken&& token, error_info* info=nullptr);
/**
* \brief Prepares a statement in the server (sync with error code version).
* \details Instructs the server to create a prepared statement. The passed
* in statement should be a SQL statement with question marks (?) as placeholders
* for the statement parameters. See the MySQL documentation on prepared statements
* for more info.
*
* Prepared statements are only valid while the connection object on which
* prepare_statement was called is alive and open. See prepared_statement docs
* for more info.
*/
prepared_statement<Stream> prepare_statement(std::string_view statement, error_code&, error_info&);
/**
* \brief Prepares a statement in the server (sync with error code version).
* \details Instructs the server to create a prepared statement. The passed
* in statement should be a SQL statement with question marks (?) as placeholders
* for the statement parameters. See the MySQL documentation on prepared statements
* for more info.
*
* Prepared statements are only valid while the connection object on which
* prepare_statement was called is alive and open. See prepared_statement docs
* for more info.
*/
prepared_statement<Stream> prepare_statement(std::string_view statement, error_code&, error_info&);
/// Prepares a statement (sync with exceptions version).
prepared_statement<Stream> prepare_statement(std::string_view statement);
/// Prepares a statement (sync with exceptions version).
prepared_statement<Stream> prepare_statement(std::string_view statement);
/// Handler signature for prepare_statement.
using prepare_statement_signature = void(error_code, prepared_statement<Stream>);
/// Handler signature for prepare_statement.
using prepare_statement_signature = void(error_code, prepared_statement<Stream>);
/// Prepares a statement (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature)
async_prepare_statement(std::string_view statement, CompletionToken&& token, error_info* info=nullptr);
/// Prepares a statement (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature)
async_prepare_statement(std::string_view statement, CompletionToken&& token, error_info* info=nullptr);
};
/**

View File

@ -26,9 +26,9 @@ namespace mysql {
*/
enum class ssl_mode
{
disable, ///< Never use TLS
enable, ///< Use TLS if the server supports it, fall back to non-encrypted connection if it does not.
require ///< Always use TLS; abort the connection if the server does not support it.
disable, ///< Never use TLS
enable, ///< Use TLS if the server supports it, fall back to non-encrypted connection if it does not.
require ///< Always use TLS; abort the connection if the server does not support it.
};
/**
@ -39,18 +39,18 @@ enum class ssl_mode
*/
class ssl_options
{
ssl_mode mode_;
ssl_mode mode_;
public:
/**
* \brief Default and initialization constructor.
* \details By default, SSL is enabled for the connection
* if the server supports is (ssl_mode::enable).
*/
explicit ssl_options(ssl_mode mode=ssl_mode::enable) noexcept:
mode_(mode) {}
/**
* \brief Default and initialization constructor.
* \details By default, SSL is enabled for the connection
* if the server supports is (ssl_mode::enable).
*/
explicit ssl_options(ssl_mode mode=ssl_mode::enable) noexcept:
mode_(mode) {}
/// Retrieves the TLS mode to be used for the connection.
ssl_mode mode() const noexcept { return mode_; }
/// Retrieves the TLS mode to be used for the connection.
ssl_mode mode() const noexcept { return mode_; }
};
@ -60,57 +60,57 @@ public:
*/
class connection_params
{
std::string_view username_;
std::string_view password_;
std::string_view database_;
collation connection_collation_;
ssl_options ssl_;
std::string_view username_;
std::string_view password_;
std::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.
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.
) :
username_(username),
password_(password),
database_(db),
connection_collation_(connection_col),
ssl_(opts)
{
}
/// 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.
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.
) :
username_(username),
password_(password),
database_(db),
connection_collation_(connection_col),
ssl_(opts)
{
}
/// Retrieves the username.
std::string_view username() const noexcept { return username_; }
/// Retrieves the username.
std::string_view username() const noexcept { return username_; }
/// Sets the username.
void set_username(std::string_view value) noexcept { username_ = value; }
/// Sets the username.
void set_username(std::string_view value) noexcept { username_ = value; }
/// Retrieves the password.
std::string_view password() const noexcept { return password_; }
/// Retrieves the password.
std::string_view password() const noexcept { return password_; }
/// Sets the password
void set_password(std::string_view value) noexcept { password_ = value; }
/// Sets the password
void set_password(std::string_view value) noexcept { password_ = value; }
/// Retrieves the database.
std::string_view database() const noexcept { return database_; }
/// Retrieves the database.
std::string_view database() const noexcept { return database_; }
/// Sets the database
void set_database(std::string_view value) noexcept { database_ = value; }
/// Sets the database
void set_database(std::string_view value) noexcept { database_ = value; }
/// Retrieves the connection collation.
collation connection_collation() const noexcept { return connection_collation_; }
/// Retrieves the connection collation.
collation connection_collation() const noexcept { return connection_collation_; }
/// Sets the connection collation
void set_connection_collation(collation value) noexcept { connection_collation_ = value; }
/// Sets the connection collation
void set_connection_collation(collation value) noexcept { connection_collation_ = value; }
/// Retrieves SSL options
const ssl_options& ssl() const noexcept { return ssl_; }
/// Retrieves SSL options
const ssl_options& ssl() const noexcept { return ssl_; }
/// Sets SSL options
void set_ssl(const ssl_options& value) noexcept { ssl_ = value; }
/// Sets SSL options
void set_ssl(const ssl_options& value) noexcept { ssl_ = value; }
};
} // mysql

View File

@ -19,36 +19,36 @@ namespace detail {
struct authentication_plugin
{
using calculator_signature = error_code (*)(
std::string_view password,
std::string_view challenge,
bool use_ssl,
std::string& output
);
using calculator_signature = error_code (*)(
std::string_view password,
std::string_view challenge,
bool use_ssl,
std::string& output
);
std::string_view name;
calculator_signature calculator;
std::string_view name;
calculator_signature calculator;
};
class auth_calculator
{
const authentication_plugin* plugin_ {nullptr};
std::string response_;
const authentication_plugin* plugin_ {nullptr};
std::string response_;
inline static const authentication_plugin* find_plugin(std::string_view name);
inline static const authentication_plugin* find_plugin(std::string_view name);
public:
inline error_code calculate(
std::string_view plugin_name,
std::string_view password,
std::string_view challenge,
bool use_ssl
);
std::string_view response() const noexcept { return response_; }
std::string_view plugin_name() const noexcept
{
assert(plugin_);
return plugin_->name;
}
inline error_code calculate(
std::string_view plugin_name,
std::string_view password,
std::string_view challenge,
bool use_ssl
);
std::string_view response() const noexcept { return response_; }
std::string_view plugin_name() const noexcept
{
assert(plugin_);
return plugin_->name;
}
};
} // detail

View File

@ -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,
bool use_ssl,
std::string& output
std::string_view password,
std::string_view challenge,
bool use_ssl,
std::string& output
);
} // caching_sha2_password

View File

@ -16,18 +16,18 @@ namespace mysql {
namespace detail {
constexpr authentication_plugin mysql_native_password_plugin {
"mysql_native_password",
&mysql_native_password::compute_response
"mysql_native_password",
&mysql_native_password::compute_response
};
constexpr authentication_plugin caching_sha2_password_plugin {
"caching_sha2_password",
&caching_sha2_password::compute_response
"caching_sha2_password",
&caching_sha2_password::compute_response
};
constexpr std::array<const authentication_plugin*, 2> all_authentication_plugins {
&mysql_native_password_plugin,
&caching_sha2_password_plugin
&mysql_native_password_plugin,
&caching_sha2_password_plugin
};
} // detail
@ -36,44 +36,44 @@ constexpr std::array<const authentication_plugin*, 2> all_authentication_plugins
inline const boost::mysql::detail::authentication_plugin*
boost::mysql::detail::auth_calculator::find_plugin(
std::string_view name
std::string_view name
)
{
auto it = std::find_if(
all_authentication_plugins.begin(),
all_authentication_plugins.end(),
[name](const authentication_plugin* plugin) { return plugin->name == name; }
);
return it == std::end(all_authentication_plugins) ? nullptr : *it;
auto it = std::find_if(
all_authentication_plugins.begin(),
all_authentication_plugins.end(),
[name](const authentication_plugin* plugin) { return plugin->name == name; }
);
return it == std::end(all_authentication_plugins) ? nullptr : *it;
}
inline boost::mysql::error_code
boost::mysql::detail::auth_calculator::calculate(
std::string_view plugin_name,
std::string_view password,
std::string_view challenge,
bool use_ssl
std::string_view plugin_name,
std::string_view password,
std::string_view challenge,
bool use_ssl
)
{
plugin_ = find_plugin(plugin_name);
if (plugin_)
{
// Blank password: we should just return an empty auth string
if (password.empty())
{
response_ = "";
return error_code();
}
else
{
return plugin_->calculator(password, challenge, use_ssl, response_);
}
}
else
{
return make_error_code(errc::unknown_auth_plugin);
}
plugin_ = find_plugin(plugin_name);
if (plugin_)
{
// Blank password: we should just return an empty auth string
if (password.empty())
{
response_ = "";
return error_code();
}
else
{
return plugin_->calculator(password, challenge, use_ssl, response_);
}
}
else
{
return make_error_code(errc::unknown_auth_plugin);
}
}

View File

@ -23,33 +23,33 @@ constexpr std::string_view perform_full_auth = "\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,
const void* challenge,
void* output
std::string_view password,
const void* challenge,
void* output
)
{
static_assert(response_length == SHA256_DIGEST_LENGTH);
static_assert(response_length == SHA256_DIGEST_LENGTH);
// SHA(SHA(password_sha) concat challenge) XOR password_sha
// hash1 = SHA(pass)
using sha_buffer = std::uint8_t [response_length];
sha_buffer password_sha;
SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha);
// SHA(SHA(password_sha) concat challenge) XOR password_sha
// hash1 = SHA(pass)
using sha_buffer = std::uint8_t [response_length];
sha_buffer password_sha;
SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha);
// SHA(password_sha) concat challenge = buffer
std::uint8_t buffer [response_length + challenge_length];
SHA256(password_sha, response_length, buffer);
std::memcpy(buffer + response_length, challenge, challenge_length);
// SHA(password_sha) concat challenge = buffer
std::uint8_t buffer [response_length + challenge_length];
SHA256(password_sha, response_length, buffer);
std::memcpy(buffer + response_length, challenge, challenge_length);
// SHA(SHA(password_sha) concat challenge) = SHA(buffer) = salted_password
sha_buffer salted_password;
SHA256(buffer, sizeof(buffer), salted_password);
// SHA(SHA(password_sha) concat challenge) = SHA(buffer) = salted_password
sha_buffer salted_password;
SHA256(buffer, sizeof(buffer), salted_password);
// salted_password XOR password_sha
for (unsigned i = 0; i < response_length; ++i)
{
static_cast<std::uint8_t*>(output)[i] = salted_password[i] ^ password_sha[i];
}
// salted_password XOR password_sha
for (unsigned i = 0; i < response_length; ++i)
{
static_cast<std::uint8_t*>(output)[i] = salted_password[i] ^ password_sha[i];
}
}
} // caching_sha2_password
@ -61,39 +61,39 @@ 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,
bool use_ssl,
std::string& output
std::string_view password,
std::string_view challenge,
bool use_ssl,
std::string& output
)
{
if (challenge == perform_full_auth)
{
if (!use_ssl)
{
return make_error_code(errc::auth_plugin_requires_ssl);
}
output = password;
output.push_back(0);
return error_code();
}
else
{
// Check challenge size
if (challenge.size() != challenge_length)
{
return make_error_code(errc::protocol_value_error);
}
if (challenge == perform_full_auth)
{
if (!use_ssl)
{
return make_error_code(errc::auth_plugin_requires_ssl);
}
output = password;
output.push_back(0);
return error_code();
}
else
{
// Check challenge size
if (challenge.size() != challenge_length)
{
return make_error_code(errc::protocol_value_error);
}
// Do the calculation
output.resize(response_length);
compute_auth_string(
password,
challenge.data(),
output.data()
);
return error_code();
}
// Do the calculation
output.resize(response_length);
compute_auth_string(
password,
challenge.data(),
output.data()
);
return error_code();
}
}

View File

@ -23,29 +23,29 @@ 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" <concat> SHA1( SHA1( password ) ) )
inline void compute_auth_string(
std::string_view password,
const void* challenge,
void* output
std::string_view password,
const void* challenge,
void* output
)
{
// SHA1 (password)
using sha1_buffer = unsigned char [SHA_DIGEST_LENGTH];
sha1_buffer password_sha1;
SHA1(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha1);
// SHA1 (password)
using sha1_buffer = unsigned char [SHA_DIGEST_LENGTH];
sha1_buffer password_sha1;
SHA1(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha1);
// Add server challenge (salt)
unsigned char salted_buffer [challenge_length + SHA_DIGEST_LENGTH];
memcpy(salted_buffer, challenge, challenge_length);
SHA1(password_sha1, sizeof(password_sha1), salted_buffer + 20);
sha1_buffer salted_sha1;
SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1);
// Add server challenge (salt)
unsigned char salted_buffer [challenge_length + SHA_DIGEST_LENGTH];
memcpy(salted_buffer, challenge, challenge_length);
SHA1(password_sha1, sizeof(password_sha1), salted_buffer + 20);
sha1_buffer salted_sha1;
SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1);
// XOR
static_assert(response_length == SHA_DIGEST_LENGTH);
for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
{
static_cast<std::uint8_t*>(output)[i] = password_sha1[i] ^ salted_sha1[i];
}
// XOR
static_assert(response_length == SHA_DIGEST_LENGTH);
for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
{
static_cast<std::uint8_t*>(output)[i] = password_sha1[i] ^ salted_sha1[i];
}
}
} // mysql_native_password
@ -56,26 +56,26 @@ 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,
bool, // use_ssl
std::string& output
std::string_view password,
std::string_view challenge,
bool, // use_ssl
std::string& output
)
{
// Check challenge size
if (challenge.size() != challenge_length)
{
return make_error_code(errc::protocol_value_error);
}
// Check challenge size
if (challenge.size() != challenge_length)
{
return make_error_code(errc::protocol_value_error);
}
// Do the calculation
output.resize(response_length);
compute_auth_string(
password,
challenge.data(),
output.data()
);
return error_code();
// Do the calculation
output.resize(response_length);
compute_auth_string(
password,
challenge.data(),
output.data()
);
return error_code();
}

View File

@ -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,
bool use_ssl,
std::string& output
std::string_view password,
std::string_view challenge,
bool use_ssl,
std::string& output
);

View File

@ -14,7 +14,7 @@
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) DEDUCED
#else
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) \
BOOST_ASIO_INITFN_RESULT_TYPE(ct, sig)
BOOST_ASIO_INITFN_RESULT_TYPE(ct, sig)
#endif
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_ASYNC_RESULT_MACRO_HPP_ */

View File

@ -22,38 +22,38 @@ struct get_handler_arg;
template <typename T>
struct get_handler_arg<void(error_code, T)>
{
using type = T;
using type = T;
};
template <>
struct get_handler_arg<void(error_code)>
{
using type = void;
using type = void;
};
template <typename HandlerType, typename HandlerArg>
constexpr bool is_handler_signature_ok()
{
if constexpr (std::is_same_v<HandlerArg, void>)
{
return std::is_invocable_v<HandlerType, error_code>;
}
else
{
return std::is_invocable_v<HandlerType, error_code, HandlerArg>;
}
if constexpr (std::is_same_v<HandlerArg, void>)
{
return std::is_invocable_v<HandlerType, error_code>;
}
else
{
return std::is_invocable_v<HandlerType, error_code, HandlerArg>;
}
}
template <typename CompletionToken, typename HandlerSignature>
constexpr void check_completion_token()
{
using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using handler_arg = typename detail::get_handler_arg<HandlerSignature>::type;
static_assert(
is_handler_signature_ok<handler_type, handler_arg>(),
"Invalid CompletionToken type. Check that CompletionToken fullfills the CompletionToken "
"requirements or that the callback signature you passed in is correct"
);
using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using handler_arg = typename detail::get_handler_arg<HandlerSignature>::type;
static_assert(
is_handler_signature_ok<handler_type, handler_arg>(),
"Invalid CompletionToken type. Check that CompletionToken fullfills the CompletionToken "
"requirements or that the callback signature you passed in is correct"
);
}
} // detail

View File

@ -17,16 +17,16 @@ namespace detail {
template <typename TLeft, typename TRight>
inline bool container_equals(
const std::vector<TLeft>& lhs,
const std::vector<TRight>& rhs
const std::vector<TLeft>& lhs,
const std::vector<TRight>& rhs
)
{
if (lhs.size() != rhs.size()) return false;
return std::equal(
lhs.begin(),
lhs.end(),
rhs.begin()
);
if (lhs.size() != rhs.size()) return false;
return std::equal(
lhs.begin(),
lhs.end(),
rhs.begin()
);
}
} // detail

View File

@ -18,9 +18,9 @@ namespace detail {
template <typename... Types>
std::string stringize(const Types&... inputs)
{
std::ostringstream ss;
(ss << ... << inputs);
return ss.str();
std::ostringstream ss;
(ss << ... << inputs);
return ss.str();
}
} // detail

View File

@ -17,13 +17,13 @@ namespace detail {
template <typename T, typename Head, typename... Tail>
struct is_one_of
{
static constexpr bool value = std::is_same_v<T, Head> || is_one_of<T, Tail...>::value;
static constexpr bool value = std::is_same_v<T, Head> || is_one_of<T, Tail...>::value;
};
template <typename T, typename Head>
struct is_one_of<T, Head>
{
static constexpr bool value = std::is_same_v<T, Head>;
static constexpr bool value = std::is_same_v<T, Head>;
};

View File

@ -17,11 +17,11 @@ namespace mysql {
namespace detail {
inline void valgrind_make_mem_defined(
[[maybe_unused]] boost::asio::const_buffer buff
[[maybe_unused]] boost::asio::const_buffer buff
)
{
#ifdef BOOST_MYSQL_VALGRIND_TESTS
VALGRIND_MAKE_MEM_DEFINED(buff.data(), buff.size());
VALGRIND_MAKE_MEM_DEFINED(buff.data(), buff.size());
#endif
}

View File

@ -16,10 +16,10 @@ namespace detail {
template <typename StreamType>
void close_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
error_code& code,
error_info& info
channel<StreamType>& chan,
std::uint32_t statement_id,
error_code& code,
error_info& info
);
using close_signature = empty_signature;
@ -27,10 +27,10 @@ using close_signature = empty_signature;
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_signature)
async_close_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
std::uint32_t statement_id,
CompletionToken&& token,
error_info* info
);
} // detail

View File

@ -20,9 +20,9 @@ namespace mysql {
namespace detail {
using deserialize_row_fn = error_code (*)(
deserialization_context&,
const std::vector<field_metadata>&,
std::vector<value>&
deserialization_context&,
const std::vector<field_metadata>&,
std::vector<value>&
);
using empty_signature = void(error_code);

View File

@ -18,12 +18,12 @@ namespace detail {
template <typename StreamType, typename Serializable>
void execute_generic(
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const Serializable& request,
resultset<StreamType>& output,
error_code& err,
error_info& info
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const Serializable& request,
resultset<StreamType>& output,
error_code& err,
error_info& info
);
template <typename StreamType>
@ -32,11 +32,11 @@ using execute_generic_signature = void(error_code, resultset<StreamType>);
template <typename StreamType, typename Serializable, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
async_execute_generic(
deserialize_row_fn deserializer,
channel<StreamType>& chan,
const Serializable& request,
CompletionToken&& token,
error_info* info
deserialize_row_fn deserializer,
channel<StreamType>& chan,
const Serializable& request,
CompletionToken&& token,
error_info* info
);
} // detail

View File

@ -19,20 +19,20 @@ namespace detail {
template <typename StreamType>
void execute_query(
channel<StreamType>& channel,
std::string_view query,
resultset<StreamType>& output,
error_code& err,
error_info& info
channel<StreamType>& channel,
std::string_view query,
resultset<StreamType>& output,
error_code& err,
error_info& info
);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
async_execute_query(
channel<StreamType>& chan,
std::string_view query,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
std::string_view query,
CompletionToken&& token,
error_info* info
);
}

View File

@ -19,24 +19,24 @@ namespace detail {
template <typename StreamType, typename ForwardIterator>
void execute_statement(
channel<StreamType>& channel,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
resultset<StreamType>& output,
error_code& err,
error_info& info
channel<StreamType>& channel,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
resultset<StreamType>& output,
error_code& err,
error_info& info
);
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
async_execute_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
CompletionToken&& token,
error_info* info
);
} // detail

View File

@ -21,10 +21,10 @@ namespace detail {
template <typename StreamType>
void hanshake(
channel<StreamType>& channel,
const connection_params& params,
error_code& err,
error_info& info
channel<StreamType>& channel,
const connection_params& params,
error_code& err,
error_info& info
);
using handshake_signature = empty_signature;
@ -32,10 +32,10 @@ using handshake_signature = empty_signature;
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
async_handshake(
channel<StreamType>& channel,
const connection_params& params,
CompletionToken&& token,
error_info* info
channel<StreamType>& channel,
const connection_params& params,
CompletionToken&& token,
error_info* info
);
} // detail

View File

@ -12,47 +12,47 @@
template <typename StreamType>
void boost::mysql::detail::close_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
error_code& code,
error_info&
channel<StreamType>& chan,
std::uint32_t statement_id,
error_code& code,
error_info&
)
{
// Compose the close message
com_stmt_close_packet packet {int4(statement_id)};
// Compose the close message
com_stmt_close_packet packet {int4(statement_id)};
// Serialize it
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
// Serialize it
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
// Send it. No response is sent back
chan.reset_sequence_number();
chan.write(boost::asio::buffer(chan.shared_buffer()), code);
// Send it. No response is sent back
chan.reset_sequence_number();
chan.write(boost::asio::buffer(chan.shared_buffer()), code);
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::close_signature
CompletionToken,
boost::mysql::detail::close_signature
)
boost::mysql::detail::async_close_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
CompletionToken&& token,
error_info*
channel<StreamType>& chan,
std::uint32_t statement_id,
CompletionToken&& token,
error_info*
)
{
// Compose the close message
com_stmt_close_packet packet {int4(statement_id)};
// Compose the close message
com_stmt_close_packet packet {int4(statement_id)};
// Serialize it
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
// Serialize it
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
// Send it. No response is sent back
chan.reset_sequence_number();
return chan.async_write(
boost::asio::buffer(chan.shared_buffer()),
std::forward<CompletionToken>(token)
);
// Send it. No response is sent back
chan.reset_sequence_number();
return chan.async_write(
boost::asio::buffer(chan.shared_buffer()),
std::forward<CompletionToken>(token)
);
}

View File

@ -18,123 +18,123 @@ namespace detail {
template <typename StreamType>
class execute_processor
{
deserialize_row_fn deserializer_;
channel<StreamType>& channel_;
bytestring buffer_;
std::size_t field_count_ {};
ok_packet ok_packet_;
std::vector<field_metadata> fields_;
std::vector<bytestring> field_buffers_;
deserialize_row_fn deserializer_;
channel<StreamType>& channel_;
bytestring buffer_;
std::size_t field_count_ {};
ok_packet ok_packet_;
std::vector<field_metadata> fields_;
std::vector<bytestring> field_buffers_;
public:
execute_processor(deserialize_row_fn deserializer, channel<StreamType>& chan):
deserializer_(deserializer), channel_(chan) {};
execute_processor(deserialize_row_fn deserializer, channel<StreamType>& chan):
deserializer_(deserializer), channel_(chan) {};
template <typename Serializable>
void process_request(
const Serializable& request
)
{
// Serialize the request
capabilities caps = channel_.current_capabilities();
serialize_message(request, caps, buffer_);
template <typename Serializable>
void process_request(
const Serializable& request
)
{
// Serialize the request
capabilities caps = channel_.current_capabilities();
serialize_message(request, caps, buffer_);
// Prepare the channel
channel_.reset_sequence_number();
}
// Prepare the channel
channel_.reset_sequence_number();
}
void process_response(
error_code& err,
error_info& info
)
{
// Response may be: ok_packet, err_packet, local infile request (not implemented)
// 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_), channel_.current_capabilities());
std::uint8_t msg_type;
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err) return;
if (msg_type == ok_packet_header)
{
err = deserialize_message(ok_packet_, ctx);
if (err) return;
field_count_ = 0;
}
else if (msg_type == error_packet_header)
{
err = process_error_packet(ctx, info);
}
else
{
// Resultset with metadata. First packet is an int_lenenc with
// the number of field definitions to expect. Message type is part
// of this packet, so we must rewind the context
ctx.rewind(1);
int_lenenc num_fields;
err = deserialize_message(num_fields, ctx);
if (err) return;
void process_response(
error_code& err,
error_info& info
)
{
// Response may be: ok_packet, err_packet, local infile request (not implemented)
// 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_), channel_.current_capabilities());
std::uint8_t msg_type;
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err) return;
if (msg_type == ok_packet_header)
{
err = deserialize_message(ok_packet_, ctx);
if (err) return;
field_count_ = 0;
}
else if (msg_type == error_packet_header)
{
err = process_error_packet(ctx, info);
}
else
{
// Resultset with metadata. First packet is an int_lenenc with
// the number of field definitions to expect. Message type is part
// of this packet, so we must rewind the context
ctx.rewind(1);
int_lenenc num_fields;
err = deserialize_message(num_fields, ctx);
if (err) return;
// For platforms where size_t is shorter than uint64_t,
// perform range check
if (num_fields.value > std::numeric_limits<std::size_t>::max())
{
err = make_error_code(errc::protocol_value_error);
return;
}
// For platforms where size_t is shorter than uint64_t,
// perform range check
if (num_fields.value > std::numeric_limits<std::size_t>::max())
{
err = make_error_code(errc::protocol_value_error);
return;
}
// Ensure we have fields, as field_count is indicative of
// a resultset with fields
field_count_ = static_cast<std::size_t>(num_fields.value);
if (field_count_ == 0)
{
err = make_error_code(errc::protocol_value_error);
return;
}
// Ensure we have fields, as field_count is indicative of
// a resultset with fields
field_count_ = static_cast<std::size_t>(num_fields.value);
if (field_count_ == 0)
{
err = make_error_code(errc::protocol_value_error);
return;
}
fields_.reserve(field_count_);
field_buffers_.reserve(field_count_);
}
}
fields_.reserve(field_count_);
field_buffers_.reserve(field_count_);
}
}
error_code process_field_definition()
{
column_definition_packet field_definition;
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
auto err = deserialize_message(field_definition, ctx);
if (err) return err;
error_code process_field_definition()
{
column_definition_packet field_definition;
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
auto err = deserialize_message(field_definition, ctx);
if (err) return err;
// Add it to our array
fields_.push_back(field_definition);
field_buffers_.push_back(std::move(buffer_));
buffer_ = bytestring();
// Add it to our array
fields_.push_back(field_definition);
field_buffers_.push_back(std::move(buffer_));
buffer_ = bytestring();
return error_code();
}
return error_code();
}
resultset<StreamType> create_resultset() &&
{
if (field_count_ == 0)
{
return resultset<StreamType>(
channel_,
std::move(buffer_),
ok_packet_
);
}
else
{
return resultset<StreamType>(
channel_,
resultset_metadata(std::move(field_buffers_), std::move(fields_)),
deserializer_
);
}
}
resultset<StreamType> create_resultset() &&
{
if (field_count_ == 0)
{
return resultset<StreamType>(
channel_,
std::move(buffer_),
ok_packet_
);
}
else
{
return resultset<StreamType>(
channel_,
resultset_metadata(std::move(field_buffers_), std::move(fields_)),
deserializer_
);
}
}
auto& get_channel() { return channel_; }
auto& get_buffer() { return buffer_; }
auto& get_channel() { return channel_; }
auto& get_buffer() { return buffer_; }
std::size_t field_count() const noexcept { return field_count_; }
std::size_t field_count() const noexcept { return field_count_; }
};
} // detail
@ -143,164 +143,164 @@ public:
template <typename StreamType, typename Serializable>
void boost::mysql::detail::execute_generic(
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const Serializable& request,
resultset<StreamType>& output,
error_code& err,
error_info& info
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const Serializable& request,
resultset<StreamType>& output,
error_code& err,
error_info& info
)
{
// Compose a com_query message, reset seq num
execute_processor<StreamType> processor (deserializer, channel);
processor.process_request(request);
// Compose a com_query message, reset seq num
execute_processor<StreamType> processor (deserializer, channel);
processor.process_request(request);
// Send it
channel.write(boost::asio::buffer(processor.get_buffer()), err);
if (err) return;
// Send it
channel.write(boost::asio::buffer(processor.get_buffer()), err);
if (err) return;
// Read the response
channel.read(processor.get_buffer(), err);
if (err) return;
// Read the response
channel.read(processor.get_buffer(), err);
if (err) return;
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
processor.process_response(err, info);
if (err) return;
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
processor.process_response(err, info);
if (err) return;
// Read all of the field definitions (zero if empty resultset)
for (std::uint64_t i = 0; i < processor.field_count(); ++i)
{
// Read the field definition packet
channel.read(processor.get_buffer(), err);
if (err) return;
// Read all of the field definitions (zero if empty resultset)
for (std::uint64_t i = 0; i < processor.field_count(); ++i)
{
// Read the field definition packet
channel.read(processor.get_buffer(), err);
if (err) return;
// Process the message
err = processor.process_field_definition();
if (err) return;
}
// Process the message
err = processor.process_field_definition();
if (err) return;
}
// No EOF packet is expected here, as we require deprecate EOF capabilities
output = std::move(processor).create_resultset();
err.clear();
// No EOF packet is expected here, as we require deprecate EOF capabilities
output = std::move(processor).create_resultset();
err.clear();
}
template <typename StreamType, typename Serializable, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
)
boost::mysql::detail::async_execute_generic(
deserialize_row_fn deserializer,
channel<StreamType>& chan,
const Serializable& request,
CompletionToken&& token,
error_info* info
deserialize_row_fn deserializer,
channel<StreamType>& chan,
const Serializable& request,
CompletionToken&& token,
error_info* info
)
{
using HandlerSignature = execute_generic_signature<StreamType>;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using ResultsetType = resultset<StreamType>;
using HandlerSignature = execute_generic_signature<StreamType>;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using ResultsetType = resultset<StreamType>;
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
struct Op: BaseType, boost::asio::coroutine
{
std::shared_ptr<execute_processor<StreamType>> processor_;
std::uint64_t remaining_fields_ {0};
error_info* output_info_;
struct Op: BaseType, boost::asio::coroutine
{
std::shared_ptr<execute_processor<StreamType>> processor_;
std::uint64_t remaining_fields_ {0};
error_info* output_info_;
Op(
HandlerType&& handler,
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const Serializable& request,
error_info* output_info
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
processor_(std::make_shared<execute_processor<StreamType>>(deserializer, channel)),
output_info_(output_info)
{
processor_->process_request(request);
}
Op(
HandlerType&& handler,
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const Serializable& request,
error_info* output_info
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
processor_(std::make_shared<execute_processor<StreamType>>(deserializer, channel)),
output_info_(output_info)
{
processor_->process_request(request);
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
this->complete(cont, err, ResultsetType());
return;
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
this->complete(cont, err, ResultsetType());
return;
}
// Non-error path
error_info info;
reenter(*this)
{
// The request message has already been composed in the ctor. Send it
yield processor_->get_channel().async_write(
boost::asio::buffer(processor_->get_buffer()),
std::move(*this)
);
// Non-error path
error_info info;
reenter(*this)
{
// The request message has already been composed in the ctor. Send it
yield processor_->get_channel().async_write(
boost::asio::buffer(processor_->get_buffer()),
std::move(*this)
);
// Read the response
yield processor_->get_channel().async_read(
processor_->get_buffer(),
std::move(*this)
);
// Read the response
yield processor_->get_channel().async_read(
processor_->get_buffer(),
std::move(*this)
);
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
processor_->process_response(err, info);
if (err)
{
conditional_assign(output_info_, std::move(info));
this->complete(cont, err, ResultsetType());
yield break;
}
remaining_fields_ = processor_->field_count();
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
processor_->process_response(err, info);
if (err)
{
conditional_assign(output_info_, std::move(info));
this->complete(cont, err, ResultsetType());
yield break;
}
remaining_fields_ = processor_->field_count();
// Read all of the field definitions
while (remaining_fields_ > 0)
{
// Read the field definition packet
yield processor_->get_channel().async_read(
processor_->get_buffer(),
std::move(*this)
);
// Read all of the field definitions
while (remaining_fields_ > 0)
{
// Read the field definition packet
yield processor_->get_channel().async_read(
processor_->get_buffer(),
std::move(*this)
);
// Process the message
err = processor_->process_field_definition();
if (err)
{
this->complete(cont, err, ResultsetType());
yield break;
}
// Process the message
err = processor_->process_field_definition();
if (err)
{
this->complete(cont, err, ResultsetType());
yield break;
}
remaining_fields_--;
}
remaining_fields_--;
}
// No EOF packet is expected here, as we require deprecate EOF capabilities
this->complete(
cont,
error_code(),
ResultsetType(std::move(*processor_).create_resultset())
);
}
}
};
// No EOF packet is expected here, as we require deprecate EOF capabilities
this->complete(
cont,
error_code(),
ResultsetType(std::move(*processor_).create_resultset())
);
}
}
};
Op(
std::move(initiator.completion_handler),
deserializer,
chan,
request,
info
)(error_code(), false);
return initiator.result.get();
Op(
std::move(initiator.completion_handler),
deserializer,
chan,
request,
info
)(error_code(), false);
return initiator.result.get();
}
#include <boost/asio/unyield.hpp>

View File

@ -13,45 +13,45 @@
template <typename StreamType>
void boost::mysql::detail::execute_query(
channel<StreamType>& channel,
std::string_view query,
resultset<StreamType>& output,
error_code& err,
error_info& info
channel<StreamType>& channel,
std::string_view query,
resultset<StreamType>& output,
error_code& err,
error_info& info
)
{
com_query_packet request { string_eof(query) };
execute_generic(
&deserialize_text_row,
channel,
request,
output,
err,
info
);
com_query_packet request { string_eof(query) };
execute_generic(
&deserialize_text_row,
channel,
request,
output,
err,
info
);
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
)
boost::mysql::detail::async_execute_query(
channel<StreamType>& chan,
std::string_view query,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
std::string_view query,
CompletionToken&& token,
error_info* info
)
{
com_query_packet request { string_eof(query) };
return async_execute_generic(
&deserialize_text_row,
chan,
request,
std::forward<CompletionToken>(token),
info
);
com_query_packet request { string_eof(query) };
return async_execute_generic(
&deserialize_text_row,
chan,
request,
std::forward<CompletionToken>(token),
info
);
}
#include <boost/asio/unyield.hpp>

View File

@ -17,19 +17,19 @@ namespace detail {
template <typename ForwardIterator>
com_stmt_execute_packet<ForwardIterator> make_stmt_execute_packet(
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end
)
{
return com_stmt_execute_packet<ForwardIterator> {
int4(statement_id),
int1(0), // flags
int4(1), // iteration count
int1(1), // new params flag: set
params_begin,
params_end
};
return com_stmt_execute_packet<ForwardIterator> {
int4(statement_id),
int1(0), // flags
int4(1), // iteration count
int1(1), // new params flag: set
params_begin,
params_end
};
}
} // detail
@ -38,46 +38,46 @@ com_stmt_execute_packet<ForwardIterator> make_stmt_execute_packet(
template <typename StreamType, typename ForwardIterator>
void boost::mysql::detail::execute_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
resultset<StreamType>& output,
error_code& err,
error_info& info
channel<StreamType>& chan,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
resultset<StreamType>& output,
error_code& err,
error_info& info
)
{
execute_generic(
&deserialize_binary_row,
chan,
make_stmt_execute_packet(statement_id, params_begin, params_end),
output,
err,
info
);
execute_generic(
&deserialize_binary_row,
chan,
make_stmt_execute_packet(statement_id, params_begin, params_end),
output,
err,
info
);
}
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
CompletionToken,
boost::mysql::detail::execute_generic_signature<StreamType>
)
boost::mysql::detail::async_execute_statement(
channel<StreamType>& chan,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
std::uint32_t statement_id,
ForwardIterator params_begin,
ForwardIterator params_end,
CompletionToken&& token,
error_info* info
)
{
return async_execute_generic(
&deserialize_binary_row,
chan,
make_stmt_execute_packet(statement_id, params_begin, params_end),
std::forward<CompletionToken>(token),
info
);
return async_execute_generic(
&deserialize_binary_row,
chan,
make_stmt_execute_packet(statement_id, params_begin, params_end),
std::forward<CompletionToken>(token),
info
);
}

View File

@ -20,36 +20,36 @@ namespace detail {
inline std::uint8_t get_collation_first_byte(collation value)
{
return static_cast<std::uint16_t>(value) % 0xff;
return static_cast<std::uint16_t>(value) % 0xff;
}
inline capabilities conditional_capability(bool condition, std::uint32_t cap)
{
return capabilities(condition ? cap : 0);
return capabilities(condition ? cap : 0);
}
inline error_code deserialize_handshake(
boost::asio::const_buffer buffer,
handshake_packet& output,
error_info& info
boost::asio::const_buffer buffer,
handshake_packet& output,
error_info& info
)
{
deserialization_context ctx (boost::asio::buffer(buffer), capabilities());
auto [err, msg_type] = deserialize_message_type(ctx);
if (err) return err;
if (msg_type == handshake_protocol_version_9)
{
return make_error_code(errc::server_unsupported);
}
else if (msg_type == error_packet_header)
{
return process_error_packet(ctx, info);
}
else if (msg_type != handshake_protocol_version_10)
{
return make_error_code(errc::protocol_value_error);
}
return deserialize_message(output, ctx);
deserialization_context ctx (boost::asio::buffer(buffer), capabilities());
auto [err, msg_type] = deserialize_message_type(ctx);
if (err) return err;
if (msg_type == handshake_protocol_version_9)
{
return make_error_code(errc::server_unsupported);
}
else if (msg_type == error_packet_header)
{
return process_error_packet(ctx, info);
}
else if (msg_type != handshake_protocol_version_10)
{
return make_error_code(errc::protocol_value_error);
}
return deserialize_message(output, ctx);
}
// When receiving an auth response from the server, several things can happen:
@ -64,172 +64,172 @@ inline error_code deserialize_handshake(
// happens just for caching_sha2_password.
enum class auth_result
{
complete,
send_more_data,
wait_for_ok,
invalid
complete,
send_more_data,
wait_for_ok,
invalid
};
class handshake_processor
{
connection_params params_;
capabilities negotiated_caps_;
auth_calculator auth_calc_;
connection_params params_;
capabilities negotiated_caps_;
auth_calculator auth_calc_;
public:
handshake_processor(const connection_params& params): params_(params) {};
capabilities negotiated_capabilities() const noexcept { return negotiated_caps_; }
const connection_params& params() const noexcept { return params_; }
bool use_ssl() const noexcept { return negotiated_caps_.has(CLIENT_SSL); }
handshake_processor(const connection_params& params): params_(params) {};
capabilities negotiated_capabilities() const noexcept { return negotiated_caps_; }
const connection_params& params() const noexcept { return params_; }
bool use_ssl() const noexcept { return negotiated_caps_.has(CLIENT_SSL); }
// Initial greeting processing
error_code process_capabilities(const handshake_packet& handshake)
{
auto ssl = params_.ssl().mode();
capabilities server_caps (handshake.capability_falgs.value);
capabilities required_caps = mandatory_capabilities |
conditional_capability(!params_.database().empty(), CLIENT_CONNECT_WITH_DB) |
conditional_capability(ssl == ssl_mode::require, CLIENT_SSL);
if (!server_caps.has_all(required_caps))
{
return make_error_code(errc::server_unsupported);
}
negotiated_caps_ = server_caps & (required_caps | optional_capabilities |
conditional_capability(ssl == ssl_mode::enable, CLIENT_SSL));
return error_code();
}
// Initial greeting processing
error_code process_capabilities(const handshake_packet& handshake)
{
auto ssl = params_.ssl().mode();
capabilities server_caps (handshake.capability_falgs.value);
capabilities required_caps = mandatory_capabilities |
conditional_capability(!params_.database().empty(), CLIENT_CONNECT_WITH_DB) |
conditional_capability(ssl == ssl_mode::require, CLIENT_SSL);
if (!server_caps.has_all(required_caps))
{
return make_error_code(errc::server_unsupported);
}
negotiated_caps_ = server_caps & (required_caps | optional_capabilities |
conditional_capability(ssl == ssl_mode::enable, CLIENT_SSL));
return error_code();
}
error_code process_handshake(bytestring& buffer, error_info& info)
{
// Deserialize server greeting
handshake_packet handshake;
auto err = deserialize_handshake(boost::asio::buffer(buffer), handshake, info);
if (err) return err;
error_code process_handshake(bytestring& buffer, error_info& info)
{
// Deserialize server greeting
handshake_packet handshake;
auto err = deserialize_handshake(boost::asio::buffer(buffer), handshake, info);
if (err) return err;
// Check capabilities
err = process_capabilities(handshake);
if (err) return err;
// Check capabilities
err = process_capabilities(handshake);
if (err) return err;
// Compute auth response
return auth_calc_.calculate(
handshake.auth_plugin_name.value,
params_.password(),
handshake.auth_plugin_data.value,
use_ssl()
);
}
// Compute auth response
return auth_calc_.calculate(
handshake.auth_plugin_name.value,
params_.password(),
handshake.auth_plugin_data.value,
use_ssl()
);
}
// Response to that initial greeting
void compose_ssl_request(bytestring& buffer)
{
ssl_request sslreq {
int4(negotiated_capabilities().get()),
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
int1(get_collation_first_byte(params_.connection_collation())),
{}
};
// Response to that initial greeting
void compose_ssl_request(bytestring& buffer)
{
ssl_request sslreq {
int4(negotiated_capabilities().get()),
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
int1(get_collation_first_byte(params_.connection_collation())),
{}
};
// Serialize and send
serialize_message(sslreq, negotiated_caps_, buffer);
}
// Serialize and send
serialize_message(sslreq, negotiated_caps_, buffer);
}
void compose_handshake_response(bytestring& buffer)
{
// Compose response
handshake_response_packet response {
int4(negotiated_caps_.get()),
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
int1(get_collation_first_byte(params_.connection_collation())),
string_null(params_.username()),
string_lenenc(auth_calc_.response()),
string_null(params_.database()),
string_null(auth_calc_.plugin_name())
};
void compose_handshake_response(bytestring& buffer)
{
// Compose response
handshake_response_packet response {
int4(negotiated_caps_.get()),
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
int1(get_collation_first_byte(params_.connection_collation())),
string_null(params_.username()),
string_lenenc(auth_calc_.response()),
string_null(params_.database()),
string_null(auth_calc_.plugin_name())
};
// Serialize
serialize_message(response, negotiated_caps_, buffer);
}
// Serialize
serialize_message(response, negotiated_caps_, buffer);
}
// Server handshake response
error_code process_handshake_server_response(
bytestring& buffer,
auth_result& result,
error_info& info
)
{
deserialization_context ctx (boost::asio::buffer(buffer), negotiated_caps_);
auto [err, msg_type] = deserialize_message_type(ctx);
if (err) return err;
if (msg_type == ok_packet_header)
{
// Auth success via fast auth path
result = auth_result::complete;
return error_code();
}
else if (msg_type == error_packet_header)
{
return process_error_packet(ctx, info);
}
else if (msg_type == auth_switch_request_header)
{
// We have received an auth switch request. Deserialize it
auth_switch_request_packet auth_sw;
err = deserialize_message(auth_sw, ctx);
if (err) return err;
// Server handshake response
error_code process_handshake_server_response(
bytestring& buffer,
auth_result& result,
error_info& info
)
{
deserialization_context ctx (boost::asio::buffer(buffer), negotiated_caps_);
auto [err, msg_type] = deserialize_message_type(ctx);
if (err) return err;
if (msg_type == ok_packet_header)
{
// Auth success via fast auth path
result = auth_result::complete;
return error_code();
}
else if (msg_type == error_packet_header)
{
return process_error_packet(ctx, info);
}
else if (msg_type == auth_switch_request_header)
{
// We have received an auth switch request. Deserialize it
auth_switch_request_packet auth_sw;
err = deserialize_message(auth_sw, ctx);
if (err) return err;
// Compute response
auth_switch_response_packet auth_sw_res;
err = auth_calc_.calculate(
auth_sw.plugin_name.value,
params_.password(),
auth_sw.auth_plugin_data.value,
use_ssl()
);
if (err) return err;
auth_sw_res.auth_plugin_data.value = auth_calc_.response();
// Compute response
auth_switch_response_packet auth_sw_res;
err = auth_calc_.calculate(
auth_sw.plugin_name.value,
params_.password(),
auth_sw.auth_plugin_data.value,
use_ssl()
);
if (err) return err;
auth_sw_res.auth_plugin_data.value = auth_calc_.response();
// Serialize
serialize_message(auth_sw_res, negotiated_caps_, buffer);
// Serialize
serialize_message(auth_sw_res, negotiated_caps_, buffer);
result = auth_result::send_more_data;
return error_code();
}
else if (msg_type == auth_more_data_header)
{
// We have received an auth more data request. Deserialize it
auth_more_data_packet more_data;
err = deserialize_message(more_data, ctx);
if (err) return err;
result = auth_result::send_more_data;
return error_code();
}
else if (msg_type == auth_more_data_header)
{
// We have received an auth more data request. Deserialize it
auth_more_data_packet more_data;
err = deserialize_message(more_data, ctx);
if (err) return err;
std::string_view challenge = more_data.auth_plugin_data.value;
if (challenge == fast_auth_complete_challenge)
{
result = auth_result::wait_for_ok;
return error_code();
}
std::string_view challenge = more_data.auth_plugin_data.value;
if (challenge == fast_auth_complete_challenge)
{
result = auth_result::wait_for_ok;
return error_code();
}
// Compute response
err = auth_calc_.calculate(
auth_calc_.plugin_name(),
params_.password(),
challenge,
use_ssl()
);
if (err) return err;
// Compute response
err = auth_calc_.calculate(
auth_calc_.plugin_name(),
params_.password(),
challenge,
use_ssl()
);
if (err) return err;
serialize_message(
auth_switch_response_packet {string_eof(auth_calc_.response())},
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();
}
else
{
return make_error_code(errc::protocol_value_error);
}
}
result = auth_result::send_more_data;
return error_code();
}
else
{
return make_error_code(errc::protocol_value_error);
}
}
};
} // detail
@ -238,192 +238,192 @@ public:
template <typename StreamType>
void boost::mysql::detail::hanshake(
channel<StreamType>& channel,
const connection_params& params,
error_code& err,
error_info& info
channel<StreamType>& channel,
const connection_params& params,
error_code& err,
error_info& info
)
{
// Set up processor
handshake_processor processor (params);
// Set up processor
handshake_processor processor (params);
// Read server greeting
channel.read(channel.shared_buffer(), err);
if (err) return;
// Read server greeting
channel.read(channel.shared_buffer(), err);
if (err) return;
// Process server greeting (handshake)
err = processor.process_handshake(channel.shared_buffer(), info);
if (err) return;
// Process server greeting (handshake)
err = processor.process_handshake(channel.shared_buffer(), info);
if (err) return;
// Setup SSL if required
if (processor.use_ssl())
{
// Send SSL request
processor.compose_ssl_request(channel.shared_buffer());
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
if (err) return;
// Setup SSL if required
if (processor.use_ssl())
{
// Send SSL request
processor.compose_ssl_request(channel.shared_buffer());
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
if (err) return;
// SSL handshake
channel.ssl_handshake(err);
if (err) return;
}
// SSL handshake
channel.ssl_handshake(err);
if (err) return;
}
// Handshake response
processor.compose_handshake_response(channel.shared_buffer());
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
if (err) return;
// Handshake response
processor.compose_handshake_response(channel.shared_buffer());
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
if (err) return;
auth_result auth_outcome = auth_result::invalid;
while (auth_outcome != auth_result::complete)
{
// Receive response
channel.read(channel.shared_buffer(), err);
if (err) return;
auth_result auth_outcome = auth_result::invalid;
while (auth_outcome != auth_result::complete)
{
// Receive response
channel.read(channel.shared_buffer(), err);
if (err) return;
// Process it
err = processor.process_handshake_server_response(channel.shared_buffer(), auth_outcome, info);
if (err) return;
// Process it
err = processor.process_handshake_server_response(channel.shared_buffer(), auth_outcome, info);
if (err) return;
if (auth_outcome == auth_result::send_more_data)
{
// We received an auth switch request and we have the response ready to be sent
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
if (err) return;
}
};
if (auth_outcome == auth_result::send_more_data)
{
// We received an auth switch request and we have the response ready to be sent
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
if (err) return;
}
};
channel.set_current_capabilities(processor.negotiated_capabilities());
channel.set_current_capabilities(processor.negotiated_capabilities());
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::handshake_signature
CompletionToken,
boost::mysql::detail::handshake_signature
)
boost::mysql::detail::async_handshake(
channel<StreamType>& chan,
const connection_params& params,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
const connection_params& params,
CompletionToken&& token,
error_info* info
)
{
using HandlerSignature = handshake_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using HandlerSignature = handshake_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
struct Op: BaseType, boost::asio::coroutine
{
channel<StreamType>& channel_;
handshake_processor processor_;
error_info info_;
error_info* output_info_;
auth_result auth_state_ {auth_result::invalid};
struct Op: BaseType, boost::asio::coroutine
{
channel<StreamType>& channel_;
handshake_processor processor_;
error_info info_;
error_info* output_info_;
auth_result auth_state_ {auth_result::invalid};
Op(
HandlerType&& handler,
channel<StreamType>& channel,
const connection_params& params,
error_info* output_info
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
channel_(channel),
processor_(params),
output_info_(output_info)
{
}
Op(
HandlerType&& handler,
channel<StreamType>& channel,
const connection_params& params,
error_info* output_info
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
channel_(channel),
processor_(params),
output_info_(output_info)
{
}
void complete(bool cont, error_code code)
{
channel_.set_current_capabilities(processor_.negotiated_capabilities());
conditional_assign(output_info_, std::move(info_));
BaseType::complete(cont, code);
}
void complete(bool cont, error_code code)
{
channel_.set_current_capabilities(processor_.negotiated_capabilities());
conditional_assign(output_info_, std::move(info_));
BaseType::complete(cont, code);
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
complete(cont, err);
return;
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
complete(cont, err);
return;
}
// Non-error path
reenter(*this)
{
// Read server greeting
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
// Non-error path
reenter(*this)
{
// Read server greeting
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
// Process server greeting
err = processor_.process_handshake(channel_.shared_buffer(), info_);
if (err)
{
complete(cont, err);
yield break;
}
// Process server greeting
err = processor_.process_handshake(channel_.shared_buffer(), info_);
if (err)
{
complete(cont, err);
yield break;
}
// SSL
if (processor_.use_ssl())
{
// Send SSL request
processor_.compose_ssl_request(channel_.shared_buffer());
yield channel_.async_write(
boost::asio::buffer(channel_.shared_buffer()),
std::move(*this)
);
// SSL
if (processor_.use_ssl())
{
// Send SSL request
processor_.compose_ssl_request(channel_.shared_buffer());
yield channel_.async_write(
boost::asio::buffer(channel_.shared_buffer()),
std::move(*this)
);
// SSL handshake
yield channel_.async_ssl_handshake(std::move(*this));
}
// SSL handshake
yield channel_.async_ssl_handshake(std::move(*this));
}
// Compose and send handshake response
processor_.compose_handshake_response(channel_.shared_buffer());
yield channel_.async_write(boost::asio::buffer(channel_.shared_buffer()), std::move(*this));
// Compose and send handshake response
processor_.compose_handshake_response(channel_.shared_buffer());
yield channel_.async_write(boost::asio::buffer(channel_.shared_buffer()), std::move(*this));
while (auth_state_ != auth_result::complete)
{
// Receive response
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
while (auth_state_ != auth_result::complete)
{
// Receive response
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
// Process it
err = processor_.process_handshake_server_response(
channel_.shared_buffer(),
auth_state_,
info_
);
if (err)
{
complete(cont, err);
yield break;
}
// Process it
err = processor_.process_handshake_server_response(
channel_.shared_buffer(),
auth_state_,
info_
);
if (err)
{
complete(cont, err);
yield break;
}
if (auth_state_ == auth_result::send_more_data)
{
// We received an auth switch response and we have the response ready to be sent
yield channel_.async_write(
boost::asio::buffer(channel_.shared_buffer()),
std::move(*this)
);
}
}
if (auth_state_ == auth_result::send_more_data)
{
// We received an auth switch response and we have the response ready to be sent
yield channel_.async_write(
boost::asio::buffer(channel_.shared_buffer()),
std::move(*this)
);
}
}
complete(cont, error_code());
}
}
};
complete(cont, error_code());
}
}
};
Op(
std::move(initiator.completion_handler),
chan,
params,
info
)(error_code(), false);
return initiator.result.get();
Op(
std::move(initiator.completion_handler),
chan,
params,
info
)(error_code(), false);
return initiator.result.get();
}
#include <boost/asio/unyield.hpp>

View File

@ -17,47 +17,47 @@ namespace detail {
template <typename StreamType>
class prepare_statement_processor
{
channel<StreamType>& channel_;
com_stmt_prepare_ok_packet response_;
channel<StreamType>& channel_;
com_stmt_prepare_ok_packet response_;
public:
prepare_statement_processor(channel<StreamType>& chan): channel_(chan) {}
void process_request(std::string_view statement)
{
com_stmt_prepare_packet packet { string_eof(statement) };
serialize_message(packet, channel_.current_capabilities(), channel_.shared_buffer());
channel_.reset_sequence_number();
}
void process_response(error_code& err, error_info& info)
{
deserialization_context ctx (
boost::asio::buffer(channel_.shared_buffer()),
channel_.current_capabilities()
);
std::uint8_t msg_type = 0;
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err) return;
prepare_statement_processor(channel<StreamType>& chan): channel_(chan) {}
void process_request(std::string_view statement)
{
com_stmt_prepare_packet packet { string_eof(statement) };
serialize_message(packet, channel_.current_capabilities(), channel_.shared_buffer());
channel_.reset_sequence_number();
}
void process_response(error_code& err, error_info& info)
{
deserialization_context ctx (
boost::asio::buffer(channel_.shared_buffer()),
channel_.current_capabilities()
);
std::uint8_t msg_type = 0;
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err) return;
if (msg_type == error_packet_header)
{
err = process_error_packet(ctx, info);
}
else if (msg_type != 0)
{
err = make_error_code(errc::protocol_value_error);
}
else
{
err = deserialize_message(response_, ctx);
}
}
auto& get_buffer() noexcept { return channel_.shared_buffer(); }
auto& get_channel() noexcept { return channel_; }
const auto& get_response() const noexcept { return response_; }
if (msg_type == error_packet_header)
{
err = process_error_packet(ctx, info);
}
else if (msg_type != 0)
{
err = make_error_code(errc::protocol_value_error);
}
else
{
err = deserialize_message(response_, ctx);
}
}
auto& get_buffer() noexcept { return channel_.shared_buffer(); }
auto& get_channel() noexcept { return channel_; }
const auto& get_response() const noexcept { return response_; }
unsigned get_num_metadata_packets() const noexcept
{
return response_.num_columns.value + response_.num_params.value;
}
unsigned get_num_metadata_packets() const noexcept
{
return response_.num_columns.value + response_.num_params.value;
}
};
} // detail
@ -66,145 +66,145 @@ public:
template <typename StreamType>
void boost::mysql::detail::prepare_statement(
channel<StreamType>& channel,
std::string_view statement,
error_code& err,
error_info& info,
prepared_statement<StreamType>& output
channel<StreamType>& channel,
std::string_view statement,
error_code& err,
error_info& info,
prepared_statement<StreamType>& output
)
{
// Prepare message
prepare_statement_processor<StreamType> processor (channel);
processor.process_request(statement);
// Prepare message
prepare_statement_processor<StreamType> processor (channel);
processor.process_request(statement);
// Write message
processor.get_channel().write(boost::asio::buffer(processor.get_buffer()), err);
if (err) return;
// Write message
processor.get_channel().write(boost::asio::buffer(processor.get_buffer()), err);
if (err) return;
// Read response
processor.get_channel().read(processor.get_buffer(), err);
if (err) return;
// Read response
processor.get_channel().read(processor.get_buffer(), err);
if (err) return;
// Process response
processor.process_response(err, info);
if (err) return;
// Process response
processor.process_response(err, info);
if (err) return;
// Server sends now one packet per parameter and field.
// We ignore these for now. TODO: do sth useful with these
for (unsigned i = 0; i < processor.get_num_metadata_packets(); ++i)
{
processor.get_channel().read(processor.get_buffer(), err);
if (err) return;
}
// Server sends now one packet per parameter and field.
// We ignore these for now. TODO: do sth useful with these
for (unsigned i = 0; i < processor.get_num_metadata_packets(); ++i)
{
processor.get_channel().read(processor.get_buffer(), err);
if (err) return;
}
// Compose response
output = prepared_statement<StreamType>(channel, processor.get_response());
// Compose response
output = prepared_statement<StreamType>(channel, processor.get_response());
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::prepare_statement_signature<StreamType>
CompletionToken,
boost::mysql::detail::prepare_statement_signature<StreamType>
)
boost::mysql::detail::async_prepare_statement(
channel<StreamType>& chan,
std::string_view statement,
CompletionToken&& token,
error_info* info
channel<StreamType>& chan,
std::string_view statement,
CompletionToken&& token,
error_info* info
)
{
using HandlerSignature = prepare_statement_signature<StreamType>;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using PreparedStatementType = prepared_statement<StreamType>;
using HandlerSignature = prepare_statement_signature<StreamType>;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using PreparedStatementType = prepared_statement<StreamType>;
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
struct Op: BaseType, boost::asio::coroutine
{
prepare_statement_processor<StreamType> processor_;
unsigned remaining_meta_;
error_info* output_info_;
struct Op: BaseType, boost::asio::coroutine
{
prepare_statement_processor<StreamType> processor_;
unsigned remaining_meta_;
error_info* output_info_;
Op(
HandlerType&& handler,
channel<StreamType>& channel,
std::string_view statement,
error_info* output_info
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
processor_(channel),
remaining_meta_(0),
output_info_(output_info)
{
processor_.process_request(statement);
}
Op(
HandlerType&& handler,
channel<StreamType>& channel,
std::string_view statement,
error_info* output_info
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
processor_(channel),
remaining_meta_(0),
output_info_(output_info)
{
processor_.process_request(statement);
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
this->complete(cont, err, PreparedStatementType());
return;
}
void operator()(
error_code err,
bool cont=true
)
{
// Error checking
if (err)
{
this->complete(cont, err, PreparedStatementType());
return;
}
// Regular coroutine body; if there has been an error, we don't get here
error_info info;
reenter(*this)
{
// Write message (already serialized at this point)
yield processor_.get_channel().async_write(
boost::asio::buffer(processor_.get_buffer()),
std::move(*this)
);
// Regular coroutine body; if there has been an error, we don't get here
error_info info;
reenter(*this)
{
// Write message (already serialized at this point)
yield processor_.get_channel().async_write(
boost::asio::buffer(processor_.get_buffer()),
std::move(*this)
);
// Read response
yield processor_.get_channel().async_read(
processor_.get_buffer(),
std::move(*this)
);
// Read response
yield processor_.get_channel().async_read(
processor_.get_buffer(),
std::move(*this)
);
// Process response
processor_.process_response(err, info);
if (err)
{
detail::conditional_assign(output_info_, std::move(info));
this->complete(cont, err, PreparedStatementType());
yield break;
}
// Process response
processor_.process_response(err, info);
if (err)
{
detail::conditional_assign(output_info_, std::move(info));
this->complete(cont, err, PreparedStatementType());
yield break;
}
// Server sends now one packet per parameter and field.
// We ignore these for now. TODO: do sth useful with these
remaining_meta_ = processor_.get_num_metadata_packets();
for (; remaining_meta_ > 0; --remaining_meta_)
{
yield processor_.get_channel().async_read(
processor_.get_buffer(),
std::move(*this)
);
}
// Server sends now one packet per parameter and field.
// We ignore these for now. TODO: do sth useful with these
remaining_meta_ = processor_.get_num_metadata_packets();
for (; remaining_meta_ > 0; --remaining_meta_)
{
yield processor_.get_channel().async_read(
processor_.get_buffer(),
std::move(*this)
);
}
// Compose response
this->complete(
cont,
err,
PreparedStatementType(processor_.get_channel(), processor_.get_response())
);
}
}
};
// Compose response
this->complete(
cont,
err,
PreparedStatementType(processor_.get_channel(), processor_.get_response())
);
}
}
};
Op(
std::move(initiator.completion_handler),
chan,
statement,
info
)(error_code(), false);
return initiator.result.get();
Op(
std::move(initiator.completion_handler),
chan,
statement,
info
)(error_code(), false);
return initiator.result.get();
}
#include <boost/asio/unyield.hpp>

View File

@ -16,44 +16,44 @@ namespace mysql {
namespace detail {
inline read_row_result process_read_message(
deserialize_row_fn deserializer,
capabilities current_capabilities,
const std::vector<field_metadata>& meta,
const bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
deserialize_row_fn deserializer,
capabilities current_capabilities,
const std::vector<field_metadata>& meta,
const bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
)
{
assert(deserializer);
assert(deserializer);
// Message type: row, error or eof?
std::uint8_t msg_type;
deserialization_context ctx (boost::asio::buffer(buffer), current_capabilities);
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err) return read_row_result::error;
if (msg_type == eof_packet_header)
{
// end of resultset
err = deserialize_message(output_ok_packet, ctx);
if (err) return read_row_result::error;
return read_row_result::eof;
}
else if (msg_type == error_packet_header)
{
// An error occurred during the generation of the rows
err = process_error_packet(ctx, info);
return read_row_result::error;
}
else
{
// An actual row
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
err = deserializer(ctx, meta, output_values);
if (err) return read_row_result::error;
return read_row_result::row;
}
// Message type: row, error or eof?
std::uint8_t msg_type;
deserialization_context ctx (boost::asio::buffer(buffer), current_capabilities);
std::tie(err, msg_type) = deserialize_message_type(ctx);
if (err) return read_row_result::error;
if (msg_type == eof_packet_header)
{
// end of resultset
err = deserialize_message(output_ok_packet, ctx);
if (err) return read_row_result::error;
return read_row_result::eof;
}
else if (msg_type == error_packet_header)
{
// An error occurred during the generation of the rows
err = process_error_packet(ctx, info);
return read_row_result::error;
}
else
{
// An actual row
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
err = deserializer(ctx, meta, output_values);
if (err) return read_row_result::error;
return read_row_result::row;
}
}
} // detail
@ -63,127 +63,127 @@ inline read_row_result process_read_message(
template <typename StreamType>
boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
)
{
// Read a packet
channel.read(buffer, err);
if (err) return read_row_result::error;
// Read a packet
channel.read(buffer, err);
if (err) return read_row_result::error;
return process_read_message(
deserializer,
channel.current_capabilities(),
meta,
buffer,
output_values,
output_ok_packet,
err,
info
);
return process_read_message(
deserializer,
channel.current_capabilities(),
meta,
buffer,
output_values,
output_ok_packet,
err,
info
);
}
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
boost::mysql::detail::read_row_signature
CompletionToken,
boost::mysql::detail::read_row_signature
)
boost::mysql::detail::async_read_row(
deserialize_row_fn deserializer,
channel<StreamType>& chan,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
CompletionToken&& token
deserialize_row_fn deserializer,
channel<StreamType>& chan,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
CompletionToken&& token
)
{
using HandlerSignature = read_row_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using HandlerSignature = read_row_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
struct Op: BaseType, boost::asio::coroutine
{
deserialize_row_fn deserializer_;
channel<StreamType>& channel_;
const std::vector<field_metadata>& meta_;
bytestring& buffer_;
std::vector<value>& output_values_;
ok_packet& output_ok_packet_;
struct Op: BaseType, boost::asio::coroutine
{
deserialize_row_fn deserializer_;
channel<StreamType>& channel_;
const std::vector<field_metadata>& meta_;
bytestring& buffer_;
std::vector<value>& output_values_;
ok_packet& output_ok_packet_;
Op(
HandlerType&& handler,
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
deserializer_(deserializer),
channel_(channel),
meta_(meta),
buffer_(buffer),
output_values_(output_values),
output_ok_packet_(output_ok_packet)
{
}
Op(
HandlerType&& handler,
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet
):
BaseType(std::move(handler), channel.next_layer().get_executor()),
deserializer_(deserializer),
channel_(channel),
meta_(meta),
buffer_(buffer),
output_values_(output_values),
output_ok_packet_(output_ok_packet)
{
}
void process_result(bool cont)
{
error_code err;
error_info info;
auto result = process_read_message(
deserializer_,
channel_.current_capabilities(),
meta_,
buffer_,
output_values_,
output_ok_packet_,
err,
info
);
this->complete(cont, err, info, result);
}
void process_result(bool cont)
{
error_code err;
error_info info;
auto result = process_read_message(
deserializer_,
channel_.current_capabilities(),
meta_,
buffer_,
output_values_,
output_ok_packet_,
err,
info
);
this->complete(cont, err, info, result);
}
void operator()(
error_code err,
bool cont=true
)
{
reenter(*this)
{
yield channel_.async_read(buffer_, std::move(*this));
if (err)
{
this->complete(cont, err, error_info(), read_row_result::error);
yield break;
}
process_result(cont);
}
}
};
void operator()(
error_code err,
bool cont=true
)
{
reenter(*this)
{
yield channel_.async_read(buffer_, std::move(*this));
if (err)
{
this->complete(cont, err, error_info(), read_row_result::error);
yield break;
}
process_result(cont);
}
}
};
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
Op(
std::move(initiator.completion_handler),
deserializer,
chan,
meta,
buffer,
output_values,
output_ok_packet
)(error_code(), false);
return initiator.result.get();
Op(
std::move(initiator.completion_handler),
deserializer,
chan,
meta,
buffer,
output_values,
output_ok_packet
)(error_code(), false);
return initiator.result.get();
}
#include <boost/asio/unyield.hpp>

View File

@ -17,11 +17,11 @@ namespace detail {
template <typename StreamType>
void prepare_statement(
::boost::mysql::detail::channel<StreamType>& channel,
std::string_view statement,
error_code& err,
error_info& info,
prepared_statement<StreamType>& output
::boost::mysql::detail::channel<StreamType>& channel,
std::string_view statement,
error_code& err,
error_info& info,
prepared_statement<StreamType>& output
);
template <typename StreamType>
@ -30,10 +30,10 @@ using prepare_statement_signature = void(error_code, prepared_statement<StreamTy
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature<StreamType>)
async_prepare_statement(
channel<StreamType>& channel,
std::string_view statement,
CompletionToken&& token,
error_info* info
channel<StreamType>& channel,
std::string_view statement,
CompletionToken&& token,
error_info* info
);
} // detail

View File

@ -19,21 +19,21 @@ namespace detail {
enum class read_row_result
{
error,
row,
eof
error,
row,
eof
};
template <typename StreamType>
read_row_result read_row(
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
error_code& err,
error_info& info
);
using read_row_signature = void(error_code, error_info, read_row_result);
@ -41,13 +41,13 @@ using read_row_signature = void(error_code, error_info, read_row_result);
template <typename StreamType, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, read_row_signature)
async_read_row(
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
CompletionToken&& token
deserialize_row_fn deserializer,
channel<StreamType>& channel,
const std::vector<field_metadata>& meta,
bytestring& buffer,
std::vector<value>& output_values,
ok_packet& output_ok_packet,
CompletionToken&& token
);

View File

@ -19,15 +19,15 @@ namespace mysql {
namespace detail {
inline errc deserialize_binary_value(
deserialization_context& ctx,
const field_metadata& meta,
value& output
deserialization_context& ctx,
const field_metadata& meta,
value& output
);
inline error_code deserialize_binary_row(
deserialization_context& ctx,
const std::vector<field_metadata>& meta,
std::vector<value>& output
deserialization_context& ctx,
const std::vector<field_metadata>& meta,
std::vector<value>& output
);
} // detail

View File

@ -46,17 +46,17 @@ constexpr std::uint32_t CLIENT_REMEMBER_OPTIONS = (1UL << 31); // Don't reset th
class capabilities
{
std::uint32_t value_;
std::uint32_t value_;
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; }
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_); }
constexpr capabilities operator&(capabilities rhs) const noexcept { return capabilities(value_ & rhs.value_); }
constexpr bool operator==(const capabilities& rhs) const noexcept { return value_ == rhs.value_; }
constexpr bool operator!=(const capabilities& rhs) const noexcept { return value_ != rhs.value_; }
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; }
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_); }
constexpr capabilities operator&(capabilities rhs) const noexcept { return capabilities(value_ & rhs.value_); }
constexpr bool operator==(const capabilities& rhs) const noexcept { return value_ == rhs.value_; }
constexpr bool operator!=(const capabilities& rhs) const noexcept { return value_ != rhs.value_; }
};
/*
@ -98,11 +98,11 @@ public:
*/
constexpr capabilities mandatory_capabilities {
CLIENT_PROTOCOL_41 |
CLIENT_PLUGIN_AUTH |
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
CLIENT_DEPRECATE_EOF |
CLIENT_SECURE_CONNECTION
CLIENT_PROTOCOL_41 |
CLIENT_PLUGIN_AUTH |
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
CLIENT_DEPRECATE_EOF |
CLIENT_SECURE_CONNECTION
};
constexpr capabilities optional_capabilities {0};

View File

@ -27,79 +27,79 @@ namespace detail {
template <typename AsyncStream>
class channel
{
// TODO: static asserts for AsyncStream concept
// TODO: actually we also require it to be SyncStream, name misleading
struct ssl_block
{
boost::asio::ssl::context ctx;
boost::asio::ssl::stream<AsyncStream&> stream;
// TODO: static asserts for AsyncStream concept
// TODO: actually we also require it to be SyncStream, name misleading
struct ssl_block
{
boost::asio::ssl::context ctx;
boost::asio::ssl::stream<AsyncStream&> stream;
ssl_block(AsyncStream& base_stream):
ctx(boost::asio::ssl::context::tls_client),
stream (base_stream, ctx) {}
};
ssl_block(AsyncStream& base_stream):
ctx(boost::asio::ssl::context::tls_client),
stream (base_stream, ctx) {}
};
AsyncStream& stream_;
std::optional<ssl_block> ssl_block_;
std::uint8_t sequence_number_ {0};
std::array<std::uint8_t, 4> header_buffer_ {}; // for async ops
bytestring shared_buff_; // for async ops
capabilities current_caps_;
AsyncStream& stream_;
std::optional<ssl_block> ssl_block_;
std::uint8_t sequence_number_ {0};
std::array<std::uint8_t, 4> header_buffer_ {}; // for async ops
bytestring shared_buff_; // for async ops
capabilities current_caps_;
bool process_sequence_number(std::uint8_t got);
std::uint8_t next_sequence_number() { return sequence_number_++; }
bool process_sequence_number(std::uint8_t got);
std::uint8_t next_sequence_number() { return sequence_number_++; }
error_code process_header_read(std::uint32_t& size_to_read); // reads from header_buffer_
void process_header_write(std::uint32_t size_to_write); // writes to header_buffer_
error_code process_header_read(std::uint32_t& size_to_read); // reads from header_buffer_
void process_header_write(std::uint32_t size_to_write); // writes to header_buffer_
void create_ssl_block() { ssl_block_.emplace(stream_); }
void create_ssl_block() { ssl_block_.emplace(stream_); }
template <typename BufferSeq>
std::size_t read_impl(BufferSeq&& buff, error_code& ec);
template <typename BufferSeq>
std::size_t read_impl(BufferSeq&& buff, error_code& ec);
template <typename BufferSeq>
std::size_t write_impl(BufferSeq&& buff, error_code& ec);
template <typename BufferSeq>
std::size_t write_impl(BufferSeq&& buff, error_code& ec);
template <typename BufferSeq, typename CompletionToken>
auto async_read_impl(BufferSeq&& buff, CompletionToken&& token);
template <typename BufferSeq, typename CompletionToken>
auto async_read_impl(BufferSeq&& buff, CompletionToken&& token);
template <typename BufferSeq, typename CompletionToken>
auto async_write_impl(BufferSeq&& buff, CompletionToken&& token);
template <typename BufferSeq, typename CompletionToken>
auto async_write_impl(BufferSeq&& buff, CompletionToken&& token);
public:
channel(AsyncStream& stream): stream_(stream) {}
channel(AsyncStream& stream): stream_(stream) {}
bool ssl_active() const noexcept { return ssl_block_.has_value(); }
bool ssl_active() const noexcept { return ssl_block_.has_value(); }
template <typename Allocator>
void read(basic_bytestring<Allocator>& buffer, error_code& code);
template <typename Allocator>
void read(basic_bytestring<Allocator>& buffer, error_code& code);
void write(boost::asio::const_buffer buffer, error_code& code);
void write(boost::asio::const_buffer buffer, error_code& code);
template <typename Allocator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
async_read(basic_bytestring<Allocator>& buffer, CompletionToken&& token);
template <typename Allocator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
async_read(basic_bytestring<Allocator>& buffer, CompletionToken&& token);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
async_write(boost::asio::const_buffer buffer, CompletionToken&& token);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
async_write(boost::asio::const_buffer buffer, CompletionToken&& token);
void ssl_handshake(error_code& ec);
void ssl_handshake(error_code& ec);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
async_ssl_handshake(CompletionToken&& token);
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
async_ssl_handshake(CompletionToken&& token);
void reset_sequence_number(std::uint8_t value = 0) { sequence_number_ = value; }
std::uint8_t sequence_number() const { return sequence_number_; }
void reset_sequence_number(std::uint8_t value = 0) { sequence_number_ = value; }
std::uint8_t sequence_number() const { return sequence_number_; }
using stream_type = AsyncStream;
stream_type& next_layer() { return stream_; }
using stream_type = AsyncStream;
stream_type& next_layer() { return stream_; }
capabilities current_capabilities() const noexcept { return current_caps_; }
void set_current_capabilities(capabilities value) noexcept { current_caps_ = value; }
capabilities current_capabilities() const noexcept { return current_caps_; }
void set_current_capabilities(capabilities value) noexcept { current_caps_ = value; }
const bytestring& shared_buffer() const noexcept { return shared_buff_; }
bytestring& shared_buffer() noexcept { return shared_buff_; }
const bytestring& shared_buffer() const noexcept { return shared_buff_; }
bytestring& shared_buffer() noexcept { return shared_buff_; }
};
template <typename ChannelType>

View File

@ -20,110 +20,110 @@ namespace detail {
// header
struct packet_header
{
int3 packet_size;
int1 sequence_number;
int3 packet_size;
int1 sequence_number;
};
template <>
struct get_struct_fields<packet_header>
{
static constexpr auto value = std::make_tuple(
&packet_header::packet_size,
&packet_header::sequence_number
);
static constexpr auto value = std::make_tuple(
&packet_header::packet_size,
&packet_header::sequence_number
);
};
// ok packet
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;
// TODO: CLIENT_SESSION_TRACK
string_lenenc info;
// 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;
// TODO: CLIENT_SESSION_TRACK
string_lenenc info;
};
template <>
struct get_struct_fields<ok_packet>
{
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
);
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 <>
struct serialization_traits<ok_packet, serialization_tag::struct_with_fields> :
noop_serialize<ok_packet>
noop_serialize<ok_packet>
{
static inline errc deserialize_(ok_packet& output, deserialization_context& ctx) noexcept;
static inline errc deserialize_(ok_packet& output, deserialization_context& ctx) noexcept;
};
// err packet
struct err_packet
{
// int<1> header 0xFF ERR packet header
int2 error_code;
string_fixed<1> sql_state_marker;
string_fixed<5> sql_state;
string_eof error_message;
// int<1> header 0xFF ERR packet header
int2 error_code;
string_fixed<1> sql_state_marker;
string_fixed<5> sql_state;
string_eof error_message;
};
template <>
struct get_struct_fields<err_packet>
{
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 constexpr auto value = std::make_tuple(
&err_packet::error_code,
&err_packet::sql_state_marker,
&err_packet::sql_state,
&err_packet::error_message
);
};
// col def
struct column_definition_packet
{
string_lenenc catalog; // always "def"
string_lenenc schema;
string_lenenc table; // virtual table
string_lenenc org_table; // physical table
string_lenenc name; // virtual column name
string_lenenc org_name; // physical column name
collation character_set;
int4 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
string_lenenc catalog; // always "def"
string_lenenc schema;
string_lenenc table; // virtual table
string_lenenc org_table; // physical table
string_lenenc name; // virtual column name
string_lenenc org_name; // physical column name
collation character_set;
int4 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
};
template <>
struct get_struct_fields<column_definition_packet>
{
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
);
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 <>
struct serialization_traits<column_definition_packet, serialization_tag::struct_with_fields> :
noop_serialize<column_definition_packet>
noop_serialize<column_definition_packet>
{
static inline errc deserialize_(column_definition_packet& output, deserialization_context& ctx) noexcept;
static inline errc deserialize_(column_definition_packet& output, deserialization_context& ctx) noexcept;
};
// aux

View File

@ -16,32 +16,32 @@ namespace detail {
enum class protocol_field_type : std::uint8_t
{
decimal = 0x00, // Apparently not sent
tiny = 0x01, // TINYINT
short_ = 0x02, // SMALLINT
long_ = 0x03, // INT
float_ = 0x04, // FLOAT
double_ = 0x05, // DOUBLE
null = 0x06, // Apparently not sent
timestamp = 0x07, // TIMESTAMP
longlong = 0x08, // BIGINT
int24 = 0x09, // MEDIUMINT
date = 0x0a, // DATE
time = 0x0b, // TIME
datetime = 0x0c, // DATETIME
year = 0x0d, // YEAR
varchar = 0x0f, // Apparently not sent
bit = 0x10, // BIT
newdecimal = 0xf6, // DECIMAL
enum_ = 0xf7, // Apparently not sent
set = 0xf8, // Apperently not sent
tiny_blob = 0xf9, // Apparently not sent
medium_blob = 0xfa,// Apparently not sent
long_blob = 0xfb, // Apparently not sent
blob = 0xfc, // Used for all TEXT and BLOB types
var_string = 0xfd, // Used for VARCHAR and VARBINARY
string = 0xfe, // Used for CHAR and BINARY, ENUM (enum flag set), SET (set flag set)
geometry = 0xff // GEOMETRY
decimal = 0x00, // Apparently not sent
tiny = 0x01, // TINYINT
short_ = 0x02, // SMALLINT
long_ = 0x03, // INT
float_ = 0x04, // FLOAT
double_ = 0x05, // DOUBLE
null = 0x06, // Apparently not sent
timestamp = 0x07, // TIMESTAMP
longlong = 0x08, // BIGINT
int24 = 0x09, // MEDIUMINT
date = 0x0a, // DATE
time = 0x0b, // TIME
datetime = 0x0c, // DATETIME
year = 0x0d, // YEAR
varchar = 0x0f, // Apparently not sent
bit = 0x10, // BIT
newdecimal = 0xf6, // DECIMAL
enum_ = 0xf7, // Apparently not sent
set = 0xf8, // Apperently not sent
tiny_blob = 0xf9, // Apparently not sent
medium_blob = 0xfa,// Apparently not sent
long_blob = 0xfb, // Apparently not sent
blob = 0xfc, // Used for all TEXT and BLOB types
var_string = 0xfd, // Used for VARCHAR and VARBINARY
string = 0xfe, // Used for CHAR and BINARY, ENUM (enum flag set), SET (set flag set)
geometry = 0xff // GEOMETRY
};

View File

@ -21,34 +21,34 @@ namespace detail {
class deserialization_context
{
const std::uint8_t* first_;
const std::uint8_t* last_;
capabilities capabilities_;
const std::uint8_t* first_;
const std::uint8_t* last_;
capabilities capabilities_;
public:
deserialization_context(const std::uint8_t* first, const std::uint8_t* last, capabilities caps) noexcept:
first_(first), last_(last), capabilities_(caps) { assert(last_ >= first_); };
deserialization_context(boost::asio::const_buffer buff, capabilities caps) noexcept:
deserialization_context(
static_cast<const std::uint8_t*>(buff.data()),
static_cast<const std::uint8_t*>(buff.data()) + buff.size(),
caps
) {};
const std::uint8_t* first() const noexcept { return first_; }
const std::uint8_t* last() const noexcept { return last_; }
void set_first(const std::uint8_t* new_first) noexcept { first_ = new_first; assert(last_ >= first_); }
void advance(std::size_t sz) noexcept { first_ += sz; assert(last_ >= first_); }
void rewind(std::size_t sz) noexcept { first_ -= sz; }
std::size_t size() const noexcept { return last_ - first_; }
bool empty() const noexcept { return last_ == first_; }
bool enough_size(std::size_t required_size) const noexcept { return size() >= required_size; }
capabilities get_capabilities() const noexcept { return capabilities_; }
errc copy(void* to, std::size_t sz) noexcept
{
if (!enough_size(sz)) return errc::incomplete_message;
memcpy(to, first_, sz);
advance(sz);
return errc::ok;
}
deserialization_context(const std::uint8_t* first, const std::uint8_t* last, capabilities caps) noexcept:
first_(first), last_(last), capabilities_(caps) { assert(last_ >= first_); };
deserialization_context(boost::asio::const_buffer buff, capabilities caps) noexcept:
deserialization_context(
static_cast<const std::uint8_t*>(buff.data()),
static_cast<const std::uint8_t*>(buff.data()) + buff.size(),
caps
) {};
const std::uint8_t* first() const noexcept { return first_; }
const std::uint8_t* last() const noexcept { return last_; }
void set_first(const std::uint8_t* new_first) noexcept { first_ = new_first; assert(last_ >= first_); }
void advance(std::size_t sz) noexcept { first_ += sz; assert(last_ >= first_); }
void rewind(std::size_t sz) noexcept { first_ -= sz; }
std::size_t size() const noexcept { return last_ - first_; }
bool empty() const noexcept { return last_ == first_; }
bool enough_size(std::size_t required_size) const noexcept { return size() >= required_size; }
capabilities get_capabilities() const noexcept { return capabilities_; }
errc copy(void* to, std::size_t sz) noexcept
{
if (!enough_size(sz)) return errc::incomplete_message;
memcpy(to, first_, sz);
advance(sz);
return errc::ok;
}
};
}

View File

@ -17,144 +17,144 @@ namespace detail {
// initial handshake
struct handshake_packet
{
// int<1> protocol version Always 10
string_null server_version;
int4 connection_id;
string_lenenc 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
string_null auth_plugin_name;
// int<1> protocol version Always 10
string_null server_version;
int4 connection_id;
string_lenenc 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
string_null auth_plugin_name;
std::array<char, 8 + 0xff> auth_plugin_data_buffer; // not an actual protocol field, the merge of two fields
std::array<char, 8 + 0xff> auth_plugin_data_buffer; // not an actual protocol field, the merge of two fields
};
template <>
struct get_struct_fields<handshake_packet>
{
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
);
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 <>
struct serialization_traits<handshake_packet, serialization_tag::struct_with_fields> :
noop_serialize<handshake_packet>
noop_serialize<handshake_packet>
{
static inline errc deserialize_(handshake_packet& output, deserialization_context& ctx) noexcept;
static inline errc deserialize_(handshake_packet& output, deserialization_context& ctx) noexcept;
};
// response
struct handshake_response_packet
{
int4 client_flag; // capabilities
int4 max_packet_size;
int1 character_set;
// string[23] filler filler to the size of the handhshake response packet. All 0s.
string_null username;
string_lenenc auth_response; // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
string_null database; // only to be serialized if CLIENT_CONNECT_WITH_DB
string_null client_plugin_name; // we require CLIENT_PLUGIN_AUTH
// TODO: CLIENT_CONNECT_ATTRS
int4 client_flag; // capabilities
int4 max_packet_size;
int1 character_set;
// string[23] filler filler to the size of the handhshake response packet. All 0s.
string_null username;
string_lenenc auth_response; // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
string_null database; // only to be serialized if CLIENT_CONNECT_WITH_DB
string_null client_plugin_name; // we require CLIENT_PLUGIN_AUTH
// TODO: CLIENT_CONNECT_ATTRS
};
template <>
struct get_struct_fields<handshake_response_packet>
{
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
);
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 <>
struct serialization_traits<handshake_response_packet, serialization_tag::struct_with_fields> :
noop_deserialize<handshake_response_packet>
noop_deserialize<handshake_response_packet>
{
static inline std::size_t get_size_(const handshake_response_packet& value, const serialization_context& ctx) noexcept;
static inline void serialize_(const handshake_response_packet& value, serialization_context& ctx) noexcept;
static inline std::size_t get_size_(const handshake_response_packet& value, const serialization_context& ctx) noexcept;
static inline void serialize_(const handshake_response_packet& value, serialization_context& ctx) noexcept;
};
// SSL request
struct ssl_request
{
int4 client_flag;
int4 max_packet_size;
int1 character_set;
string_fixed<23> filler {};
int4 client_flag;
int4 max_packet_size;
int1 character_set;
string_fixed<23> filler {};
};
template <>
struct get_struct_fields<ssl_request>
{
static constexpr auto value = std::make_tuple(
&ssl_request::client_flag,
&ssl_request::max_packet_size,
&ssl_request::character_set,
&ssl_request::filler
);
static constexpr auto value = std::make_tuple(
&ssl_request::client_flag,
&ssl_request::max_packet_size,
&ssl_request::character_set,
&ssl_request::filler
);
};
// auth switch request
struct auth_switch_request_packet
{
string_null plugin_name;
string_eof auth_plugin_data;
string_null plugin_name;
string_eof auth_plugin_data;
};
template <>
struct get_struct_fields<auth_switch_request_packet>
{
static constexpr auto value = std::make_tuple(
&auth_switch_request_packet::plugin_name,
&auth_switch_request_packet::auth_plugin_data
);
static constexpr auto value = std::make_tuple(
&auth_switch_request_packet::plugin_name,
&auth_switch_request_packet::auth_plugin_data
);
};
// response
struct auth_switch_response_packet
{
string_eof auth_plugin_data;
string_eof auth_plugin_data;
};
template <>
struct get_struct_fields<auth_switch_response_packet>
{
static constexpr auto value = std::make_tuple(
&auth_switch_response_packet::auth_plugin_data
);
static constexpr auto value = std::make_tuple(
&auth_switch_response_packet::auth_plugin_data
);
};
template <>
struct serialization_traits<auth_switch_request_packet, serialization_tag::struct_with_fields> :
noop_serialize<auth_switch_request_packet>
noop_serialize<auth_switch_request_packet>
{
static inline errc deserialize_(auth_switch_request_packet& output, deserialization_context& ctx) noexcept;
static inline errc deserialize_(auth_switch_request_packet& output, deserialization_context& ctx) noexcept;
};
// more data (like auth switch request, but for the same plugin)
struct auth_more_data_packet
{
string_eof auth_plugin_data;
string_eof auth_plugin_data;
};
template <>
struct get_struct_fields<auth_more_data_packet>
{
static constexpr auto value = std::make_tuple(
&auth_more_data_packet::auth_plugin_data
);
static constexpr auto value = std::make_tuple(
&auth_more_data_packet::auth_plugin_data
);
};

View File

@ -18,58 +18,58 @@ namespace mysql {
namespace detail {
using binary_protocol_value = std::variant<
int1,
int2,
int4,
int8,
int1_signed,
int2_signed,
int4_signed,
int8_signed,
string_lenenc,
float,
double,
date,
datetime,
time
int1,
int2,
int4,
int8,
int1_signed,
int2_signed,
int4_signed,
int8_signed,
string_lenenc,
float,
double,
date,
datetime,
time
>;
template <typename SignedType, typename UnsignedType>
binary_protocol_value get_int_deserializable_type(
const field_metadata& meta
const field_metadata& meta
)
{
return meta.is_unsigned() ? binary_protocol_value(UnsignedType()) :
binary_protocol_value(SignedType());
return meta.is_unsigned() ? binary_protocol_value(UnsignedType()) :
binary_protocol_value(SignedType());
}
inline binary_protocol_value get_deserializable_type(
const field_metadata& meta
const field_metadata& meta
)
{
switch (meta.protocol_type())
{
switch (meta.protocol_type())
{
case protocol_field_type::tiny:
return get_int_deserializable_type<int1_signed, int1>(meta);
return get_int_deserializable_type<int1_signed, int1>(meta);
case protocol_field_type::short_:
case protocol_field_type::year:
return get_int_deserializable_type<int2_signed, int2>(meta);
return get_int_deserializable_type<int2_signed, int2>(meta);
case protocol_field_type::int24:
case protocol_field_type::long_:
return get_int_deserializable_type<int4_signed, int4>(meta);
return get_int_deserializable_type<int4_signed, int4>(meta);
case protocol_field_type::longlong:
return get_int_deserializable_type<int8_signed, int8>(meta);
return get_int_deserializable_type<int8_signed, int8>(meta);
case protocol_field_type::float_:
return float();
return float();
case protocol_field_type::double_:
return double();
return double();
case protocol_field_type::timestamp:
case protocol_field_type::datetime:
return datetime();
return datetime();
case protocol_field_type::date:
return date();
return date();
case protocol_field_type::time:
return time();
return time();
// True string types
case protocol_field_type::varchar:
case protocol_field_type::var_string:
@ -86,8 +86,8 @@ inline binary_protocol_value get_deserializable_type(
case protocol_field_type::newdecimal:
case protocol_field_type::geometry:
default:
return string_lenenc();
}
return string_lenenc();
}
}
} // detail
@ -95,76 +95,76 @@ inline binary_protocol_value get_deserializable_type(
} // boost
inline boost::mysql::errc boost::mysql::detail::deserialize_binary_value(
deserialization_context& ctx,
const field_metadata& meta,
value& output
deserialization_context& ctx,
const field_metadata& meta,
value& output
)
{
auto protocol_value = get_deserializable_type(meta);
return std::visit([&output, &ctx](auto typed_protocol_value) {
using type = decltype(typed_protocol_value);
auto err = deserialize(typed_protocol_value, ctx);
if (err == errc::ok)
{
if constexpr (std::is_constructible_v<value, type>) // not a value holder
{
output = typed_protocol_value;
}
else if constexpr (is_one_of_v<type, int1, int2>)
{
// regular promotion would make this int32_t. Force it be uint32_t
// TODO: check here
output = std::uint32_t(typed_protocol_value.value);
}
else
{
output = typed_protocol_value.value;
}
}
return err;
}, protocol_value);
auto protocol_value = get_deserializable_type(meta);
return std::visit([&output, &ctx](auto typed_protocol_value) {
using type = decltype(typed_protocol_value);
auto err = deserialize(typed_protocol_value, ctx);
if (err == errc::ok)
{
if constexpr (std::is_constructible_v<value, type>) // not a value holder
{
output = typed_protocol_value;
}
else if constexpr (is_one_of_v<type, int1, int2>)
{
// regular promotion would make this int32_t. Force it be uint32_t
// TODO: check here
output = std::uint32_t(typed_protocol_value.value);
}
else
{
output = typed_protocol_value.value;
}
}
return err;
}, protocol_value);
}
inline boost::mysql::error_code boost::mysql::detail::deserialize_binary_row(
deserialization_context& ctx,
const std::vector<field_metadata>& meta,
std::vector<value>& output
deserialization_context& ctx,
const std::vector<field_metadata>& meta,
std::vector<value>& output
)
{
// Skip packet header (it is not part of the message in the binary
// protocol but it is in the text protocol, so we include it for homogeneity)
// The caller will have checked we have this byte already for us
assert(ctx.enough_size(1));
ctx.advance(1);
// Skip packet header (it is not part of the message in the binary
// protocol but it is in the text protocol, so we include it for homogeneity)
// The caller will have checked we have this byte already for us
assert(ctx.enough_size(1));
ctx.advance(1);
// Number of fields
auto num_fields = meta.size();
output.resize(num_fields);
// Number of fields
auto num_fields = meta.size();
output.resize(num_fields);
// Null bitmap
null_bitmap_traits null_bitmap (binary_row_null_bitmap_offset, num_fields);
const std::uint8_t* null_bitmap_begin = ctx.first();
if (!ctx.enough_size(null_bitmap.byte_count())) return make_error_code(errc::incomplete_message);
ctx.advance(null_bitmap.byte_count());
// Null bitmap
null_bitmap_traits null_bitmap (binary_row_null_bitmap_offset, num_fields);
const std::uint8_t* null_bitmap_begin = ctx.first();
if (!ctx.enough_size(null_bitmap.byte_count())) return make_error_code(errc::incomplete_message);
ctx.advance(null_bitmap.byte_count());
// Actual values
for (std::vector<value>::size_type i = 0; i < output.size(); ++i)
{
if (null_bitmap.is_null(null_bitmap_begin, i))
{
output[i] = nullptr;
}
else
{
auto err = deserialize_binary_value(ctx, meta[i], output[i]);
if (err != errc::ok) return make_error_code(err);
}
}
// Actual values
for (std::vector<value>::size_type i = 0; i < output.size(); ++i)
{
if (null_bitmap.is_null(null_bitmap_begin, i))
{
output[i] = nullptr;
}
else
{
auto err = deserialize_binary_value(ctx, meta[i], output[i]);
if (err != errc::ok) return make_error_code(err);
}
}
// Check for remaining bytes
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
// Check for remaining bytes
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
return error_code();
}

View File

@ -20,14 +20,14 @@ namespace mysql {
namespace detail {
inline std::uint32_t compute_size_to_write(
std::size_t buffer_size,
std::size_t transferred_size
std::size_t buffer_size,
std::size_t transferred_size
)
{
return static_cast<std::uint32_t>(std::min(
MAX_PACKET_SIZE,
buffer_size - transferred_size
));
return static_cast<std::uint32_t>(std::min(
MAX_PACKET_SIZE,
buffer_size - transferred_size
));
}
} // detail
@ -36,194 +36,194 @@ inline std::uint32_t compute_size_to_write(
template <typename AsyncStream>
bool boost::mysql::detail::channel<AsyncStream>::process_sequence_number(
std::uint8_t got
std::uint8_t got
)
{
if (got == sequence_number_)
{
++sequence_number_;
return true;
}
else
{
return false;
}
if (got == sequence_number_)
{
++sequence_number_;
return true;
}
else
{
return false;
}
}
template <typename AsyncStream>
boost::mysql::error_code boost::mysql::detail::channel<AsyncStream>::process_header_read(
std::uint32_t& size_to_read
std::uint32_t& size_to_read
)
{
packet_header header;
deserialization_context ctx (boost::asio::buffer(header_buffer_), capabilities(0)); // unaffected by capabilities
[[maybe_unused]] errc err = deserialize(header, ctx);
assert(err == errc::ok); // this should always succeed
if (!process_sequence_number(header.sequence_number.value))
{
return make_error_code(errc::sequence_number_mismatch);
}
size_to_read = header.packet_size.value;
return error_code();
packet_header header;
deserialization_context ctx (boost::asio::buffer(header_buffer_), capabilities(0)); // unaffected by capabilities
[[maybe_unused]] errc err = deserialize(header, ctx);
assert(err == errc::ok); // this should always succeed
if (!process_sequence_number(header.sequence_number.value))
{
return make_error_code(errc::sequence_number_mismatch);
}
size_to_read = header.packet_size.value;
return error_code();
}
template <typename AsyncStream>
void boost::mysql::detail::channel<AsyncStream>::process_header_write(
std::uint32_t size_to_write
std::uint32_t size_to_write
)
{
packet_header header;
header.packet_size.value = size_to_write;
header.sequence_number.value = next_sequence_number();
serialization_context ctx (capabilities(0), header_buffer_.data()); // capabilities not relevant here
serialize(header, ctx);
packet_header header;
header.packet_size.value = size_to_write;
header.sequence_number.value = next_sequence_number();
serialization_context ctx (capabilities(0), header_buffer_.data()); // capabilities not relevant here
serialize(header, ctx);
}
template <typename AsyncStream>
template <typename BufferSeq>
std::size_t boost::mysql::detail::channel<AsyncStream>::read_impl(
BufferSeq&& buff,
error_code& ec
BufferSeq&& buff,
error_code& ec
)
{
if (ssl_active())
{
return boost::asio::read(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
}
else
{
return boost::asio::read(stream_, std::forward<BufferSeq>(buff), ec);
}
if (ssl_active())
{
return boost::asio::read(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
}
else
{
return boost::asio::read(stream_, std::forward<BufferSeq>(buff), ec);
}
}
template <typename AsyncStream>
template <typename BufferSeq>
std::size_t boost::mysql::detail::channel<AsyncStream>::write_impl(
BufferSeq&& buff,
error_code& ec
BufferSeq&& buff,
error_code& ec
)
{
if (ssl_active())
{
return boost::asio::write(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
}
else
{
return boost::asio::write(stream_, std::forward<BufferSeq>(buff), ec);
}
if (ssl_active())
{
return boost::asio::write(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
}
else
{
return boost::asio::write(stream_, std::forward<BufferSeq>(buff), ec);
}
}
template <typename AsyncStream>
template <typename BufferSeq, typename CompletionToken>
auto boost::mysql::detail::channel<AsyncStream>::async_read_impl(
BufferSeq&& buff,
CompletionToken&& token
BufferSeq&& buff,
CompletionToken&& token
)
{
if (ssl_active())
{
return boost::asio::async_read(
ssl_block_->stream,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
else
{
return boost::asio::async_read(
stream_,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
if (ssl_active())
{
return boost::asio::async_read(
ssl_block_->stream,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
else
{
return boost::asio::async_read(
stream_,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
}
template <typename AsyncStream>
template <typename BufferSeq, typename CompletionToken>
auto boost::mysql::detail::channel<AsyncStream>::async_write_impl(
BufferSeq&& buff,
CompletionToken&& token
BufferSeq&& buff,
CompletionToken&& token
)
{
if (ssl_active())
{
return boost::asio::async_write(
ssl_block_->stream,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
else
{
return boost::asio::async_write(
stream_,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
if (ssl_active())
{
return boost::asio::async_write(
ssl_block_->stream,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
else
{
return boost::asio::async_write(
stream_,
std::forward<BufferSeq>(buff),
std::forward<CompletionToken>(token)
);
}
}
template <typename AsyncStream>
template <typename Allocator>
void boost::mysql::detail::channel<AsyncStream>::read(
basic_bytestring<Allocator>& buffer,
error_code& code
basic_bytestring<Allocator>& buffer,
error_code& code
)
{
std::size_t transferred_size = 0;
std::uint32_t size_to_read = 0;
buffer.clear();
code.clear();
auto header_buff = boost::asio::buffer(header_buffer_);
std::size_t transferred_size = 0;
std::uint32_t size_to_read = 0;
buffer.clear();
code.clear();
auto header_buff = boost::asio::buffer(header_buffer_);
do
{
// Read header
read_impl(header_buff, code);
valgrind_make_mem_defined(header_buff);
if (code) return;
do
{
// Read header
read_impl(header_buff, code);
valgrind_make_mem_defined(header_buff);
if (code) return;
// See how many bytes we should be reading
code = process_header_read(size_to_read);
if (code) return;
// See how many bytes we should be reading
code = process_header_read(size_to_read);
if (code) return;
// Read the rest of the message
buffer.resize(buffer.size() + size_to_read);
auto read_buffer = boost::asio::buffer(buffer.data() + transferred_size, size_to_read);
read_impl(read_buffer, code);
valgrind_make_mem_defined(read_buffer);
if (code) return;
transferred_size += size_to_read;
// Read the rest of the message
buffer.resize(buffer.size() + size_to_read);
auto read_buffer = boost::asio::buffer(buffer.data() + transferred_size, size_to_read);
read_impl(read_buffer, code);
valgrind_make_mem_defined(read_buffer);
if (code) return;
transferred_size += size_to_read;
} while (size_to_read == MAX_PACKET_SIZE);
} while (size_to_read == MAX_PACKET_SIZE);
}
template <typename AsyncStream>
void boost::mysql::detail::channel<AsyncStream>::write(
boost::asio::const_buffer buffer,
error_code& code
boost::asio::const_buffer buffer,
error_code& code
)
{
std::size_t transferred_size = 0;
auto bufsize = buffer.size();
auto first = static_cast<const std::uint8_t*>(buffer.data());
std::size_t transferred_size = 0;
auto bufsize = buffer.size();
auto first = static_cast<const std::uint8_t*>(buffer.data());
// If the packet is empty, we should still write the header, saying
// we are sending an empty packet.
do
{
auto size_to_write = compute_size_to_write(bufsize, transferred_size);
process_header_write(size_to_write);
write_impl(
std::array<boost::asio::const_buffer, 2> {
boost::asio::buffer(header_buffer_),
boost::asio::buffer(first + transferred_size, size_to_write)
},
code
);
if (code) return;
transferred_size += size_to_write;
} while (transferred_size < bufsize);
// If the packet is empty, we should still write the header, saying
// we are sending an empty packet.
do
{
auto size_to_write = compute_size_to_write(bufsize, transferred_size);
process_header_write(size_to_write);
write_impl(
std::array<boost::asio::const_buffer, 2> {
boost::asio::buffer(header_buffer_),
boost::asio::buffer(first + transferred_size, size_to_write)
},
code
);
if (code) return;
transferred_size += size_to_write;
} while (transferred_size < bufsize);
}
@ -231,185 +231,185 @@ template <typename AsyncStream>
template <typename Allocator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
boost::mysql::detail::channel<AsyncStream>::async_read(
basic_bytestring<Allocator>& buffer,
CompletionToken&& token
basic_bytestring<Allocator>& buffer,
CompletionToken&& token
)
{
using HandlerSignature = void(mysql::error_code);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename AsyncStream::executor_type>;
using HandlerSignature = void(mysql::error_code);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename AsyncStream::executor_type>;
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
struct Op: BaseType, boost::asio::coroutine
{
channel<AsyncStream>& stream_;
basic_bytestring<Allocator>& buffer_;
std::size_t total_transferred_size_ = 0;
struct Op: BaseType, boost::asio::coroutine
{
channel<AsyncStream>& stream_;
basic_bytestring<Allocator>& buffer_;
std::size_t total_transferred_size_ = 0;
Op(
HandlerType&& handler,
channel<AsyncStream>& stream,
basic_bytestring<Allocator>& buffer
):
BaseType(std::move(handler), stream.next_layer().get_executor()),
stream_(stream),
buffer_(buffer)
{
}
Op(
HandlerType&& handler,
channel<AsyncStream>& stream,
basic_bytestring<Allocator>& buffer
):
BaseType(std::move(handler), stream.next_layer().get_executor()),
stream_(stream),
buffer_(buffer)
{
}
void operator()(
error_code code,
std::size_t bytes_transferred,
bool cont=true
)
{
// Error checking
if (code)
{
this->complete(cont, code);
return;
}
void operator()(
error_code code,
std::size_t bytes_transferred,
bool cont=true
)
{
// Error checking
if (code)
{
this->complete(cont, code);
return;
}
// Non-error path
std::uint32_t size_to_read = 0;
reenter(*this)
{
do
{
yield stream_.async_read_impl(
boost::asio::buffer(stream_.header_buffer_),
std::move(*this)
);
valgrind_make_mem_defined(boost::asio::buffer(stream_.header_buffer_));
// Non-error path
std::uint32_t size_to_read = 0;
reenter(*this)
{
do
{
yield stream_.async_read_impl(
boost::asio::buffer(stream_.header_buffer_),
std::move(*this)
);
valgrind_make_mem_defined(boost::asio::buffer(stream_.header_buffer_));
code = stream_.process_header_read(size_to_read);
if (code)
{
this->complete(cont, code);
yield break;
}
code = stream_.process_header_read(size_to_read);
if (code)
{
this->complete(cont, code);
yield break;
}
buffer_.resize(buffer_.size() + size_to_read);
buffer_.resize(buffer_.size() + size_to_read);
yield stream_.async_read_impl(
boost::asio::buffer(buffer_.data() + total_transferred_size_, size_to_read),
std::move(*this)
);
valgrind_make_mem_defined(
boost::asio::buffer(buffer_.data() + total_transferred_size_, bytes_transferred)
);
yield stream_.async_read_impl(
boost::asio::buffer(buffer_.data() + total_transferred_size_, size_to_read),
std::move(*this)
);
valgrind_make_mem_defined(
boost::asio::buffer(buffer_.data() + total_transferred_size_, bytes_transferred)
);
total_transferred_size_ += bytes_transferred;
} while (bytes_transferred == MAX_PACKET_SIZE);
total_transferred_size_ += bytes_transferred;
} while (bytes_transferred == MAX_PACKET_SIZE);
this->complete(cont, error_code());
}
}
};
this->complete(cont, error_code());
}
}
};
buffer.clear();
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
return initiator.result.get();
buffer.clear();
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
return initiator.result.get();
}
template <typename AsyncStream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
boost::mysql::detail::channel<AsyncStream>::async_write(
boost::asio::const_buffer buffer,
CompletionToken&& token
boost::asio::const_buffer buffer,
CompletionToken&& token
)
{
using HandlerSignature = void(mysql::error_code);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename AsyncStream::executor_type>;
using HandlerSignature = void(mysql::error_code);
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename AsyncStream::executor_type>;
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
struct Op : BaseType, boost::asio::coroutine
{
channel<AsyncStream>& stream_;
boost::asio::const_buffer buffer_;
std::size_t total_transferred_size_ = 0;
struct Op : BaseType, boost::asio::coroutine
{
channel<AsyncStream>& stream_;
boost::asio::const_buffer buffer_;
std::size_t total_transferred_size_ = 0;
Op(
HandlerType&& handler,
channel<AsyncStream>& stream,
boost::asio::const_buffer buffer
):
BaseType(std::move(handler), stream.next_layer().get_executor()),
stream_(stream),
buffer_(buffer)
{
}
Op(
HandlerType&& handler,
channel<AsyncStream>& stream,
boost::asio::const_buffer buffer
):
BaseType(std::move(handler), stream.next_layer().get_executor()),
stream_(stream),
buffer_(buffer)
{
}
void operator()(
error_code code,
std::size_t bytes_transferred,
bool cont=true
)
{
// Error handling
if (code)
{
this->complete(cont, code);
return;
}
void operator()(
error_code code,
std::size_t bytes_transferred,
bool cont=true
)
{
// Error handling
if (code)
{
this->complete(cont, code);
return;
}
// Non-error path
std::uint32_t size_to_write;
reenter(*this)
{
// Force write the packet header on an empty packet, at least.
do
{
size_to_write = compute_size_to_write(buffer_.size(), total_transferred_size_);
stream_.process_header_write(size_to_write);
// Non-error path
std::uint32_t size_to_write;
reenter(*this)
{
// Force write the packet header on an empty packet, at least.
do
{
size_to_write = compute_size_to_write(buffer_.size(), total_transferred_size_);
stream_.process_header_write(size_to_write);
yield stream_.async_write_impl(
std::array<boost::asio::const_buffer, 2> {
boost::asio::buffer(stream_.header_buffer_),
boost::asio::buffer(buffer_ + total_transferred_size_, size_to_write)
},
std::move(*this)
);
yield stream_.async_write_impl(
std::array<boost::asio::const_buffer, 2> {
boost::asio::buffer(stream_.header_buffer_),
boost::asio::buffer(buffer_ + total_transferred_size_, size_to_write)
},
std::move(*this)
);
total_transferred_size_ += (bytes_transferred - 4); // header size
total_transferred_size_ += (bytes_transferred - 4); // header size
} while (total_transferred_size_ < buffer_.size());
} while (total_transferred_size_ < buffer_.size());
this->complete(cont, error_code());
}
}
this->complete(cont, error_code());
}
}
};
};
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
return initiator.result.get();
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
return initiator.result.get();
}
template <typename Stream>
void boost::mysql::detail::channel<Stream>::ssl_handshake(
error_code& ec
error_code& ec
)
{
create_ssl_block();
ssl_block_->stream.handshake(boost::asio::ssl::stream_base::client, ec);
create_ssl_block();
ssl_block_->stream.handshake(boost::asio::ssl::stream_base::client, ec);
}
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
boost::mysql::detail::channel<Stream>::async_ssl_handshake(
CompletionToken&& token
CompletionToken&& token
)
{
create_ssl_block();
return ssl_block_->stream.async_handshake(
boost::asio::ssl::stream_base::client,
std::forward<CompletionToken>(token)
);
create_ssl_block();
return ssl_block_->stream.async_handshake(
boost::asio::ssl::stream_base::client,
std::forward<CompletionToken>(token)
);
}
#include <boost/asio/unyield.hpp>

View File

@ -10,68 +10,68 @@
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::ok_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::ok_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::deserialize_(
ok_packet& output,
deserialization_context& ctx
ok_packet& output,
deserialization_context& ctx
) noexcept
{
{
auto err = deserialize_fields(
ctx,
output.affected_rows,
output.last_insert_id,
output.status_flags,
output.warnings
);
if (err == errc::ok && ctx.enough_size(1)) // message is optional, may be omitted
{
err = deserialize(output.info, ctx);
}
return err;
}
{
auto err = deserialize_fields(
ctx,
output.affected_rows,
output.last_insert_id,
output.status_flags,
output.warnings
);
if (err == errc::ok && ctx.enough_size(1)) // message is optional, may be omitted
{
err = deserialize(output.info, ctx);
}
return err;
}
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::column_definition_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::column_definition_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::deserialize_(
column_definition_packet& output,
deserialization_context& ctx
column_definition_packet& output,
deserialization_context& ctx
) noexcept
{
int_lenenc length_of_fixed_fields;
int2 final_padding;
return deserialize_fields(
ctx,
output.catalog,
output.schema,
output.table,
output.org_table,
output.name,
output.org_name,
length_of_fixed_fields,
output.character_set,
output.column_length,
output.type,
output.flags,
output.decimals,
final_padding
);
int_lenenc length_of_fixed_fields;
int2 final_padding;
return deserialize_fields(
ctx,
output.catalog,
output.schema,
output.table,
output.org_table,
output.name,
output.org_name,
length_of_fixed_fields,
output.character_set,
output.column_length,
output.type,
output.flags,
output.decimals,
final_padding
);
}
inline boost::mysql::error_code boost::mysql::detail::process_error_packet(
deserialization_context& ctx,
error_info& info
deserialization_context& ctx,
error_info& info
)
{
err_packet error_packet;
auto code = deserialize_message(error_packet, ctx);
if (code) return code;
info.set_message(std::string(error_packet.error_message.value));
return make_error_code(static_cast<errc>(error_packet.error_code.value));
err_packet error_packet;
auto code = deserialize_message(error_packet, ctx);
if (code) return code;
info.set_message(std::string(error_packet.error_message.value));
return make_error_code(static_cast<errc>(error_packet.error_code.value));
}

View File

@ -11,137 +11,137 @@
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::handshake_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::handshake_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::deserialize_(
handshake_packet& output,
deserialization_context& ctx
handshake_packet& output,
deserialization_context& ctx
) noexcept
{
constexpr std::uint8_t auth1_length = 8;
string_fixed<auth1_length> 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; // TODO: double check this
string_fixed<10> reserved;
constexpr std::uint8_t auth1_length = 8;
string_fixed<auth1_length> 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; // TODO: double check this
string_fixed<10> reserved;
auto err = deserialize_fields(
ctx,
output.server_version,
output.connection_id,
auth_plugin_data_part_1,
filler, // TODO: docs state fields below the filler are optional
capability_flags_low,
output.character_set,
output.status_flags,
capability_flags_high
);
if (err != errc::ok) return err;
auto err = deserialize_fields(
ctx,
output.server_version,
output.connection_id,
auth_plugin_data_part_1,
filler, // TODO: docs state fields below the filler are optional
capability_flags_low,
output.character_set,
output.status_flags,
capability_flags_high
);
if (err != errc::ok) return err;
// Compose capabilities
auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&output.capability_falgs.value);
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);
// Compose capabilities
auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&output.capability_falgs.value);
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);
// Check minimum server capabilities to deserialize this frame
capabilities cap (output.capability_falgs.value);
if (!cap.has(CLIENT_PLUGIN_AUTH)) return errc::server_unsupported;
// Check minimum server capabilities to deserialize this frame
capabilities cap (output.capability_falgs.value);
if (!cap.has(CLIENT_PLUGIN_AUTH)) return errc::server_unsupported;
// Deserialize the rest of the frame
err = deserialize_fields(
ctx,
auth_plugin_data_len,
reserved
);
if (err != errc::ok) return err;
auto auth2_length = static_cast<std::uint8_t>(
std::max(13, auth_plugin_data_len.value - auth1_length));
err = ctx.copy(output.auth_plugin_data_buffer.data() + auth1_length, auth2_length);
if (err != errc::ok) return err;
err = deserialize(output.auth_plugin_name, ctx);
if (err != errc::ok) return err;
// Deserialize the rest of the frame
err = deserialize_fields(
ctx,
auth_plugin_data_len,
reserved
);
if (err != errc::ok) return err;
auto auth2_length = static_cast<std::uint8_t>(
std::max(13, auth_plugin_data_len.value - auth1_length));
err = ctx.copy(output.auth_plugin_data_buffer.data() + auth1_length, auth2_length);
if (err != errc::ok) return err;
err = deserialize(output.auth_plugin_name, ctx);
if (err != errc::ok) return err;
// Compose auth_plugin_data
memcpy(
output.auth_plugin_data_buffer.data(),
auth_plugin_data_part_1.data(),
auth1_length
);
output.auth_plugin_data.value = std::string_view(
output.auth_plugin_data_buffer.data(),
auth1_length + auth2_length - 1 // discard trailing null byte
);
// Compose auth_plugin_data
memcpy(
output.auth_plugin_data_buffer.data(),
auth_plugin_data_part_1.data(),
auth1_length
);
output.auth_plugin_data.value = std::string_view(
output.auth_plugin_data_buffer.data(),
auth1_length + auth2_length - 1 // discard trailing null byte
);
return errc::ok;
return errc::ok;
}
std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::detail::handshake_response_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::handshake_response_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::get_size_(
const handshake_response_packet& value,
const serialization_context& ctx
const handshake_response_packet& value,
const serialization_context& ctx
) noexcept
{
std::size_t res =
get_size(value.client_flag, ctx) +
get_size(value.max_packet_size, ctx) +
get_size(value.character_set, ctx) +
23 + // filler
get_size(value.username, ctx) +
get_size(value.auth_response, ctx);
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
{
res += get_size(value.database, ctx);
}
res += get_size(value.client_plugin_name, ctx);
return res;
std::size_t res =
get_size(value.client_flag, ctx) +
get_size(value.max_packet_size, ctx) +
get_size(value.character_set, ctx) +
23 + // filler
get_size(value.username, ctx) +
get_size(value.auth_response, ctx);
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
{
res += get_size(value.database, ctx);
}
res += get_size(value.client_plugin_name, ctx);
return res;
}
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::detail::handshake_response_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::handshake_response_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::serialize_(
const handshake_response_packet& value,
serialization_context& ctx
const handshake_response_packet& value,
serialization_context& ctx
) noexcept
{
serialize(value.client_flag, ctx);
serialize(value.max_packet_size, ctx);
serialize(value.character_set, ctx);
std::uint8_t buffer [23] {};
ctx.write(buffer, sizeof(buffer));
serialize(value.username, ctx);
serialize(value.auth_response, ctx);
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
{
serialize(value.database, ctx);
}
serialize(value.client_plugin_name, ctx);
serialize(value.client_flag, ctx);
serialize(value.max_packet_size, ctx);
serialize(value.character_set, ctx);
std::uint8_t buffer [23] {};
ctx.write(buffer, sizeof(buffer));
serialize(value.username, ctx);
serialize(value.auth_response, ctx);
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
{
serialize(value.database, ctx);
}
serialize(value.client_plugin_name, ctx);
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::auth_switch_request_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::auth_switch_request_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::deserialize_(
auth_switch_request_packet& output,
deserialization_context& ctx
auth_switch_request_packet& output,
deserialization_context& ctx
) noexcept
{
auto err = deserialize_fields(ctx, output.plugin_name, output.auth_plugin_data);
auto& auth_data = output.auth_plugin_data.value;
// Discard an additional NULL at the end of auth data
if (!auth_data.empty())
{
assert(auth_data.back() == 0);
auth_data = auth_data.substr(0, auth_data.size() - 1);
}
return err;
auto err = deserialize_fields(ctx, output.plugin_name, output.auth_plugin_data);
auto& auth_data = output.auth_plugin_data.value;
// Discard an additional NULL at the end of auth data
if (!auth_data.empty())
{
assert(auth_data.back() == 0);
auth_data = auth_data.substr(0, auth_data.size() - 1);
}
return err;
}

View File

@ -16,38 +16,38 @@ namespace detail {
// Maps from an actual value to a protocol_field_type. Only value's type is used
inline protocol_field_type get_protocol_field_type(
const value& input
const value& input
) noexcept
{
struct visitor
{
constexpr auto operator()(std::int32_t) const noexcept { return protocol_field_type::long_; }
constexpr auto operator()(std::uint32_t) const noexcept { return protocol_field_type::long_; }
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; }
};
return std::visit(visitor(), input);
struct visitor
{
constexpr auto operator()(std::int32_t) const noexcept { return protocol_field_type::long_; }
constexpr auto operator()(std::uint32_t) const noexcept { return protocol_field_type::long_; }
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; }
};
return std::visit(visitor(), input);
}
// Whether to include the unsigned flag in the statement execute message
// for a given value or not. Only value's type is used
inline bool is_unsigned(
const value& input
const value& input
) noexcept
{
// By default, return false; just for integer types explicitly unsigned return true
return std::visit([](auto v) {
using type = decltype(v);
return std::is_same_v<type, std::uint32_t> ||
std::is_same_v<type, std::uint64_t>;
}, input);
// By default, return false; just for integer types explicitly unsigned return true
return std::visit([](auto v) {
using type = decltype(v);
return std::is_same_v<type, std::uint32_t> ||
std::is_same_v<type, std::uint64_t>;
}, input);
}
// Performs a mapping from T to a type that can be serialized
@ -60,12 +60,12 @@ using get_serializable_type_t = typename get_serializable_type<T>::type;
// Indicate no serialization is required
struct dummy_serializable
{
explicit dummy_serializable(std::nullptr_t) {}
explicit dummy_serializable(std::nullptr_t) {}
};
template <>
struct serialization_traits<dummy_serializable, serialization_tag::none> :
noop_serialize<dummy_serializable>
noop_serialize<dummy_serializable>
{
};
@ -79,27 +79,27 @@ template <> struct get_serializable_type<std::nullptr_t> { using type = dummy_se
template <typename T>
inline get_serializable_type_t<T> to_serializable_type(T input) noexcept
{
return get_serializable_type_t<T>(input);
return get_serializable_type_t<T>(input);
}
inline std::size_t get_binary_value_size(
const value& input,
const serialization_context& ctx
const value& input,
const serialization_context& ctx
) noexcept
{
return std::visit([&ctx](const auto& v) {
return get_size(to_serializable_type(v), ctx);
}, input);
return std::visit([&ctx](const auto& v) {
return get_size(to_serializable_type(v), ctx);
}, input);
}
inline void serialize_binary_value(
const value& input,
serialization_context& ctx
const value& input,
serialization_context& ctx
) noexcept
{
std::visit([&ctx](const auto& v) {
serialize(to_serializable_type(v), ctx);
}, input);
std::visit([&ctx](const auto& v) {
serialize(to_serializable_type(v), ctx);
}, input);
}
} // detail
@ -108,99 +108,99 @@ inline void serialize_binary_value(
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::com_stmt_prepare_ok_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::com_stmt_prepare_ok_packet,
boost::mysql::detail::serialization_tag::struct_with_fields
>::deserialize_(
com_stmt_prepare_ok_packet& output,
deserialization_context& ctx
com_stmt_prepare_ok_packet& output,
deserialization_context& ctx
) noexcept
{
int1 reserved;
return deserialize_fields(
ctx,
output.statement_id,
output.num_columns,
output.num_params,
reserved,
output.warning_count
);
int1 reserved;
return deserialize_fields(
ctx,
output.statement_id,
output.num_columns,
output.num_params,
reserved,
output.warning_count
);
}
template <typename ForwardIterator>
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
boost::mysql::detail::serialization_tag::struct_with_fields
>::get_size_(
const com_stmt_execute_packet<ForwardIterator>& value,
const serialization_context& ctx
const com_stmt_execute_packet<ForwardIterator>& value,
const serialization_context& ctx
) noexcept
{
std::size_t res = 1 + // command ID
get_size(value.statement_id, ctx) +
get_size(value.flags, ctx) +
get_size(value.iteration_count, ctx);
auto num_params = std::distance(value.params_begin, value.params_end);
assert(num_params >= 0 && num_params <= 255);
res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
res += get_size(value.new_params_bind_flag, ctx);
res += get_size(com_stmt_execute_param_meta_packet{}, ctx) * num_params;
for (auto it = value.params_begin; it != value.params_end; ++it)
{
res += get_binary_value_size(*it, ctx);
}
return res;
std::size_t res = 1 + // command ID
get_size(value.statement_id, ctx) +
get_size(value.flags, ctx) +
get_size(value.iteration_count, ctx);
auto num_params = std::distance(value.params_begin, value.params_end);
assert(num_params >= 0 && num_params <= 255);
res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
res += get_size(value.new_params_bind_flag, ctx);
res += get_size(com_stmt_execute_param_meta_packet{}, ctx) * num_params;
for (auto it = value.params_begin; it != value.params_end; ++it)
{
res += get_binary_value_size(*it, ctx);
}
return res;
}
template <typename ForwardIterator>
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
boost::mysql::detail::serialization_tag::struct_with_fields
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
boost::mysql::detail::serialization_tag::struct_with_fields
>::serialize_(
const com_stmt_execute_packet<ForwardIterator>& input,
serialization_context& ctx
const com_stmt_execute_packet<ForwardIterator>& input,
serialization_context& ctx
) noexcept
{
serialize(int1(com_stmt_execute_packet<ForwardIterator>::command_id), ctx);
serialize(input.statement_id, ctx);
serialize(input.flags, ctx);
serialize(input.iteration_count, ctx);
serialize(int1(com_stmt_execute_packet<ForwardIterator>::command_id), ctx);
serialize(input.statement_id, ctx);
serialize(input.flags, ctx);
serialize(input.iteration_count, ctx);
// Number of parameters
auto num_params = std::distance(input.params_begin, input.params_end);
assert(num_params >= 0 && num_params <= 255);
// Number of parameters
auto num_params = std::distance(input.params_begin, input.params_end);
assert(num_params >= 0 && num_params <= 255);
// NULL bitmap (already size zero if num_params == 0)
null_bitmap_traits traits (stmt_execute_null_bitmap_offset, num_params);
std::size_t i = 0;
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
{
if (std::holds_alternative<std::nullptr_t>(*it))
{
traits.set_null(ctx.first(), i);
}
}
ctx.advance(traits.byte_count());
// NULL bitmap (already size zero if num_params == 0)
null_bitmap_traits traits (stmt_execute_null_bitmap_offset, num_params);
std::size_t i = 0;
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
{
if (std::holds_alternative<std::nullptr_t>(*it))
{
traits.set_null(ctx.first(), i);
}
}
ctx.advance(traits.byte_count());
// new parameters bind flag
serialize(input.new_params_bind_flag, ctx);
// new parameters bind flag
serialize(input.new_params_bind_flag, ctx);
// value metadata
com_stmt_execute_param_meta_packet meta;
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;
serialize(meta, ctx);
}
// value metadata
com_stmt_execute_param_meta_packet meta;
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;
serialize(meta, ctx);
}
// actual values
for (auto it = input.params_begin; it != input.params_end; ++it)
{
serialize_binary_value(*it, ctx);
}
// actual values
for (auto it = input.params_begin; it != input.params_end; ++it)
{
serialize_binary_value(*it, ctx);
}
}

View File

@ -16,9 +16,9 @@ namespace detail {
template <typename T>
constexpr std::size_t get_int_size()
{
static_assert(is_fixed_size_int<T>());
return std::is_same<T, int3>::value ? 3 :
std::is_same<T, int6>::value ? 6 : sizeof(T);
static_assert(is_fixed_size_int<T>());
return std::is_same<T, int3>::value ? 3 :
std::is_same<T, int6>::value ? 6 : sizeof(T);
}
// Helpers for structs
@ -38,101 +38,101 @@ struct is_command : decltype(is_command_helper::get<T>(nullptr))
template <std::size_t index, typename T>
errc deserialize_struct(
[[maybe_unused]] T& output,
[[maybe_unused]] deserialization_context& ctx
[[maybe_unused]] T& output,
[[maybe_unused]] deserialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return errc::ok;
}
else
{
constexpr auto pmem = std::get<index>(fields);
errc err = deserialize(output.*pmem, ctx);
if (err != errc::ok)
{
return err;
}
else
{
return deserialize_struct<index+1>(output, ctx);
}
}
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return errc::ok;
}
else
{
constexpr auto pmem = std::get<index>(fields);
errc err = deserialize(output.*pmem, ctx);
if (err != errc::ok)
{
return err;
}
else
{
return deserialize_struct<index+1>(output, ctx);
}
}
}
template <std::size_t index, typename T>
void serialize_struct(
[[maybe_unused]] const T& value,
[[maybe_unused]] serialization_context& ctx
[[maybe_unused]] const T& value,
[[maybe_unused]] serialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index < std::tuple_size<decltype(fields)>::value)
{
auto pmem = std::get<index>(fields);
serialize(value.*pmem, ctx);
serialize_struct<index+1>(value, ctx);
}
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index < std::tuple_size<decltype(fields)>::value)
{
auto pmem = std::get<index>(fields);
serialize(value.*pmem, ctx);
serialize_struct<index+1>(value, ctx);
}
}
template <std::size_t index, typename T>
std::size_t get_size_struct(
[[maybe_unused]] const T& input,
[[maybe_unused]] const serialization_context& ctx
[[maybe_unused]] const T& input,
[[maybe_unused]] const serialization_context& ctx
) noexcept
{
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return 0;
}
else
{
constexpr auto pmem = std::get<index>(fields);
return get_size_struct<index+1>(input, ctx) +
get_size(input.*pmem, ctx);
}
constexpr auto fields = get_struct_fields<T>::value;
if constexpr (index == std::tuple_size<decltype(fields)>::value)
{
return 0;
}
else
{
constexpr auto pmem = std::get<index>(fields);
return get_size_struct<index+1>(input, ctx) +
get_size(input.*pmem, ctx);
}
}
// Helpers for (de)serialize_fields
template <typename FirstType>
errc deserialize_fields_helper(deserialization_context& ctx, FirstType& field) noexcept
{
return deserialize(field, ctx);
return deserialize(field, ctx);
}
template <typename FirstType, typename... Types>
errc deserialize_fields_helper(
deserialization_context& ctx,
FirstType& field,
Types&... fields_tail
deserialization_context& ctx,
FirstType& field,
Types&... fields_tail
) noexcept
{
errc err = deserialize(field, ctx);
if (err == errc::ok)
{
err = deserialize_fields_helper(ctx, fields_tail...);
}
return err;
errc err = deserialize(field, ctx);
if (err == errc::ok)
{
err = deserialize_fields_helper(ctx, fields_tail...);
}
return err;
}
template <typename FirstType>
void serialize_fields_helper(serialization_context& ctx, const FirstType& field) noexcept
{
serialize(field, ctx);
serialize(field, ctx);
}
template <typename FirstType, typename... Types>
void serialize_fields_helper(
serialization_context& ctx,
const FirstType& field,
const Types&... fields_tail
serialization_context& ctx,
const FirstType& field,
const Types&... fields_tail
)
{
serialize(field, ctx);
serialize_fields_helper(ctx, fields_tail...);
serialize(field, ctx);
serialize_fields_helper(ctx, fields_tail...);
}
} // detail
@ -143,149 +143,149 @@ void serialize_fields_helper(
template <typename T>
constexpr bool boost::mysql::detail::is_fixed_size_int()
{
return
std::is_integral<get_value_type_t<T>>::value &&
std::is_base_of<value_holder<get_value_type_t<T>>, T>::value &&
!std::is_same<T, int_lenenc>::value;
return
std::is_integral<get_value_type_t<T>>::value &&
std::is_base_of<value_holder<get_value_type_t<T>>, T>::value &&
!std::is_same<T, int_lenenc>::value;
}
template <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::fixed_size_int
T,
boost::mysql::detail::serialization_tag::fixed_size_int
>::deserialize_(
T& output,
deserialization_context& ctx
T& output,
deserialization_context& ctx
) noexcept
{
static_assert(std::is_standard_layout_v<decltype(T::value)>);
static_assert(std::is_standard_layout_v<decltype(T::value)>);
constexpr auto size = get_int_size<T>();
if (!ctx.enough_size(size))
{
return errc::incomplete_message;
}
constexpr auto size = get_int_size<T>();
if (!ctx.enough_size(size))
{
return errc::incomplete_message;
}
memset(&output.value, 0, sizeof(output.value));
memcpy(&output.value, ctx.first(), size);
boost::endian::little_to_native_inplace(output);
ctx.advance(size);
memset(&output.value, 0, sizeof(output.value));
memcpy(&output.value, ctx.first(), size);
boost::endian::little_to_native_inplace(output);
ctx.advance(size);
return errc::ok;
return errc::ok;
}
template <typename T>
void boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::fixed_size_int
T,
boost::mysql::detail::serialization_tag::fixed_size_int
>::serialize_(
T input,
serialization_context& ctx
T input,
serialization_context& ctx
) noexcept
{
boost::endian::native_to_little_inplace(input);
ctx.write(&input.value, get_int_size<T>());
boost::endian::native_to_little_inplace(input);
ctx.write(&input.value, get_int_size<T>());
}
template <typename T>
constexpr std::size_t boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::fixed_size_int
T,
boost::mysql::detail::serialization_tag::fixed_size_int
>::get_size_(T, const serialization_context&) noexcept
{
return get_int_size<T>();
return get_int_size<T>();
}
// Fixed size strings
template <std::size_t N>
boost::mysql::errc boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_fixed<N>,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::string_fixed<N>,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
string_fixed<N>& output,
deserialization_context& ctx
string_fixed<N>& output,
deserialization_context& ctx
) noexcept
{
if (!ctx.enough_size(N))
{
return errc::incomplete_message;
}
memcpy(output.data(), ctx.first(), N);
ctx.advance(N);
return errc::ok;
if (!ctx.enough_size(N))
{
return errc::incomplete_message;
}
memcpy(output.data(), ctx.first(), N);
ctx.advance(N);
return errc::ok;
}
// Enums
template <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::enumeration
T,
boost::mysql::detail::serialization_tag::enumeration
>::deserialize_(
T& output,
deserialization_context& ctx
T& output,
deserialization_context& ctx
) noexcept
{
serializable_type value;
errc err = deserialize(value, ctx);
if (err != errc::ok)
{
return err;
}
output = static_cast<T>(value.value);
return errc::ok;
serializable_type value;
errc err = deserialize(value, ctx);
if (err != errc::ok)
{
return err;
}
output = static_cast<T>(value.value);
return errc::ok;
}
template <typename T>
std::size_t boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::enumeration
T,
boost::mysql::detail::serialization_tag::enumeration
>::get_size_(T, const serialization_context&) noexcept
{
return get_int_size<serializable_type>();
return get_int_size<serializable_type>();
}
// Floating points
template <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::floating_point
T,
boost::mysql::detail::serialization_tag::floating_point
>::deserialize_(T& output, deserialization_context& ctx) noexcept
{
// Size check
if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message;
// Size check
if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message;
// Endianness conversion
// Boost.Endian support for floats start at 1.71. TODO: maybe update requirements and CI
// Endianness conversion
// Boost.Endian support for floats start at 1.71. TODO: maybe update requirements and CI
#if BOOST_ENDIAN_BIG_BYTE
char buf [sizeof(T)];
std::memcpy(buf, ctx.first(), sizeof(T));
std::reverse(buf, buf + sizeof(T));
std::memcpy(&output, buf, sizeof(T));
char buf [sizeof(T)];
std::memcpy(buf, ctx.first(), sizeof(T));
std::reverse(buf, buf + sizeof(T));
std::memcpy(&output, buf, sizeof(T));
#else
std::memcpy(&output, ctx.first(), sizeof(T));
std::memcpy(&output, ctx.first(), sizeof(T));
#endif
ctx.advance(sizeof(T));
return errc::ok;
ctx.advance(sizeof(T));
return errc::ok;
}
template <typename T>
void
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::floating_point
T,
boost::mysql::detail::serialization_tag::floating_point
>::serialize_(T input, serialization_context& ctx) noexcept
{
// Endianness conversion
// 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));
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));
ctx.write(&input, sizeof(T));
#endif
}
@ -293,110 +293,110 @@ boost::mysql::detail::serialization_traits<
template <typename T>
constexpr bool boost::mysql::detail::is_struct_with_fields()
{
return !std::is_same_v<
std::decay_t<decltype(get_struct_fields<T>::value)>,
not_a_struct_with_fields
>;
return !std::is_same_v<
std::decay_t<decltype(get_struct_fields<T>::value)>,
not_a_struct_with_fields
>;
}
template <typename T>
boost::mysql::errc
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::struct_with_fields
T,
boost::mysql::detail::serialization_tag::struct_with_fields
>::deserialize_(T& output, deserialization_context& ctx) noexcept
{
return deserialize_struct<0>(output, ctx);
return deserialize_struct<0>(output, ctx);
}
template <typename T>
void
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::struct_with_fields
T,
boost::mysql::detail::serialization_tag::struct_with_fields
>::serialize_(
[[maybe_unused]] const T& input,
[[maybe_unused]] serialization_context& ctx
[[maybe_unused]] const T& input,
[[maybe_unused]] serialization_context& ctx
) 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<T>::value)
{
serialize(int1(T::command_id), ctx);
}
serialize_struct<0>(input, ctx);
// 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<T>::value)
{
serialize(int1(T::command_id), ctx);
}
serialize_struct<0>(input, ctx);
}
template <typename T>
std::size_t
boost::mysql::detail::serialization_traits<
T,
boost::mysql::detail::serialization_tag::struct_with_fields
T,
boost::mysql::detail::serialization_tag::struct_with_fields
>::get_size_(const T& input, const serialization_context& ctx) noexcept
{
std::size_t res = is_command<T>::value ? 1 : 0;
res += get_size_struct<0>(input, ctx);
return res;
std::size_t res = is_command<T>::value ? 1 : 0;
res += get_size_struct<0>(input, ctx);
return res;
}
// Miscellanea
template <typename Serializable, typename Allocator>
void boost::mysql::detail::serialize_message(
const Serializable& input,
capabilities caps,
basic_bytestring<Allocator>& buffer
const Serializable& input,
capabilities caps,
basic_bytestring<Allocator>& buffer
)
{
serialization_context ctx (caps);
std::size_t size = get_size(input, ctx);
buffer.resize(size);
ctx.set_first(buffer.data());
serialize(input, ctx);
assert(ctx.first() == buffer.data() + buffer.size());
serialization_context ctx (caps);
std::size_t size = get_size(input, ctx);
buffer.resize(size);
ctx.set_first(buffer.data());
serialize(input, ctx);
assert(ctx.first() == buffer.data() + buffer.size());
}
template <typename Deserializable>
boost::mysql::error_code boost::mysql::detail::deserialize_message(
Deserializable& output,
deserialization_context& ctx
Deserializable& output,
deserialization_context& ctx
)
{
auto err = deserialize(output, ctx);
if (err != errc::ok) return make_error_code(err);
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
auto err = deserialize(output, ctx);
if (err != errc::ok) return make_error_code(err);
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
}
template <typename... Types>
boost::mysql::errc
boost::mysql::detail::deserialize_fields(
deserialization_context& ctx,
Types&... fields
deserialization_context& ctx,
Types&... fields
) noexcept
{
return deserialize_fields_helper(ctx, fields...);
return deserialize_fields_helper(ctx, fields...);
}
template <typename... Types>
void boost::mysql::detail::serialize_fields(
serialization_context& ctx,
const Types&... fields
serialization_context& ctx,
const Types&... fields
) noexcept
{
serialize_fields_helper(ctx, fields...);
serialize_fields_helper(ctx, fields...);
}
template <typename T>
constexpr boost::mysql::detail::serialization_tag
boost::mysql::detail::get_serialization_tag()
{
return
is_fixed_size_int<T>() ? serialization_tag::fixed_size_int :
std::is_floating_point<T>::value ? serialization_tag::floating_point :
std::is_enum<T>::value ? serialization_tag::enumeration :
is_struct_with_fields<T>() ? serialization_tag::struct_with_fields :
serialization_tag::none;
return
is_fixed_size_int<T>() ? serialization_tag::fixed_size_int :
std::is_floating_point<T>::value ? serialization_tag::floating_point :
std::is_enum<T>::value ? serialization_tag::enumeration :
is_struct_with_fields<T>() ? serialization_tag::struct_with_fields :
serialization_tag::none;
}

View File

@ -15,112 +15,112 @@ namespace mysql {
namespace detail {
inline std::string_view get_string(
const std::uint8_t* from,
std::size_t size
const std::uint8_t* from,
std::size_t size
)
{
return std::string_view(reinterpret_cast<const char*>(from), size);
return std::string_view(reinterpret_cast<const char*>(from), size);
}
inline errc deserialize_binary_date(
date& output,
std::uint8_t length,
deserialization_context& ctx
date& output,
std::uint8_t length,
deserialization_context& ctx
) noexcept
{
int2 year;
int1 month;
int1 day;
int2 year;
int1 month;
int1 day;
if (length >= 4) // if length is zero, year, month and day are zero
{
auto err = deserialize_fields(ctx, year, month, day);
if (err != errc::ok) return err;
}
if (length >= 4) // if length is zero, year, month and day are zero
{
auto err = deserialize_fields(ctx, year, month, day);
if (err != errc::ok) return err;
}
// TODO: how does this handle zero dates?
::date::year_month_day ymd (::date::year(year.value), ::date::month(month.value), ::date::day(day.value));
output = date(ymd);
return errc::ok;
// TODO: how does this handle zero dates?
::date::year_month_day ymd (::date::year(year.value), ::date::month(month.value), ::date::day(day.value));
output = date(ymd);
return errc::ok;
}
// Does not add the length prefix byte
inline void serialize_binary_ymd(
const ::date::year_month_day& ymd,
serialization_context& ctx
const ::date::year_month_day& ymd,
serialization_context& ctx
) noexcept
{
serialize_fields(
ctx,
int2(static_cast<std::uint16_t>(static_cast<int>(ymd.year()))),
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.month()))),
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.day())))
);
serialize_fields(
ctx,
int2(static_cast<std::uint16_t>(static_cast<int>(ymd.year()))),
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.month()))),
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.day())))
);
}
struct broken_datetime
{
::date::year_month_day ymd;
::date::time_of_day<std::chrono::microseconds> tod;
::date::year_month_day ymd;
::date::time_of_day<std::chrono::microseconds> tod;
broken_datetime(const datetime& input) noexcept :
ymd(::date::floor<::date::days>(input)),
tod(input - ::date::floor<::date::days>(input))
{
}
broken_datetime(const datetime& input) noexcept :
ymd(::date::floor<::date::days>(input)),
tod(input - ::date::floor<::date::days>(input))
{
}
// Doesn't count the first length byte
std::uint8_t binary_serialized_length() const noexcept
{
std::uint8_t res = 11; // base length
if (tod.subseconds().count() == 0)
{
res -= 4;
if (tod.seconds().count() == 0 &&
tod.minutes().count() == 0 &&
tod.hours().count() == 0)
{
res -= 3;
}
}
return res;
}
// Doesn't count the first length byte
std::uint8_t binary_serialized_length() const noexcept
{
std::uint8_t res = 11; // base length
if (tod.subseconds().count() == 0)
{
res -= 4;
if (tod.seconds().count() == 0 &&
tod.minutes().count() == 0 &&
tod.hours().count() == 0)
{
res -= 3;
}
}
return res;
}
};
struct broken_time
{
::date::days days;
std::chrono::hours hours;
std::chrono::minutes minutes;
std::chrono::seconds seconds;
std::chrono::microseconds microseconds;
::date::days days;
std::chrono::hours hours;
std::chrono::minutes minutes;
std::chrono::seconds seconds;
std::chrono::microseconds microseconds;
broken_time(const time& input) noexcept :
days(std::chrono::duration_cast<::date::days>(input)),
hours(std::chrono::duration_cast<std::chrono::hours>(input % ::date::days(1))),
minutes(std::chrono::duration_cast<std::chrono::minutes>(input % std::chrono::hours(1))),
seconds(std::chrono::duration_cast<std::chrono::seconds>(input % std::chrono::minutes(1))),
microseconds(input % std::chrono::seconds(1))
{
}
broken_time(const time& input) noexcept :
days(std::chrono::duration_cast<::date::days>(input)),
hours(std::chrono::duration_cast<std::chrono::hours>(input % ::date::days(1))),
minutes(std::chrono::duration_cast<std::chrono::minutes>(input % std::chrono::hours(1))),
seconds(std::chrono::duration_cast<std::chrono::seconds>(input % std::chrono::minutes(1))),
microseconds(input % std::chrono::seconds(1))
{
}
// Doesn't count the first length byte
std::uint8_t binary_serialized_length() const noexcept
{
std::uint8_t res = 12;
if (microseconds.count() == 0)
{
res -= 4;
if (seconds.count() == 0 &&
minutes.count() == 0 &&
hours.count() == 0 &&
days.count() == 0)
{
res -= 8;
}
}
return res;
}
// Doesn't count the first length byte
std::uint8_t binary_serialized_length() const noexcept
{
std::uint8_t res = 12;
if (microseconds.count() == 0)
{
res -= 4;
if (seconds.count() == 0 &&
minutes.count() == 0 &&
hours.count() == 0 &&
days.count() == 0)
{
res -= 8;
}
}
return res;
}
};
} // detail
@ -129,394 +129,394 @@ struct broken_time
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::int_lenenc,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::int_lenenc,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
int_lenenc& output,
deserialization_context& ctx
int_lenenc& output,
deserialization_context& ctx
) noexcept
{
int1 first_byte;
errc err = deserialize(first_byte, ctx);
if (err != errc::ok)
{
return err;
}
int1 first_byte;
errc err = deserialize(first_byte, ctx);
if (err != errc::ok)
{
return err;
}
if (first_byte.value == 0xFC)
{
int2 value;
err = deserialize(value, ctx);
output.value = value.value;
}
else if (first_byte.value == 0xFD)
{
int3 value;
err = deserialize(value, ctx);
output.value = value.value;
}
else if (first_byte.value == 0xFE)
{
int8 value;
err = deserialize(value, ctx);
output.value = value.value;
}
else
{
err = errc::ok;
output.value = first_byte.value;
}
return err;
if (first_byte.value == 0xFC)
{
int2 value;
err = deserialize(value, ctx);
output.value = value.value;
}
else if (first_byte.value == 0xFD)
{
int3 value;
err = deserialize(value, ctx);
output.value = value.value;
}
else if (first_byte.value == 0xFE)
{
int8 value;
err = deserialize(value, ctx);
output.value = value.value;
}
else
{
err = errc::ok;
output.value = first_byte.value;
}
return err;
}
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::detail::int_lenenc,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::int_lenenc,
boost::mysql::detail::serialization_tag::none
>::serialize_(
int_lenenc input,
serialization_context& ctx
int_lenenc input,
serialization_context& ctx
) noexcept
{
if (input.value < 251)
{
serialize(int1(static_cast<std::uint8_t>(input.value)), ctx);
}
else if (input.value < 0x10000)
{
ctx.write(0xfc);
serialize(int2(static_cast<std::uint16_t>(input.value)), ctx);
}
else if (input.value < 0x1000000)
{
ctx.write(0xfd);
serialize(int3(static_cast<std::uint32_t>(input.value)), ctx);
}
else
{
ctx.write(0xfe);
serialize(int8(static_cast<std::uint64_t>(input.value)), ctx);
}
if (input.value < 251)
{
serialize(int1(static_cast<std::uint8_t>(input.value)), ctx);
}
else if (input.value < 0x10000)
{
ctx.write(0xfc);
serialize(int2(static_cast<std::uint16_t>(input.value)), ctx);
}
else if (input.value < 0x1000000)
{
ctx.write(0xfd);
serialize(int3(static_cast<std::uint32_t>(input.value)), ctx);
}
else
{
ctx.write(0xfe);
serialize(int8(static_cast<std::uint64_t>(input.value)), ctx);
}
}
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::detail::int_lenenc,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::int_lenenc,
boost::mysql::detail::serialization_tag::none
>::get_size_(
int_lenenc input,
const serialization_context&
int_lenenc input,
const serialization_context&
) noexcept
{
if (input.value < 251) return 1;
else if (input.value < 0x10000) return 3;
else if (input.value < 0x1000000) return 4;
else return 9;
if (input.value < 251) return 1;
else if (input.value < 0x10000) return 3;
else if (input.value < 0x1000000) return 4;
else return 9;
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_null,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::string_null,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
string_null& output,
deserialization_context& ctx
string_null& output,
deserialization_context& ctx
) noexcept
{
auto string_end = std::find(ctx.first(), ctx.last(), 0);
if (string_end == ctx.last())
{
return errc::incomplete_message;
}
output.value = get_string(ctx.first(), string_end-ctx.first());
ctx.set_first(string_end + 1); // skip the null terminator
return errc::ok;
auto string_end = std::find(ctx.first(), ctx.last(), 0);
if (string_end == ctx.last())
{
return errc::incomplete_message;
}
output.value = get_string(ctx.first(), string_end-ctx.first());
ctx.set_first(string_end + 1); // skip the null terminator
return errc::ok;
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_eof,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::string_eof,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
string_eof& output,
deserialization_context& ctx
string_eof& output,
deserialization_context& ctx
) noexcept
{
output.value = get_string(ctx.first(), ctx.last()-ctx.first());
ctx.set_first(ctx.last());
return errc::ok;
output.value = get_string(ctx.first(), ctx.last()-ctx.first());
ctx.set_first(ctx.last());
return errc::ok;
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::detail::string_lenenc,
boost::mysql::detail::serialization_tag::none
boost::mysql::detail::string_lenenc,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
string_lenenc& output,
deserialization_context& ctx
string_lenenc& output,
deserialization_context& ctx
) noexcept
{
int_lenenc length;
errc err = deserialize(length, ctx);
if (err != errc::ok)
{
return err;
}
if (length.value > std::numeric_limits<std::size_t>::max())
{
return errc::protocol_value_error;
}
auto len = static_cast<std::size_t>(length.value);
if (!ctx.enough_size(len))
{
return errc::incomplete_message;
}
int_lenenc length;
errc err = deserialize(length, ctx);
if (err != errc::ok)
{
return err;
}
if (length.value > std::numeric_limits<std::size_t>::max())
{
return errc::protocol_value_error;
}
auto len = static_cast<std::size_t>(length.value);
if (!ctx.enough_size(len))
{
return errc::incomplete_message;
}
output.value = get_string(ctx.first(), len);
ctx.advance(len);
return errc::ok;
output.value = get_string(ctx.first(), len);
ctx.advance(len);
return errc::ok;
}
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::date,
boost::mysql::detail::serialization_tag::none
boost::mysql::date,
boost::mysql::detail::serialization_tag::none
>::get_size_(
const date&,
const serialization_context&
const date&,
const serialization_context&
) noexcept
{
// TODO: consider zero dates?
return 5; // length, year, month, day
// TODO: consider zero dates?
return 5; // length, year, month, day
}
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::date,
boost::mysql::detail::serialization_tag::none
boost::mysql::date,
boost::mysql::detail::serialization_tag::none
>::serialize_(
const date& input,
serialization_context& ctx
const date& input,
serialization_context& ctx
) noexcept
{
// TODO: consider zero dates?
serialize(int1(4), ctx); //
serialize_binary_ymd(::date::year_month_day (input), ctx);
// TODO: consider zero dates?
serialize(int1(4), ctx); //
serialize_binary_ymd(::date::year_month_day (input), ctx);
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::date,
boost::mysql::detail::serialization_tag::none
boost::mysql::date,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
date& output,
deserialization_context& ctx
date& output,
deserialization_context& ctx
) noexcept
{
int1 length;
auto err = deserialize(length, ctx);
if (err != errc::ok) return err;
return deserialize_binary_date(output, length.value, ctx);
int1 length;
auto err = deserialize(length, ctx);
if (err != errc::ok) return err;
return deserialize_binary_date(output, length.value, ctx);
}
// datetime
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::datetime,
boost::mysql::detail::serialization_tag::none
boost::mysql::datetime,
boost::mysql::detail::serialization_tag::none
>::get_size_(
const datetime& input,
const serialization_context&
const datetime& input,
const serialization_context&
) noexcept
{
broken_datetime dt (input);
return dt.binary_serialized_length() + 1; // extra length prefix byte
broken_datetime dt (input);
return dt.binary_serialized_length() + 1; // extra length prefix byte
}
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::datetime,
boost::mysql::detail::serialization_tag::none
boost::mysql::datetime,
boost::mysql::detail::serialization_tag::none
>::serialize_(
const datetime& input,
serialization_context& ctx
const datetime& input,
serialization_context& ctx
) noexcept
{
broken_datetime brokendt (input);
auto length = brokendt.binary_serialized_length();
serialize(int1(length), ctx);
if (length >= 4) // TODO: refactor these magic constants
{
serialize_binary_ymd(brokendt.ymd, ctx);
}
if (length >= 7)
{
serialize_fields(
ctx,
int1(static_cast<std::uint8_t>(brokendt.tod.hours().count())),
int1(static_cast<std::uint8_t>(brokendt.tod.minutes().count())),
int1(static_cast<std::uint8_t>(brokendt.tod.seconds().count()))
);
}
if (length >= 11)
{
auto micros = static_cast<std::uint32_t>(brokendt.tod.subseconds().count());
serialize(int4(micros), ctx);
}
broken_datetime brokendt (input);
auto length = brokendt.binary_serialized_length();
serialize(int1(length), ctx);
if (length >= 4) // TODO: refactor these magic constants
{
serialize_binary_ymd(brokendt.ymd, ctx);
}
if (length >= 7)
{
serialize_fields(
ctx,
int1(static_cast<std::uint8_t>(brokendt.tod.hours().count())),
int1(static_cast<std::uint8_t>(brokendt.tod.minutes().count())),
int1(static_cast<std::uint8_t>(brokendt.tod.seconds().count()))
);
}
if (length >= 11)
{
auto micros = static_cast<std::uint32_t>(brokendt.tod.subseconds().count());
serialize(int4(micros), ctx);
}
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::datetime,
boost::mysql::detail::serialization_tag::none
boost::mysql::datetime,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
datetime& output,
deserialization_context& ctx
datetime& output,
deserialization_context& ctx
) noexcept
{
int1 length;
date date_part;
int1 hours;
int1 minutes;
int1 seconds;
int4 micros;
int1 length;
date date_part;
int1 hours;
int1 minutes;
int1 seconds;
int4 micros;
// Deserialize length
auto err = deserialize(length, ctx);
if (err != errc::ok) return err;
// Deserialize length
auto err = deserialize(length, ctx);
if (err != errc::ok) return err;
// Based on length, deserialize the rest of the fields
err = deserialize_binary_date(date_part, length.value, ctx);
if (err != errc::ok) return err;
if (length.value >= 7)
{
err = deserialize_fields(ctx, hours, minutes, seconds);
if (err != errc::ok) return err;
}
if (length.value >= 11)
{
err = deserialize(micros, ctx);
if (err != errc::ok) return err;
}
// Based on length, deserialize the rest of the fields
err = deserialize_binary_date(date_part, length.value, ctx);
if (err != errc::ok) return err;
if (length.value >= 7)
{
err = deserialize_fields(ctx, hours, minutes, seconds);
if (err != errc::ok) return err;
}
if (length.value >= 11)
{
err = deserialize(micros, ctx);
if (err != errc::ok) return err;
}
// Compose the final datetime. Doing time of day and date separately to avoid overflow
auto time_of_day_part = std::chrono::hours(hours.value) + std::chrono::minutes(minutes.value) +
std::chrono::seconds(seconds.value) + std::chrono::microseconds(micros.value);
output = date_part + time_of_day_part;
return errc::ok;
// Compose the final datetime. Doing time of day and date separately to avoid overflow
auto time_of_day_part = std::chrono::hours(hours.value) + std::chrono::minutes(minutes.value) +
std::chrono::seconds(seconds.value) + std::chrono::microseconds(micros.value);
output = date_part + time_of_day_part;
return errc::ok;
}
// time
inline std::size_t
boost::mysql::detail::serialization_traits<
boost::mysql::time,
boost::mysql::detail::serialization_tag::none
boost::mysql::time,
boost::mysql::detail::serialization_tag::none
>::get_size_(
const time& input,
const serialization_context&
const time& input,
const serialization_context&
) noexcept
{
return broken_time(input).binary_serialized_length() + 1; // length byte
return broken_time(input).binary_serialized_length() + 1; // length byte
}
inline void
boost::mysql::detail::serialization_traits<
boost::mysql::time,
boost::mysql::detail::serialization_tag::none
boost::mysql::time,
boost::mysql::detail::serialization_tag::none
>::serialize_(
const time& input,
serialization_context& ctx
const time& input,
serialization_context& ctx
) noexcept
{
broken_time broken (input);
auto length = broken.binary_serialized_length();
serialize(int1(length), ctx);
if (length >= 8) // TODO: magic constants
{
int1 is_negative (input.count() < 0 ? 1 : 0);
serialize_fields(
ctx,
is_negative,
int4(static_cast<std::uint32_t>(std::abs(broken.days.count()))),
int1(static_cast<std::uint8_t>(std::abs(broken.hours.count()))),
int1(static_cast<std::uint8_t>(std::abs(broken.minutes.count()))),
int1(static_cast<std::uint8_t>(std::abs(broken.seconds.count())))
);
}
if (length >= 12)
{
auto micros = static_cast<std::uint32_t>(std::abs(broken.microseconds.count()));
serialize(int4(micros), ctx);
}
broken_time broken (input);
auto length = broken.binary_serialized_length();
serialize(int1(length), ctx);
if (length >= 8) // TODO: magic constants
{
int1 is_negative (input.count() < 0 ? 1 : 0);
serialize_fields(
ctx,
is_negative,
int4(static_cast<std::uint32_t>(std::abs(broken.days.count()))),
int1(static_cast<std::uint8_t>(std::abs(broken.hours.count()))),
int1(static_cast<std::uint8_t>(std::abs(broken.minutes.count()))),
int1(static_cast<std::uint8_t>(std::abs(broken.seconds.count())))
);
}
if (length >= 12)
{
auto micros = static_cast<std::uint32_t>(std::abs(broken.microseconds.count()));
serialize(int4(micros), ctx);
}
}
inline boost::mysql::errc
boost::mysql::detail::serialization_traits<
boost::mysql::time,
boost::mysql::detail::serialization_tag::none
boost::mysql::time,
boost::mysql::detail::serialization_tag::none
>::deserialize_(
time& output,
deserialization_context& ctx
time& output,
deserialization_context& ctx
) noexcept
{
// Length
int1 length;
auto err = deserialize(length, ctx);
if (err != errc::ok) return err;
// Length
int1 length;
auto err = deserialize(length, ctx);
if (err != errc::ok) return err;
int1 is_negative (0);
int4 days (0);
int1 hours (0);
int1 minutes(0);
int1 seconds(0);
int4 microseconds(0);
int1 is_negative (0);
int4 days (0);
int1 hours (0);
int1 minutes(0);
int1 seconds(0);
int4 microseconds(0);
if (length.value >= 8)
{
err = deserialize_fields(
ctx,
is_negative,
days,
hours,
minutes,
seconds
);
if (err != errc::ok) return err;
}
if (length.value >= 12)
{
err = deserialize(microseconds, ctx);
if (err != errc::ok) return err;
}
if (length.value >= 8)
{
err = deserialize_fields(
ctx,
is_negative,
days,
hours,
minutes,
seconds
);
if (err != errc::ok) return err;
}
if (length.value >= 12)
{
err = deserialize(microseconds, ctx);
if (err != errc::ok) return err;
}
output = (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)
);
return errc::ok;
output = (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)
);
return errc::ok;
}
inline std::pair<boost::mysql::error_code, std::uint8_t>
boost::mysql::detail::deserialize_message_type(
deserialization_context& ctx
deserialization_context& ctx
)
{
int1 msg_type;
std::pair<mysql::error_code, std::uint8_t> res {};
auto err = deserialize(msg_type, ctx);
if (err == errc::ok)
{
res.second = msg_type.value;
}
else
{
res.first = make_error_code(err);
}
return res;
int1 msg_type;
std::pair<mysql::error_code, std::uint8_t> res {};
auto err = deserialize(msg_type, ctx);
if (err == errc::ok)
{
res.second = msg_type.value;
}
else
{
res.first = make_error_code(err);
}
return res;
}

View File

@ -18,90 +18,90 @@ namespace mysql {
namespace detail {
inline errc deserialize_text_value_impl(
std::string_view from,
date& to
std::string_view from,
date& to
)
{
constexpr std::size_t size = 4 + 2 + 2 + 2; // year, month, day, separators
if (from.size() != size) return errc::protocol_value_error;
unsigned year, month, day;
char buffer [size + 1] {};
memcpy(buffer, from.data(), from.size());
int parsed = sscanf(buffer, "%4u-%2u-%2u", &year, &month, &day);
if (parsed != 3) return errc::protocol_value_error;
::date::year_month_day result (::date::year(year)/::date::month(month)/::date::day(day));
if (!result.ok()) return errc::protocol_value_error;
if (result > max_date || result < min_date) return errc::protocol_value_error;
to = result;
return errc::ok;
constexpr std::size_t size = 4 + 2 + 2 + 2; // year, month, day, separators
if (from.size() != size) return errc::protocol_value_error;
unsigned year, month, day;
char buffer [size + 1] {};
memcpy(buffer, from.data(), from.size());
int parsed = sscanf(buffer, "%4u-%2u-%2u", &year, &month, &day);
if (parsed != 3) return errc::protocol_value_error;
::date::year_month_day result (::date::year(year)/::date::month(month)/::date::day(day));
if (!result.ok()) return errc::protocol_value_error;
if (result > max_date || result < min_date) return errc::protocol_value_error;
to = result;
return errc::ok;
}
inline errc deserialize_text_value_impl(
std::string_view from,
time& to,
unsigned decimals
std::string_view from,
time& to,
unsigned decimals
)
{
// size check
constexpr std::size_t min_size = 2 + 2 + 2 + 2; // hours, mins, seconds, no micros
constexpr std::size_t max_size = min_size + 1 + 1 + 7; // hour extra character, sign and micros
decimals = std::min(decimals, 6u);
if (from.size() < min_size || from.size() > max_size) return errc::protocol_value_error;
// size check
constexpr std::size_t min_size = 2 + 2 + 2 + 2; // hours, mins, seconds, no micros
constexpr std::size_t max_size = min_size + 1 + 1 + 7; // hour extra character, sign and micros
decimals = std::min(decimals, 6u);
if (from.size() < min_size || from.size() > max_size) return errc::protocol_value_error;
// Parse it
int hours;
unsigned minutes, seconds, micros = 0;
char buffer [max_size + 1] {};
memcpy(buffer, from.data(), from.size());
int parsed = decimals ? sscanf(buffer, "%4d:%2u:%2u.%6u", &hours, &minutes, &seconds, &micros) : // sign adds 1 char
sscanf(buffer, "%4d:%2u:%2u", &hours, &minutes, &seconds);
if ((decimals && parsed != 4) || (!decimals && parsed != 3)) return errc::protocol_value_error;
micros *= static_cast<unsigned>(std::pow(10, 6 - decimals));
hours = std::abs(hours);
bool is_negative = from[0] == '-';
// Parse it
int hours;
unsigned minutes, seconds, micros = 0;
char buffer [max_size + 1] {};
memcpy(buffer, from.data(), from.size());
int parsed = decimals ? sscanf(buffer, "%4d:%2u:%2u.%6u", &hours, &minutes, &seconds, &micros) : // sign adds 1 char
sscanf(buffer, "%4d:%2u:%2u", &hours, &minutes, &seconds);
if ((decimals && parsed != 4) || (!decimals && parsed != 3)) return errc::protocol_value_error;
micros *= static_cast<unsigned>(std::pow(10, 6 - decimals));
hours = std::abs(hours);
bool is_negative = from[0] == '-';
// Sum it
auto res = std::chrono::hours(hours) + std::chrono::minutes(minutes) +
std::chrono::seconds(seconds) + std::chrono::microseconds(micros);
if (is_negative)
{
res = -res;
}
// Sum it
auto res = std::chrono::hours(hours) + std::chrono::minutes(minutes) +
std::chrono::seconds(seconds) + std::chrono::microseconds(micros);
if (is_negative)
{
res = -res;
}
// Range check
if (res > max_time || res < min_time) return errc::protocol_value_error;
// Range check
if (res > max_time || res < min_time) return errc::protocol_value_error;
to = res;
return errc::ok;
to = res;
return errc::ok;
}
inline errc deserialize_text_value_impl(
std::string_view from,
datetime& to,
unsigned decimals
std::string_view from,
datetime& to,
unsigned decimals
)
{
// Length check
constexpr std::size_t min_size = 4 + 5*2 + 5; // year, month, day, hour, minute, seconds, separators
decimals = std::min(decimals, 6u);
std::size_t expected_size = min_size + (decimals ? decimals + 1 : 0);
if (from.size() != expected_size) return errc::protocol_value_error;
// Length check
constexpr std::size_t min_size = 4 + 5*2 + 5; // year, month, day, hour, minute, seconds, separators
decimals = std::min(decimals, 6u);
std::size_t expected_size = min_size + (decimals ? decimals + 1 : 0);
if (from.size() != expected_size) return errc::protocol_value_error;
// Date part
date dt;
auto err = deserialize_text_value_impl(from.substr(0, 10), dt);
if (err != errc::ok) return err;
// Date part
date dt;
auto err = deserialize_text_value_impl(from.substr(0, 10), dt);
if (err != errc::ok) return err;
// Time of day part
time time_of_day;
err = deserialize_text_value_impl(from.substr(11), time_of_day, decimals);
if (err != errc::ok) return err;
constexpr auto max_time_of_day = std::chrono::hours(24) - std::chrono::microseconds(1);
if (time_of_day < std::chrono::seconds(0) || time_of_day > max_time_of_day) return errc::protocol_value_error;
// Time of day part
time time_of_day;
err = deserialize_text_value_impl(from.substr(11), time_of_day, decimals);
if (err != errc::ok) return err;
constexpr auto max_time_of_day = std::chrono::hours(24) - std::chrono::microseconds(1);
if (time_of_day < std::chrono::seconds(0) || time_of_day > max_time_of_day) return errc::protocol_value_error;
// Sum it up
to = dt + time_of_day;
return errc::ok;
// Sum it up
to = dt + time_of_day;
return errc::ok;
}
@ -109,43 +109,43 @@ template <typename T>
std::enable_if_t<std::is_arithmetic_v<T>, errc>
deserialize_text_value_impl(std::string_view from, T& to)
{
bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), to);
return ok ? errc::ok : errc::protocol_value_error;
bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), to);
return ok ? errc::ok : errc::protocol_value_error;
}
inline errc deserialize_text_value_impl(std::string_view from, std::string_view& to)
{
to = from;
return errc::ok;
to = from;
return errc::ok;
}
template <typename T, typename... Args>
errc deserialize_text_value_to_variant(std::string_view from, value& to, Args&&... args)
{
T value;
auto err = deserialize_text_value_impl(from, value, std::forward<Args>(args)...);
if (err == errc::ok)
{
to = value;
}
return err;
T value;
auto err = deserialize_text_value_impl(from, value, std::forward<Args>(args)...);
if (err == errc::ok)
{
to = value;
}
return err;
}
inline bool is_next_field_null(
deserialization_context& ctx
deserialization_context& ctx
)
{
int1 type_byte;
errc err = deserialize(type_byte, ctx);
if (err == errc::ok)
{
if (type_byte.value == 0xfb)
{
return true; // it was null, do not rewind
}
ctx.rewind(1); // it was not null, rewind (this byte is part of the actual message)
}
return false;
int1 type_byte;
errc err = deserialize(type_byte, ctx);
if (err == errc::ok)
{
if (type_byte.value == 0xfb)
{
return true; // it was null, do not rewind
}
ctx.rewind(1); // it was not null, rewind (this byte is part of the actual message)
}
return false;
}
} // detail
@ -153,36 +153,36 @@ inline bool is_next_field_null(
} // boost
inline boost::mysql::errc boost::mysql::detail::deserialize_text_value(
std::string_view from,
const field_metadata& meta,
value& output
std::string_view from,
const field_metadata& meta,
value& output
)
{
switch (meta.protocol_type())
{
switch (meta.protocol_type())
{
case protocol_field_type::tiny:
case protocol_field_type::short_:
case protocol_field_type::int24:
case protocol_field_type::long_:
case protocol_field_type::year:
return meta.is_unsigned() ?
deserialize_text_value_to_variant<std::uint32_t>(from, output) :
deserialize_text_value_to_variant<std::int32_t>(from, output);
return meta.is_unsigned() ?
deserialize_text_value_to_variant<std::uint32_t>(from, output) :
deserialize_text_value_to_variant<std::int32_t>(from, output);
case protocol_field_type::longlong:
return meta.is_unsigned() ?
deserialize_text_value_to_variant<std::uint64_t>(from, output) :
deserialize_text_value_to_variant<std::int64_t>(from, output);
return meta.is_unsigned() ?
deserialize_text_value_to_variant<std::uint64_t>(from, output) :
deserialize_text_value_to_variant<std::int64_t>(from, output);
case protocol_field_type::float_:
return deserialize_text_value_to_variant<float>(from, output);
return deserialize_text_value_to_variant<float>(from, output);
case protocol_field_type::double_:
return deserialize_text_value_to_variant<double>(from, output);
return deserialize_text_value_to_variant<double>(from, output);
case protocol_field_type::timestamp:
case protocol_field_type::datetime:
return deserialize_text_value_to_variant<datetime>(from, output, meta.decimals());
return deserialize_text_value_to_variant<datetime>(from, output, meta.decimals());
case protocol_field_type::date:
return deserialize_text_value_to_variant<date>(from, output);
return deserialize_text_value_to_variant<date>(from, output);
case protocol_field_type::time:
return deserialize_text_value_to_variant<time>(from, output, meta.decimals());
return deserialize_text_value_to_variant<time>(from, output, meta.decimals());
// True string types
case protocol_field_type::varchar:
case protocol_field_type::var_string:
@ -199,36 +199,36 @@ inline boost::mysql::errc boost::mysql::detail::deserialize_text_value(
case protocol_field_type::newdecimal:
case protocol_field_type::geometry:
default:
return deserialize_text_value_to_variant<std::string_view>(from, output);
}
return deserialize_text_value_to_variant<std::string_view>(from, output);
}
}
boost::mysql::error_code boost::mysql::detail::deserialize_text_row(
deserialization_context& ctx,
const std::vector<field_metadata>& fields,
std::vector<value>& output
deserialization_context& ctx,
const std::vector<field_metadata>& fields,
std::vector<value>& output
)
{
output.resize(fields.size());
for (std::vector<value>::size_type i = 0; i < fields.size(); ++i)
{
bool is_null = is_next_field_null(ctx);
if (is_null)
{
output[i] = nullptr;
}
else
{
string_lenenc value_str;
errc err = deserialize(value_str, ctx);
if (err != errc::ok) return make_error_code(err);
err = deserialize_text_value(value_str.value, fields[i], output[i]);
if (err != errc::ok) return make_error_code(err);
}
}
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
output.resize(fields.size());
for (std::vector<value>::size_type i = 0; i < fields.size(); ++i)
{
bool is_null = is_next_field_null(ctx);
if (is_null)
{
output[i] = nullptr;
}
else
{
string_lenenc value_str;
errc err = deserialize(value_str, ctx);
if (err != errc::ok) return make_error_code(err);
err = deserialize_text_value(value_str.value, fields[i], output[i]);
if (err != errc::ok) return make_error_code(err);
}
}
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
return error_code();
}

View File

@ -17,28 +17,28 @@ namespace detail {
class null_bitmap_traits
{
std::size_t offset_;
std::size_t num_fields_;
std::size_t offset_;
std::size_t num_fields_;
constexpr std::size_t byte_pos(std::size_t field_pos) const noexcept { return (field_pos + offset_) / 8; }
constexpr std::size_t bit_pos(std::size_t field_pos) const noexcept { return (field_pos + offset_) % 8; }
constexpr std::size_t byte_pos(std::size_t field_pos) const noexcept { return (field_pos + offset_) / 8; }
constexpr std::size_t bit_pos(std::size_t field_pos) const noexcept { return (field_pos + offset_) % 8; }
public:
constexpr null_bitmap_traits(std::size_t offset, std::size_t num_fields) noexcept:
offset_(offset), num_fields_ {num_fields} {};
constexpr std::size_t offset() const noexcept { return offset_; }
constexpr std::size_t num_fields() const noexcept { return num_fields_; }
constexpr null_bitmap_traits(std::size_t offset, std::size_t num_fields) noexcept:
offset_(offset), num_fields_ {num_fields} {};
constexpr std::size_t offset() const noexcept { return offset_; }
constexpr std::size_t num_fields() const noexcept { return num_fields_; }
constexpr std::size_t byte_count() const noexcept { return (num_fields_ + 7 + offset_) / 8; }
bool is_null(const std::uint8_t* null_bitmap_begin, std::size_t field_pos) const noexcept
{
assert(field_pos < num_fields_);
return null_bitmap_begin[byte_pos(field_pos)] & (1 << bit_pos(field_pos));
}
void set_null(std::uint8_t* null_bitmap_begin, std::size_t field_pos) const noexcept
{
assert(field_pos < num_fields_);
null_bitmap_begin[byte_pos(field_pos)] |= (1 << bit_pos(field_pos));
}
constexpr std::size_t byte_count() const noexcept { return (num_fields_ + 7 + offset_) / 8; }
bool is_null(const std::uint8_t* null_bitmap_begin, std::size_t field_pos) const noexcept
{
assert(field_pos < num_fields_);
return null_bitmap_begin[byte_pos(field_pos)] & (1 << bit_pos(field_pos));
}
void set_null(std::uint8_t* null_bitmap_begin, std::size_t field_pos) const noexcept
{
assert(field_pos < num_fields_);
null_bitmap_begin[byte_pos(field_pos)] |= (1 << bit_pos(field_pos));
}
};
constexpr std::size_t stmt_execute_null_bitmap_offset = 0;

View File

@ -19,117 +19,117 @@ namespace detail {
// prepare
struct com_stmt_prepare_packet
{
string_eof statement;
string_eof statement;
static constexpr std::uint8_t command_id = 0x16;
static constexpr std::uint8_t command_id = 0x16;
};
template <>
struct get_struct_fields<com_stmt_prepare_packet>
{
static constexpr auto value = std::make_tuple(
&com_stmt_prepare_packet::statement
);
static constexpr auto value = std::make_tuple(
&com_stmt_prepare_packet::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;
// TODO: int1 metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA
// int1 status: must be 0
int4 statement_id;
int2 num_columns;
int2 num_params;
// int1 reserved_1: must be 0
int2 warning_count;
// TODO: int1 metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA
};
template <>
struct get_struct_fields<com_stmt_prepare_ok_packet>
{
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
);
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 <>
struct serialization_traits<com_stmt_prepare_ok_packet, serialization_tag::struct_with_fields> :
noop_serialize<com_stmt_prepare_ok_packet>
noop_serialize<com_stmt_prepare_ok_packet>
{
static inline errc deserialize_(com_stmt_prepare_ok_packet& output,
deserialization_context& ctx) noexcept;
static inline errc deserialize_(com_stmt_prepare_ok_packet& output,
deserialization_context& ctx) noexcept;
};
// execute
template <typename ForwardIterator>
struct com_stmt_execute_packet
{
int4 statement_id;
int1 flags;
int4 iteration_count;
// if num_params > 0: NULL bitmap
int1 new_params_bind_flag;
ForwardIterator params_begin;
ForwardIterator params_end;
int4 statement_id;
int1 flags;
int4 iteration_count;
// if num_params > 0: NULL bitmap
int1 new_params_bind_flag;
ForwardIterator params_begin;
ForwardIterator params_end;
static constexpr std::uint8_t command_id = 0x17;
static constexpr std::uint8_t command_id = 0x17;
};
template <typename ForwardIterator>
struct get_struct_fields<com_stmt_execute_packet<ForwardIterator>>
{
static constexpr auto value = std::make_tuple(
&com_stmt_execute_packet<ForwardIterator>::statement_id,
&com_stmt_execute_packet<ForwardIterator>::flags,
&com_stmt_execute_packet<ForwardIterator>::iteration_count,
&com_stmt_execute_packet<ForwardIterator>::new_params_bind_flag
);
static constexpr auto value = std::make_tuple(
&com_stmt_execute_packet<ForwardIterator>::statement_id,
&com_stmt_execute_packet<ForwardIterator>::flags,
&com_stmt_execute_packet<ForwardIterator>::iteration_count,
&com_stmt_execute_packet<ForwardIterator>::new_params_bind_flag
);
};
template <typename ForwardIterator>
struct serialization_traits<
com_stmt_execute_packet<ForwardIterator>,
serialization_tag::struct_with_fields
com_stmt_execute_packet<ForwardIterator>,
serialization_tag::struct_with_fields
> : noop_deserialize<com_stmt_execute_packet<ForwardIterator>>
{
static inline std::size_t get_size_(const com_stmt_execute_packet<ForwardIterator>& value,
const serialization_context& ctx) noexcept;
static inline void serialize_(const com_stmt_execute_packet<ForwardIterator>& input,
serialization_context& ctx) noexcept;
static inline std::size_t get_size_(const com_stmt_execute_packet<ForwardIterator>& value,
const serialization_context& ctx) noexcept;
static inline void serialize_(const com_stmt_execute_packet<ForwardIterator>& input,
serialization_context& ctx) noexcept;
};
struct com_stmt_execute_param_meta_packet
{
protocol_field_type type;
int1 unsigned_flag;
protocol_field_type type;
int1 unsigned_flag;
};
template <>
struct get_struct_fields<com_stmt_execute_param_meta_packet>
{
static constexpr auto value = std::make_tuple(
&com_stmt_execute_param_meta_packet::type,
&com_stmt_execute_param_meta_packet::unsigned_flag
);
static constexpr auto value = std::make_tuple(
&com_stmt_execute_param_meta_packet::type,
&com_stmt_execute_param_meta_packet::unsigned_flag
);
};
// close
struct com_stmt_close_packet
{
int4 statement_id;
int4 statement_id;
static constexpr std::uint8_t command_id = 0x19;
static constexpr std::uint8_t command_id = 0x19;
};
template <>
struct get_struct_fields<com_stmt_close_packet>
{
static constexpr auto value = std::make_tuple(
&com_stmt_close_packet::statement_id
);
static constexpr auto value = std::make_tuple(
&com_stmt_close_packet::statement_id
);
};

View File

@ -17,17 +17,17 @@ namespace detail {
struct com_query_packet
{
string_eof query;
string_eof query;
static constexpr std::uint8_t command_id = 3;
static constexpr std::uint8_t command_id = 3;
};
template <>
struct get_struct_fields<com_query_packet>
{
static constexpr auto value = std::make_tuple(
&com_query_packet::query
);
static constexpr auto value = std::make_tuple(
&com_query_packet::query
);
};
}

View File

@ -24,11 +24,11 @@ namespace detail {
enum class serialization_tag
{
none,
fixed_size_int,
floating_point,
enumeration,
struct_with_fields
none,
fixed_size_int,
floating_point,
enumeration,
struct_with_fields
};
template <typename T>
@ -40,19 +40,19 @@ struct serialization_traits;
template <typename T>
errc deserialize(T& output, deserialization_context& ctx) noexcept
{
return serialization_traits<T>::deserialize_(output, ctx);
return serialization_traits<T>::deserialize_(output, ctx);
}
template <typename T>
void serialize(const T& input, serialization_context& ctx) noexcept
{
serialization_traits<T>::serialize_(input, ctx);
serialization_traits<T>::serialize_(input, ctx);
}
template <typename T>
std::size_t get_size(const T& input, const serialization_context& ctx) noexcept
{
return serialization_traits<T>::get_size_(input, ctx);
return serialization_traits<T>::get_size_(input, ctx);
}
// Fixed-size integers
@ -62,18 +62,18 @@ constexpr bool is_fixed_size_int();
template <typename T>
struct serialization_traits<T, serialization_tag::fixed_size_int>
{
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept;
static constexpr std::size_t get_size_(T, const serialization_context&) noexcept;
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept;
static constexpr std::size_t get_size_(T, const serialization_context&) noexcept;
};
// int_lenenc
template <>
struct serialization_traits<int_lenenc, serialization_tag::none>
{
static inline errc deserialize_(int_lenenc& output, deserialization_context& ctx) noexcept;
static inline void serialize_(int_lenenc input, serialization_context& ctx) noexcept;
static inline std::size_t get_size_(int_lenenc input, const serialization_context&) noexcept;
static inline errc deserialize_(int_lenenc& output, deserialization_context& ctx) noexcept;
static inline void serialize_(int_lenenc input, serialization_context& ctx) noexcept;
static inline std::size_t get_size_(int_lenenc input, const serialization_context&) noexcept;
};
@ -81,15 +81,15 @@ struct serialization_traits<int_lenenc, serialization_tag::none>
template <std::size_t N>
struct serialization_traits<string_fixed<N>, serialization_tag::none>
{
static errc deserialize_(string_fixed<N>& output, deserialization_context& ctx) noexcept;
static void serialize_(const string_fixed<N>& input, serialization_context& ctx) noexcept
{
ctx.write(input.data(), N);
}
static constexpr std::size_t get_size_(const string_fixed<N>&, const serialization_context&) noexcept
{
return N;
}
static errc deserialize_(string_fixed<N>& output, deserialization_context& ctx) noexcept;
static void serialize_(const string_fixed<N>& input, serialization_context& ctx) noexcept
{
ctx.write(input.data(), N);
}
static constexpr std::size_t get_size_(const string_fixed<N>&, const serialization_context&) noexcept
{
return N;
}
};
@ -97,75 +97,75 @@ struct serialization_traits<string_fixed<N>, serialization_tag::none>
template <>
struct serialization_traits<string_null, serialization_tag::none>
{
static inline errc deserialize_(string_null& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_null input, serialization_context& ctx) noexcept
{
ctx.write(input.value.data(), input.value.size());
ctx.write(0); // null terminator
}
static inline std::size_t get_size_(string_null input, const serialization_context&) noexcept
{
return input.value.size() + 1;
}
static inline errc deserialize_(string_null& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_null input, serialization_context& ctx) noexcept
{
ctx.write(input.value.data(), input.value.size());
ctx.write(0); // null terminator
}
static inline std::size_t get_size_(string_null input, const serialization_context&) noexcept
{
return input.value.size() + 1;
}
};
// string_eof
template <>
struct serialization_traits<string_eof, serialization_tag::none>
{
static inline errc deserialize_(string_eof& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_eof input, serialization_context& ctx) noexcept
{
ctx.write(input.value.data(), input.value.size());
}
static inline std::size_t get_size_(string_eof input, const serialization_context&) noexcept
{
return input.value.size();
}
static inline errc deserialize_(string_eof& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_eof input, serialization_context& ctx) noexcept
{
ctx.write(input.value.data(), input.value.size());
}
static inline std::size_t get_size_(string_eof input, const serialization_context&) noexcept
{
return input.value.size();
}
};
// string_lenenc
template <>
struct serialization_traits<string_lenenc, serialization_tag::none>
{
static inline errc deserialize_(string_lenenc& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_lenenc input, serialization_context& ctx) noexcept
{
serialize(int_lenenc(input.value.size()), ctx);
ctx.write(input.value.data(), input.value.size());
}
static inline std::size_t get_size_(string_lenenc input, const serialization_context& ctx) noexcept
{
return get_size(int_lenenc(input.value.size()), ctx) + input.value.size();
}
static inline errc deserialize_(string_lenenc& output, deserialization_context& ctx) noexcept;
static inline void serialize_(string_lenenc input, serialization_context& ctx) noexcept
{
serialize(int_lenenc(input.value.size()), ctx);
ctx.write(input.value.data(), input.value.size());
}
static inline std::size_t get_size_(string_lenenc input, const serialization_context& ctx) noexcept
{
return get_size(int_lenenc(input.value.size()), ctx) + input.value.size();
}
};
// Enums
template <typename T>
struct serialization_traits<T, serialization_tag::enumeration>
{
using underlying_type = std::underlying_type_t<T>;
using serializable_type = value_holder<underlying_type>;
using underlying_type = std::underlying_type_t<T>;
using serializable_type = value_holder<underlying_type>;
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept
{
serialize(serializable_type(static_cast<underlying_type>(input)), ctx);
}
static std::size_t get_size_(T, const serialization_context&) noexcept;
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept
{
serialize(serializable_type(static_cast<underlying_type>(input)), ctx);
}
static std::size_t get_size_(T, const serialization_context&) noexcept;
};
// Floating points
template <typename T>
struct serialization_traits<T, serialization_tag::floating_point>
{
static_assert(std::numeric_limits<T>::is_iec559);
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept;
static std::size_t get_size_(T, const serialization_context&) noexcept
{
return sizeof(T);
}
static_assert(std::numeric_limits<T>::is_iec559);
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(T input, serialization_context& ctx) noexcept;
static std::size_t get_size_(T, const serialization_context&) noexcept
{
return sizeof(T);
}
};
@ -173,25 +173,25 @@ struct serialization_traits<T, serialization_tag::floating_point>
template <>
struct serialization_traits<date, serialization_tag::none>
{
static inline std::size_t get_size_(const date& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const date& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(date& output, deserialization_context& ctx) noexcept;
static inline std::size_t get_size_(const date& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const date& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(date& output, deserialization_context& ctx) noexcept;
};
template <>
struct serialization_traits<datetime, serialization_tag::none>
{
static inline std::size_t get_size_(const datetime& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const datetime& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(datetime& output, deserialization_context& ctx) noexcept;
static inline std::size_t get_size_(const datetime& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const datetime& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(datetime& output, deserialization_context& ctx) noexcept;
};
template <>
struct serialization_traits<time, serialization_tag::none>
{
static inline std::size_t get_size_(const time& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const time& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(time& output, deserialization_context& ctx) noexcept;
static inline std::size_t get_size_(const time& input, const serialization_context& ctx) noexcept;
static inline void serialize_(const time& input, serialization_context& ctx) noexcept;
static inline errc deserialize_(time& output, deserialization_context& ctx) noexcept;
};
// Structs and commands (messages)
@ -204,7 +204,7 @@ struct not_a_struct_with_fields {}; // Tag indicating a type is not a struct wit
template <typename T>
struct get_struct_fields
{
static constexpr not_a_struct_with_fields value {};
static constexpr not_a_struct_with_fields value {};
};
template <typename T>
@ -213,9 +213,9 @@ constexpr bool is_struct_with_fields();
template <typename T>
struct serialization_traits<T, serialization_tag::struct_with_fields>
{
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(const T& input, serialization_context& ctx) noexcept;
static std::size_t get_size_(const T& input, const serialization_context& ctx) noexcept;
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
static void serialize_(const T& input, serialization_context& ctx) noexcept;
static std::size_t get_size_(const T& input, const serialization_context& ctx) noexcept;
};
// Use these to make all messages implement all methods, leaving the not required
@ -224,28 +224,28 @@ struct serialization_traits<T, serialization_tag::struct_with_fields>
template <typename T>
struct noop_serialize
{
static inline std::size_t get_size_(const T&, const serialization_context&) noexcept { return 0; }
static inline void serialize_(const T&, serialization_context&) noexcept {}
static inline std::size_t get_size_(const T&, const serialization_context&) noexcept { return 0; }
static inline void serialize_(const T&, serialization_context&) noexcept {}
};
template <typename T>
struct noop_deserialize
{
static inline errc deserialize_(T&, deserialization_context&) noexcept { return errc::ok; }
static inline errc deserialize_(T&, deserialization_context&) noexcept { return errc::ok; }
};
// Helper to serialize top-level messages
template <typename Serializable, typename Allocator>
void serialize_message(
const Serializable& input,
capabilities caps,
basic_bytestring<Allocator>& buffer
const Serializable& input,
capabilities caps,
basic_bytestring<Allocator>& buffer
);
template <typename Deserializable>
error_code deserialize_message(
Deserializable& output,
deserialization_context& ctx
Deserializable& output,
deserialization_context& ctx
);
// Helpers for (de) serializing a set of fields
@ -256,7 +256,7 @@ template <typename... Types>
void serialize_fields(serialization_context& ctx, const Types&... fields) noexcept;
inline std::pair<error_code, std::uint8_t> deserialize_message_type(
deserialization_context& ctx
deserialization_context& ctx
);

View File

@ -19,18 +19,18 @@ namespace detail {
class serialization_context
{
std::uint8_t* first_;
capabilities capabilities_;
std::uint8_t* first_;
capabilities capabilities_;
public:
serialization_context(capabilities caps, std::uint8_t* first = nullptr) noexcept:
first_(first), capabilities_(caps) {};
std::uint8_t* first() const noexcept { return first_; }
void set_first(std::uint8_t* new_first) noexcept { first_ = new_first; }
void set_first(boost::asio::mutable_buffer buff) noexcept { first_ = static_cast<std::uint8_t*>(buff.data()); }
void advance(std::size_t size) noexcept { first_ += size; }
capabilities get_capabilities() const noexcept { return capabilities_; }
void write(const void* buffer, std::size_t size) noexcept { memcpy(first_, buffer, size); advance(size); }
void write(std::uint8_t elm) noexcept { *first_ = elm; ++first_; }
serialization_context(capabilities caps, std::uint8_t* first = nullptr) noexcept:
first_(first), capabilities_(caps) {};
std::uint8_t* first() const noexcept { return first_; }
void set_first(std::uint8_t* new_first) noexcept { first_ = new_first; }
void set_first(boost::asio::mutable_buffer buff) noexcept { first_ = static_cast<std::uint8_t*>(buff.data()); }
void advance(std::size_t size) noexcept { first_ += size; }
capabilities get_capabilities() const noexcept { return capabilities_; }
void write(const void* buffer, std::size_t size) noexcept { memcpy(first_, buffer, size); advance(size); }
void write(std::uint8_t elm) noexcept { *first_ = elm; ++first_; }
};
}

View File

@ -19,15 +19,15 @@ namespace mysql {
namespace detail {
inline errc deserialize_text_value(
std::string_view from,
const field_metadata& meta,
value& output
std::string_view from,
const field_metadata& meta,
value& output
);
inline error_code deserialize_text_row(
deserialization_context& ctx,
const std::vector<field_metadata>& meta,
std::vector<value>& output
deserialization_context& ctx,
const std::vector<field_metadata>& meta,
std::vector<value>& output
);
} // detail

View File

@ -18,20 +18,20 @@ namespace detail {
template <typename T>
struct value_holder
{
using value_type = T;
static_assert(std::is_nothrow_default_constructible_v<T>);
using value_type = T;
static_assert(std::is_nothrow_default_constructible_v<T>);
value_type value;
value_type value;
value_holder() noexcept: value{} {};
value_holder() noexcept: value{} {};
template <typename U>
explicit constexpr value_holder(U&& u)
noexcept(std::is_nothrow_constructible_v<T, decltype(u)>):
value(std::forward<U>(u)) {}
template <typename U>
explicit constexpr value_holder(U&& u)
noexcept(std::is_nothrow_constructible_v<T, decltype(u)>):
value(std::forward<U>(u)) {}
constexpr bool operator==(const value_holder<T>& rhs) const { return value == rhs.value; }
constexpr bool operator!=(const value_holder<T>& rhs) const { return value != rhs.value; }
constexpr bool operator==(const value_holder<T>& rhs) const { return value == rhs.value; }
constexpr bool operator!=(const value_holder<T>& rhs) const { return value != rhs.value; }
};
// Operations on value holders

View File

@ -30,20 +30,20 @@ namespace mysql {
*/
enum class errc : int
{
ok = 0, ///< No error.
ok = 0, ///< No error.
// Server returned errors
#include "boost/mysql/impl/server_error_enum.hpp"
// Server returned errors
#include "boost/mysql/impl/server_error_enum.hpp"
// Protocol errors
incomplete_message = 0x10000, ///< An incomplete message was received from the server.
extra_bytes, ///< Unexpected extra bytes at the end of a message were received.
sequence_number_mismatch, ///< A sequence number mismatched happened.
server_unsupported, ///< The server does not support the minimum required capabilities to establish the connection.
protocol_value_error, ///< An unexpected value was found in a server-received message.
unknown_auth_plugin, ///< The user employs an authentication plugin not known to this library.
auth_plugin_requires_ssl, ///< The authentication plugin requires the connection to use SSL.
wrong_num_params ///< The number of parameters passed to the prepared statement does not match the number of actual parameters.
// Protocol errors
incomplete_message = 0x10000, ///< An incomplete message was received from the server.
extra_bytes, ///< Unexpected extra bytes at the end of a message were received.
sequence_number_mismatch, ///< A sequence number mismatched happened.
server_unsupported, ///< The server does not support the minimum required capabilities to establish the connection.
protocol_value_error, ///< An unexpected value was found in a server-received message.
unknown_auth_plugin, ///< The user employs an authentication plugin not known to this library.
auth_plugin_requires_ssl, ///< The authentication plugin requires the connection to use SSL.
wrong_num_params ///< The number of parameters passed to the prepared statement does not match the number of actual parameters.
};
/**
@ -61,22 +61,22 @@ using error_code = boost::system::error_code;
*/
class error_info
{
std::string msg_;
std::string msg_;
public:
/// Default constructor.
error_info() = default;
/// Default constructor.
error_info() = default;
/// Initialization constructor.
error_info(std::string&& err) noexcept: msg_(std::move(err)) {}
/// Initialization constructor.
error_info(std::string&& err) noexcept: msg_(std::move(err)) {}
/// Gets the error message.
const std::string& message() const noexcept { return msg_; }
/// Gets the error message.
const std::string& message() const noexcept { return msg_; }
/// Sets the error message.
void set_message(std::string&& err) { msg_ = std::move(err); }
/// Sets the error message.
void set_message(std::string&& err) { msg_ = std::move(err); }
/// Restores the object to its initial state.
void clear() noexcept { msg_.clear(); }
/// Restores the object to its initial state.
void clear() noexcept { msg_.clear(); }
};
/**

View File

@ -23,32 +23,32 @@ namespace mysql {
*/
enum class field_type
{
tinyint, ///< TINYINT (signed and unsigned).
smallint, ///< SMALLINT (signed and unsigned).
mediumint, ///< MEDIUMINT (signed and unsigned).
int_, ///< Plain INT (signed and unsigned).
bigint, ///< BIGINT (signed and unsigned).
float_, ///< FLOAT (warning: FLOAT(p) where p >= 24 creates a DOUBLE column).
double_, ///< DOUBLE
decimal, ///< DECIMAL
bit, ///< BIT
year, ///< YEAR
time, ///< TIME
date, ///< DATE
datetime, ///< DATETIME
timestamp, ///< TIMESTAMP
char_, ///< CHAR (any length)
varchar, ///< VARCHAR (any length)
binary, ///< BINARY (any length)
varbinary, ///< VARBINARY (any length)
text, ///< TINYTEXT, TEXT, MEDIUMTEXT and LONGTEXT
blob, ///< TINYBLOB, BLOB, MEDIUMBLOB and LONGBLOB
enum_, ///< ENUM
set, ///< SET
geometry, ///< GEOMETRY
tinyint, ///< TINYINT (signed and unsigned).
smallint, ///< SMALLINT (signed and unsigned).
mediumint, ///< MEDIUMINT (signed and unsigned).
int_, ///< Plain INT (signed and unsigned).
bigint, ///< BIGINT (signed and unsigned).
float_, ///< FLOAT (warning: FLOAT(p) where p >= 24 creates a DOUBLE column).
double_, ///< DOUBLE
decimal, ///< DECIMAL
bit, ///< BIT
year, ///< YEAR
time, ///< TIME
date, ///< DATE
datetime, ///< DATETIME
timestamp, ///< TIMESTAMP
char_, ///< CHAR (any length)
varchar, ///< VARCHAR (any length)
binary, ///< BINARY (any length)
varbinary, ///< VARBINARY (any length)
text, ///< TINYTEXT, TEXT, MEDIUMTEXT and LONGTEXT
blob, ///< TINYBLOB, BLOB, MEDIUMBLOB and LONGBLOB
enum_, ///< ENUM
set, ///< SET
geometry, ///< GEOMETRY
unknown, ///< None of the known types; maybe a new MySQL type we have no knowledge of.
_not_computed,
unknown, ///< None of the known types; maybe a new MySQL type we have no knowledge of.
_not_computed,
};
} // mysql

View File

@ -16,147 +16,147 @@
template <typename Stream>
void boost::mysql::connection<Stream>::handshake(
const connection_params& params,
error_code& code,
error_info& info
const connection_params& params,
error_code& code,
error_info& info
)
{
code.clear();
info.clear();
detail::hanshake(channel_, params, code, info);
// TODO: should we close() the stream in case of error?
code.clear();
info.clear();
detail::hanshake(channel_, params, code, info);
// TODO: should we close() the stream in case of error?
}
template <typename Stream>
void boost::mysql::connection<Stream>::handshake(
const connection_params& params
const connection_params& params
)
{
error_code code;
error_info info;
handshake(params, code, info);
detail::check_error_code(code, info);
error_code code;
error_info info;
handshake(params, code, info);
detail::check_error_code(code, info);
}
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::handshake_signature
CompletionToken,
typename boost::mysql::connection<Stream>::handshake_signature
)
boost::mysql::connection<Stream>::async_handshake(
const connection_params& params,
CompletionToken&& token,
error_info* info
const connection_params& params,
CompletionToken&& token,
error_info* info
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, handshake_signature>();
return detail::async_handshake(
channel_,
params,
std::forward<CompletionToken>(token),
info
);
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, handshake_signature>();
return detail::async_handshake(
channel_,
params,
std::forward<CompletionToken>(token),
info
);
}
// Query
template <typename Stream>
boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
std::string_view query_string,
error_code& err,
error_info& info
std::string_view query_string,
error_code& err,
error_info& info
)
{
err.clear();
info.clear();
resultset<Stream> res;
detail::execute_query(channel_, query_string, res, err, info);
return res;
err.clear();
info.clear();
resultset<Stream> res;
detail::execute_query(channel_, query_string, res, err, info);
return res;
}
template <typename Stream>
boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
std::string_view query_string
std::string_view query_string
)
{
resultset<Stream> res;
error_code err;
error_info info;
detail::execute_query(channel_, query_string, res, err, info);
detail::check_error_code(err, info);
return res;
resultset<Stream> res;
error_code err;
error_info info;
detail::execute_query(channel_, query_string, res, err, info);
detail::check_error_code(err, info);
return res;
}
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::query_signature
CompletionToken,
typename boost::mysql::connection<Stream>::query_signature
)
boost::mysql::connection<Stream>::async_query(
std::string_view query_string,
CompletionToken&& token,
error_info* info
std::string_view query_string,
CompletionToken&& token,
error_info* info
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, query_signature>();
return detail::async_execute_query(
channel_,
query_string,
std::forward<CompletionToken>(token),
info
);
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, query_signature>();
return detail::async_execute_query(
channel_,
query_string,
std::forward<CompletionToken>(token),
info
);
}
template <typename Stream>
boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepare_statement(
std::string_view statement,
error_code& err,
error_info& info
std::string_view statement,
error_code& err,
error_info& info
)
{
mysql::prepared_statement<Stream> res;
err.clear();
info.clear();
detail::prepare_statement(channel_, statement, err, info, res);
return res;
mysql::prepared_statement<Stream> res;
err.clear();
info.clear();
detail::prepare_statement(channel_, statement, err, info, res);
return res;
}
template <typename Stream>
boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepare_statement(
std::string_view statement
std::string_view statement
)
{
mysql::prepared_statement<Stream> res;
error_code err;
error_info info;
detail::prepare_statement(channel_, statement, err, info, res);
detail::check_error_code(err, info);
return res;
mysql::prepared_statement<Stream> res;
error_code err;
error_info info;
detail::prepare_statement(channel_, statement, err, info, res);
detail::check_error_code(err, info);
return res;
}
template <typename Stream>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::connection<Stream>::prepare_statement_signature
CompletionToken,
typename boost::mysql::connection<Stream>::prepare_statement_signature
)
boost::mysql::connection<Stream>::async_prepare_statement(
std::string_view statement,
CompletionToken&& token,
error_info* info
std::string_view statement,
CompletionToken&& token,
error_info* info
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, prepare_statement_signature>();
return detail::async_prepare_statement(
channel_,
statement,
std::forward<CompletionToken>(token),
info
);
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, prepare_statement_signature>();
return detail::async_prepare_statement(
channel_,
statement,
std::forward<CompletionToken>(token),
info
);
}

View File

@ -17,7 +17,7 @@ namespace system {
template <>
struct is_error_code_enum<mysql::errc>
{
static constexpr bool value = true;
static constexpr bool value = true;
};
} // system
@ -27,61 +27,61 @@ namespace detail {
inline const char* error_to_string(errc error) noexcept
{
switch (error)
{
case errc::ok: return "no error";
case errc::incomplete_message: return "The message read was incomplete (not enough bytes to fully decode it)";
case errc::extra_bytes: return "Extra bytes at the end of the message";
case errc::sequence_number_mismatch: return "Mismatched sequence numbers";
case errc::server_unsupported: return "The server does not implement the minimum features to be supported";
case errc::protocol_value_error: return "A field in a message had an unexpected value";
case errc::unknown_auth_plugin: return "The user employs an authentication plugin unknown to the client";
case errc::wrong_num_params: return "The provided parameter count does not match the prepared statement parameter count";
switch (error)
{
case errc::ok: return "no error";
case errc::incomplete_message: return "The message read was incomplete (not enough bytes to fully decode it)";
case errc::extra_bytes: return "Extra bytes at the end of the message";
case errc::sequence_number_mismatch: return "Mismatched sequence numbers";
case errc::server_unsupported: return "The server does not implement the minimum features to be supported";
case errc::protocol_value_error: return "A field in a message had an unexpected value";
case errc::unknown_auth_plugin: return "The user employs an authentication plugin unknown to the client";
case errc::wrong_num_params: return "The provided parameter count does not match the prepared statement parameter count";
#include "boost/mysql/impl/server_error_descriptions.hpp"
#include "boost/mysql/impl/server_error_descriptions.hpp"
default: return "<unknown error>";
}
default: return "<unknown error>";
}
}
class mysql_error_category_t : public boost::system::error_category
{
public:
const char* name() const noexcept final override { return "mysql"; }
std::string message(int ev) const final override
{
return error_to_string(static_cast<errc>(ev));
}
const char* name() const noexcept final override { return "mysql"; }
std::string message(int ev) const final override
{
return error_to_string(static_cast<errc>(ev));
}
};
inline mysql_error_category_t mysql_error_category;
inline boost::system::error_code make_error_code(errc error)
{
return boost::system::error_code(static_cast<int>(error), mysql_error_category);
return boost::system::error_code(static_cast<int>(error), mysql_error_category);
}
inline void check_error_code(const error_code& code, const error_info& info)
{
if (code)
{
throw boost::system::system_error(code, info.message());
}
if (code)
{
throw boost::system::system_error(code, info.message());
}
}
inline void conditional_clear(error_info* info) noexcept
{
if (info)
{
info->clear();
}
if (info)
{
info->clear();
}
}
inline void conditional_assign(error_info* to, error_info&& from)
{
if (to)
{
*to = std::move(from);
}
if (to)
{
*to = std::move(from);
}
}
} // detail

View File

@ -13,60 +13,60 @@ namespace mysql {
namespace detail {
inline field_type compute_field_type_string(
std::uint32_t flags
std::uint32_t flags
)
{
if (flags & column_flags::set) return field_type::set;
else if (flags & column_flags::enum_) return field_type::enum_;
else if (flags & column_flags::binary) return field_type::binary;
else return field_type::char_;
if (flags & column_flags::set) return field_type::set;
else if (flags & column_flags::enum_) return field_type::enum_;
else if (flags & column_flags::binary) return field_type::binary;
else return field_type::char_;
}
inline field_type compute_field_type_var_string(
std::uint32_t flags
std::uint32_t flags
)
{
if (flags & column_flags::binary) return field_type::varbinary;
else return field_type::varchar;
if (flags & column_flags::binary) return field_type::varbinary;
else return field_type::varchar;
}
inline field_type compute_field_type_blob(
std::uint32_t flags
std::uint32_t flags
)
{
if (flags & column_flags::binary) return field_type::blob;
else return field_type::text;
if (flags & column_flags::binary) return field_type::blob;
else return field_type::text;
}
inline field_type compute_field_type(
protocol_field_type protocol_type,
std::uint32_t flags
protocol_field_type protocol_type,
std::uint32_t flags
)
{
switch (protocol_type)
{
case protocol_field_type::decimal:
case protocol_field_type::newdecimal:
return field_type::decimal;
case protocol_field_type::geometry: return field_type::geometry;
case protocol_field_type::tiny: return field_type::tinyint;
case protocol_field_type::short_: return field_type::smallint;
case protocol_field_type::int24: return field_type::mediumint;
case protocol_field_type::long_: return field_type::int_;
case protocol_field_type::longlong: return field_type::bigint;
case protocol_field_type::float_: return field_type::float_;
case protocol_field_type::double_: return field_type::double_;
case protocol_field_type::bit: return field_type::bit;
case protocol_field_type::date: return field_type::date;
case protocol_field_type::datetime: return field_type::datetime;
case protocol_field_type::timestamp: return field_type::timestamp;
case protocol_field_type::time: return field_type::time;
case protocol_field_type::year: return field_type::year;
case protocol_field_type::string: return compute_field_type_string(flags);
case protocol_field_type::var_string: return compute_field_type_var_string(flags);
case protocol_field_type::blob: return compute_field_type_blob(flags);
default: return field_type::unknown;
}
switch (protocol_type)
{
case protocol_field_type::decimal:
case protocol_field_type::newdecimal:
return field_type::decimal;
case protocol_field_type::geometry: return field_type::geometry;
case protocol_field_type::tiny: return field_type::tinyint;
case protocol_field_type::short_: return field_type::smallint;
case protocol_field_type::int24: return field_type::mediumint;
case protocol_field_type::long_: return field_type::int_;
case protocol_field_type::longlong: return field_type::bigint;
case protocol_field_type::float_: return field_type::float_;
case protocol_field_type::double_: return field_type::double_;
case protocol_field_type::bit: return field_type::bit;
case protocol_field_type::date: return field_type::date;
case protocol_field_type::datetime: return field_type::datetime;
case protocol_field_type::timestamp: return field_type::timestamp;
case protocol_field_type::time: return field_type::time;
case protocol_field_type::year: return field_type::year;
case protocol_field_type::string: return compute_field_type_string(flags);
case protocol_field_type::var_string: return compute_field_type_var_string(flags);
case protocol_field_type::blob: return compute_field_type_blob(flags);
default: return field_type::unknown;
}
}
} // detail
@ -75,12 +75,12 @@ inline field_type compute_field_type(
inline boost::mysql::field_type boost::mysql::field_metadata::type() const noexcept
{
if (field_type_ == field_type::_not_computed)
{
field_type_ = detail::compute_field_type(msg_.type, msg_.flags.value);
assert(field_type_ != field_type::_not_computed);
}
return field_type_;
if (field_type_ == field_type::_not_computed)
{
field_type_ = detail::compute_field_type(msg_.type, msg_.flags.value);
assert(field_type_ != field_type::_not_computed);
}
return field_type_;
}
#endif

View File

@ -17,160 +17,160 @@
template <typename Stream>
template <typename ForwardIterator>
void boost::mysql::prepared_statement<Stream>::check_num_params(
ForwardIterator first,
ForwardIterator last,
error_code& err,
error_info& info
ForwardIterator first,
ForwardIterator last,
error_code& err,
error_info& info
) const
{
auto param_count = std::distance(first, last);
if (param_count != num_params())
{
err = detail::make_error_code(errc::wrong_num_params);
info.set_message(detail::stringize(
"prepared_statement::execute: expected ", num_params(), " params, but got ", param_count));
}
auto param_count = std::distance(first, last);
if (param_count != num_params())
{
err = detail::make_error_code(errc::wrong_num_params);
info.set_message(detail::stringize(
"prepared_statement::execute: expected ", num_params(), " params, but got ", param_count));
}
}
template <typename Stream>
template <typename ForwardIterator>
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
ForwardIterator params_first,
ForwardIterator params_last,
error_code& err,
error_info& info
ForwardIterator params_first,
ForwardIterator params_last,
error_code& err,
error_info& info
) const
{
assert(valid());
assert(valid());
mysql::resultset<Stream> res;
err.clear();
info.clear();
mysql::resultset<Stream> res;
err.clear();
info.clear();
// Verify we got passed the right number of params
check_num_params(params_first, params_last, err, info);
if (!err)
{
detail::execute_statement(
*channel_,
stmt_msg_.statement_id.value,
params_first,
params_last,
res,
err,
info
);
}
// Verify we got passed the right number of params
check_num_params(params_first, params_last, err, info);
if (!err)
{
detail::execute_statement(
*channel_,
stmt_msg_.statement_id.value,
params_first,
params_last,
res,
err,
info
);
}
return res;
return res;
}
template <typename Stream>
template <typename ForwardIterator>
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
ForwardIterator params_first,
ForwardIterator params_last
ForwardIterator params_first,
ForwardIterator params_last
) const
{
error_code err;
error_info info;
auto res = execute(params_first, params_last, err, info);
detail::check_error_code(err, info);
return res;
error_code err;
error_info info;
auto res = execute(params_first, params_last, err, info);
detail::check_error_code(err, info);
return res;
}
template <typename StreamType>
template <typename ForwardIterator, typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::prepared_statement<StreamType>::execute_signature
CompletionToken,
typename boost::mysql::prepared_statement<StreamType>::execute_signature
)
boost::mysql::prepared_statement<StreamType>::async_execute(
ForwardIterator params_first,
ForwardIterator params_last,
CompletionToken&& token,
error_info* info
ForwardIterator params_first,
ForwardIterator params_last,
CompletionToken&& token,
error_info* info
) const
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, execute_signature>();
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, execute_signature>();
// Check we got passed the right number of params
error_code err;
error_info nonnull_info;
check_num_params(params_first, params_last, err, nonnull_info);
if (err)
{
detail::conditional_assign(info, std::move(nonnull_info));
boost::asio::async_completion<CompletionToken, execute_signature> completion (token);
// TODO: is executor correctly preserved here?
boost::asio::post(
channel_->next_layer().get_executor(),
boost::beast::bind_front_handler(
std::move(completion.completion_handler),
err,
resultset<StreamType>()
)
);
return completion.result.get();
}
else
{
// Actually execute the statement
return detail::async_execute_statement(
*channel_,
stmt_msg_.statement_id.value,
params_first,
params_last,
std::forward<CompletionToken>(token),
info
);
}
// Check we got passed the right number of params
error_code err;
error_info nonnull_info;
check_num_params(params_first, params_last, err, nonnull_info);
if (err)
{
detail::conditional_assign(info, std::move(nonnull_info));
boost::asio::async_completion<CompletionToken, execute_signature> completion (token);
// TODO: is executor correctly preserved here?
boost::asio::post(
channel_->next_layer().get_executor(),
boost::beast::bind_front_handler(
std::move(completion.completion_handler),
err,
resultset<StreamType>()
)
);
return completion.result.get();
}
else
{
// Actually execute the statement
return detail::async_execute_statement(
*channel_,
stmt_msg_.statement_id.value,
params_first,
params_last,
std::forward<CompletionToken>(token),
info
);
}
}
template <typename StreamType>
void boost::mysql::prepared_statement<StreamType>::close(
error_code& code,
error_info& info
error_code& code,
error_info& info
)
{
assert(valid());
code.clear();
info.clear();
detail::close_statement(*channel_, id(), code, info);
assert(valid());
code.clear();
info.clear();
detail::close_statement(*channel_, id(), code, info);
}
template <typename StreamType>
void boost::mysql::prepared_statement<StreamType>::close()
{
assert(valid());
error_code code;
error_info info;
detail::close_statement(*channel_, id(), code, info);
detail::check_error_code(code, info);
assert(valid());
error_code code;
error_info info;
detail::close_statement(*channel_, id(), code, info);
detail::check_error_code(code, info);
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::prepared_statement<StreamType>::close_signature
CompletionToken,
typename boost::mysql::prepared_statement<StreamType>::close_signature
)
boost::mysql::prepared_statement<StreamType>::async_close(
CompletionToken&& token,
error_info* info
CompletionToken&& token,
error_info* info
)
{
assert(valid());
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, close_signature>();
return detail::async_close_statement(
*channel_,
id(),
std::forward<CompletionToken>(token),
info
);
assert(valid());
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, close_signature>();
return detail::async_close_statement(
*channel_,
id(),
std::forward<CompletionToken>(token),
info
);
}
#endif /* INCLUDE_BOOST_MYSQL_IMPL_PREPARED_STATEMENT_HPP_ */

View File

@ -17,322 +17,322 @@
template <typename StreamType>
const boost::mysql::row* boost::mysql::resultset<StreamType>::fetch_one(
error_code& err,
error_info& info
error_code& err,
error_info& info
)
{
assert(valid());
assert(valid());
err.clear();
info.clear();
err.clear();
info.clear();
if (complete())
{
return nullptr;
}
auto result = detail::read_row(
deserializer_,
*channel_,
meta_.fields(),
buffer_,
current_row_.values(),
ok_packet_,
err,
info
);
eof_received_ = result == detail::read_row_result::eof;
return result == detail::read_row_result::row ? &current_row_ : nullptr;
if (complete())
{
return nullptr;
}
auto result = detail::read_row(
deserializer_,
*channel_,
meta_.fields(),
buffer_,
current_row_.values(),
ok_packet_,
err,
info
);
eof_received_ = result == detail::read_row_result::eof;
return result == detail::read_row_result::row ? &current_row_ : nullptr;
}
template <typename StreamType>
const boost::mysql::row* boost::mysql::resultset<StreamType>::fetch_one()
{
error_code code;
error_info info;
const row* res = fetch_one(code, info);
detail::check_error_code(code, info);
return res;
error_code code;
error_info info;
const row* res = fetch_one(code, info);
detail::check_error_code(code, info);
return res;
}
template <typename StreamType>
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_many(
std::size_t count,
error_code& err,
error_info& info
std::size_t count,
error_code& err,
error_info& info
)
{
assert(valid());
assert(valid());
err.clear();
info.clear();
err.clear();
info.clear();
std::vector<mysql::owning_row> res;
std::vector<mysql::owning_row> res;
if (!complete()) // support calling fetch on already exhausted resultset
{
for (std::size_t i = 0; i < count; ++i)
{
detail::bytestring buff;
std::vector<value> values;
if (!complete()) // support calling fetch on already exhausted resultset
{
for (std::size_t i = 0; i < count; ++i)
{
detail::bytestring buff;
std::vector<value> values;
auto result = detail::read_row(
deserializer_,
*channel_,
meta_.fields(),
buff,
values,
ok_packet_,
err,
info
);
eof_received_ = result == detail::read_row_result::eof;
if (result == detail::read_row_result::row)
{
res.emplace_back(std::move(values), std::move(buff));
}
else
{
break;
}
}
}
auto result = detail::read_row(
deserializer_,
*channel_,
meta_.fields(),
buff,
values,
ok_packet_,
err,
info
);
eof_received_ = result == detail::read_row_result::eof;
if (result == detail::read_row_result::row)
{
res.emplace_back(std::move(values), std::move(buff));
}
else
{
break;
}
}
}
return res;
return res;
}
template <typename StreamType>
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_many(
std::size_t count
std::size_t count
)
{
error_code code;
error_info info;
auto res = fetch_many(count, code, info);
detail::check_error_code(code, info);
return res;
error_code code;
error_info info;
auto res = fetch_many(count, code, info);
detail::check_error_code(code, info);
return res;
}
template <typename StreamType>
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_all(
error_code& err,
error_info& info
error_code& err,
error_info& info
)
{
return fetch_many(std::numeric_limits<std::size_t>::max(), err, info);
return fetch_many(std::numeric_limits<std::size_t>::max(), err, info);
}
template <typename StreamType>
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_all()
{
return fetch_many(std::numeric_limits<std::size_t>::max());
return fetch_many(std::numeric_limits<std::size_t>::max());
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_one_signature
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_one_signature
)
boost::mysql::resultset<StreamType>::async_fetch_one(
CompletionToken&& token,
error_info* info
CompletionToken&& token,
error_info* info
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, fetch_one_signature>();
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, fetch_one_signature>();
using HandlerSignature = fetch_one_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using HandlerSignature = fetch_one_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
struct Op: BaseType, boost::asio::coroutine
{
resultset<StreamType>& resultset_;
error_info* output_info_;
struct Op: BaseType, boost::asio::coroutine
{
resultset<StreamType>& resultset_;
error_info* output_info_;
Op(
HandlerType&& handler,
resultset<StreamType>& obj,
error_info* output_info
):
BaseType(std::move(handler), obj.channel_->next_layer().get_executor()),
resultset_(obj),
output_info_(output_info)
{
};
Op(
HandlerType&& handler,
resultset<StreamType>& obj,
error_info* output_info
):
BaseType(std::move(handler), obj.channel_->next_layer().get_executor()),
resultset_(obj),
output_info_(output_info)
{
};
void operator()(
error_code err,
error_info info,
detail::read_row_result result,
bool cont=true
)
{
reenter(*this)
{
if (resultset_.complete())
{
this->complete(cont, error_code(), nullptr);
}
else
{
yield detail::async_read_row(
resultset_.deserializer_,
*resultset_.channel_,
resultset_.meta_.fields(),
resultset_.buffer_,
resultset_.current_row_.values(),
resultset_.ok_packet_,
std::move(*this)
);
resultset_.eof_received_ = result == detail::read_row_result::eof;
detail::conditional_assign(output_info_, std::move(info));
this->complete(
cont,
err,
result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr
);
}
}
}
};
void operator()(
error_code err,
error_info info,
detail::read_row_result result,
bool cont=true
)
{
reenter(*this)
{
if (resultset_.complete())
{
this->complete(cont, error_code(), nullptr);
}
else
{
yield detail::async_read_row(
resultset_.deserializer_,
*resultset_.channel_,
resultset_.meta_.fields(),
resultset_.buffer_,
resultset_.current_row_.values(),
resultset_.ok_packet_,
std::move(*this)
);
resultset_.eof_received_ = result == detail::read_row_result::eof;
detail::conditional_assign(output_info_, std::move(info));
this->complete(
cont,
err,
result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr
);
}
}
}
};
assert(valid());
assert(valid());
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
Op(
std::move(initiator.completion_handler),
*this,
info
)(error_code(), error_info(), detail::read_row_result::error, false);
return initiator.result.get();
Op(
std::move(initiator.completion_handler),
*this,
info
)(error_code(), error_info(), detail::read_row_result::error, false);
return initiator.result.get();
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_many_signature
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_many_signature
)
boost::mysql::resultset<StreamType>::async_fetch_many(
std::size_t count,
CompletionToken&& token,
error_info* info
std::size_t count,
CompletionToken&& token,
error_info* info
)
{
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, fetch_many_signature>();
detail::conditional_clear(info);
detail::check_completion_token<CompletionToken, fetch_many_signature>();
using HandlerSignature = fetch_many_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
using HandlerSignature = fetch_many_signature;
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
struct OpImpl
{
resultset<StreamType>& parent_resultset;
std::vector<owning_row> rows;
detail::bytestring buffer;
std::vector<value> values;
std::size_t remaining;
error_info* output_info_;
struct OpImpl
{
resultset<StreamType>& parent_resultset;
std::vector<owning_row> rows;
detail::bytestring buffer;
std::vector<value> values;
std::size_t remaining;
error_info* output_info_;
OpImpl(resultset<StreamType>& obj, std::size_t count, error_info* output_info):
parent_resultset(obj),
remaining(count),
output_info_(output_info)
{
};
OpImpl(resultset<StreamType>& obj, std::size_t count, error_info* output_info):
parent_resultset(obj),
remaining(count),
output_info_(output_info)
{
};
void row_received()
{
rows.emplace_back(std::move(values), std::move(buffer));
values = std::vector<value>();
buffer = detail::bytestring();
--remaining;
}
};
void row_received()
{
rows.emplace_back(std::move(values), std::move(buffer));
values = std::vector<value>();
buffer = detail::bytestring();
--remaining;
}
};
struct Op: BaseType, boost::asio::coroutine
{
std::shared_ptr<OpImpl> impl_;
struct Op: BaseType, boost::asio::coroutine
{
std::shared_ptr<OpImpl> impl_;
Op(
HandlerType&& handler,
std::shared_ptr<OpImpl>&& impl
):
BaseType(std::move(handler), impl->parent_resultset.channel_->next_layer().get_executor()),
impl_(std::move(impl))
{
};
Op(
HandlerType&& handler,
std::shared_ptr<OpImpl>&& impl
):
BaseType(std::move(handler), impl->parent_resultset.channel_->next_layer().get_executor()),
impl_(std::move(impl))
{
};
void operator()(
error_code err,
error_info info,
detail::read_row_result result,
bool cont=true
)
{
reenter(*this)
{
while (!impl_->parent_resultset.complete() && impl_->remaining > 0)
{
yield detail::async_read_row(
impl_->parent_resultset.deserializer_,
*impl_->parent_resultset.channel_,
impl_->parent_resultset.meta_.fields(),
impl_->buffer,
impl_->values,
impl_->parent_resultset.ok_packet_,
std::move(*this)
);
if (result == detail::read_row_result::error)
{
detail::conditional_assign(impl_->output_info_, std::move(info));
this->complete(cont, err, std::move(impl_->rows));
yield break;
}
else if (result == detail::read_row_result::eof)
{
impl_->parent_resultset.eof_received_ = true;
}
else
{
impl_->row_received();
}
}
this->complete(cont, err, std::move(impl_->rows));
}
}
};
void operator()(
error_code err,
error_info info,
detail::read_row_result result,
bool cont=true
)
{
reenter(*this)
{
while (!impl_->parent_resultset.complete() && impl_->remaining > 0)
{
yield detail::async_read_row(
impl_->parent_resultset.deserializer_,
*impl_->parent_resultset.channel_,
impl_->parent_resultset.meta_.fields(),
impl_->buffer,
impl_->values,
impl_->parent_resultset.ok_packet_,
std::move(*this)
);
if (result == detail::read_row_result::error)
{
detail::conditional_assign(impl_->output_info_, std::move(info));
this->complete(cont, err, std::move(impl_->rows));
yield break;
}
else if (result == detail::read_row_result::eof)
{
impl_->parent_resultset.eof_received_ = true;
}
else
{
impl_->row_received();
}
}
this->complete(cont, err, std::move(impl_->rows));
}
}
};
assert(valid());
assert(valid());
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
Op(
std::move(initiator.completion_handler),
std::make_shared<OpImpl>(*this, count, info)
)(error_code(), error_info(), detail::read_row_result::error, false);
return initiator.result.get();
Op(
std::move(initiator.completion_handler),
std::make_shared<OpImpl>(*this, count, info)
)(error_code(), error_info(), detail::read_row_result::error, false);
return initiator.result.get();
}
template <typename StreamType>
template <typename CompletionToken>
BOOST_ASIO_INITFN_RESULT_TYPE(
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_all_signature
CompletionToken,
typename boost::mysql::resultset<StreamType>::fetch_all_signature
)
boost::mysql::resultset<StreamType>::async_fetch_all(
CompletionToken&& token,
error_info* info
CompletionToken&& token,
error_info* info
)
{
return async_fetch_many(
std::numeric_limits<std::size_t>::max(),
std::forward<CompletionToken>(token),
info
);
return async_fetch_many(
std::numeric_limits<std::size_t>::max(),
std::forward<CompletionToken>(token),
info
);
}

View File

@ -32,35 +32,35 @@ static_assert(time::max() >= max_time);
struct print_visitor
{
std::ostream& os;
std::ostream& os;
print_visitor(std::ostream& os): os(os) {};
print_visitor(std::ostream& os): os(os) {};
template <typename T>
void operator()(const T& value) const { os << value; }
template <typename T>
void operator()(const T& value) const { os << value; }
void operator()(const date& value) const { ::date::operator<<(os, value); }
void operator()(const time& value) const
{
char buffer [100] {};
const char* sign = value < std::chrono::microseconds(0) ? "-" : "";
auto hours = std::abs(std::chrono::duration_cast<std::chrono::hours>(value).count());
auto mins = std::abs(std::chrono::duration_cast<std::chrono::minutes>(value % std::chrono::hours(1)).count());
auto secs = std::abs(std::chrono::duration_cast<std::chrono::seconds>(value % std::chrono::minutes(1)).count());
auto micros = std::abs((value % std::chrono::seconds(1)).count());
void operator()(const date& value) const { ::date::operator<<(os, value); }
void operator()(const time& value) const
{
char buffer [100] {};
const char* sign = value < std::chrono::microseconds(0) ? "-" : "";
auto hours = std::abs(std::chrono::duration_cast<std::chrono::hours>(value).count());
auto mins = std::abs(std::chrono::duration_cast<std::chrono::minutes>(value % std::chrono::hours(1)).count());
auto secs = std::abs(std::chrono::duration_cast<std::chrono::seconds>(value % std::chrono::minutes(1)).count());
auto micros = std::abs((value % std::chrono::seconds(1)).count());
snprintf(buffer, sizeof(buffer), "%s%02d:%02u:%02u:%06u",
sign,
static_cast<int>(hours),
static_cast<unsigned>(mins),
static_cast<unsigned>(secs),
static_cast<unsigned>(micros)
);
snprintf(buffer, sizeof(buffer), "%s%02d:%02u:%02u:%06u",
sign,
static_cast<int>(hours),
static_cast<unsigned>(mins),
static_cast<unsigned>(secs),
static_cast<unsigned>(micros)
);
os << buffer;
}
void operator()(const datetime& value) const { ::date::operator<<(os, value); }
void operator()(std::nullptr_t) const { os << "<NULL>"; }
os << buffer;
}
void operator()(const datetime& value) const { ::date::operator<<(os, value); }
void operator()(std::nullptr_t) const { os << "<NULL>"; }
};
} // detail
@ -68,41 +68,41 @@ struct print_visitor
} // boost
inline bool boost::mysql::operator==(
const value& lhs,
const value& rhs
const value& lhs,
const value& rhs
)
{
if (lhs.index() != rhs.index()) return false;
return std::visit([&lhs](const auto& rhs_value) {
using T = std::decay_t<decltype(rhs_value)>;
return std::get<T>(lhs) == rhs_value;
}, rhs);
if (lhs.index() != rhs.index()) return false;
return std::visit([&lhs](const auto& rhs_value) {
using T = std::decay_t<decltype(rhs_value)>;
return std::get<T>(lhs) == rhs_value;
}, rhs);
}
inline bool boost::mysql::operator==(
const std::vector<value>& lhs,
const std::vector<value>& rhs
const std::vector<value>& lhs,
const std::vector<value>& rhs
)
{
return detail::container_equals(lhs, rhs);
return detail::container_equals(lhs, rhs);
}
inline std::ostream& boost::mysql::operator<<(
std::ostream& os,
const value& value
std::ostream& os,
const value& value
)
{
std::visit(detail::print_visitor(os), value);
return os;
std::visit(detail::print_visitor(os), value);
return os;
}
template <typename... Types>
std::array<boost::mysql::value, sizeof...(Types)>
boost::mysql::make_values(
Types&&... args
Types&&... args
)
{
return std::array<value, sizeof...(Types)>{value(std::forward<Types>(args))...};
return std::array<value, sizeof...(Types)>{value(std::forward<Types>(args))...};
}

View File

@ -23,104 +23,104 @@ namespace mysql {
*/
class field_metadata
{
detail::column_definition_packet msg_;
mutable field_type field_type_ { field_type::_not_computed };
detail::column_definition_packet msg_;
mutable field_type field_type_ { field_type::_not_computed };
bool flag_set(std::uint16_t flag) const noexcept { return msg_.flags.value & flag; }
bool flag_set(std::uint16_t flag) const noexcept { return msg_.flags.value & flag; }
public:
/// Default constructor.
field_metadata() = default;
/// Default constructor.
field_metadata() = default;
// Private, do not use.
field_metadata(const detail::column_definition_packet& msg) noexcept: msg_(msg) {};
// Private, do not use.
field_metadata(const detail::column_definition_packet& msg) noexcept: msg_(msg) {};
/// Returns the name of the database (schema) the field belongs to.
std::string_view database() const noexcept { return msg_.schema.value; }
/// Returns the name of the database (schema) the field belongs to.
std::string_view database() const noexcept { return msg_.schema.value; }
/**
* \brief Returns the name of the virtual table the field belongs to.
* \details If the table was aliased, this will be the name of the alias
* (e.g. in "SELECT * FROM employees emp", table() will be "emp").
*/
std::string_view table() const noexcept { return msg_.table.value; }
/**
* \brief Returns the name of the virtual table the field belongs to.
* \details If the table was aliased, this will be the name of the alias
* (e.g. in "SELECT * FROM employees emp", table() will be "emp").
*/
std::string_view table() const noexcept { return msg_.table.value; }
/**
* \brief Returns the name of the physical table the field belongs to.
* \details E.g. in "SELECT * FROM employees emp",
* original_table() will be "employees".
*/
std::string_view original_table() const noexcept { return msg_.org_table.value; }
/**
* \brief Returns the name of the physical table the field belongs to.
* \details E.g. in "SELECT * FROM employees emp",
* original_table() will be "employees".
*/
std::string_view original_table() const noexcept { return msg_.org_table.value; }
/**
* \brief Returns the actual name of the field.
* \details If the field was aliased, this will be the name of the alias
* (e.g. in "SELECT id AS employee_id FROM employees",
* field_name() will be "employee_id").
*/
std::string_view field_name() const noexcept { return msg_.name.value; }
/**
* \brief Returns the actual name of the field.
* \details If the field was aliased, this will be the name of the alias
* (e.g. in "SELECT id AS employee_id FROM employees",
* field_name() will be "employee_id").
*/
std::string_view field_name() const noexcept { return msg_.name.value; }
/**
* \brief Returns the original (physical) name of the field.
* \details E.g. in "SELECT id AS employee_id FROM employees",
* original_field_name() will be "id".
*/
std::string_view original_field_name() const noexcept { return msg_.org_name.value; }
/**
* \brief Returns the original (physical) name of the field.
* \details E.g. in "SELECT id AS employee_id FROM employees",
* original_field_name() will be "id".
*/
std::string_view original_field_name() const noexcept { return msg_.org_name.value; }
/// Returns the maximum length of the field.
unsigned column_length() const noexcept { return msg_.column_length.value; }
/// Returns the maximum length of the field.
unsigned column_length() const noexcept { return msg_.column_length.value; }
detail::protocol_field_type protocol_type() const noexcept { return msg_.type; }
detail::protocol_field_type protocol_type() const noexcept { return msg_.type; }
/// Returns the type of the field (see field_type for more info).
field_type type() const noexcept;
/// Returns the type of the field (see field_type for more info).
field_type type() const noexcept;
/// Returns the number of decimals of the field.
unsigned decimals() const noexcept { return msg_.decimals.value; }
/// Returns the number of decimals of the field.
unsigned decimals() const noexcept { return msg_.decimals.value; }
/// Returns true if the field is not allowed to be NULL, false if it is nullable.
bool is_not_null() const noexcept { return flag_set(detail::column_flags::not_null); }
/// Returns true if the field is not allowed to be NULL, false if it is nullable.
bool is_not_null() const noexcept { return flag_set(detail::column_flags::not_null); }
/// Returns true if the field is part of a PRIMARY KEY.
bool is_primary_key() const noexcept { return flag_set(detail::column_flags::pri_key); }
/// Returns true if the field is part of a PRIMARY KEY.
bool is_primary_key() const noexcept { return flag_set(detail::column_flags::pri_key); }
/// Returns true if the field is part of a UNIQUE KEY (but not a PRIMARY KEY).
bool is_unique_key() const noexcept { return flag_set(detail::column_flags::unique_key); }
/// Returns true if the field is part of a UNIQUE KEY (but not a PRIMARY KEY).
bool is_unique_key() const noexcept { return flag_set(detail::column_flags::unique_key); }
/// Returns true if the field is part of a KEY (but not a UNIQUE KEY or PRIMARY KEY).
bool is_multiple_key() const noexcept { return flag_set(detail::column_flags::multiple_key); }
/// Returns true if the field is part of a KEY (but not a UNIQUE KEY or PRIMARY KEY).
bool is_multiple_key() const noexcept { return flag_set(detail::column_flags::multiple_key); }
/// Returns true if the field has no sign.
bool is_unsigned() const noexcept { return flag_set(detail::column_flags::unsigned_); }
/// Returns true if the field has no sign.
bool is_unsigned() const noexcept { return flag_set(detail::column_flags::unsigned_); }
/// Returns true if the field is defined as zerofill (padded to its maximum length by zeros).
bool is_zerofill() const noexcept { return flag_set(detail::column_flags::zerofill); }
/// Returns true if the field is defined as zerofill (padded to its maximum length by zeros).
bool is_zerofill() const noexcept { return flag_set(detail::column_flags::zerofill); }
/// Returns true if the field is defined as AUTO_INCREMENT
bool is_auto_increment() const noexcept { return flag_set(detail::column_flags::auto_increment); }
/// Returns true if the field is defined as AUTO_INCREMENT
bool is_auto_increment() const noexcept { return flag_set(detail::column_flags::auto_increment); }
/// Returns true if the field does not have a default value.
bool has_no_default_value() const noexcept { return flag_set(detail::column_flags::no_default_value); }
/// Returns true if the field does not have a default value.
bool has_no_default_value() const noexcept { return flag_set(detail::column_flags::no_default_value); }
/// Returns true if the field is defined as ON UPDATE CURRENT_TIMESTAMP.
bool is_set_to_now_on_update() const noexcept { return flag_set(detail::column_flags::on_update_now); }
/// Returns true if the field is defined as ON UPDATE CURRENT_TIMESTAMP.
bool is_set_to_now_on_update() const noexcept { return flag_set(detail::column_flags::on_update_now); }
};
namespace detail {
class resultset_metadata
{
std::vector<bytestring> buffers_;
std::vector<field_metadata> fields_;
std::vector<bytestring> buffers_;
std::vector<field_metadata> fields_;
public:
resultset_metadata() = default;
resultset_metadata(std::vector<bytestring>&& buffers, std::vector<field_metadata>&& fields):
buffers_(std::move(buffers)), fields_(std::move(fields)) {};
resultset_metadata(const resultset_metadata&) = delete;
resultset_metadata(resultset_metadata&&) = default;
resultset_metadata& operator=(const resultset_metadata&) = delete;
resultset_metadata& operator=(resultset_metadata&&) = default;
~resultset_metadata() = default;
const auto& fields() const noexcept { return fields_; }
resultset_metadata() = default;
resultset_metadata(std::vector<bytestring>&& buffers, std::vector<field_metadata>&& fields):
buffers_(std::move(buffers)), fields_(std::move(fields)) {};
resultset_metadata(const resultset_metadata&) = delete;
resultset_metadata(resultset_metadata&&) = default;
resultset_metadata& operator=(const resultset_metadata&) = delete;
resultset_metadata& operator=(resultset_metadata&&) = default;
~resultset_metadata() = default;
const auto& fields() const noexcept { return fields_; }
};
} // detail

View File

@ -64,122 +64,122 @@ constexpr std::array<value, 0> no_statement_params {};
template <typename Stream>
class prepared_statement
{
detail::channel<Stream>* channel_ {};
detail::com_stmt_prepare_ok_packet stmt_msg_;
detail::channel<Stream>* channel_ {};
detail::com_stmt_prepare_ok_packet stmt_msg_;
template <typename ForwardIterator>
void check_num_params(ForwardIterator first, ForwardIterator last, error_code& err, error_info& info) const;
template <typename ForwardIterator>
void check_num_params(ForwardIterator first, ForwardIterator last, error_code& err, error_info& info) const;
public:
/// Default constructor.
prepared_statement() = default;
/// Default constructor.
prepared_statement() = default;
// Private. Do not use.
prepared_statement(detail::channel<Stream>& chan, const detail::com_stmt_prepare_ok_packet& msg) noexcept:
channel_(&chan), stmt_msg_(msg) {}
// Private. Do not use.
prepared_statement(detail::channel<Stream>& chan, const detail::com_stmt_prepare_ok_packet& msg) noexcept:
channel_(&chan), stmt_msg_(msg) {}
/// Retrieves the stream object associated with the underlying connection.
Stream& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
/// Retrieves the stream object associated with the underlying connection.
Stream& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
/// Retrieves the stream object associated with the underlying connection.
const Stream& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
/// Retrieves the stream object associated with the underlying connection.
const Stream& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
/// Returns true if the statement is not a default-constructed object.
bool valid() const noexcept { return channel_ != nullptr; }
/// Returns true if the statement is not a default-constructed object.
bool valid() const noexcept { return channel_ != nullptr; }
/// Returns a server-side identifier for the statement (unique in a per-connection basis).
unsigned id() const noexcept { assert(valid()); return stmt_msg_.statement_id.value; }
/// Returns a server-side identifier for the statement (unique in a per-connection basis).
unsigned id() const noexcept { assert(valid()); return stmt_msg_.statement_id.value; }
/// Returns the number of parameters that should be passed to execute().
unsigned num_params() const noexcept { assert(valid()); return stmt_msg_.num_params.value; }
/// Returns the number of parameters that should be passed to execute().
unsigned num_params() const noexcept { assert(valid()); return stmt_msg_.num_params.value; }
/**
* \brief Executes a statement (collection, sync with error code version).
* \details Collection should be a sequence for which std::begin() and
* std::end() return forward iterators to a valid boost::mysql::value range.
* Use no_statement_params to execute a statement with no params.
*/
template <typename Collection>
resultset<Stream> execute(const Collection& params, error_code& err, error_info& info) const
{
return execute(std::begin(params), std::end(params), err, info);
}
/**
* \brief Executes a statement (collection, sync with error code version).
* \details Collection should be a sequence for which std::begin() and
* std::end() return forward iterators to a valid boost::mysql::value range.
* Use no_statement_params to execute a statement with no params.
*/
template <typename Collection>
resultset<Stream> execute(const Collection& params, error_code& err, error_info& info) const
{
return execute(std::begin(params), std::end(params), err, info);
}
/// Executes a statement (collection, sync with exceptions code version).
template <typename Collection>
resultset<Stream> execute(const Collection& params) const
{
return execute(std::begin(params), std::end(params));
}
/// Executes a statement (collection, sync with exceptions code version).
template <typename Collection>
resultset<Stream> execute(const Collection& params) const
{
return execute(std::begin(params), std::end(params));
}
/// The handler signature for execute.
using execute_signature = void(error_code, resultset<Stream>);
/// The handler signature for execute.
using execute_signature = void(error_code, resultset<Stream>);
/**
* \brief Executes a statement (collection, sync with exceptions code version).
* \details It is **not** necessary to keep the collection of parameters or the
* values they may point to alive.
*/
template <typename Collection, typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr) const
{
return async_execute(
std::begin(params),
std::end(params),
std::forward<CompletionToken>(token),
info
);
}
/**
* \brief Executes a statement (collection, sync with exceptions code version).
* \details It is **not** necessary to keep the collection of parameters or the
* values they may point to alive.
*/
template <typename Collection, typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr) const
{
return async_execute(
std::begin(params),
std::end(params),
std::forward<CompletionToken>(token),
info
);
}
/**
* \brief Executes a statement (iterator, sync with error code version).
* \details params_first and params_last should point to a valid range, identifying
* the parameters to be used. They should be forward iterators, at least. The value_type
* of these iterators should be convertible to boost::mysql::value.
*/
template <typename ForwardIterator>
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last,
error_code&, error_info&) const;
/**
* \brief Executes a statement (iterator, sync with error code version).
* \details params_first and params_last should point to a valid range, identifying
* the parameters to be used. They should be forward iterators, at least. The value_type
* of these iterators should be convertible to boost::mysql::value.
*/
template <typename ForwardIterator>
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last,
error_code&, error_info&) const;
/// Executes a statement (iterator, sync with exceptions version).
template <typename ForwardIterator>
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last) const;
/// Executes a statement (iterator, sync with exceptions version).
template <typename ForwardIterator>
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last) const;
/**
* \brief Executes a statement (iterator, async version).
* \details The sequence [params_first, params_last) and the values that may be pointed
* by the elements of the sequence need **not** be kept alive.
*/
template <typename ForwardIterator, typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(ForwardIterator params_first, ForwardIterator params_last,
CompletionToken&& token, error_info* info=nullptr) const;
/**
* \brief Executes a statement (iterator, async version).
* \details The sequence [params_first, params_last) and the values that may be pointed
* by the elements of the sequence need **not** be kept alive.
*/
template <typename ForwardIterator, typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
async_execute(ForwardIterator params_first, ForwardIterator params_last,
CompletionToken&& token, error_info* info=nullptr) const;
/**
* \brief Closes a prepared statement, deallocating it from the server (sync with error code version).
* \details This function may be used to free resources from the server.
* Closing the parent connection object causes all prepared statements associated
* with the connection to also be deallocated from the server. Thus, you should be
* using close() when reusing connection objects but creating and destroying prepared
* statements. If you do not reuse connections, or if you just create your prepared
* statements at application startup, it is not required to call this function.
*
* After calling this function, no further functions may be called on this prepared
* statement, other than assignment. Failing to do so results in undefined behavior.
*/
void close(error_code&, error_info&);
/**
* \brief Closes a prepared statement, deallocating it from the server (sync with error code version).
* \details This function may be used to free resources from the server.
* Closing the parent connection object causes all prepared statements associated
* with the connection to also be deallocated from the server. Thus, you should be
* using close() when reusing connection objects but creating and destroying prepared
* statements. If you do not reuse connections, or if you just create your prepared
* statements at application startup, it is not required to call this function.
*
* After calling this function, no further functions may be called on this prepared
* statement, other than assignment. Failing to do so results in undefined behavior.
*/
void close(error_code&, error_info&);
/// Closes a prepared statement, deallocating it from the server (sync with exceptions version).
void close();
/// Closes a prepared statement, deallocating it from the server (sync with exceptions version).
void close();
/// The handler signature for close.
using close_signature = void(error_code);
/// The handler signature for close.
using close_signature = void(error_code);
/// Closes a prepared statement, deallocating it from the server (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, close_signature)
async_close(CompletionToken&& token, error_info* info=nullptr);
/// Closes a prepared statement, deallocating it from the server (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, close_signature)
async_close(CompletionToken&& token, error_info* info=nullptr);
};
/**

View File

@ -62,148 +62,148 @@ namespace mysql {
* resultset, other than assignment. Resultsets are movable but not copyable.
*/
template <
typename StreamType
typename StreamType
>
class resultset
{
using channel_type = detail::channel<StreamType>;
using channel_type = detail::channel<StreamType>;
detail::deserialize_row_fn deserializer_ {};
channel_type* channel_;
detail::resultset_metadata meta_;
row current_row_;
detail::bytestring buffer_;
detail::ok_packet ok_packet_;
bool eof_received_ {false};
detail::deserialize_row_fn deserializer_ {};
channel_type* channel_;
detail::resultset_metadata meta_;
row current_row_;
detail::bytestring buffer_;
detail::ok_packet ok_packet_;
bool eof_received_ {false};
public:
/// Default constructor.
resultset(): channel_(nullptr) {};
/// Default constructor.
resultset(): channel_(nullptr) {};
// Private, do not use
resultset(channel_type& channel, detail::resultset_metadata&& meta, detail::deserialize_row_fn deserializer):
deserializer_(deserializer), channel_(&channel), meta_(std::move(meta)) {};
resultset(channel_type& channel, detail::bytestring&& buffer, const detail::ok_packet& ok_pack):
channel_(&channel), buffer_(std::move(buffer)), ok_packet_(ok_pack), eof_received_(true) {};
// Private, do not use
resultset(channel_type& channel, detail::resultset_metadata&& meta, detail::deserialize_row_fn deserializer):
deserializer_(deserializer), channel_(&channel), meta_(std::move(meta)) {};
resultset(channel_type& channel, detail::bytestring&& buffer, const detail::ok_packet& ok_pack):
channel_(&channel), buffer_(std::move(buffer)), ok_packet_(ok_pack), eof_received_(true) {};
/// Retrieves the stream object associated with the underlying connection.
StreamType& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
/// Retrieves the stream object associated with the underlying connection.
StreamType& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
/// Retrieves the stream object associated with the underlying connection.
const StreamType& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
/// Retrieves the stream object associated with the underlying connection.
const StreamType& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
/**
* \brief Fetches a single row (sync with error code version).
* \details The returned object will be nullptr if there are no more rows
* to be read. Calling fetch_one on a complete resultset returns nullptr.
*
* The returned row points into memory owned by the resultset. Destroying
* or moving the resultset object invalidates the returned row. Calling
* any of the fetch methods again does also invalidate the returned row.
* fetch_one is the fetch method that performs the less memory allocations
* of the three.
*/
const row* fetch_one(error_code& err, error_info& info);
/**
* \brief Fetches a single row (sync with error code version).
* \details The returned object will be nullptr if there are no more rows
* to be read. Calling fetch_one on a complete resultset returns nullptr.
*
* The returned row points into memory owned by the resultset. Destroying
* or moving the resultset object invalidates the returned row. Calling
* any of the fetch methods again does also invalidate the returned row.
* fetch_one is the fetch method that performs the less memory allocations
* of the three.
*/
const row* fetch_one(error_code& err, error_info& info);
/// Fetches a single row (sync with exceptions version).
const row* fetch_one();
/// Fetches a single row (sync with exceptions version).
const row* fetch_one();
/**
* \brief Fetches at most count rows (sync with error code version).
* \details The returned rows are guaranteed to be valid as long as the
* resultset is alive. Contrary to fetch_one, subsequent calls to any of
* the fetch methods do not invalidate the returned rows.
*
* Only if count is **greater** than the available number of rows,
* the resultset will be completed.
*/
std::vector<owning_row> fetch_many(std::size_t count, error_code& err, error_info& info);
/**
* \brief Fetches at most count rows (sync with error code version).
* \details The returned rows are guaranteed to be valid as long as the
* resultset is alive. Contrary to fetch_one, subsequent calls to any of
* the fetch methods do not invalidate the returned rows.
*
* Only if count is **greater** than the available number of rows,
* the resultset will be completed.
*/
std::vector<owning_row> fetch_many(std::size_t count, error_code& err, error_info& info);
/// Fetches at most count rows (sync with exceptions version).
std::vector<owning_row> fetch_many(std::size_t count);
/// Fetches at most count rows (sync with exceptions version).
std::vector<owning_row> fetch_many(std::size_t count);
/**
* \brief Fetches all available rows (sync with error code version).
* \details The returned rows are guaranteed to be valid as long as the
* resultset is alive. Contrary to fetch_one, subsequent calls to any of
* the fetch methods do not invalidate the returned rows.
*
* The resultset is guaranteed to be complete() after this call returns.
*/
std::vector<owning_row> fetch_all(error_code& err, error_info& info);
/**
* \brief Fetches all available rows (sync with error code version).
* \details The returned rows are guaranteed to be valid as long as the
* resultset is alive. Contrary to fetch_one, subsequent calls to any of
* the fetch methods do not invalidate the returned rows.
*
* The resultset is guaranteed to be complete() after this call returns.
*/
std::vector<owning_row> fetch_all(error_code& err, error_info& info);
/// Fetches all available rows (sync with exceptions version).
std::vector<owning_row> fetch_all();
/// Fetches all available rows (sync with exceptions version).
std::vector<owning_row> fetch_all();
/// Handler signature for fetch_one.
using fetch_one_signature = void(error_code, const row*);
/// Handler signature for fetch_one.
using fetch_one_signature = void(error_code, const row*);
/// Fetchs a single row (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_one_signature)
async_fetch_one(CompletionToken&& token, error_info* info=nullptr);
/// Fetchs a single row (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_one_signature)
async_fetch_one(CompletionToken&& token, error_info* info=nullptr);
/// Handler signature for fetch_many.
using fetch_many_signature = void(error_code, std::vector<owning_row>);
/// Handler signature for fetch_many.
using fetch_many_signature = void(error_code, std::vector<owning_row>);
/// Fetches at most count rows (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_many_signature)
async_fetch_many(std::size_t count, CompletionToken&& token, error_info* info=nullptr);
/// Fetches at most count rows (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_many_signature)
async_fetch_many(std::size_t count, CompletionToken&& token, error_info* info=nullptr);
/// Handler signature for fetch_all.
using fetch_all_signature = void(error_code, std::vector<owning_row>);
/// Handler signature for fetch_all.
using fetch_all_signature = void(error_code, std::vector<owning_row>);
/// Fetches all available rows (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_all_signature)
async_fetch_all(CompletionToken&& token, error_info* info=nullptr);
/// Fetches all available rows (async version).
template <typename CompletionToken>
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_all_signature)
async_fetch_all(CompletionToken&& token, error_info* info=nullptr);
/**
* \brief Returns whether this object represents a valid resultset.
* \details Returns false for default-constructed resultsets. It is
* undefined to call any member function on an invalid resultset,
* except assignment.
*/
bool valid() const noexcept { return channel_ != nullptr; }
/**
* \brief Returns whether this object represents a valid resultset.
* \details Returns false for default-constructed resultsets. It is
* undefined to call any member function on an invalid resultset,
* except assignment.
*/
bool valid() const noexcept { return channel_ != nullptr; }
/// Returns whether the resultset has been completely read or not.
bool complete() const noexcept { return eof_received_; }
/// Returns whether the resultset has been completely read or not.
bool complete() const noexcept { return eof_received_; }
/**
* \brief Returns metadata about the fields in the query.
* \details There will be as many field_metadata objects as fields
* in the SQL query, and in the same order. For SQL statements
* that do not return values (like UPDATEs), it will be empty.
* \see field_metadata for more details.
*/
const std::vector<field_metadata>& fields() const noexcept { return meta_.fields(); }
/**
* \brief Returns metadata about the fields in the query.
* \details There will be as many field_metadata objects as fields
* in the SQL query, and in the same order. For SQL statements
* that do not return values (like UPDATEs), it will be empty.
* \see field_metadata for more details.
*/
const std::vector<field_metadata>& fields() const noexcept { return meta_.fields(); }
/**
* \brief The number of rows affected by the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
std::uint64_t affected_rows() const noexcept { assert(complete()); return ok_packet_.affected_rows.value; }
/**
* \brief The number of rows affected by the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
std::uint64_t affected_rows() const noexcept { assert(complete()); return ok_packet_.affected_rows.value; }
/**
* \brief The last insert ID produced by the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
std::uint64_t last_insert_id() const noexcept { assert(complete()); return ok_packet_.last_insert_id.value; }
/**
* \brief The last insert ID produced by the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
std::uint64_t last_insert_id() const noexcept { assert(complete()); return ok_packet_.last_insert_id.value; }
/**
* \brief The number of warnings produced by the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
unsigned warning_count() const noexcept { assert(complete()); return ok_packet_.warnings.value; }
/**
* \brief The number of warnings produced by the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
unsigned warning_count() const noexcept { assert(complete()); return ok_packet_.warnings.value; }
/**
* \brief Additionat text information about the execution of
* the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
std::string_view info() const noexcept { assert(complete()); return ok_packet_.info.value; }
/**
* \brief Additionat text information about the execution of
* the SQL that generated this resultset.
* \warning The resultset **must be complete** before calling this function.
*/
std::string_view info() const noexcept { assert(complete()); return ok_packet_.info.value; }
// TODO: status flags accessors
// TODO: status flags accessors
};
/**

View File

@ -32,17 +32,17 @@ namespace mysql {
*/
class row
{
std::vector<value> values_;
std::vector<value> values_;
public:
/// Default and initializing constructor.
row(std::vector<value>&& values = {}):
values_(std::move(values)) {};
/// Default and initializing constructor.
row(std::vector<value>&& values = {}):
values_(std::move(values)) {};
/// Accessor for the sequence of values.
const std::vector<value>& values() const noexcept { return values_; }
/// Accessor for the sequence of values.
const std::vector<value>& values() const noexcept { return values_; }
/// Accessor for the sequence of values.
std::vector<value>& values() noexcept { return values_; }
/// Accessor for the sequence of values.
std::vector<value>& values() noexcept { return values_; }
};
/**
@ -52,16 +52,16 @@ public:
*/
class owning_row : public row
{
detail::bytestring buffer_;
detail::bytestring buffer_;
public:
owning_row() = default;
owning_row(std::vector<value>&& values, detail::bytestring&& buffer) :
row(std::move(values)), buffer_(std::move(buffer)) {};
owning_row(const owning_row&) = delete;
owning_row(owning_row&&) = default;
owning_row& operator=(const owning_row&) = delete;
owning_row& operator=(owning_row&&) = default;
~owning_row() = default;
owning_row() = default;
owning_row(std::vector<value>&& values, detail::bytestring&& buffer) :
row(std::move(values)), buffer_(std::move(buffer)) {};
owning_row(const owning_row&) = delete;
owning_row(owning_row&&) = default;
owning_row& operator=(const owning_row&) = delete;
owning_row& operator=(owning_row&&) = default;
~owning_row() = default;
};
/**
@ -82,38 +82,38 @@ inline bool operator!=(const row& lhs, const row& rhs) { return !(lhs == rhs); }
*/
inline std::ostream& operator<<(std::ostream& os, const row& value)
{
os << '{';
const auto& arr = value.values();
if (!arr.empty())
{
os << arr[0];
for (auto it = std::next(arr.begin()); it != arr.end(); ++it)
{
os << ", " << *it;
}
}
return os << '}';
os << '{';
const auto& arr = value.values();
if (!arr.empty())
{
os << arr[0];
for (auto it = std::next(arr.begin()); it != arr.end(); ++it)
{
os << ", " << *it;
}
}
return os << '}';
}
// Allow comparisons between vectors of rows and owning rows
template <
typename RowTypeLeft,
typename RowTypeRight,
typename=std::enable_if_t<std::is_base_of_v<row, RowTypeLeft> && std::is_base_of_v<row, RowTypeRight>>
typename RowTypeLeft,
typename RowTypeRight,
typename=std::enable_if_t<std::is_base_of_v<row, RowTypeLeft> && std::is_base_of_v<row, RowTypeRight>>
>
inline bool operator==(const std::vector<RowTypeLeft>& lhs, const std::vector<RowTypeRight>& rhs)
{
return detail::container_equals(lhs, rhs);
return detail::container_equals(lhs, rhs);
}
template <
typename RowTypeLeft,
typename RowTypeRight,
typename=std::enable_if_t<std::is_base_of_v<row, RowTypeLeft> && std::is_base_of_v<row, RowTypeRight>>
typename RowTypeLeft,
typename RowTypeRight,
typename=std::enable_if_t<std::is_base_of_v<row, RowTypeLeft> && std::is_base_of_v<row, RowTypeRight>>
>
inline bool operator!=(const std::vector<RowTypeLeft>& lhs, const std::vector<RowTypeRight>& rhs)
{
return !(lhs == rhs);
return !(lhs == rhs);
}
} // mysql

View File

@ -57,17 +57,17 @@ using time = std::chrono::microseconds;
* They are represented as binary strings.
*/
using value = std::variant<
std::int32_t, // signed TINYINT, SMALLINT, MEDIUMINT, INT
std::int64_t, // signed BIGINT
std::uint32_t, // unsigned TINYINT, SMALLINT, MEDIUMINT, INT, YEAR
std::uint64_t, // unsigned BIGINT
std::string_view, // CHAR, VARCHAR, BINARY, VARBINARY, TEXT (all sizes), BLOB (all sizes), ENUM, SET, DECIMAL, BIT, GEOMTRY
float, // FLOAT
double, // DOUBLE
date, // DATE
datetime, // DATETIME, TIMESTAMP
time, // TIME
std::nullptr_t // Any of the above when the value is NULL
std::int32_t, // signed TINYINT, SMALLINT, MEDIUMINT, INT
std::int64_t, // signed BIGINT
std::uint32_t, // unsigned TINYINT, SMALLINT, MEDIUMINT, INT, YEAR
std::uint64_t, // unsigned BIGINT
std::string_view, // CHAR, VARCHAR, BINARY, VARBINARY, TEXT (all sizes), BLOB (all sizes), ENUM, SET, DECIMAL, BIT, GEOMTRY
float, // FLOAT
double, // DOUBLE
date, // DATE
datetime, // DATETIME, TIMESTAMP
time, // TIME
std::nullptr_t // Any of the above when the value is NULL
>;
/**

View File

@ -22,150 +22,150 @@ endif()
# Unit testing. We do not use gtest_discover_tests because
# of the runtime penalty (specially considerable in integration tests)
add_executable(
mysql_unittests
unit/detail/auth/auth_calculator.cpp
unit/detail/protocol/serialization_test_common.cpp
unit/detail/protocol/serialization.cpp
unit/detail/protocol/common_messages.cpp
unit/detail/protocol/handshake_messages.cpp
unit/detail/protocol/query_messages.cpp
unit/detail/protocol/prepared_statement_messages.cpp
unit/detail/protocol/capabilities.cpp
unit/detail/protocol/text_deserialization.cpp
unit/detail/protocol/binary_deserialization.cpp
unit/detail/protocol/null_bitmap_traits.cpp
unit/metadata.cpp
unit/value.cpp
unit/row.cpp
unit/error.cpp
unit/prepared_statement.cpp
mysql_unittests
unit/detail/auth/auth_calculator.cpp
unit/detail/protocol/serialization_test_common.cpp
unit/detail/protocol/serialization.cpp
unit/detail/protocol/common_messages.cpp
unit/detail/protocol/handshake_messages.cpp
unit/detail/protocol/query_messages.cpp
unit/detail/protocol/prepared_statement_messages.cpp
unit/detail/protocol/capabilities.cpp
unit/detail/protocol/text_deserialization.cpp
unit/detail/protocol/binary_deserialization.cpp
unit/detail/protocol/null_bitmap_traits.cpp
unit/metadata.cpp
unit/value.cpp
unit/row.cpp
unit/error.cpp
unit/prepared_statement.cpp
)
# A codegen issue in MSVC C++17 makes gmock expectations not work
if (NOT MSVC)
target_sources(mysql_unittests PRIVATE unit/detail/protocol/channel.cpp)
target_sources(mysql_unittests PRIVATE unit/detail/protocol/channel.cpp)
endif()
target_include_directories(
mysql_unittests
PRIVATE
common
mysql_unittests
PRIVATE
common
)
target_link_libraries(
mysql_unittests
PRIVATE
gtest
gtest_main
gmock
mysql_asio
mysql_unittests
PRIVATE
gtest
gtest_main
gmock
mysql_asio
)
_mysql_common_target_settings(mysql_unittests)
if (BOOST_MYSQL_VALGRIND_TESTS)
Mysqlvalgrind_AddMemcheckTest(
NAME mysql_unittests_memcheck
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
)
Mysqlvalgrind_AddMemcheckTest(
NAME mysql_unittests_memcheck
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
)
else()
add_test(
NAME mysql_unittests
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
)
add_test(
NAME mysql_unittests
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
)
endif()
# Integration testing
find_package(Boost REQUIRED COMPONENTS coroutine)
add_executable(
mysql_integrationtests
integration/metadata_validator.cpp
integration/network_functions.cpp
integration/handshake.cpp
integration/query.cpp
integration/prepare_statement.cpp
integration/execute_statement.cpp
integration/close_statement.cpp
integration/resultset.cpp
integration/prepared_statement_lifecycle.cpp
integration/database_types.cpp
mysql_integrationtests
integration/metadata_validator.cpp
integration/network_functions.cpp
integration/handshake.cpp
integration/query.cpp
integration/prepare_statement.cpp
integration/execute_statement.cpp
integration/close_statement.cpp
integration/resultset.cpp
integration/prepared_statement_lifecycle.cpp
integration/database_types.cpp
)
target_link_libraries(
mysql_integrationtests
PRIVATE
gtest
gtest_main
gmock
mysql_asio
Boost::coroutine
mysql_integrationtests
PRIVATE
gtest
gtest_main
gmock
mysql_asio
Boost::coroutine
)
target_include_directories(
mysql_integrationtests
PRIVATE
common
mysql_integrationtests
PRIVATE
common
)
_mysql_common_target_settings(mysql_integrationtests)
# Integration test setup
_mysql_sql_setup_fixture(
TEST_NAME mysql_integrationtests_setup
FIXTURE_NAME mysql_integrationtests_fixture
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup.sql
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
TEST_NAME mysql_integrationtests_setup
FIXTURE_NAME mysql_integrationtests_fixture
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup.sql
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
)
# Regular integration tests
add_test(
NAME mysql_integrationtests
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
"--gtest_filter=-*RequiresSha256*" # Exclude anything using SHA256
NAME mysql_integrationtests
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
"--gtest_filter=-*RequiresSha256*" # Exclude anything using SHA256
)
set_tests_properties(
mysql_integrationtests
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
mysql_integrationtests
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
)
# If we are using memcheck, then run a subset of the integration tests
# under valgrind. Coroutine tests don't work well under Valgrind, and
# SSL tests are too slow
if (BOOST_MYSQL_VALGRIND_TESTS)
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
set(GTEST_FILTER "--gtest_filter=-*sslrequire*:*coroutine*")
else()
set(GTEST_FILTER "--gtest_filter=-*RequiresSha256*:*sslrequire*:*coroutine*")
endif()
add_test(
NAME mysql_integrationtests_memcheck
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
${GTEST_FILTER}
)
set_tests_properties(
mysql_integrationtests_memcheck
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
)
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
set(GTEST_FILTER "--gtest_filter=-*sslrequire*:*coroutine*")
else()
set(GTEST_FILTER "--gtest_filter=-*RequiresSha256*:*sslrequire*:*coroutine*")
endif()
add_test(
NAME mysql_integrationtests_memcheck
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
${GTEST_FILTER}
)
set_tests_properties(
mysql_integrationtests_memcheck
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
)
endif()
# SHA256 tests
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
# Setup
_mysql_sql_setup_fixture(
TEST_NAME mysql_integrationtests_sha256_setup
FIXTURE_NAME mysql_integrationtests_sha256_fixture
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup_sha256.sql
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
)
# Actual tests
add_test(
NAME mysql_integrationtests_sha256
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
"--gtest_filter=*RequiresSha256*" # Run only SHA256 stuff
)
set_tests_properties(
mysql_integrationtests_sha256
PROPERTIES FIXTURES_REQUIRED
"mysql_integrationtests_fixture;mysql_integrationtests_sha256_fixture"
)
# Setup
_mysql_sql_setup_fixture(
TEST_NAME mysql_integrationtests_sha256_setup
FIXTURE_NAME mysql_integrationtests_sha256_fixture
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup_sha256.sql
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
)
# Actual tests
add_test(
NAME mysql_integrationtests_sha256
COMMAND
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
"--gtest_filter=*RequiresSha256*" # Run only SHA256 stuff
)
set_tests_properties(
mysql_integrationtests_sha256
PROPERTIES FIXTURES_REQUIRED
"mysql_integrationtests_fixture;mysql_integrationtests_sha256_fixture"
)
endif()

View File

@ -31,146 +31,146 @@ namespace test {
template <typename... Types>
std::vector<value> makevalues(Types&&... args)
{
return std::vector<value>{mysql::value(std::forward<Types>(args))...};
return std::vector<value>{mysql::value(std::forward<Types>(args))...};
}
template <typename... Types>
row makerow(Types&&... args)
{
return row(makevalues(std::forward<Types>(args)...));
return row(makevalues(std::forward<Types>(args)...));
}
template <typename... Types>
std::vector<row> makerows(std::size_t row_size, Types&&... args)
{
auto values = makevalues(std::forward<Types>(args)...);
assert(values.size() % row_size == 0);
std::vector<row> res;
for (std::size_t i = 0; i < values.size(); i += row_size)
{
std::vector<value> row_values (values.begin() + i, values.begin() + i + row_size);
res.push_back(row(std::move(row_values)));
}
return res;
auto values = makevalues(std::forward<Types>(args)...);
assert(values.size() % row_size == 0);
std::vector<row> res;
for (std::size_t i = 0; i < values.size(); i += row_size)
{
std::vector<value> row_values (values.begin() + i, values.begin() + i + row_size);
res.push_back(row(std::move(row_values)));
}
return res;
}
inline date makedate(int years, int months, int days)
{
return mysql::date(::date::year(years)/::date::month(months)/::date::day(days));
return mysql::date(::date::year(years)/::date::month(months)/::date::day(days));
}
inline datetime makedt(int years, int months, int days, int hours=0, int mins=0, int secs=0, int micros=0)
{
return mysql::datetime(mysql::date(::date::year(years)/::date::month(months)/::date::day(days))) +
std::chrono::hours(hours) + std::chrono::minutes(mins) +
std::chrono::seconds(secs) + std::chrono::microseconds(micros);
return mysql::datetime(mysql::date(::date::year(years)/::date::month(months)/::date::day(days))) +
std::chrono::hours(hours) + std::chrono::minutes(mins) +
std::chrono::seconds(secs) + std::chrono::microseconds(micros);
}
inline mysql::time maket(int hours, int mins, int secs, int micros=0)
{
return std::chrono::hours(hours) + std::chrono::minutes(mins)
+ std::chrono::seconds(secs) + std::chrono::microseconds(micros);
return std::chrono::hours(hours) + std::chrono::minutes(mins)
+ std::chrono::seconds(secs) + std::chrono::microseconds(micros);
}
template <std::size_t N>
inline std::string_view makesv(const char (&value) [N])
{
static_assert(N>=1);
return std::string_view(value, N-1); // discard null terminator
static_assert(N>=1);
return std::string_view(value, N-1); // discard null terminator
}
template <std::size_t N>
inline std::string_view makesv(const std::uint8_t (&value) [N])
{
return std::string_view(reinterpret_cast<const char*>(value), N);
return std::string_view(reinterpret_cast<const char*>(value), N);
}
inline std::string_view makesv(const std::uint8_t* value, std::size_t size)
{
return std::string_view(reinterpret_cast<const char*>(value), size);
return std::string_view(reinterpret_cast<const char*>(value), size);
}
inline void validate_string_contains(std::string value, const std::vector<std::string>& to_check)
{
std::transform(value.begin(), value.end(), value.begin(), &tolower);
for (const auto& elm: to_check)
{
EXPECT_THAT(value, testing::HasSubstr(elm));
}
std::transform(value.begin(), value.end(), value.begin(), &tolower);
for (const auto& elm: to_check)
{
EXPECT_THAT(value, testing::HasSubstr(elm));
}
}
inline void validate_error_info(const boost::mysql::error_info& value, const std::vector<std::string>& to_check)
{
validate_string_contains(value.message(), to_check);
validate_string_contains(value.message(), to_check);
}
inline std::string buffer_diff(std::string_view s0, std::string_view s1)
{
std::ostringstream ss;
ss << std::hex;
for (std::size_t i = 0; i < std::min(s0.size(), s1.size()); ++i)
{
unsigned b0 = reinterpret_cast<const std::uint8_t*>(s0.data())[i];
unsigned b1 = reinterpret_cast<const std::uint8_t*>(s1.data())[i];
if (b0 != b1)
{
ss << "i=" << i << ": " << b0 << " != " << b1 << "\n";
}
}
if (s0.size() != s1.size())
{
ss << "sizes: " << s0.size() << " != " << s1.size() << "\n";
}
return ss.str();
std::ostringstream ss;
ss << std::hex;
for (std::size_t i = 0; i < std::min(s0.size(), s1.size()); ++i)
{
unsigned b0 = reinterpret_cast<const std::uint8_t*>(s0.data())[i];
unsigned b1 = reinterpret_cast<const std::uint8_t*>(s1.data())[i];
if (b0 != b1)
{
ss << "i=" << i << ": " << b0 << " != " << b1 << "\n";
}
}
if (s0.size() != s1.size())
{
ss << "sizes: " << s0.size() << " != " << s1.size() << "\n";
}
return ss.str();
}
inline void compare_buffers(std::string_view s0, std::string_view s1, const char* msg = "")
{
EXPECT_EQ(s0, s1) << msg << ":\n" << buffer_diff(s0, s1);
EXPECT_EQ(s0, s1) << msg << ":\n" << buffer_diff(s0, s1);
}
inline void concat(std::vector<std::uint8_t>& lhs, boost::asio::const_buffer rhs)
{
auto current_size = lhs.size();
lhs.resize(current_size + rhs.size());
memcpy(lhs.data() + current_size, rhs.data(), rhs.size());
auto current_size = lhs.size();
lhs.resize(current_size + rhs.size());
memcpy(lhs.data() + current_size, rhs.data(), rhs.size());
}
inline void concat(std::vector<std::uint8_t>& lhs, const std::vector<uint8_t>& rhs)
{
concat(lhs, boost::asio::buffer(rhs));
concat(lhs, boost::asio::buffer(rhs));
}
inline std::vector<std::uint8_t> concat_copy(
std::vector<uint8_t>&& lhs,
const std::vector<uint8_t>& rhs
std::vector<uint8_t>&& lhs,
const std::vector<uint8_t>& rhs
)
{
concat(lhs, rhs);
return std::move(lhs);
concat(lhs, rhs);
return std::move(lhs);
}
inline void check_call(const std::string& command)
{
int code = std::system(command.c_str());
if (code != 0) // we are assuming 0 means success
{
throw std::runtime_error(
detail::stringize("Command '", command, "' returned status code ", code)
);
}
int code = std::system(command.c_str());
if (code != 0) // we are assuming 0 means success
{
throw std::runtime_error(
detail::stringize("Command '", command, "' returned status code ", code)
);
}
}
inline const char* to_string(ssl_mode m)
{
switch (m)
{
case ssl_mode::disable: return "ssldisable";
case ssl_mode::enable: return "sslenable";
case ssl_mode::require: return "sslrequire";
default: assert(false); return "";
}
switch (m)
{
case ssl_mode::disable: return "ssldisable";
case ssl_mode::enable: return "sslenable";
case ssl_mode::require: return "sslrequire";
default: assert(false); return "";
}
}
struct named_param {};
@ -179,11 +179,11 @@ template <typename T, typename=std::enable_if_t<std::is_base_of_v<named_param, T
std::ostream& operator<<(std::ostream& os, const T& v) { return os << v.name; }
constexpr auto test_name_generator = [](const auto& param_info) {
std::ostringstream os;
os << param_info.param;
std::string res = os.str();
std::replace_if(res.begin(), res.end(), [](char c) { return !std::isalnum(c); }, '_');
return res;
std::ostringstream os;
os << param_info.param;
std::string res = os.str();
std::replace_if(res.begin(), res.end(), [](char c) { return !std::isalnum(c); }, '_');
return res;
};
} // test

View File

@ -17,31 +17,31 @@ template <typename Stream>
struct CloseStatementTest : NetworkTest<Stream>
{
void ExistingOrClosedStatement()
{
auto* net = this->GetParam().net;
void ExistingOrClosedStatement()
{
auto* net = this->GetParam().net;
// Prepare a statement
auto stmt = net->prepare_statement(this->conn, "SELECT * FROM empty_table");
stmt.validate_no_error();
// Prepare a statement
auto stmt = net->prepare_statement(this->conn, "SELECT * FROM empty_table");
stmt.validate_no_error();
// Verify it works fine
auto exec_result = net->execute_statement(stmt.value, {});
exec_result.validate_no_error();
exec_result.value.fetch_all(); // clean all packets sent for this execution
// Verify it works fine
auto exec_result = net->execute_statement(stmt.value, {});
exec_result.validate_no_error();
exec_result.value.fetch_all(); // clean all packets sent for this execution
// Close the statement
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
// Close the statement
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
// Close it again
close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
// Close it again
close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
// Verify close took effect
exec_result = net->execute_statement(stmt.value, {});
exec_result.validate_error(boost::mysql::errc::unknown_stmt_handler, {"unknown prepared statement"});
}
// Verify close took effect
exec_result = net->execute_statement(stmt.value, {});
exec_result.validate_error(boost::mysql::errc::unknown_stmt_handler, {"unknown prepared statement"});
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(CloseStatementTest)

View File

@ -32,118 +32,118 @@ namespace
*/
struct database_types_testcase
{
std::string table;
std::string field;
std::string row_id;
value expected_value;
meta_validator mvalid;
std::string table;
std::string field;
std::string row_id;
value expected_value;
meta_validator mvalid;
template <typename ValueType, typename... Args>
database_types_testcase(std::string table, std::string field, std::string row_id,
ValueType&& expected_value, Args&&... args) :
table(table),
field(field),
row_id(move(row_id)),
expected_value(std::forward<ValueType>(expected_value)),
mvalid(move(table), move(field), std::forward<Args>(args)...)
{
}
template <typename ValueType, typename... Args>
database_types_testcase(std::string table, std::string field, std::string row_id,
ValueType&& expected_value, Args&&... args) :
table(table),
field(field),
row_id(move(row_id)),
expected_value(std::forward<ValueType>(expected_value)),
mvalid(move(table), move(field), std::forward<Args>(args)...)
{
}
};
std::ostream& operator<<(std::ostream& os, const database_types_testcase& v)
{
return os << v.table << "." << v.field << "." << v.row_id;
return os << v.table << "." << v.field << "." << v.row_id;
}
// Note: NetworkTest with do_handshake=true requires GetParam() to have an ssl data member
struct DatabaseTypesTest :
NetworkTest<boost::asio::ip::tcp::socket, database_types_testcase, false>
NetworkTest<boost::asio::ip::tcp::socket, database_types_testcase, false>
{
DatabaseTypesTest()
{
handshake(boost::mysql::ssl_mode::disable);
}
DatabaseTypesTest()
{
handshake(boost::mysql::ssl_mode::disable);
}
};
TEST_P(DatabaseTypesTest, Query_MetadataAndValueCorrect)
{
const auto& param = GetParam();
const auto& param = GetParam();
// Compose the query
std::ostringstream queryss;
queryss << "SELECT " << param.field
<< " FROM " << param.table
<< " WHERE id = '" << param.row_id << "'";
auto query = queryss.str();
// Compose the query
std::ostringstream queryss;
queryss << "SELECT " << param.field
<< " FROM " << param.table
<< " WHERE id = '" << param.row_id << "'";
auto query = queryss.str();
// Execute it
auto result = conn.query(query);
auto rows = result.fetch_all();
// Execute it
auto result = conn.query(query);
auto rows = result.fetch_all();
// Validate the received metadata
validate_meta(result.fields(), {param.mvalid});
// Validate the received metadata
validate_meta(result.fields(), {param.mvalid});
// Validate the returned value
row expected_row ({param.expected_value});
ASSERT_EQ(rows.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
// Validate the returned value
row expected_row ({param.expected_value});
ASSERT_EQ(rows.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
}
TEST_P(DatabaseTypesTest, PreparedStatementExecuteResult_MetadataAndValueCorrect)
{
// Parameter
const auto& param = GetParam();
// Parameter
const auto& param = GetParam();
// Prepare the statement
std::ostringstream stmtss;
stmtss << "SELECT " << param.field
<< " FROM " << param.table
<< " WHERE id = ?";
auto stmt_sql = stmtss.str();
auto stmt = conn.prepare_statement(stmt_sql);
// Prepare the statement
std::ostringstream stmtss;
stmtss << "SELECT " << param.field
<< " FROM " << param.table
<< " WHERE id = ?";
auto stmt_sql = stmtss.str();
auto stmt = conn.prepare_statement(stmt_sql);
// Execute it with the provided parameters
auto result = stmt.execute(makevalues(param.row_id));
auto rows = result.fetch_all();
// Execute it with the provided parameters
auto result = stmt.execute(makevalues(param.row_id));
auto rows = result.fetch_all();
// Validate the received metadata
validate_meta(result.fields(), {param.mvalid});
// Validate the received metadata
validate_meta(result.fields(), {param.mvalid});
// Validate the returned value
row expected_row ({param.expected_value});
ASSERT_EQ(rows.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
// Validate the returned value
row expected_row ({param.expected_value});
ASSERT_EQ(rows.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
}
TEST_P(DatabaseTypesTest, PreparedStatementExecuteParam_ValueSerializedCorrectly)
{
// This test is not applicable (yet) to nullptr values or bit values.
// Doing "field = ?" where ? is nullptr never matches anything.
// Bit values are returned as strings bit need to be sent as integers in
// prepared statements. TODO: make this better.
const auto& param = GetParam();
if (param.expected_value == value(nullptr) ||
param.mvalid.type() == field_type::bit)
{
return;
}
// This test is not applicable (yet) to nullptr values or bit values.
// Doing "field = ?" where ? is nullptr never matches anything.
// Bit values are returned as strings bit need to be sent as integers in
// prepared statements. TODO: make this better.
const auto& param = GetParam();
if (param.expected_value == value(nullptr) ||
param.mvalid.type() == field_type::bit)
{
return;
}
// Prepare the statement
std::ostringstream stmtss;
stmtss << "SELECT " << param.field
<< " FROM " << param.table
<< " WHERE id = ? AND " << param.field << " = ?";
auto stmt_sql = stmtss.str();
auto stmt = conn.prepare_statement(stmt_sql);
// Prepare the statement
std::ostringstream stmtss;
stmtss << "SELECT " << param.field
<< " FROM " << param.table
<< " WHERE id = ? AND " << param.field << " = ?";
auto stmt_sql = stmtss.str();
auto stmt = conn.prepare_statement(stmt_sql);
// Execute it with the provided parameters
auto result = stmt.execute(makevalues(param.row_id, param.expected_value));
auto rows = result.fetch_all();
// Execute it with the provided parameters
auto result = stmt.execute(makevalues(param.row_id, param.expected_value));
auto rows = result.fetch_all();
// Validate the returned value
row expected_row ({param.expected_value});
ASSERT_EQ(rows.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
// Validate the returned value
row expected_row ({param.expected_value});
ASSERT_EQ(rows.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
}
using flagsvec = std::vector<meta_validator::flag_getter>;
@ -154,147 +154,147 @@ const flagsvec flags_zerofill { &field_metadata::is_unsigned, &field_metadata::i
// Integers
INSTANTIATE_TEST_SUITE_P(TINYINT, DatabaseTypesTest, Values(
database_types_testcase("types_tinyint", "field_signed", "regular", std::int32_t(20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "negative", std::int32_t(-20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "min", std::int32_t(-0x80), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "max", std::int32_t(0x7f), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "regular", std::int32_t(20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "negative", std::int32_t(-20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "min", std::int32_t(-0x80), field_type::tinyint),
database_types_testcase("types_tinyint", "field_signed", "max", std::int32_t(0x7f), field_type::tinyint),
database_types_testcase("types_tinyint", "field_unsigned", "regular", std::uint32_t(20), field_type::tinyint, flags_unsigned),
database_types_testcase("types_tinyint", "field_unsigned", "min", std::uint32_t(0), field_type::tinyint, flags_unsigned),
database_types_testcase("types_tinyint", "field_unsigned", "max", std::uint32_t(0xff), field_type::tinyint, flags_unsigned),
database_types_testcase("types_tinyint", "field_unsigned", "regular", std::uint32_t(20), field_type::tinyint, flags_unsigned),
database_types_testcase("types_tinyint", "field_unsigned", "min", std::uint32_t(0), field_type::tinyint, flags_unsigned),
database_types_testcase("types_tinyint", "field_unsigned", "max", std::uint32_t(0xff), field_type::tinyint, flags_unsigned),
database_types_testcase("types_tinyint", "field_width", "regular", std::int32_t(20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_width", "negative", std::int32_t(-20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_width", "regular", std::int32_t(20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_width", "negative", std::int32_t(-20), field_type::tinyint),
database_types_testcase("types_tinyint", "field_zerofill", "regular", std::uint32_t(20), field_type::tinyint, flags_zerofill),
database_types_testcase("types_tinyint", "field_zerofill", "min", std::uint32_t(0), field_type::tinyint, flags_zerofill)
database_types_testcase("types_tinyint", "field_zerofill", "regular", std::uint32_t(20), field_type::tinyint, flags_zerofill),
database_types_testcase("types_tinyint", "field_zerofill", "min", std::uint32_t(0), field_type::tinyint, flags_zerofill)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(SMALLINT, DatabaseTypesTest, Values(
database_types_testcase("types_smallint", "field_signed", "regular", std::int32_t(20), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "negative", std::int32_t(-20), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "min", std::int32_t(-0x8000), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "max", std::int32_t(0x7fff), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "regular", std::int32_t(20), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "negative", std::int32_t(-20), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "min", std::int32_t(-0x8000), field_type::smallint),
database_types_testcase("types_smallint", "field_signed", "max", std::int32_t(0x7fff), field_type::smallint),
database_types_testcase("types_smallint", "field_unsigned", "regular", std::uint32_t(20), field_type::smallint, flags_unsigned),
database_types_testcase("types_smallint", "field_unsigned", "min", std::uint32_t(0), field_type::smallint, flags_unsigned),
database_types_testcase("types_smallint", "field_unsigned", "max", std::uint32_t(0xffff), field_type::smallint, flags_unsigned),
database_types_testcase("types_smallint", "field_unsigned", "regular", std::uint32_t(20), field_type::smallint, flags_unsigned),
database_types_testcase("types_smallint", "field_unsigned", "min", std::uint32_t(0), field_type::smallint, flags_unsigned),
database_types_testcase("types_smallint", "field_unsigned", "max", std::uint32_t(0xffff), field_type::smallint, flags_unsigned),
database_types_testcase("types_smallint", "field_width", "regular", std::int32_t(20), field_type::smallint),
database_types_testcase("types_smallint", "field_width", "negative", std::int32_t(-20), field_type::smallint),
database_types_testcase("types_smallint", "field_width", "regular", std::int32_t(20), field_type::smallint),
database_types_testcase("types_smallint", "field_width", "negative", std::int32_t(-20), field_type::smallint),
database_types_testcase("types_smallint", "field_zerofill", "regular", std::uint32_t(20), field_type::smallint, flags_zerofill),
database_types_testcase("types_smallint", "field_zerofill", "min", std::uint32_t(0), field_type::smallint, flags_zerofill)
database_types_testcase("types_smallint", "field_zerofill", "regular", std::uint32_t(20), field_type::smallint, flags_zerofill),
database_types_testcase("types_smallint", "field_zerofill", "min", std::uint32_t(0), field_type::smallint, flags_zerofill)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(MEDIUMINT, DatabaseTypesTest, Values(
database_types_testcase("types_mediumint", "field_signed", "regular", std::int32_t(20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "negative", std::int32_t(-20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "min", std::int32_t(-0x800000), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "max", std::int32_t(0x7fffff), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "regular", std::int32_t(20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "negative", std::int32_t(-20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "min", std::int32_t(-0x800000), field_type::mediumint),
database_types_testcase("types_mediumint", "field_signed", "max", std::int32_t(0x7fffff), field_type::mediumint),
database_types_testcase("types_mediumint", "field_unsigned", "regular", std::uint32_t(20), field_type::mediumint, flags_unsigned),
database_types_testcase("types_mediumint", "field_unsigned", "min", std::uint32_t(0), field_type::mediumint, flags_unsigned),
database_types_testcase("types_mediumint", "field_unsigned", "max", std::uint32_t(0xffffff), field_type::mediumint, flags_unsigned),
database_types_testcase("types_mediumint", "field_unsigned", "regular", std::uint32_t(20), field_type::mediumint, flags_unsigned),
database_types_testcase("types_mediumint", "field_unsigned", "min", std::uint32_t(0), field_type::mediumint, flags_unsigned),
database_types_testcase("types_mediumint", "field_unsigned", "max", std::uint32_t(0xffffff), field_type::mediumint, flags_unsigned),
database_types_testcase("types_mediumint", "field_width", "regular", std::int32_t(20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_width", "negative", std::int32_t(-20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_width", "regular", std::int32_t(20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_width", "negative", std::int32_t(-20), field_type::mediumint),
database_types_testcase("types_mediumint", "field_zerofill", "regular", std::uint32_t(20), field_type::mediumint, flags_zerofill),
database_types_testcase("types_mediumint", "field_zerofill", "min", std::uint32_t(0), field_type::mediumint, flags_zerofill)
database_types_testcase("types_mediumint", "field_zerofill", "regular", std::uint32_t(20), field_type::mediumint, flags_zerofill),
database_types_testcase("types_mediumint", "field_zerofill", "min", std::uint32_t(0), field_type::mediumint, flags_zerofill)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(INT, DatabaseTypesTest, Values(
database_types_testcase("types_int", "field_signed", "regular", std::int32_t(20), field_type::int_),
database_types_testcase("types_int", "field_signed", "negative", std::int32_t(-20), field_type::int_),
database_types_testcase("types_int", "field_signed", "min", std::int32_t(-0x80000000), field_type::int_),
database_types_testcase("types_int", "field_signed", "max", std::int32_t(0x7fffffff), field_type::int_),
database_types_testcase("types_int", "field_signed", "regular", std::int32_t(20), field_type::int_),
database_types_testcase("types_int", "field_signed", "negative", std::int32_t(-20), field_type::int_),
database_types_testcase("types_int", "field_signed", "min", std::int32_t(-0x80000000), field_type::int_),
database_types_testcase("types_int", "field_signed", "max", std::int32_t(0x7fffffff), field_type::int_),
database_types_testcase("types_int", "field_unsigned", "regular", std::uint32_t(20), field_type::int_, flags_unsigned),
database_types_testcase("types_int", "field_unsigned", "min", std::uint32_t(0), field_type::int_, flags_unsigned),
database_types_testcase("types_int", "field_unsigned", "max", std::uint32_t(0xffffffff), field_type::int_, flags_unsigned),
database_types_testcase("types_int", "field_unsigned", "regular", std::uint32_t(20), field_type::int_, flags_unsigned),
database_types_testcase("types_int", "field_unsigned", "min", std::uint32_t(0), field_type::int_, flags_unsigned),
database_types_testcase("types_int", "field_unsigned", "max", std::uint32_t(0xffffffff), field_type::int_, flags_unsigned),
database_types_testcase("types_int", "field_width", "regular", std::int32_t(20), field_type::int_),
database_types_testcase("types_int", "field_width", "negative", std::int32_t(-20), field_type::int_),
database_types_testcase("types_int", "field_width", "regular", std::int32_t(20), field_type::int_),
database_types_testcase("types_int", "field_width", "negative", std::int32_t(-20), field_type::int_),
database_types_testcase("types_int", "field_zerofill", "regular", std::uint32_t(20), field_type::int_, flags_zerofill),
database_types_testcase("types_int", "field_zerofill", "min", std::uint32_t(0), field_type::int_, flags_zerofill)
database_types_testcase("types_int", "field_zerofill", "regular", std::uint32_t(20), field_type::int_, flags_zerofill),
database_types_testcase("types_int", "field_zerofill", "min", std::uint32_t(0), field_type::int_, flags_zerofill)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(BIGINT, DatabaseTypesTest, Values(
database_types_testcase("types_bigint", "field_signed", "regular", std::int64_t(20), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "negative", std::int64_t(-20), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "min", std::int64_t(-0x8000000000000000), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "max", std::int64_t(0x7fffffffffffffff), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "regular", std::int64_t(20), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "negative", std::int64_t(-20), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "min", std::int64_t(-0x8000000000000000), field_type::bigint),
database_types_testcase("types_bigint", "field_signed", "max", std::int64_t(0x7fffffffffffffff), field_type::bigint),
database_types_testcase("types_bigint", "field_unsigned", "regular", std::uint64_t(20), field_type::bigint, flags_unsigned),
database_types_testcase("types_bigint", "field_unsigned", "min", std::uint64_t(0), field_type::bigint, flags_unsigned),
database_types_testcase("types_bigint", "field_unsigned", "max", std::uint64_t(0xffffffffffffffff), field_type::bigint, flags_unsigned),
database_types_testcase("types_bigint", "field_unsigned", "regular", std::uint64_t(20), field_type::bigint, flags_unsigned),
database_types_testcase("types_bigint", "field_unsigned", "min", std::uint64_t(0), field_type::bigint, flags_unsigned),
database_types_testcase("types_bigint", "field_unsigned", "max", std::uint64_t(0xffffffffffffffff), field_type::bigint, flags_unsigned),
database_types_testcase("types_bigint", "field_width", "regular", std::int64_t(20), field_type::bigint),
database_types_testcase("types_bigint", "field_width", "negative", std::int64_t(-20), field_type::bigint),
database_types_testcase("types_bigint", "field_width", "regular", std::int64_t(20), field_type::bigint),
database_types_testcase("types_bigint", "field_width", "negative", std::int64_t(-20), field_type::bigint),
database_types_testcase("types_bigint", "field_zerofill", "regular", std::uint64_t(20), field_type::bigint, flags_zerofill),
database_types_testcase("types_bigint", "field_zerofill", "min", std::uint64_t(0), field_type::bigint, flags_zerofill)
database_types_testcase("types_bigint", "field_zerofill", "regular", std::uint64_t(20), field_type::bigint, flags_zerofill),
database_types_testcase("types_bigint", "field_zerofill", "min", std::uint64_t(0), field_type::bigint, flags_zerofill)
), test_name_generator);
// Floating point
INSTANTIATE_TEST_SUITE_P(FLOAT, DatabaseTypesTest, Values(
database_types_testcase("types_float", "field_signed", "zero", 0.0f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "int_positive", 4.0f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "int_negative", -4.0f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "fractional_positive", 4.2f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "fractional_negative", -4.2f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_positive_int", 3e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_negative_int", -3e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_positive_fractional", 3.14e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_negative_fractional", -3.14e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "negative_exp_positive_fractional", 3.14e-20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "zero", 0.0f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "int_positive", 4.0f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "int_negative", -4.0f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "fractional_positive", 4.2f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "fractional_negative", -4.2f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_positive_int", 3e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_negative_int", -3e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_positive_fractional", 3.14e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "positive_exp_negative_fractional", -3.14e20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_signed", "negative_exp_positive_fractional", 3.14e-20f, field_type::float_, no_flags, 31),
database_types_testcase("types_float", "field_unsigned", "zero", 0.0f, field_type::float_, flags_unsigned, 31),
database_types_testcase("types_float", "field_unsigned", "fractional_positive", 4.2f, field_type::float_, flags_unsigned, 31),
database_types_testcase("types_float", "field_unsigned", "zero", 0.0f, field_type::float_, flags_unsigned, 31),
database_types_testcase("types_float", "field_unsigned", "fractional_positive", 4.2f, field_type::float_, flags_unsigned, 31),
database_types_testcase("types_float", "field_width", "zero", 0.0f, field_type::float_, no_flags, 10),
database_types_testcase("types_float", "field_width", "fractional_positive", 4.2f, field_type::float_, no_flags, 10),
database_types_testcase("types_float", "field_width", "fractional_negative", -4.2f, field_type::float_, no_flags, 10),
database_types_testcase("types_float", "field_width", "zero", 0.0f, field_type::float_, no_flags, 10),
database_types_testcase("types_float", "field_width", "fractional_positive", 4.2f, field_type::float_, no_flags, 10),
database_types_testcase("types_float", "field_width", "fractional_negative", -4.2f, field_type::float_, no_flags, 10),
database_types_testcase("types_float", "field_zerofill", "zero", 0.0f, field_type::float_, flags_zerofill, 31),
database_types_testcase("types_float", "field_zerofill", "fractional_positive", 4.2f, field_type::float_, flags_zerofill, 31),
database_types_testcase("types_float", "field_zerofill", "positive_exp_positive_fractional", 3.14e20f, field_type::float_, flags_zerofill, 31),
database_types_testcase("types_float", "field_zerofill", "negative_exp_positive_fractional", 3.14e-20f, field_type::float_, flags_zerofill, 31)
database_types_testcase("types_float", "field_zerofill", "zero", 0.0f, field_type::float_, flags_zerofill, 31),
database_types_testcase("types_float", "field_zerofill", "fractional_positive", 4.2f, field_type::float_, flags_zerofill, 31),
database_types_testcase("types_float", "field_zerofill", "positive_exp_positive_fractional", 3.14e20f, field_type::float_, flags_zerofill, 31),
database_types_testcase("types_float", "field_zerofill", "negative_exp_positive_fractional", 3.14e-20f, field_type::float_, flags_zerofill, 31)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(DOUBLE, DatabaseTypesTest, Values(
database_types_testcase("types_double", "field_signed", "zero", 0.0, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "int_positive", 4.0, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "int_negative", -4.0, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "fractional_positive", 4.2, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "fractional_negative", -4.2, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_positive_int", 3e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_negative_int", -3e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_positive_fractional", 3.14e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_negative_fractional", -3.14e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "negative_exp_positive_fractional", 3.14e-200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "zero", 0.0, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "int_positive", 4.0, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "int_negative", -4.0, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "fractional_positive", 4.2, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "fractional_negative", -4.2, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_positive_int", 3e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_negative_int", -3e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_positive_fractional", 3.14e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "positive_exp_negative_fractional", -3.14e200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_signed", "negative_exp_positive_fractional", 3.14e-200, field_type::double_, no_flags, 31),
database_types_testcase("types_double", "field_unsigned", "zero", 0.0, field_type::double_, flags_unsigned, 31),
database_types_testcase("types_double", "field_unsigned", "fractional_positive", 4.2, field_type::double_, flags_unsigned, 31),
database_types_testcase("types_double", "field_unsigned", "zero", 0.0, field_type::double_, flags_unsigned, 31),
database_types_testcase("types_double", "field_unsigned", "fractional_positive", 4.2, field_type::double_, flags_unsigned, 31),
database_types_testcase("types_double", "field_width", "zero", 0.0, field_type::double_, no_flags, 10),
database_types_testcase("types_double", "field_width", "fractional_positive", 4.2, field_type::double_, no_flags, 10),
database_types_testcase("types_double", "field_width", "fractional_negative", -4.2, field_type::double_, no_flags, 10),
database_types_testcase("types_double", "field_width", "zero", 0.0, field_type::double_, no_flags, 10),
database_types_testcase("types_double", "field_width", "fractional_positive", 4.2, field_type::double_, no_flags, 10),
database_types_testcase("types_double", "field_width", "fractional_negative", -4.2, field_type::double_, no_flags, 10),
database_types_testcase("types_double", "field_zerofill", "zero", 0.0, field_type::double_, flags_zerofill, 31),
database_types_testcase("types_double", "field_zerofill", "fractional_positive", 4.2, field_type::double_, flags_zerofill, 31),
database_types_testcase("types_double", "field_zerofill", "positive_exp_positive_fractional", 3.14e200, field_type::double_, flags_zerofill, 31),
database_types_testcase("types_double", "field_zerofill", "negative_exp_positive_fractional", 3.14e-200, field_type::double_, flags_zerofill, 31)
database_types_testcase("types_double", "field_zerofill", "zero", 0.0, field_type::double_, flags_zerofill, 31),
database_types_testcase("types_double", "field_zerofill", "fractional_positive", 4.2, field_type::double_, flags_zerofill, 31),
database_types_testcase("types_double", "field_zerofill", "positive_exp_positive_fractional", 3.14e200, field_type::double_, flags_zerofill, 31),
database_types_testcase("types_double", "field_zerofill", "negative_exp_positive_fractional", 3.14e-200, field_type::double_, flags_zerofill, 31)
), test_name_generator);
// Dates and times
INSTANTIATE_TEST_SUITE_P(DATE, DatabaseTypesTest, Values(
database_types_testcase("types_date", "field_date", "regular", 2010_y/3/28_d, field_type::date),
database_types_testcase("types_date", "field_date", "leap", 1788_y/2/29_d, field_type::date),
database_types_testcase("types_date", "field_date", "min", 1000_y/1/1_d, field_type::date),
database_types_testcase("types_date", "field_date", "max", 9999_y/12/31_d, field_type::date)
database_types_testcase("types_date", "field_date", "regular", 2010_y/3/28_d, field_type::date),
database_types_testcase("types_date", "field_date", "leap", 1788_y/2/29_d, field_type::date),
database_types_testcase("types_date", "field_date", "min", 1000_y/1/1_d, field_type::date),
database_types_testcase("types_date", "field_date", "max", 9999_y/12/31_d, field_type::date)
), test_name_generator);
// Infrastructure to generate DATETIME and TIMESTAMP test cases
@ -302,182 +302,182 @@ INSTANTIATE_TEST_SUITE_P(DATE, DatabaseTypesTest, Values(
// Given a number of microseconds, removes the least significant part according to decimals
int round_micros(int input, int decimals)
{
assert(decimals >= 0 && decimals <= 6);
if (decimals == 0) return 0;
auto modulus = static_cast<int>(std::pow(10, 6 - decimals));
return (input / modulus) * modulus;
assert(decimals >= 0 && decimals <= 6);
if (decimals == 0) return 0;
auto modulus = static_cast<int>(std::pow(10, 6 - decimals));
return (input / modulus) * modulus;
}
std::chrono::microseconds round_micros(std::chrono::microseconds input, int decimals)
{
return std::chrono::microseconds(round_micros(static_cast<int>(input.count()), decimals));
return std::chrono::microseconds(round_micros(static_cast<int>(input.count()), decimals));
}
std::pair<std::string, datetime> datetime_from_id(std::bitset<4> id, int decimals)
{
// id represents which components (h, m, s, u) should the test case have
constexpr struct
{
char letter;
std::chrono::microseconds offset;
} bit_meaning [] = {
{ 'h', std::chrono::hours(23) }, // bit 0
{ 'm', std::chrono::minutes(1) },
{ 's', std::chrono::seconds(50) },
{ 'u', std::chrono::microseconds(123456) }
};
// id represents which components (h, m, s, u) should the test case have
constexpr struct
{
char letter;
std::chrono::microseconds offset;
} bit_meaning [] = {
{ 'h', std::chrono::hours(23) }, // bit 0
{ 'm', std::chrono::minutes(1) },
{ 's', std::chrono::seconds(50) },
{ 'u', std::chrono::microseconds(123456) }
};
std::string name;
datetime dt = makedt(2010, 5, 2); // components all tests have
std::string name;
datetime dt = makedt(2010, 5, 2); // components all tests have
for (std::size_t i = 0; i < id.size(); ++i)
{
if (id[i]) // component present
{
char letter = bit_meaning[i].letter;
auto offset = bit_meaning[i].offset;
name.push_back(letter); // add to name
dt += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
}
}
if (name.empty()) name = "date";
for (std::size_t i = 0; i < id.size(); ++i)
{
if (id[i]) // component present
{
char letter = bit_meaning[i].letter;
auto offset = bit_meaning[i].offset;
name.push_back(letter); // add to name
dt += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
}
}
if (name.empty()) name = "date";
return {name, dt};
return {name, dt};
}
database_types_testcase create_datetime_testcase(
int decimals,
std::string id,
value expected,
field_type type
int decimals,
std::string id,
value expected,
field_type type
)
{
static std::unordered_map<field_type, const char*> table_map {
{ field_type::datetime, "types_datetime" },
{ field_type::timestamp, "types_timestamp" },
{ field_type::time, "types_time" }
};
// Inconsistencies between Maria and MySQL in the unsigned flag
// we don't really care here about signedness of timestamps
return database_types_testcase(
table_map.at(type),
"field_" + std::to_string(decimals),
std::move(id),
expected,
type,
no_flags,
decimals,
flagsvec{ &field_metadata::is_unsigned }
);
static std::unordered_map<field_type, const char*> table_map {
{ field_type::datetime, "types_datetime" },
{ field_type::timestamp, "types_timestamp" },
{ field_type::time, "types_time" }
};
// Inconsistencies between Maria and MySQL in the unsigned flag
// we don't really care here about signedness of timestamps
return database_types_testcase(
table_map.at(type),
"field_" + std::to_string(decimals),
std::move(id),
expected,
type,
no_flags,
decimals,
flagsvec{ &field_metadata::is_unsigned }
);
}
std::pair<std::string, boost::mysql::time> time_from_id(std::bitset<6> id, int decimals)
{
// id represents which components (h, m, s, u) should the test case have
constexpr struct
{
char letter;
std::chrono::microseconds offset;
} bit_meaning [] = {
{ 'n', std::chrono::hours(0) }, // bit 0: negative bit
{ 'd', std::chrono::hours(48) }, // bit 1
{ 'h', std::chrono::hours(23) }, // bit 2
{ 'm', std::chrono::minutes(1) },
{ 's', std::chrono::seconds(50) },
{ 'u', std::chrono::microseconds(123456) }
};
// id represents which components (h, m, s, u) should the test case have
constexpr struct
{
char letter;
std::chrono::microseconds offset;
} bit_meaning [] = {
{ 'n', std::chrono::hours(0) }, // bit 0: negative bit
{ 'd', std::chrono::hours(48) }, // bit 1
{ 'h', std::chrono::hours(23) }, // bit 2
{ 'm', std::chrono::minutes(1) },
{ 's', std::chrono::seconds(50) },
{ 'u', std::chrono::microseconds(123456) }
};
std::string name;
boost::mysql::time t {0};
std::string name;
boost::mysql::time t {0};
for (std::size_t i = 1; i < id.size(); ++i)
{
if (id[i]) // component present
{
char letter = bit_meaning[i].letter;
auto offset = bit_meaning[i].offset;
name.push_back(letter); // add to name
t += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
}
}
if (name.empty()) name = "zero";
if (id[0]) // bit sign
{
name = "negative_" + name;
t = -t;
}
for (std::size_t i = 1; i < id.size(); ++i)
{
if (id[i]) // component present
{
char letter = bit_meaning[i].letter;
auto offset = bit_meaning[i].offset;
name.push_back(letter); // add to name
t += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
}
}
if (name.empty()) name = "zero";
if (id[0]) // bit sign
{
name = "negative_" + name;
t = -t;
}
return {name, t};
return {name, t};
}
// shared between DATETIME and TIMESTAMP
std::vector<database_types_testcase> generate_common_datetime_cases(
field_type type
field_type type
)
{
std::vector<database_types_testcase> res;
std::vector<database_types_testcase> res;
for (int decimals = 0; decimals <= 6; ++decimals)
{
// Regular values
auto max_int_id = static_cast<std::size_t>(std::pow(2, 4)); // 4 components can be varied
for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
{
std::bitset<4> bitset_id (int_id);
if (bitset_id[3] && decimals == 0) continue; // cases with micros don't make sense for fields with no decimals
auto [id, value] = datetime_from_id(int_id, decimals);
res.push_back(create_datetime_testcase(decimals, move(id), value, type));
}
}
for (int decimals = 0; decimals <= 6; ++decimals)
{
// Regular values
auto max_int_id = static_cast<std::size_t>(std::pow(2, 4)); // 4 components can be varied
for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
{
std::bitset<4> bitset_id (int_id);
if (bitset_id[3] && decimals == 0) continue; // cases with micros don't make sense for fields with no decimals
auto [id, value] = datetime_from_id(int_id, decimals);
res.push_back(create_datetime_testcase(decimals, move(id), value, type));
}
}
return res;
return res;
}
std::vector<database_types_testcase> generate_datetime_cases()
{
auto res = generate_common_datetime_cases(field_type::datetime);
auto res = generate_common_datetime_cases(field_type::datetime);
// min and max
for (int decimals = 0; decimals <= 6; ++decimals)
{
res.push_back(create_datetime_testcase(decimals, "min",
makedt(1000, 1, 1), field_type::datetime));
res.push_back(create_datetime_testcase(decimals, "max",
makedt(9999, 12, 31, 23, 59, 59, round_micros(999999, decimals)), field_type::datetime));
}
// min and max
for (int decimals = 0; decimals <= 6; ++decimals)
{
res.push_back(create_datetime_testcase(decimals, "min",
makedt(1000, 1, 1), field_type::datetime));
res.push_back(create_datetime_testcase(decimals, "max",
makedt(9999, 12, 31, 23, 59, 59, round_micros(999999, decimals)), field_type::datetime));
}
return res;
return res;
}
std::vector<database_types_testcase> generate_timestamp_cases()
{
return generate_common_datetime_cases(field_type::timestamp);
return generate_common_datetime_cases(field_type::timestamp);
}
std::vector<database_types_testcase> generate_time_cases()
{
std::vector<database_types_testcase> res;
std::vector<database_types_testcase> res;
for (int decimals = 0; decimals <= 6; ++decimals)
{
// Regular values
auto max_int_id = static_cast<std::size_t>(std::pow(2, 6)); // 6 components can be varied
for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
{
std::bitset<6> bitset_id (int_id);
if (bitset_id[5] && decimals == 0) continue; // cases with micros don't make sense for fields with no decimals
if (bitset_id.to_ulong() == 1) continue; // negative zero does not make sense
auto [id, value] = time_from_id(int_id, decimals);
res.push_back(create_datetime_testcase(decimals, move(id), value, field_type::time));
}
for (int decimals = 0; decimals <= 6; ++decimals)
{
// Regular values
auto max_int_id = static_cast<std::size_t>(std::pow(2, 6)); // 6 components can be varied
for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
{
std::bitset<6> bitset_id (int_id);
if (bitset_id[5] && decimals == 0) continue; // cases with micros don't make sense for fields with no decimals
if (bitset_id.to_ulong() == 1) continue; // negative zero does not make sense
auto [id, value] = time_from_id(int_id, decimals);
res.push_back(create_datetime_testcase(decimals, move(id), value, field_type::time));
}
// min and max
auto max_value = decimals == 0 ? maket(838, 59, 59) : maket(838, 59, 58, round_micros(999999, decimals));
res.push_back(create_datetime_testcase(decimals, "min", -max_value, field_type::time));
res.push_back(create_datetime_testcase(decimals, "max", max_value, field_type::time));
}
// min and max
auto max_value = decimals == 0 ? maket(838, 59, 59) : maket(838, 59, 58, round_micros(999999, decimals));
res.push_back(create_datetime_testcase(decimals, "min", -max_value, field_type::time));
res.push_back(create_datetime_testcase(decimals, "max", max_value, field_type::time));
}
return res;
return res;
}
@ -486,98 +486,98 @@ INSTANTIATE_TEST_SUITE_P(TIMESTAMP, DatabaseTypesTest, ValuesIn(generate_timesta
INSTANTIATE_TEST_SUITE_P(TIME, DatabaseTypesTest, ValuesIn(generate_time_cases()), test_name_generator);
INSTANTIATE_TEST_SUITE_P(YEAR, DatabaseTypesTest, Values(
database_types_testcase("types_year", "field_default", "regular", std::uint32_t(2019), field_type::year, flags_zerofill),
database_types_testcase("types_year", "field_default", "min", std::uint32_t(1901), field_type::year, flags_zerofill),
database_types_testcase("types_year", "field_default", "max", std::uint32_t(2155), field_type::year, flags_zerofill),
database_types_testcase("types_year", "field_default", "zero", std::uint32_t(0), field_type::year, flags_zerofill)
database_types_testcase("types_year", "field_default", "regular", std::uint32_t(2019), field_type::year, flags_zerofill),
database_types_testcase("types_year", "field_default", "min", std::uint32_t(1901), field_type::year, flags_zerofill),
database_types_testcase("types_year", "field_default", "max", std::uint32_t(2155), field_type::year, flags_zerofill),
database_types_testcase("types_year", "field_default", "zero", std::uint32_t(0), field_type::year, flags_zerofill)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(STRING, DatabaseTypesTest, Values(
database_types_testcase("types_string", "field_char", "regular", "test_char", field_type::char_),
database_types_testcase("types_string", "field_char", "utf8", u8"\u00F1", field_type::char_),
database_types_testcase("types_string", "field_char", "empty", "", field_type::char_),
database_types_testcase("types_string", "field_char", "regular", "test_char", field_type::char_),
database_types_testcase("types_string", "field_char", "utf8", u8"\u00F1", field_type::char_),
database_types_testcase("types_string", "field_char", "empty", "", field_type::char_),
database_types_testcase("types_string", "field_varchar", "regular", "test_varchar", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "utf8", u8"\u00D1", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "empty", "", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "regular", "test_varchar", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "utf8", u8"\u00D1", field_type::varchar),
database_types_testcase("types_string", "field_varchar", "empty", "", field_type::varchar),
database_types_testcase("types_string", "field_tinytext", "regular", "test_tinytext", field_type::text),
database_types_testcase("types_string", "field_tinytext", "utf8", u8"\u00e1", field_type::text),
database_types_testcase("types_string", "field_tinytext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_tinytext", "regular", "test_tinytext", field_type::text),
database_types_testcase("types_string", "field_tinytext", "utf8", u8"\u00e1", field_type::text),
database_types_testcase("types_string", "field_tinytext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_text", "regular", "test_text", field_type::text),
database_types_testcase("types_string", "field_text", "utf8", u8"\u00e9", field_type::text),
database_types_testcase("types_string", "field_text", "empty", "", field_type::text),
database_types_testcase("types_string", "field_text", "regular", "test_text", field_type::text),
database_types_testcase("types_string", "field_text", "utf8", u8"\u00e9", field_type::text),
database_types_testcase("types_string", "field_text", "empty", "", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "regular", "test_mediumtext", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "utf8", u8"\u00ed", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "regular", "test_mediumtext", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "utf8", u8"\u00ed", field_type::text),
database_types_testcase("types_string", "field_mediumtext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_longtext", "regular", "test_longtext", field_type::text),
database_types_testcase("types_string", "field_longtext", "utf8", u8"\u00f3", field_type::text),
database_types_testcase("types_string", "field_longtext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_longtext", "regular", "test_longtext", field_type::text),
database_types_testcase("types_string", "field_longtext", "utf8", u8"\u00f3", field_type::text),
database_types_testcase("types_string", "field_longtext", "empty", "", field_type::text),
database_types_testcase("types_string", "field_enum", "regular", "red", field_type::enum_),
database_types_testcase("types_string", "field_enum", "regular", "red", field_type::enum_),
database_types_testcase("types_string", "field_set", "regular", "red,green", field_type::set),
database_types_testcase("types_string", "field_set", "empty", "", field_type::set)
database_types_testcase("types_string", "field_set", "regular", "red,green", field_type::set),
database_types_testcase("types_string", "field_set", "empty", "", field_type::set)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(BINARY, DatabaseTypesTest, Values(
// BINARY values get padded with zeros to the declared length
database_types_testcase("types_binary", "field_binary", "regular", makesv("\0_binary\0\0"), field_type::binary),
database_types_testcase("types_binary", "field_binary", "nonascii", makesv("\0\xff" "\0\0\0\0\0\0\0\0"), field_type::binary),
database_types_testcase("types_binary", "field_binary", "empty", makesv("\0\0\0\0\0\0\0\0\0\0"), field_type::binary),
// BINARY values get padded with zeros to the declared length
database_types_testcase("types_binary", "field_binary", "regular", makesv("\0_binary\0\0"), field_type::binary),
database_types_testcase("types_binary", "field_binary", "nonascii", makesv("\0\xff" "\0\0\0\0\0\0\0\0"), field_type::binary),
database_types_testcase("types_binary", "field_binary", "empty", makesv("\0\0\0\0\0\0\0\0\0\0"), field_type::binary),
database_types_testcase("types_binary", "field_varbinary", "regular", makesv("\0_varbinary"), field_type::varbinary),
database_types_testcase("types_binary", "field_varbinary", "nonascii", makesv("\1\xfe"), field_type::varbinary),
database_types_testcase("types_binary", "field_varbinary", "empty", "", field_type::varbinary),
database_types_testcase("types_binary", "field_varbinary", "regular", makesv("\0_varbinary"), field_type::varbinary),
database_types_testcase("types_binary", "field_varbinary", "nonascii", makesv("\1\xfe"), field_type::varbinary),
database_types_testcase("types_binary", "field_varbinary", "empty", "", field_type::varbinary),
database_types_testcase("types_binary", "field_tinyblob", "regular", makesv("\0_tinyblob"), field_type::blob),
database_types_testcase("types_binary", "field_tinyblob", "nonascii", makesv("\2\xfd"), field_type::blob),
database_types_testcase("types_binary", "field_tinyblob", "empty", "", field_type::blob),
database_types_testcase("types_binary", "field_tinyblob", "regular", makesv("\0_tinyblob"), field_type::blob),
database_types_testcase("types_binary", "field_tinyblob", "nonascii", makesv("\2\xfd"), field_type::blob),
database_types_testcase("types_binary", "field_tinyblob", "empty", "", field_type::blob),
database_types_testcase("types_binary", "field_blob", "regular", makesv("\0_blob"), field_type::blob),
database_types_testcase("types_binary", "field_blob", "nonascii", makesv("\3\xfc"), field_type::blob),
database_types_testcase("types_binary", "field_blob", "empty", "", field_type::blob),
database_types_testcase("types_binary", "field_blob", "regular", makesv("\0_blob"), field_type::blob),
database_types_testcase("types_binary", "field_blob", "nonascii", makesv("\3\xfc"), field_type::blob),
database_types_testcase("types_binary", "field_blob", "empty", "", field_type::blob),
database_types_testcase("types_binary", "field_mediumblob", "regular", makesv("\0_mediumblob"), field_type::blob),
database_types_testcase("types_binary", "field_mediumblob", "nonascii", makesv("\4\xfb"), field_type::blob),
database_types_testcase("types_binary", "field_mediumblob", "empty", "", field_type::blob),
database_types_testcase("types_binary", "field_mediumblob", "regular", makesv("\0_mediumblob"), field_type::blob),
database_types_testcase("types_binary", "field_mediumblob", "nonascii", makesv("\4\xfb"), field_type::blob),
database_types_testcase("types_binary", "field_mediumblob", "empty", "", field_type::blob),
database_types_testcase("types_binary", "field_longblob", "regular", makesv("\0_longblob"), field_type::blob),
database_types_testcase("types_binary", "field_longblob", "nonascii", makesv("\5\xfa"), field_type::blob),
database_types_testcase("types_binary", "field_longblob", "empty", "", field_type::blob)
database_types_testcase("types_binary", "field_longblob", "regular", makesv("\0_longblob"), field_type::blob),
database_types_testcase("types_binary", "field_longblob", "nonascii", makesv("\5\xfa"), field_type::blob),
database_types_testcase("types_binary", "field_longblob", "empty", "", field_type::blob)
), test_name_generator);
// These types do not have a more concrete representation in the library yet.
// Check we get them as strings and we get the metadata correctly
std::uint8_t geometry_value [] = {
0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40
0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40
};
INSTANTIATE_TEST_SUITE_P(NOT_IMPLEMENTED_TYPES, DatabaseTypesTest, Values(
database_types_testcase("types_not_implemented", "field_bit", "regular", "\xfe", field_type::bit, flags_unsigned),
database_types_testcase("types_not_implemented", "field_decimal", "regular", "300", field_type::decimal),
database_types_testcase("types_not_implemented", "field_geometry", "regular", makesv(geometry_value), field_type::geometry)
database_types_testcase("types_not_implemented", "field_bit", "regular", "\xfe", field_type::bit, flags_unsigned),
database_types_testcase("types_not_implemented", "field_decimal", "regular", "300", field_type::decimal),
database_types_testcase("types_not_implemented", "field_geometry", "regular", makesv(geometry_value), field_type::geometry)
), test_name_generator);
// Tests for certain metadata flags and NULL values
INSTANTIATE_TEST_SUITE_P(METADATA_FLAGS, DatabaseTypesTest, Values(
database_types_testcase("types_flags", "field_timestamp", "default", nullptr, field_type::timestamp,
flagsvec{&field_metadata::is_set_to_now_on_update}, 0, flagsvec{&field_metadata::is_unsigned}),
database_types_testcase("types_flags", "field_primary_key", "default", std::int32_t(50), field_type::int_,
flagsvec{&field_metadata::is_primary_key, &field_metadata::is_not_null,
&field_metadata::is_auto_increment}),
database_types_testcase("types_flags", "field_not_null", "default", "char", field_type::char_,
flagsvec{&field_metadata::is_not_null}),
database_types_testcase("types_flags", "field_unique", "default", std::int32_t(21), field_type::int_,
flagsvec{&field_metadata::is_unique_key}),
database_types_testcase("types_flags", "field_indexed", "default", std::int32_t(42), field_type::int_,
flagsvec{&field_metadata::is_multiple_key})
database_types_testcase("types_flags", "field_timestamp", "default", nullptr, field_type::timestamp,
flagsvec{&field_metadata::is_set_to_now_on_update}, 0, flagsvec{&field_metadata::is_unsigned}),
database_types_testcase("types_flags", "field_primary_key", "default", std::int32_t(50), field_type::int_,
flagsvec{&field_metadata::is_primary_key, &field_metadata::is_not_null,
&field_metadata::is_auto_increment}),
database_types_testcase("types_flags", "field_not_null", "default", "char", field_type::char_,
flagsvec{&field_metadata::is_not_null}),
database_types_testcase("types_flags", "field_unique", "default", std::int32_t(21), field_type::int_,
flagsvec{&field_metadata::is_unique_key}),
database_types_testcase("types_flags", "field_indexed", "default", std::int32_t(42), field_type::int_,
flagsvec{&field_metadata::is_multiple_key})
), test_name_generator);

View File

@ -30,25 +30,25 @@ INSERT INTO updates_table (field_varchar, field_int)
VALUES ('f0', 42), ('f1', 43), ('fnull', NULL);
CREATE TABLE empty_table (
id INT,
field_varchar VARCHAR(255)
id INT,
field_varchar VARCHAR(255)
);
CREATE TABLE one_row_table (
id INT,
field_varchar VARCHAR(255)
id INT,
field_varchar VARCHAR(255)
);
INSERT INTO one_row_table VALUES (1, 'f0');
CREATE TABLE two_rows_table (
id INT,
field_varchar VARCHAR(255)
id INT,
field_varchar VARCHAR(255)
);
INSERT INTO two_rows_table VALUES (1, 'f0'), (2, 'f1');
CREATE TABLE three_rows_table (
id INT,
field_varchar VARCHAR(255)
id INT,
field_varchar VARCHAR(255)
);
INSERT INTO three_rows_table VALUES (1, 'f0'), (2, 'f1'), (3, 'f2');
@ -58,168 +58,168 @@ INSERT INTO three_rows_table VALUES (1, 'f0'), (2, 'f1'), (3, 'f2');
-- Integer types
CREATE TABLE types_tinyint(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed TINYINT,
field_unsigned TINYINT UNSIGNED,
field_width TINYINT(4),
field_zerofill TINYINT(6) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed TINYINT,
field_unsigned TINYINT UNSIGNED,
field_width TINYINT(4),
field_zerofill TINYINT(6) ZEROFILL
);
INSERT INTO types_tinyint VALUES
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x80, 0, NULL, 0),
("max", 0x7f, 0xff, NULL, NULL)
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x80, 0, NULL, 0),
("max", 0x7f, 0xff, NULL, NULL)
;
CREATE TABLE types_smallint(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed SMALLINT,
field_unsigned SMALLINT UNSIGNED,
field_width SMALLINT(8),
field_zerofill SMALLINT(7) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed SMALLINT,
field_unsigned SMALLINT UNSIGNED,
field_width SMALLINT(8),
field_zerofill SMALLINT(7) ZEROFILL
);
INSERT INTO types_smallint VALUES
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x8000, 0, NULL, 0),
("max", 0x7fff, 0xffff, NULL, NULL)
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x8000, 0, NULL, 0),
("max", 0x7fff, 0xffff, NULL, NULL)
;
CREATE TABLE types_mediumint(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed MEDIUMINT,
field_unsigned MEDIUMINT UNSIGNED,
field_width MEDIUMINT(8),
field_zerofill MEDIUMINT(7) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed MEDIUMINT,
field_unsigned MEDIUMINT UNSIGNED,
field_width MEDIUMINT(8),
field_zerofill MEDIUMINT(7) ZEROFILL
);
INSERT INTO types_mediumint VALUES
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x800000, 0, NULL, 0),
("max", 0x7fffff, 0xffffff, NULL, NULL)
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x800000, 0, NULL, 0),
("max", 0x7fffff, 0xffffff, NULL, NULL)
;
CREATE TABLE types_int(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed INT,
field_unsigned INT UNSIGNED,
field_width INT(8),
field_zerofill INT(7) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed INT,
field_unsigned INT UNSIGNED,
field_width INT(8),
field_zerofill INT(7) ZEROFILL
);
INSERT INTO types_int VALUES
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x80000000, 0, NULL, 0),
("max", 0x7fffffff, 0xffffffff, NULL, NULL)
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x80000000, 0, NULL, 0),
("max", 0x7fffffff, 0xffffffff, NULL, NULL)
;
CREATE TABLE types_bigint(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed BIGINT,
field_unsigned BIGINT UNSIGNED,
field_width BIGINT(8),
field_zerofill BIGINT(7) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed BIGINT,
field_unsigned BIGINT UNSIGNED,
field_width BIGINT(8),
field_zerofill BIGINT(7) ZEROFILL
);
INSERT INTO types_bigint VALUES
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x8000000000000000, 0, NULL, 0),
("max", 0x7fffffffffffffff, 0xffffffffffffffff, NULL, NULL)
("regular", 20, 20, 20, 20),
("negative", -20, NULL, -20, NULL),
("min", -0x8000000000000000, 0, NULL, 0),
("max", 0x7fffffffffffffff, 0xffffffffffffffff, NULL, NULL)
;
-- Floating point types
CREATE TABLE types_float(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed FLOAT,
field_unsigned FLOAT UNSIGNED,
field_width FLOAT(30, 10),
field_zerofill FLOAT(20) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed FLOAT,
field_unsigned FLOAT UNSIGNED,
field_width FLOAT(30, 10),
field_zerofill FLOAT(20) ZEROFILL
);
INSERT INTO types_float VALUES
("zero", 0, 0, 0, 0),
("int_positive", 4, NULL, NULL, NULL),
("int_negative", -4, NULL, NULL, NULL),
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
("fractional_negative", -4.2, NULL, -4.2, NULL),
("positive_exp_positive_int", 3e20, NULL, NULL, NULL),
("positive_exp_negative_int", -3e20, NULL, NULL, NULL),
("positive_exp_positive_fractional", 3.14e20, NULL, NULL, 3.14e20),
("positive_exp_negative_fractional", -3.14e20, NULL, NULL, NULL),
("negative_exp_positive_fractional", 3.14e-20, NULL, NULL, 3.14e-20)
("zero", 0, 0, 0, 0),
("int_positive", 4, NULL, NULL, NULL),
("int_negative", -4, NULL, NULL, NULL),
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
("fractional_negative", -4.2, NULL, -4.2, NULL),
("positive_exp_positive_int", 3e20, NULL, NULL, NULL),
("positive_exp_negative_int", -3e20, NULL, NULL, NULL),
("positive_exp_positive_fractional", 3.14e20, NULL, NULL, 3.14e20),
("positive_exp_negative_fractional", -3.14e20, NULL, NULL, NULL),
("negative_exp_positive_fractional", 3.14e-20, NULL, NULL, 3.14e-20)
;
CREATE TABLE types_double(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed DOUBLE,
field_unsigned DOUBLE UNSIGNED,
field_width DOUBLE(60, 10),
field_zerofill FLOAT(40) ZEROFILL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_signed DOUBLE,
field_unsigned DOUBLE UNSIGNED,
field_width DOUBLE(60, 10),
field_zerofill FLOAT(40) ZEROFILL
);
INSERT INTO types_double VALUES
("zero", 0, 0, 0, 0),
("int_positive", 4, NULL, NULL, NULL),
("int_negative", -4, NULL, NULL, NULL),
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
("fractional_negative", -4.2, NULL, -4.2, NULL),
("positive_exp_positive_int", 3e200, NULL, NULL, NULL),
("positive_exp_negative_int", -3e200, NULL, NULL, NULL),
("positive_exp_positive_fractional", 3.14e200, NULL, NULL, 3.14e200),
("positive_exp_negative_fractional", -3.14e200, NULL, NULL, NULL),
("negative_exp_positive_fractional", 3.14e-200, NULL, NULL, 3.14e-200)
("zero", 0, 0, 0, 0),
("int_positive", 4, NULL, NULL, NULL),
("int_negative", -4, NULL, NULL, NULL),
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
("fractional_negative", -4.2, NULL, -4.2, NULL),
("positive_exp_positive_int", 3e200, NULL, NULL, NULL),
("positive_exp_negative_int", -3e200, NULL, NULL, NULL),
("positive_exp_positive_fractional", 3.14e200, NULL, NULL, 3.14e200),
("positive_exp_negative_fractional", -3.14e200, NULL, NULL, NULL),
("negative_exp_positive_fractional", 3.14e-200, NULL, NULL, 3.14e-200)
;
-- Dates and times
CREATE TABLE types_date(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_date DATE
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_date DATE
);
INSERT INTO types_date VALUES
("regular", "2010-03-28"),
("leap", "1788-02-29"),
("min", "1000-01-01"),
("max", "9999-12-31")
("regular", "2010-03-28"),
("leap", "1788-02-29"),
("min", "1000-01-01"),
("max", "9999-12-31")
;
CREATE TABLE types_datetime(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_0 DATETIME(0),
field_1 DATETIME(1),
field_2 DATETIME(2),
field_3 DATETIME(3),
field_4 DATETIME(4),
field_5 DATETIME(5),
field_6 DATETIME(6)
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_0 DATETIME(0),
field_1 DATETIME(1),
field_2 DATETIME(2),
field_3 DATETIME(3),
field_4 DATETIME(4),
field_5 DATETIME(5),
field_6 DATETIME(6)
);
INSERT INTO types_datetime VALUES
("date", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00"),
("u", NULL, "2010-05-02 00:00:00.1", "2010-05-02 00:00:00.12", "2010-05-02 00:00:00.123", "2010-05-02 00:00:00.1234", "2010-05-02 00:00:00.12345", "2010-05-02 00:00:00.123456"),
("s", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50"),
("m", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00"),
("hs", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50"),
("ms", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50"),
("hu", NULL, "2010-05-02 23:00:00.1", "2010-05-02 23:00:00.12", "2010-05-02 23:00:00.123", "2010-05-02 23:00:00.1234", "2010-05-02 23:00:00.12345", "2010-05-02 23:00:00.123456"),
("mu", NULL, "2010-05-02 00:01:00.1", "2010-05-02 00:01:00.12", "2010-05-02 00:01:00.123", "2010-05-02 00:01:00.1234", "2010-05-02 00:01:00.12345", "2010-05-02 00:01:00.123456"),
("hmu", NULL, "2010-05-02 23:01:00.1", "2010-05-02 23:01:00.12", "2010-05-02 23:01:00.123", "2010-05-02 23:01:00.1234", "2010-05-02 23:01:00.12345", "2010-05-02 23:01:00.123456"),
("su", NULL, "2010-05-02 00:00:50.1", "2010-05-02 00:00:50.12", "2010-05-02 00:00:50.123", "2010-05-02 00:00:50.1234", "2010-05-02 00:00:50.12345", "2010-05-02 00:00:50.123456"),
("hsu", NULL, "2010-05-02 23:00:50.1", "2010-05-02 23:00:50.12", "2010-05-02 23:00:50.123", "2010-05-02 23:00:50.1234", "2010-05-02 23:00:50.12345", "2010-05-02 23:00:50.123456"),
("msu", NULL, "2010-05-02 00:01:50.1", "2010-05-02 00:01:50.12", "2010-05-02 00:01:50.123", "2010-05-02 00:01:50.1234", "2010-05-02 00:01:50.12345", "2010-05-02 00:01:50.123456"),
("h", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00"),
("hm", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00"),
("hms", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50"),
("hmsu", NULL, "2010-05-02 23:01:50.1", "2010-05-02 23:01:50.12", "2010-05-02 23:01:50.123", "2010-05-02 23:01:50.1234", "2010-05-02 23:01:50.12345", "2010-05-02 23:01:50.123456"),
("min", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01"),
("max", "9999-12-31 23:59:59", "9999-12-31 23:59:59.9", "9999-12-31 23:59:59.99", "9999-12-31 23:59:59.999", "9999-12-31 23:59:59.9999", "9999-12-31 23:59:59.99999", "9999-12-31 23:59:59.999999")
("date", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00", "2010-05-02 00:00:00"),
("u", NULL, "2010-05-02 00:00:00.1", "2010-05-02 00:00:00.12", "2010-05-02 00:00:00.123", "2010-05-02 00:00:00.1234", "2010-05-02 00:00:00.12345", "2010-05-02 00:00:00.123456"),
("s", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50", "2010-05-02 00:00:50"),
("m", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00", "2010-05-02 00:01:00"),
("hs", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50", "2010-05-02 23:00:50"),
("ms", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50", "2010-05-02 00:01:50"),
("hu", NULL, "2010-05-02 23:00:00.1", "2010-05-02 23:00:00.12", "2010-05-02 23:00:00.123", "2010-05-02 23:00:00.1234", "2010-05-02 23:00:00.12345", "2010-05-02 23:00:00.123456"),
("mu", NULL, "2010-05-02 00:01:00.1", "2010-05-02 00:01:00.12", "2010-05-02 00:01:00.123", "2010-05-02 00:01:00.1234", "2010-05-02 00:01:00.12345", "2010-05-02 00:01:00.123456"),
("hmu", NULL, "2010-05-02 23:01:00.1", "2010-05-02 23:01:00.12", "2010-05-02 23:01:00.123", "2010-05-02 23:01:00.1234", "2010-05-02 23:01:00.12345", "2010-05-02 23:01:00.123456"),
("su", NULL, "2010-05-02 00:00:50.1", "2010-05-02 00:00:50.12", "2010-05-02 00:00:50.123", "2010-05-02 00:00:50.1234", "2010-05-02 00:00:50.12345", "2010-05-02 00:00:50.123456"),
("hsu", NULL, "2010-05-02 23:00:50.1", "2010-05-02 23:00:50.12", "2010-05-02 23:00:50.123", "2010-05-02 23:00:50.1234", "2010-05-02 23:00:50.12345", "2010-05-02 23:00:50.123456"),
("msu", NULL, "2010-05-02 00:01:50.1", "2010-05-02 00:01:50.12", "2010-05-02 00:01:50.123", "2010-05-02 00:01:50.1234", "2010-05-02 00:01:50.12345", "2010-05-02 00:01:50.123456"),
("h", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00", "2010-05-02 23:00:00"),
("hm", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00", "2010-05-02 23:01:00"),
("hms", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50", "2010-05-02 23:01:50"),
("hmsu", NULL, "2010-05-02 23:01:50.1", "2010-05-02 23:01:50.12", "2010-05-02 23:01:50.123", "2010-05-02 23:01:50.1234", "2010-05-02 23:01:50.12345", "2010-05-02 23:01:50.123456"),
("min", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01", "1000-01-01"),
("max", "9999-12-31 23:59:59", "9999-12-31 23:59:59.9", "9999-12-31 23:59:59.99", "9999-12-31 23:59:59.999", "9999-12-31 23:59:59.9999", "9999-12-31 23:59:59.99999", "9999-12-31 23:59:59.999999")
;
CREATE TABLE types_timestamp(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_0 TIMESTAMP(0) NULL DEFAULT NULL,
field_1 TIMESTAMP(1) NULL DEFAULT NULL,
field_2 TIMESTAMP(2) NULL DEFAULT NULL,
field_3 TIMESTAMP(3) NULL DEFAULT NULL,
field_4 TIMESTAMP(4) NULL DEFAULT NULL,
field_5 TIMESTAMP(5) NULL DEFAULT NULL,
field_6 TIMESTAMP(6) NULL DEFAULT NULL
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_0 TIMESTAMP(0) NULL DEFAULT NULL,
field_1 TIMESTAMP(1) NULL DEFAULT NULL,
field_2 TIMESTAMP(2) NULL DEFAULT NULL,
field_3 TIMESTAMP(3) NULL DEFAULT NULL,
field_4 TIMESTAMP(4) NULL DEFAULT NULL,
field_5 TIMESTAMP(5) NULL DEFAULT NULL,
field_6 TIMESTAMP(6) NULL DEFAULT NULL
);
-- TODO: min and max not reproducible due to time zones. Find a programmatic way of determining the values here
INSERT INTO types_timestamp
@ -227,149 +227,149 @@ SELECT * FROM types_datetime
WHERE id NOT IN ('min', 'max');
CREATE TABLE types_time(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_0 TIME(0),
field_1 TIME(1),
field_2 TIME(2),
field_3 TIME(3),
field_4 TIME(4),
field_5 TIME(5),
field_6 TIME(6)
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_0 TIME(0),
field_1 TIME(1),
field_2 TIME(2),
field_3 TIME(3),
field_4 TIME(4),
field_5 TIME(5),
field_6 TIME(6)
);
INSERT INTO types_time VALUES
("zero", "00:00:00", "00:00:00", "00:00:00", "00:00:00", "00:00:00", "00:00:00", "00:00:00"),
("d", "48:00:00", "48:00:00", "48:00:00", "48:00:00", "48:00:00", "48:00:00", "48:00:00"),
("negative_d", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00"),
("h", "23:00:00", "23:00:00", "23:00:00", "23:00:00", "23:00:00", "23:00:00", "23:00:00"),
("negative_h", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00"),
("dh", "71:00:00", "71:00:00", "71:00:00", "71:00:00", "71:00:00", "71:00:00", "71:00:00"),
("negative_dh", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00"),
("m", "00:01:00", "00:01:00", "00:01:00", "00:01:00", "00:01:00", "00:01:00", "00:01:00"),
("negative_m", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00"),
("dm", "48:01:00", "48:01:00", "48:01:00", "48:01:00", "48:01:00", "48:01:00", "48:01:00"),
("negative_dm", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00"),
("hm", "23:01:00", "23:01:00", "23:01:00", "23:01:00", "23:01:00", "23:01:00", "23:01:00"),
("negative_hm", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00"),
("dhm", "71:01:00", "71:01:00", "71:01:00", "71:01:00", "71:01:00", "71:01:00", "71:01:00"),
("negative_dhm", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00"),
("s", "00:00:50", "00:00:50", "00:00:50", "00:00:50", "00:00:50", "00:00:50", "00:00:50"),
("negative_s", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50"),
("ds", "48:00:50", "48:00:50", "48:00:50", "48:00:50", "48:00:50", "48:00:50", "48:00:50"),
("negative_ds", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50"),
("hs", "23:00:50", "23:00:50", "23:00:50", "23:00:50", "23:00:50", "23:00:50", "23:00:50"),
("negative_hs", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50"),
("dhs", "71:00:50", "71:00:50", "71:00:50", "71:00:50", "71:00:50", "71:00:50", "71:00:50"),
("negative_dhs", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50"),
("ms", "00:01:50", "00:01:50", "00:01:50", "00:01:50", "00:01:50", "00:01:50", "00:01:50"),
("negative_ms", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50"),
("dms", "48:01:50", "48:01:50", "48:01:50", "48:01:50", "48:01:50", "48:01:50", "48:01:50"),
("negative_dms", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50"),
("hms", "23:01:50", "23:01:50", "23:01:50", "23:01:50", "23:01:50", "23:01:50", "23:01:50"),
("negative_hms", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50"),
("dhms", "71:01:50", "71:01:50", "71:01:50", "71:01:50", "71:01:50", "71:01:50", "71:01:50"),
("negative_dhms", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50"),
("u", NULL, "00:00:00.1", "00:00:00.12", "00:00:00.123", "00:00:00.1234", "00:00:00.12345", "00:00:00.123456"),
("negative_u", NULL, "-00:00:00.1", "-00:00:00.12", "-00:00:00.123", "-00:00:00.1234", "-00:00:00.12345", "-00:00:00.123456"),
("du", NULL, "48:00:00.1", "48:00:00.12", "48:00:00.123", "48:00:00.1234", "48:00:00.12345", "48:00:00.123456"),
("negative_du", NULL, "-48:00:00.1", "-48:00:00.12", "-48:00:00.123", "-48:00:00.1234", "-48:00:00.12345", "-48:00:00.123456"),
("hu", NULL, "23:00:00.1", "23:00:00.12", "23:00:00.123", "23:00:00.1234", "23:00:00.12345", "23:00:00.123456"),
("negative_hu", NULL, "-23:00:00.1", "-23:00:00.12", "-23:00:00.123", "-23:00:00.1234", "-23:00:00.12345", "-23:00:00.123456"),
("dhu", NULL, "71:00:00.1", "71:00:00.12", "71:00:00.123", "71:00:00.1234", "71:00:00.12345", "71:00:00.123456"),
("negative_dhu", NULL, "-71:00:00.1", "-71:00:00.12", "-71:00:00.123", "-71:00:00.1234", "-71:00:00.12345", "-71:00:00.123456"),
("mu", NULL, "00:01:00.1", "00:01:00.12", "00:01:00.123", "00:01:00.1234", "00:01:00.12345", "00:01:00.123456"),
("negative_mu", NULL, "-00:01:00.1", "-00:01:00.12", "-00:01:00.123", "-00:01:00.1234", "-00:01:00.12345", "-00:01:00.123456"),
("dmu", NULL, "48:01:00.1", "48:01:00.12", "48:01:00.123", "48:01:00.1234", "48:01:00.12345", "48:01:00.123456"),
("negative_dmu", NULL, "-48:01:00.1", "-48:01:00.12", "-48:01:00.123", "-48:01:00.1234", "-48:01:00.12345", "-48:01:00.123456"),
("hmu", NULL, "23:01:00.1", "23:01:00.12", "23:01:00.123", "23:01:00.1234", "23:01:00.12345", "23:01:00.123456"),
("negative_hmu", NULL, "-23:01:00.1", "-23:01:00.12", "-23:01:00.123", "-23:01:00.1234", "-23:01:00.12345", "-23:01:00.123456"),
("dhmu", NULL, "71:01:00.1", "71:01:00.12", "71:01:00.123", "71:01:00.1234", "71:01:00.12345", "71:01:00.123456"),
("negative_dhmu", NULL, "-71:01:00.1", "-71:01:00.12", "-71:01:00.123", "-71:01:00.1234", "-71:01:00.12345", "-71:01:00.123456"),
("su", NULL, "00:00:50.1", "00:00:50.12", "00:00:50.123", "00:00:50.1234", "00:00:50.12345", "00:00:50.123456"),
("negative_su", NULL, "-00:00:50.1", "-00:00:50.12", "-00:00:50.123", "-00:00:50.1234", "-00:00:50.12345", "-00:00:50.123456"),
("dsu", NULL, "48:00:50.1", "48:00:50.12", "48:00:50.123", "48:00:50.1234", "48:00:50.12345", "48:00:50.123456"),
("negative_dsu", NULL, "-48:00:50.1", "-48:00:50.12", "-48:00:50.123", "-48:00:50.1234", "-48:00:50.12345", "-48:00:50.123456"),
("hsu", NULL, "23:00:50.1", "23:00:50.12", "23:00:50.123", "23:00:50.1234", "23:00:50.12345", "23:00:50.123456"),
("negative_hsu", NULL, "-23:00:50.1", "-23:00:50.12", "-23:00:50.123", "-23:00:50.1234", "-23:00:50.12345", "-23:00:50.123456"),
("dhsu", NULL, "71:00:50.1", "71:00:50.12", "71:00:50.123", "71:00:50.1234", "71:00:50.12345", "71:00:50.123456"),
("negative_dhsu", NULL, "-71:00:50.1", "-71:00:50.12", "-71:00:50.123", "-71:00:50.1234", "-71:00:50.12345", "-71:00:50.123456"),
("msu", NULL, "00:01:50.1", "00:01:50.12", "00:01:50.123", "00:01:50.1234", "00:01:50.12345", "00:01:50.123456"),
("negative_msu", NULL, "-00:01:50.1", "-00:01:50.12", "-00:01:50.123", "-00:01:50.1234", "-00:01:50.12345", "-00:01:50.123456"),
("dmsu", NULL, "48:01:50.1", "48:01:50.12", "48:01:50.123", "48:01:50.1234", "48:01:50.12345", "48:01:50.123456"),
("negative_dmsu", NULL, "-48:01:50.1", "-48:01:50.12", "-48:01:50.123", "-48:01:50.1234", "-48:01:50.12345", "-48:01:50.123456"),
("hmsu", NULL, "23:01:50.1", "23:01:50.12", "23:01:50.123", "23:01:50.1234", "23:01:50.12345", "23:01:50.123456"),
("negative_hmsu", NULL, "-23:01:50.1", "-23:01:50.12", "-23:01:50.123", "-23:01:50.1234", "-23:01:50.12345", "-23:01:50.123456"),
("dhmsu", NULL, "71:01:50.1", "71:01:50.12", "71:01:50.123", "71:01:50.1234", "71:01:50.12345", "71:01:50.123456"),
("negative_dhmsu", NULL, "-71:01:50.1", "-71:01:50.12", "-71:01:50.123", "-71:01:50.1234", "-71:01:50.12345", "-71:01:50.123456"),
("min", "-838:59:59", "-838:59:58.9", "-838:59:58.99", "-838:59:58.999", "-838:59:58.9999", "-838:59:58.99999", "-838:59:58.999999"),
("max", "838:59:59", "838:59:58.9", "838:59:58.99", "838:59:58.999", "838:59:58.9999", "838:59:58.99999", "838:59:58.999999")
("zero", "00:00:00", "00:00:00", "00:00:00", "00:00:00", "00:00:00", "00:00:00", "00:00:00"),
("d", "48:00:00", "48:00:00", "48:00:00", "48:00:00", "48:00:00", "48:00:00", "48:00:00"),
("negative_d", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00", "-48:00:00"),
("h", "23:00:00", "23:00:00", "23:00:00", "23:00:00", "23:00:00", "23:00:00", "23:00:00"),
("negative_h", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00", "-23:00:00"),
("dh", "71:00:00", "71:00:00", "71:00:00", "71:00:00", "71:00:00", "71:00:00", "71:00:00"),
("negative_dh", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00", "-71:00:00"),
("m", "00:01:00", "00:01:00", "00:01:00", "00:01:00", "00:01:00", "00:01:00", "00:01:00"),
("negative_m", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00", "-00:01:00"),
("dm", "48:01:00", "48:01:00", "48:01:00", "48:01:00", "48:01:00", "48:01:00", "48:01:00"),
("negative_dm", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00", "-48:01:00"),
("hm", "23:01:00", "23:01:00", "23:01:00", "23:01:00", "23:01:00", "23:01:00", "23:01:00"),
("negative_hm", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00", "-23:01:00"),
("dhm", "71:01:00", "71:01:00", "71:01:00", "71:01:00", "71:01:00", "71:01:00", "71:01:00"),
("negative_dhm", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00", "-71:01:00"),
("s", "00:00:50", "00:00:50", "00:00:50", "00:00:50", "00:00:50", "00:00:50", "00:00:50"),
("negative_s", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50", "-00:00:50"),
("ds", "48:00:50", "48:00:50", "48:00:50", "48:00:50", "48:00:50", "48:00:50", "48:00:50"),
("negative_ds", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50", "-48:00:50"),
("hs", "23:00:50", "23:00:50", "23:00:50", "23:00:50", "23:00:50", "23:00:50", "23:00:50"),
("negative_hs", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50", "-23:00:50"),
("dhs", "71:00:50", "71:00:50", "71:00:50", "71:00:50", "71:00:50", "71:00:50", "71:00:50"),
("negative_dhs", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50", "-71:00:50"),
("ms", "00:01:50", "00:01:50", "00:01:50", "00:01:50", "00:01:50", "00:01:50", "00:01:50"),
("negative_ms", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50", "-00:01:50"),
("dms", "48:01:50", "48:01:50", "48:01:50", "48:01:50", "48:01:50", "48:01:50", "48:01:50"),
("negative_dms", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50", "-48:01:50"),
("hms", "23:01:50", "23:01:50", "23:01:50", "23:01:50", "23:01:50", "23:01:50", "23:01:50"),
("negative_hms", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50", "-23:01:50"),
("dhms", "71:01:50", "71:01:50", "71:01:50", "71:01:50", "71:01:50", "71:01:50", "71:01:50"),
("negative_dhms", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50", "-71:01:50"),
("u", NULL, "00:00:00.1", "00:00:00.12", "00:00:00.123", "00:00:00.1234", "00:00:00.12345", "00:00:00.123456"),
("negative_u", NULL, "-00:00:00.1", "-00:00:00.12", "-00:00:00.123", "-00:00:00.1234", "-00:00:00.12345", "-00:00:00.123456"),
("du", NULL, "48:00:00.1", "48:00:00.12", "48:00:00.123", "48:00:00.1234", "48:00:00.12345", "48:00:00.123456"),
("negative_du", NULL, "-48:00:00.1", "-48:00:00.12", "-48:00:00.123", "-48:00:00.1234", "-48:00:00.12345", "-48:00:00.123456"),
("hu", NULL, "23:00:00.1", "23:00:00.12", "23:00:00.123", "23:00:00.1234", "23:00:00.12345", "23:00:00.123456"),
("negative_hu", NULL, "-23:00:00.1", "-23:00:00.12", "-23:00:00.123", "-23:00:00.1234", "-23:00:00.12345", "-23:00:00.123456"),
("dhu", NULL, "71:00:00.1", "71:00:00.12", "71:00:00.123", "71:00:00.1234", "71:00:00.12345", "71:00:00.123456"),
("negative_dhu", NULL, "-71:00:00.1", "-71:00:00.12", "-71:00:00.123", "-71:00:00.1234", "-71:00:00.12345", "-71:00:00.123456"),
("mu", NULL, "00:01:00.1", "00:01:00.12", "00:01:00.123", "00:01:00.1234", "00:01:00.12345", "00:01:00.123456"),
("negative_mu", NULL, "-00:01:00.1", "-00:01:00.12", "-00:01:00.123", "-00:01:00.1234", "-00:01:00.12345", "-00:01:00.123456"),
("dmu", NULL, "48:01:00.1", "48:01:00.12", "48:01:00.123", "48:01:00.1234", "48:01:00.12345", "48:01:00.123456"),
("negative_dmu", NULL, "-48:01:00.1", "-48:01:00.12", "-48:01:00.123", "-48:01:00.1234", "-48:01:00.12345", "-48:01:00.123456"),
("hmu", NULL, "23:01:00.1", "23:01:00.12", "23:01:00.123", "23:01:00.1234", "23:01:00.12345", "23:01:00.123456"),
("negative_hmu", NULL, "-23:01:00.1", "-23:01:00.12", "-23:01:00.123", "-23:01:00.1234", "-23:01:00.12345", "-23:01:00.123456"),
("dhmu", NULL, "71:01:00.1", "71:01:00.12", "71:01:00.123", "71:01:00.1234", "71:01:00.12345", "71:01:00.123456"),
("negative_dhmu", NULL, "-71:01:00.1", "-71:01:00.12", "-71:01:00.123", "-71:01:00.1234", "-71:01:00.12345", "-71:01:00.123456"),
("su", NULL, "00:00:50.1", "00:00:50.12", "00:00:50.123", "00:00:50.1234", "00:00:50.12345", "00:00:50.123456"),
("negative_su", NULL, "-00:00:50.1", "-00:00:50.12", "-00:00:50.123", "-00:00:50.1234", "-00:00:50.12345", "-00:00:50.123456"),
("dsu", NULL, "48:00:50.1", "48:00:50.12", "48:00:50.123", "48:00:50.1234", "48:00:50.12345", "48:00:50.123456"),
("negative_dsu", NULL, "-48:00:50.1", "-48:00:50.12", "-48:00:50.123", "-48:00:50.1234", "-48:00:50.12345", "-48:00:50.123456"),
("hsu", NULL, "23:00:50.1", "23:00:50.12", "23:00:50.123", "23:00:50.1234", "23:00:50.12345", "23:00:50.123456"),
("negative_hsu", NULL, "-23:00:50.1", "-23:00:50.12", "-23:00:50.123", "-23:00:50.1234", "-23:00:50.12345", "-23:00:50.123456"),
("dhsu", NULL, "71:00:50.1", "71:00:50.12", "71:00:50.123", "71:00:50.1234", "71:00:50.12345", "71:00:50.123456"),
("negative_dhsu", NULL, "-71:00:50.1", "-71:00:50.12", "-71:00:50.123", "-71:00:50.1234", "-71:00:50.12345", "-71:00:50.123456"),
("msu", NULL, "00:01:50.1", "00:01:50.12", "00:01:50.123", "00:01:50.1234", "00:01:50.12345", "00:01:50.123456"),
("negative_msu", NULL, "-00:01:50.1", "-00:01:50.12", "-00:01:50.123", "-00:01:50.1234", "-00:01:50.12345", "-00:01:50.123456"),
("dmsu", NULL, "48:01:50.1", "48:01:50.12", "48:01:50.123", "48:01:50.1234", "48:01:50.12345", "48:01:50.123456"),
("negative_dmsu", NULL, "-48:01:50.1", "-48:01:50.12", "-48:01:50.123", "-48:01:50.1234", "-48:01:50.12345", "-48:01:50.123456"),
("hmsu", NULL, "23:01:50.1", "23:01:50.12", "23:01:50.123", "23:01:50.1234", "23:01:50.12345", "23:01:50.123456"),
("negative_hmsu", NULL, "-23:01:50.1", "-23:01:50.12", "-23:01:50.123", "-23:01:50.1234", "-23:01:50.12345", "-23:01:50.123456"),
("dhmsu", NULL, "71:01:50.1", "71:01:50.12", "71:01:50.123", "71:01:50.1234", "71:01:50.12345", "71:01:50.123456"),
("negative_dhmsu", NULL, "-71:01:50.1", "-71:01:50.12", "-71:01:50.123", "-71:01:50.1234", "-71:01:50.12345", "-71:01:50.123456"),
("min", "-838:59:59", "-838:59:58.9", "-838:59:58.99", "-838:59:58.999", "-838:59:58.9999", "-838:59:58.99999", "-838:59:58.999999"),
("max", "838:59:59", "838:59:58.9", "838:59:58.99", "838:59:58.999", "838:59:58.9999", "838:59:58.99999", "838:59:58.999999")
;
CREATE TABLE types_year(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_default YEAR
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_default YEAR
);
INSERT INTO types_year VALUES
("regular", 2019),
("min", 1901),
("max", 2155),
("zero", 0)
("regular", 2019),
("min", 1901),
("max", 2155),
("zero", 0)
;
CREATE TABLE types_string(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_char CHAR(20),
field_varchar VARCHAR(30),
field_tinytext TINYTEXT,
field_text TEXT,
field_mediumtext MEDIUMTEXT,
field_longtext LONGTEXT,
field_enum ENUM("red", "green", "blue"),
field_set SET("red", "green", "blue")
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_char CHAR(20),
field_varchar VARCHAR(30),
field_tinytext TINYTEXT,
field_text TEXT,
field_mediumtext MEDIUMTEXT,
field_longtext LONGTEXT,
field_enum ENUM("red", "green", "blue"),
field_set SET("red", "green", "blue")
);
INSERT INTO types_string VALUES
("regular", "test_char", "test_varchar", "test_tinytext", "test_text", "test_mediumtext", "test_longtext", "red", "red,green"),
("utf8", "ñ", "Ñ", "á", "é", "í", "ó", NULL, NULL),
("empty", "", "", "", "", "", "", NULL, "")
("regular", "test_char", "test_varchar", "test_tinytext", "test_text", "test_mediumtext", "test_longtext", "red", "red,green"),
("utf8", "ñ", "Ñ", "á", "é", "í", "ó", NULL, NULL),
("empty", "", "", "", "", "", "", NULL, "")
;
CREATE TABLE types_binary(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_binary BINARY(10),
field_varbinary VARBINARY(30),
field_tinyblob TINYBLOB,
field_blob BLOB,
field_mediumblob MEDIUMBLOB,
field_longblob LONGBLOB
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_binary BINARY(10),
field_varbinary VARBINARY(30),
field_tinyblob TINYBLOB,
field_blob BLOB,
field_mediumblob MEDIUMBLOB,
field_longblob LONGBLOB
);
INSERT INTO types_binary VALUES
("regular", "\0_binary", "\0_varbinary", "\0_tinyblob", "\0_blob", "\0_mediumblob", "\0_longblob"),
("nonascii", X'00FF', X'01FE', X'02FD', X'03FC', X'04FB', X'05FA'),
("empty", "", "", "", "", "", "")
("regular", "\0_binary", "\0_varbinary", "\0_tinyblob", "\0_blob", "\0_mediumblob", "\0_longblob"),
("nonascii", X'00FF', X'01FE', X'02FD', X'03FC', X'04FB', X'05FA'),
("empty", "", "", "", "", "", "")
;
CREATE TABLE types_not_implemented(
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_bit BIT(8),
field_decimal DECIMAL,
field_geometry GEOMETRY
id VARCHAR(50) NOT NULL PRIMARY KEY,
field_bit BIT(8),
field_decimal DECIMAL,
field_geometry GEOMETRY
);
INSERT INTO types_not_implemented VALUES
("regular", 0xfe, 300, POINT(1, 2))
("regular", 0xfe, 300, POINT(1, 2))
;
CREATE TABLE types_flags(
id VARCHAR(50) NOT NULL,
field_timestamp TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
field_primary_key INT NOT NULL AUTO_INCREMENT,
field_not_null CHAR(8) NOT NULL DEFAULT "",
field_unique INT,
field_indexed INT,
PRIMARY KEY (field_primary_key, id),
UNIQUE KEY (field_unique),
KEY (field_indexed)
id VARCHAR(50) NOT NULL,
field_timestamp TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
field_primary_key INT NOT NULL AUTO_INCREMENT,
field_not_null CHAR(8) NOT NULL DEFAULT "",
field_unique INT,
field_indexed INT,
PRIMARY KEY (field_primary_key, id),
UNIQUE KEY (field_unique),
KEY (field_indexed)
);
INSERT INTO types_flags VALUES
("default", NULL, 50, "char", 21, 42)
("default", NULL, 50, "char", 21, 42)
;
-- Users

View File

@ -21,94 +21,94 @@ namespace
template <typename Stream>
struct ExecuteStatementTest : public NetworkTest<Stream>
{
prepared_statement<Stream> do_prepare(std::string_view stmt)
{
auto res = this->GetParam().net->prepare_statement(this->conn, stmt);
res.validate_no_error();
return std::move(res.value);
}
prepared_statement<Stream> do_prepare(std::string_view stmt)
{
auto res = this->GetParam().net->prepare_statement(this->conn, stmt);
res.validate_no_error();
return std::move(res.value);
}
auto do_execute(prepared_statement<Stream>& stmt, value_list_it first, value_list_it last)
{
return this->GetParam().net->execute_statement(stmt, first, last);
}
auto do_execute(prepared_statement<Stream>& stmt, value_list_it first, value_list_it last)
{
return this->GetParam().net->execute_statement(stmt, first, last);
}
auto do_execute(prepared_statement<Stream>& stmt, const std::vector<value>& params)
{
return this->GetParam().net->execute_statement(stmt, params);
}
auto do_execute(prepared_statement<Stream>& stmt, const std::vector<value>& params)
{
return this->GetParam().net->execute_statement(stmt, params);
}
// Iterator version
void Iterator_OkNoParams()
{
std::forward_list<value> params;
auto stmt = do_prepare("SELECT * FROM empty_table");
auto result = do_execute(stmt, params.begin(), params.end()); // execute
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
// Iterator version
void Iterator_OkNoParams()
{
std::forward_list<value> params;
auto stmt = do_prepare("SELECT * FROM empty_table");
auto result = do_execute(stmt, params.begin(), params.end()); // execute
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
void Iterator_OkWithParams()
{
std::forward_list<value> params { value("item"), value(42) };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params.begin(), params.end());
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
void Iterator_OkWithParams()
{
std::forward_list<value> params { value("item"), value(42) };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params.begin(), params.end());
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
void Iterator_MismatchedNumParams()
{
std::forward_list<value> params { value("item") };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params.begin(), params.end());
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
EXPECT_FALSE(result.value.valid());
}
void Iterator_MismatchedNumParams()
{
std::forward_list<value> params { value("item") };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params.begin(), params.end());
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
EXPECT_FALSE(result.value.valid());
}
void Iterator_ServerError()
{
std::forward_list<value> params { value("f0"), value("bad_date") };
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
auto result = do_execute(stmt, params.begin(), params.end());
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
EXPECT_FALSE(result.value.valid());
}
void Iterator_ServerError()
{
std::forward_list<value> params { value("f0"), value("bad_date") };
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
auto result = do_execute(stmt, params.begin(), params.end());
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
EXPECT_FALSE(result.value.valid());
}
// Container version
void Container_OkNoParams()
{
auto stmt = do_prepare("SELECT * FROM empty_table");
auto result = do_execute(stmt, std::vector<value>()); // execute
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
// Container version
void Container_OkNoParams()
{
auto stmt = do_prepare("SELECT * FROM empty_table");
auto result = do_execute(stmt, std::vector<value>()); // execute
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
void Container_OkWithParams()
{
std::vector<value> params { value("item"), value(42) };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params);
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
void Container_OkWithParams()
{
std::vector<value> params { value("item"), value(42) };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params);
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
}
void Container_MismatchedNumParams()
{
std::vector<value> params { value("item") };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params);
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
EXPECT_FALSE(result.value.valid());
}
void Container_MismatchedNumParams()
{
std::vector<value> params { value("item") };
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = do_execute(stmt, params);
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
EXPECT_FALSE(result.value.valid());
}
void Container_ServerError()
{
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
auto result = do_execute(stmt, makevalues("f0", "bad_date"));
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
EXPECT_FALSE(result.value.valid());
}
void Container_ServerError()
{
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
auto result = do_execute(stmt, makevalues("f0", "bad_date"));
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
EXPECT_FALSE(result.value.valid());
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(ExecuteStatementTest)
@ -127,22 +127,22 @@ BOOST_MYSQL_NETWORK_TEST(ExecuteStatementTest, Container_ServerError)
// Other containers
struct ExecuteStatementOtherContainersTest : IntegTest<boost::asio::ip::tcp::socket>
{
ExecuteStatementOtherContainersTest() { handshake(boost::mysql::ssl_mode::disable); }
ExecuteStatementOtherContainersTest() { handshake(boost::mysql::ssl_mode::disable); }
};
TEST_F(ExecuteStatementOtherContainersTest, NoParams_CanUseNoStatementParamsVariable)
{
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
auto result = stmt.execute(boost::mysql::no_statement_params);
EXPECT_TRUE(result.valid());
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
auto result = stmt.execute(boost::mysql::no_statement_params);
EXPECT_TRUE(result.valid());
}
TEST_F(ExecuteStatementOtherContainersTest, CArray)
{
value arr [] = { value("hola"), value(10) };
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = stmt.execute(arr);
EXPECT_TRUE(result.valid());
value arr [] = { value("hola"), value(10) };
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
auto result = stmt.execute(arr);
EXPECT_TRUE(result.valid());
}
}

View File

@ -32,67 +32,67 @@ namespace
// Handshake tests not depending on whether we use SSL or not
template <typename Stream>
struct SslIndifferentHandshakeTest :
NetworkTest<Stream, network_testcase_with_ssl<Stream>, false>
NetworkTest<Stream, network_testcase_with_ssl<Stream>, false>
{
auto do_handshake()
{
this->connection_params.set_ssl(boost::mysql::ssl_options(this->GetParam().ssl));
return this->GetParam().net->handshake(this->conn, this->connection_params);
}
auto do_handshake()
{
this->connection_params.set_ssl(boost::mysql::ssl_options(this->GetParam().ssl));
return this->GetParam().net->handshake(this->conn, this->connection_params);
}
// does handshake and verifies it went OK
void do_handshake_ok()
{
auto result = do_handshake();
result.validate_no_error();
this->validate_ssl(this->GetParam().ssl);
}
// does handshake and verifies it went OK
void do_handshake_ok()
{
auto result = do_handshake();
result.validate_no_error();
this->validate_ssl(this->GetParam().ssl);
}
};
template <typename Stream>
struct SslSensitiveHandshakeTest : NetworkTest<Stream, network_testcase<Stream>, false>
{
void set_ssl(ssl_mode m)
{
this->connection_params.set_ssl(boost::mysql::ssl_options(m));
}
void set_ssl(ssl_mode m)
{
this->connection_params.set_ssl(boost::mysql::ssl_options(m));
}
auto do_handshake()
{
return this->GetParam().net->handshake(this->conn, this->connection_params);
}
auto do_handshake()
{
return this->GetParam().net->handshake(this->conn, this->connection_params);
}
void do_handshake_ok(ssl_mode m)
{
set_ssl(m);
auto result = do_handshake();
result.validate_no_error();
this->validate_ssl(m);
}
void do_handshake_ok(ssl_mode m)
{
set_ssl(m);
auto result = do_handshake();
result.validate_no_error();
this->validate_ssl(m);
}
};
// mysql_native_password
template <typename Stream>
struct MysqlNativePasswordHandshakeTest : SslIndifferentHandshakeTest<Stream>
{
void RegularUser_SuccessfulLogin()
{
this->set_credentials("mysqlnp_user", "mysqlnp_password");
this->do_handshake_ok();
}
void RegularUser_SuccessfulLogin()
{
this->set_credentials("mysqlnp_user", "mysqlnp_password");
this->do_handshake_ok();
}
void EmptyPassword_SuccessfulLogin()
{
this->set_credentials("mysqlnp_empty_password_user", "");
this->do_handshake_ok();
}
void EmptyPassword_SuccessfulLogin()
{
this->set_credentials("mysqlnp_empty_password_user", "");
this->do_handshake_ok();
}
void BadPassword_FailedLogin()
{
this->set_credentials("mysqlnp_user", "bad_password");
auto result = this->do_handshake();
result.validate_error(errc::access_denied_error, {"access denied", "mysqlnp_user"});
}
void BadPassword_FailedLogin()
{
this->set_credentials("mysqlnp_user", "bad_password");
auto result = this->do_handshake();
result.validate_error(errc::access_denied_error, {"access denied", "mysqlnp_user"});
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(MysqlNativePasswordHandshakeTest)
@ -106,26 +106,26 @@ BOOST_MYSQL_NETWORK_TEST(MysqlNativePasswordHandshakeTest, BadPassword_FailedLog
template <typename Stream>
struct MiscSslIndifferentHandshakeTest : SslIndifferentHandshakeTest<Stream>
{
void NoDatabase_SuccessfulLogin()
{
this->connection_params.set_database("");
this->do_handshake_ok();
}
void NoDatabase_SuccessfulLogin()
{
this->connection_params.set_database("");
this->do_handshake_ok();
}
void BadDatabase_FailedLogin()
{
this->connection_params.set_database("bad_database");
auto result = this->do_handshake();
result.validate_error(errc::dbaccess_denied_error, {"database", "bad_database"});
}
void BadDatabase_FailedLogin()
{
this->connection_params.set_database("bad_database");
auto result = this->do_handshake();
result.validate_error(errc::dbaccess_denied_error, {"database", "bad_database"});
}
void UnknownAuthPlugin_FailedLogin_RequiresSha256()
{
// Note: sha256_password is not supported, so it's an unknown plugin to us
this->set_credentials("sha2p_user", "sha2p_password");
auto result = this->do_handshake();
result.validate_error(errc::unknown_auth_plugin, {});
}
void UnknownAuthPlugin_FailedLogin_RequiresSha256()
{
// Note: sha256_password is not supported, so it's an unknown plugin to us
this->set_credentials("sha2p_user", "sha2p_password");
auto result = this->do_handshake();
result.validate_error(errc::unknown_auth_plugin, {});
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(MiscSslIndifferentHandshakeTest)
@ -138,104 +138,104 @@ BOOST_MYSQL_NETWORK_TEST(MiscSslIndifferentHandshakeTest, UnknownAuthPlugin_Fail
template <typename Stream>
struct CachingSha2HandshakeTest : SslSensitiveHandshakeTest<Stream>
{
void load_sha256_cache(const std::string& user, const std::string& password)
{
if (password.empty())
{
check_call(stringize("mysql -u ", user, " -e \"\""));
}
else
{
check_call(stringize("mysql -u ", user, " -p", password, " -e \"\""));
}
}
void load_sha256_cache(const std::string& user, const std::string& password)
{
if (password.empty())
{
check_call(stringize("mysql -u ", user, " -e \"\""));
}
else
{
check_call(stringize("mysql -u ", user, " -p", password, " -e \"\""));
}
}
void clear_sha256_cache()
{
check_call("mysql -u root -e \"FLUSH PRIVILEGES\"");
}
void clear_sha256_cache()
{
check_call("mysql -u root -e \"FLUSH PRIVILEGES\"");
}
// Actual tests
void SslOnCacheHit_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_user", "csha2p_password");
load_sha256_cache("csha2p_user", "csha2p_password");
this->do_handshake_ok(ssl_mode::require);
}
// Actual tests
void SslOnCacheHit_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_user", "csha2p_password");
load_sha256_cache("csha2p_user", "csha2p_password");
this->do_handshake_ok(ssl_mode::require);
}
void SslOffCacheHit_SuccessfulLogin_RequiresSha256()
{
// As we are sending password hashed, it is OK to not have SSL for this
this->set_credentials("csha2p_user", "csha2p_password");
load_sha256_cache("csha2p_user", "csha2p_password");
this->do_handshake_ok(ssl_mode::disable);
}
void SslOffCacheHit_SuccessfulLogin_RequiresSha256()
{
// As we are sending password hashed, it is OK to not have SSL for this
this->set_credentials("csha2p_user", "csha2p_password");
load_sha256_cache("csha2p_user", "csha2p_password");
this->do_handshake_ok(ssl_mode::disable);
}
void SslOnCacheMiss_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_user", "csha2p_password");
clear_sha256_cache();
this->do_handshake_ok(ssl_mode::require);
}
void SslOnCacheMiss_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_user", "csha2p_password");
clear_sha256_cache();
this->do_handshake_ok(ssl_mode::require);
}
void SslOffCacheMiss_FailedLogin_RequiresSha256()
{
// A cache miss would force us send a plaintext password over
// a non-TLS connection, so we fail
this->set_ssl(ssl_mode::disable);
this->set_credentials("csha2p_user", "csha2p_password");
clear_sha256_cache();
auto result = this->do_handshake();
result.validate_error(errc::auth_plugin_requires_ssl, {});
}
void SslOffCacheMiss_FailedLogin_RequiresSha256()
{
// A cache miss would force us send a plaintext password over
// a non-TLS connection, so we fail
this->set_ssl(ssl_mode::disable);
this->set_credentials("csha2p_user", "csha2p_password");
clear_sha256_cache();
auto result = this->do_handshake();
result.validate_error(errc::auth_plugin_requires_ssl, {});
}
void EmptyPasswordSslOnCacheHit_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_empty_password_user", "");
load_sha256_cache("csha2p_empty_password_user", "");
this->do_handshake_ok(ssl_mode::require);
}
void EmptyPasswordSslOnCacheHit_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_empty_password_user", "");
load_sha256_cache("csha2p_empty_password_user", "");
this->do_handshake_ok(ssl_mode::require);
}
void EmptyPasswordSslOffCacheHit_SuccessfulLogin_RequiresSha256()
{
// Empty passwords are allowed over non-TLS connections
this->set_credentials("csha2p_empty_password_user", "");
load_sha256_cache("csha2p_empty_password_user", "");
this->do_handshake_ok(ssl_mode::disable);
}
void EmptyPasswordSslOffCacheHit_SuccessfulLogin_RequiresSha256()
{
// Empty passwords are allowed over non-TLS connections
this->set_credentials("csha2p_empty_password_user", "");
load_sha256_cache("csha2p_empty_password_user", "");
this->do_handshake_ok(ssl_mode::disable);
}
void EmptyPasswordSslOnCacheMiss_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_empty_password_user", "");
clear_sha256_cache();
this->do_handshake_ok(ssl_mode::require);
}
void EmptyPasswordSslOnCacheMiss_SuccessfulLogin_RequiresSha256()
{
this->set_credentials("csha2p_empty_password_user", "");
clear_sha256_cache();
this->do_handshake_ok(ssl_mode::require);
}
void EmptyPasswordSslOffCacheMiss_SuccessfulLogin_RequiresSha256()
{
// Empty passwords are allowed over non-TLS connections
this->set_credentials("csha2p_empty_password_user", "");
clear_sha256_cache();
this->do_handshake_ok(ssl_mode::disable);
}
void EmptyPasswordSslOffCacheMiss_SuccessfulLogin_RequiresSha256()
{
// Empty passwords are allowed over non-TLS connections
this->set_credentials("csha2p_empty_password_user", "");
clear_sha256_cache();
this->do_handshake_ok(ssl_mode::disable);
}
void BadPasswordSslOnCacheMiss_FailedLogin_RequiresSha256()
{
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
clear_sha256_cache();
this->set_credentials("csha2p_user", "bad_password");
auto result = this->do_handshake();
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
}
void BadPasswordSslOnCacheMiss_FailedLogin_RequiresSha256()
{
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
clear_sha256_cache();
this->set_credentials("csha2p_user", "bad_password");
auto result = this->do_handshake();
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
}
void BadPasswordSslOnCacheHit_FailedLogin_RequiresSha256()
{
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
this->set_credentials("csha2p_user", "bad_password");
load_sha256_cache("csha2p_user", "csha2p_password");
auto result = this->do_handshake();
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
}
void BadPasswordSslOnCacheHit_FailedLogin_RequiresSha256()
{
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
this->set_credentials("csha2p_user", "bad_password");
load_sha256_cache("csha2p_user", "csha2p_password");
auto result = this->do_handshake();
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(CachingSha2HandshakeTest)
@ -255,23 +255,23 @@ BOOST_MYSQL_NETWORK_TEST(CachingSha2HandshakeTest, BadPasswordSslOnCacheHit_Fail
template <typename Stream>
struct MiscSslSensitiveHandshakeTest : SslSensitiveHandshakeTest<Stream>
{
void BadUser_FailedLogin()
{
// unreliable without SSL. If the default plugin requires SSL
// (like SHA256), this would fail with 'ssl required'
this->set_ssl(ssl_mode::require);
this->set_credentials("non_existing_user", "bad_password");
auto result = this->do_handshake();
EXPECT_NE(result.err, error_code()); // may be access denied or unknown auth plugin
}
void BadUser_FailedLogin()
{
// unreliable without SSL. If the default plugin requires SSL
// (like SHA256), this would fail with 'ssl required'
this->set_ssl(ssl_mode::require);
this->set_credentials("non_existing_user", "bad_password");
auto result = this->do_handshake();
EXPECT_NE(result.err, error_code()); // may be access denied or unknown auth plugin
}
void SslEnable_SuccessfulLogin()
{
// In all our CI systems, our servers support SSL, so
// ssl_mode::enable will do the same as ssl_mode::require.
// We test for this fact.
this->do_handshake_ok(ssl_mode::enable);
}
void SslEnable_SuccessfulLogin()
{
// In all our CI systems, our servers support SSL, so
// ssl_mode::enable will do the same as ssl_mode::require.
// We test for this fact.
this->do_handshake_ok(ssl_mode::enable);
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(MiscSslSensitiveHandshakeTest)

View File

@ -26,15 +26,15 @@ namespace test {
inline void physical_connect(tcp_connection& conn)
{
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::loopback(), 3306);
conn.next_layer().connect(endpoint);
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::loopback(), 3306);
conn.next_layer().connect(endpoint);
}
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
inline void physical_connect(unix_connection& conn)
{
boost::asio::local::stream_protocol::endpoint endpoint ("/var/run/mysqld/mysqld.sock");
conn.next_layer().connect(endpoint);
boost::asio::local::stream_protocol::endpoint endpoint ("/var/run/mysqld/mysqld.sock");
conn.next_layer().connect(endpoint);
}
#endif
@ -50,102 +50,102 @@ inline void physical_connect(unix_connection& conn)
template <typename Stream>
struct IntegTest : testing::Test
{
using stream_type = Stream;
using stream_type = Stream;
mysql::connection_params connection_params {"integ_user", "integ_password", "awesome"};
boost::asio::io_context ctx;
mysql::connection<Stream> conn {ctx};
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard { ctx.get_executor() };
std::thread runner {[this]{ ctx.run(); } };
mysql::connection_params connection_params {"integ_user", "integ_password", "awesome"};
boost::asio::io_context ctx;
mysql::connection<Stream> conn {ctx};
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard { ctx.get_executor() };
std::thread runner {[this]{ ctx.run(); } };
IntegTest()
{
try
{
physical_connect(conn);
}
catch (...) // prevent terminate without an active exception on connect error
{
guard.reset();
runner.join();
throw;
}
}
IntegTest()
{
try
{
physical_connect(conn);
}
catch (...) // prevent terminate without an active exception on connect error
{
guard.reset();
runner.join();
throw;
}
}
~IntegTest()
{
error_code code;
conn.next_layer().close(code);
guard.reset();
runner.join();
}
~IntegTest()
{
error_code code;
conn.next_layer().close(code);
guard.reset();
runner.join();
}
void set_credentials(std::string_view user, std::string_view password)
{
connection_params.set_username(user);
connection_params.set_password(password);
}
void set_credentials(std::string_view user, std::string_view password)
{
connection_params.set_username(user);
connection_params.set_password(password);
}
void handshake(ssl_mode m = ssl_mode::require)
{
connection_params.set_ssl(ssl_options(m));
conn.handshake(connection_params);
validate_ssl(m);
}
void handshake(ssl_mode m = ssl_mode::require)
{
connection_params.set_ssl(ssl_options(m));
conn.handshake(connection_params);
validate_ssl(m);
}
static bool should_use_ssl(ssl_mode m)
{
return m == ssl_mode::enable || m == ssl_mode::require;
}
static bool should_use_ssl(ssl_mode m)
{
return m == ssl_mode::enable || m == ssl_mode::require;
}
// Verifies that we are or are not using SSL, depending on what mode was requested.
void validate_ssl(ssl_mode m)
{
if (should_use_ssl(m))
{
// All our test systems MUST support SSL to run these tests
EXPECT_TRUE(conn.uses_ssl());
}
else
{
EXPECT_FALSE(conn.uses_ssl());
}
}
// Verifies that we are or are not using SSL, depending on what mode was requested.
void validate_ssl(ssl_mode m)
{
if (should_use_ssl(m))
{
// All our test systems MUST support SSL to run these tests
EXPECT_TRUE(conn.uses_ssl());
}
else
{
EXPECT_FALSE(conn.uses_ssl());
}
}
void validate_eof(
const resultset<Stream>& result,
int affected_rows=0,
int warnings=0,
int last_insert=0,
std::string_view info=""
)
{
EXPECT_TRUE(result.valid());
EXPECT_TRUE(result.complete());
EXPECT_EQ(result.affected_rows(), affected_rows);
EXPECT_EQ(result.warning_count(), warnings);
EXPECT_EQ(result.last_insert_id(), last_insert);
EXPECT_EQ(result.info(), info);
}
void validate_eof(
const resultset<Stream>& result,
int affected_rows=0,
int warnings=0,
int last_insert=0,
std::string_view info=""
)
{
EXPECT_TRUE(result.valid());
EXPECT_TRUE(result.complete());
EXPECT_EQ(result.affected_rows(), affected_rows);
EXPECT_EQ(result.warning_count(), warnings);
EXPECT_EQ(result.last_insert_id(), last_insert);
EXPECT_EQ(result.info(), info);
}
void validate_2fields_meta(
const std::vector<field_metadata>& fields,
const std::string& table
) const
{
validate_meta(fields, {
meta_validator(table, "id", field_type::int_),
meta_validator(table, "field_varchar", field_type::varchar)
});
}
void validate_2fields_meta(
const std::vector<field_metadata>& fields,
const std::string& table
) const
{
validate_meta(fields, {
meta_validator(table, "id", field_type::int_),
meta_validator(table, "field_varchar", field_type::varchar)
});
}
void validate_2fields_meta(
const resultset<Stream>& result,
const std::string& table
) const
{
validate_2fields_meta(result.fields(), table);
}
void validate_2fields_meta(
const resultset<Stream>& result,
const std::string& table
) const
{
validate_2fields_meta(result.fields(), table);
}
};
@ -154,19 +154,19 @@ struct IntegTest : testing::Test
template <typename Stream>
struct network_testcase
{
network_functions<Stream>* net;
network_functions<Stream>* net;
std::string name() const { return net->name(); }
std::string name() const { return net->name(); }
static std::vector<network_testcase<Stream>> make_all()
{
std::vector<network_testcase<Stream>> res;
for (auto* net: make_all_network_functions<Stream>())
{
res.push_back(network_testcase<Stream>{net});
}
return res;
}
static std::vector<network_testcase<Stream>> make_all()
{
std::vector<network_testcase<Stream>> res;
for (auto* net: make_all_network_functions<Stream>())
{
res.push_back(network_testcase<Stream>{net});
}
return res;
}
};
// To be used as test parameter, when a test should be run over
@ -174,26 +174,26 @@ struct network_testcase
template <typename Stream>
struct network_testcase_with_ssl
{
network_functions<Stream>* net;
ssl_mode ssl;
network_functions<Stream>* net;
ssl_mode ssl;
std::string name() const
{
return detail::stringize(this->net->name(), '_', to_string(ssl));
}
std::string name() const
{
return detail::stringize(this->net->name(), '_', to_string(ssl));
}
static std::vector<network_testcase_with_ssl<Stream>> make_all()
{
std::vector<network_testcase_with_ssl<Stream>> res;
for (auto* net: make_all_network_functions<Stream>())
{
for (auto ssl: {ssl_mode::require, ssl_mode::disable})
{
res.push_back(network_testcase_with_ssl<Stream>{net, ssl});
}
}
return res;
}
static std::vector<network_testcase_with_ssl<Stream>> make_all()
{
std::vector<network_testcase_with_ssl<Stream>> res;
for (auto* net: make_all_network_functions<Stream>())
{
for (auto ssl: {ssl_mode::require, ssl_mode::disable})
{
res.push_back(network_testcase_with_ssl<Stream>{net, ssl});
}
}
return res;
}
};
/**
@ -217,20 +217,20 @@ struct network_testcase_with_ssl
* calling the method you defined in the fixture.
*/
template <
typename Stream,
typename Param=network_testcase_with_ssl<Stream>,
bool do_handshake=true
typename Stream,
typename Param=network_testcase_with_ssl<Stream>,
bool do_handshake=true
>
struct NetworkTest : public IntegTest<Stream>,
public testing::WithParamInterface<Param>
public testing::WithParamInterface<Param>
{
NetworkTest()
{
if constexpr (do_handshake)
{
this->handshake(this->GetParam().ssl);
}
}
NetworkTest()
{
if constexpr (do_handshake)
{
this->handshake(this->GetParam().ssl);
}
}
};
} // test
@ -239,66 +239,66 @@ struct NetworkTest : public IntegTest<Stream>,
// Typedefs
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_HELPER(TestSuiteName, Suffix, Stream) \
using TestSuiteName##Suffix = TestSuiteName<Stream>;
using TestSuiteName##Suffix = TestSuiteName<Stream>;
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_TCP(TestSuiteName) \
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_HELPER(TestSuiteName, TCP, boost::asio::ip::tcp::socket)
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_HELPER(TestSuiteName, TCP, boost::asio::ip::tcp::socket)
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName) \
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_HELPER(TestSuiteName, UNIX, boost::asio::local::stream_protocol::socket)
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_HELPER(TestSuiteName, UNIX, boost::asio::local::stream_protocol::socket)
#else
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
#endif
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_TCP(TestSuiteName) \
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_TCP(TestSuiteName) \
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
// Test definition
#define BOOST_MYSQL_NETWORK_TEST_HELPER(TestSuiteName, TestName, Suffix) \
TEST_P(TestSuiteName##Suffix, TestName) { this->TestName(); }
TEST_P(TestSuiteName##Suffix, TestName) { this->TestName(); }
#define BOOST_MYSQL_NETWORK_TEST_TCP(TestSuiteName, TestName) \
BOOST_MYSQL_NETWORK_TEST_HELPER(TestSuiteName, TestName, TCP)
BOOST_MYSQL_NETWORK_TEST_HELPER(TestSuiteName, TestName, TCP)
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
#define BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName) \
BOOST_MYSQL_NETWORK_TEST_HELPER(TestSuiteName, TestName, UNIX)
BOOST_MYSQL_NETWORK_TEST_HELPER(TestSuiteName, TestName, UNIX)
#else
#define BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
#endif
#define BOOST_MYSQL_NETWORK_TEST(TestSuiteName, TestName) \
BOOST_MYSQL_NETWORK_TEST_TCP(TestSuiteName, TestName) \
BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
BOOST_MYSQL_NETWORK_TEST_TCP(TestSuiteName, TestName) \
BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
// Test suite instantiation
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, Suffix) \
INSTANTIATE_TEST_SUITE_P(Default, TestSuiteName##Suffix, testing::ValuesIn( \
TestSuiteName##Suffix::ParamType::make_all() \
), [](const auto& param_info) { \
return param_info.param.name(); \
});
INSTANTIATE_TEST_SUITE_P(Default, TestSuiteName##Suffix, testing::ValuesIn( \
TestSuiteName##Suffix::ParamType::make_all() \
), [](const auto& param_info) { \
return param_info.param.name(); \
});
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_TCP(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, TCP)
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, TCP)
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, UNIX)
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, UNIX)
#else
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
#endif
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_TCP(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_TCP(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
// Typedefs + Instantiation
#define BOOST_MYSQL_NETWORK_TEST_SUITE(TestSuiteName) \
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName)
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName)

View File

@ -11,80 +11,80 @@
using namespace boost::mysql::test;
#define MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(getter) \
{ #getter, &boost::mysql::field_metadata::getter }
{ #getter, &boost::mysql::field_metadata::getter }
static struct flag_entry
{
const char* name;
meta_validator::flag_getter getter;
const char* name;
meta_validator::flag_getter getter;
} flag_names [] = {
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_not_null),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_primary_key),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unique_key),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_multiple_key),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unsigned),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_zerofill),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_auto_increment),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(has_no_default_value),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_set_to_now_on_update)
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_not_null),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_primary_key),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unique_key),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_multiple_key),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unsigned),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_zerofill),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_auto_increment),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(has_no_default_value),
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_set_to_now_on_update)
};
static bool contains(
meta_validator::flag_getter flag,
const std::vector<meta_validator::flag_getter>& flagvec
meta_validator::flag_getter flag,
const std::vector<meta_validator::flag_getter>& flagvec
)
{
return std::find(flagvec.begin(), flagvec.end(), flag) != flagvec.end();
return std::find(flagvec.begin(), flagvec.end(), flag) != flagvec.end();
}
void meta_validator::validate(
const mysql::field_metadata& value
const mysql::field_metadata& value
) const
{
// Fixed fields
EXPECT_EQ(value.database(), "awesome");
EXPECT_EQ(value.table(), table_);
EXPECT_EQ(value.original_table(), org_table_);
EXPECT_EQ(value.field_name(), field_);
EXPECT_EQ(value.original_field_name(), org_field_);
EXPECT_GT(value.column_length(), 0u);
EXPECT_EQ(value.type(), type_);
EXPECT_EQ(value.decimals(), decimals_);
// Fixed fields
EXPECT_EQ(value.database(), "awesome");
EXPECT_EQ(value.table(), table_);
EXPECT_EQ(value.original_table(), org_table_);
EXPECT_EQ(value.field_name(), field_);
EXPECT_EQ(value.original_field_name(), org_field_);
EXPECT_GT(value.column_length(), 0u);
EXPECT_EQ(value.type(), type_);
EXPECT_EQ(value.decimals(), decimals_);
// Flags
std::vector<flag_entry> all_flags (std::begin(flag_names), std::end(flag_names));
// Flags
std::vector<flag_entry> all_flags (std::begin(flag_names), std::end(flag_names));
for (flag_getter true_flag: flags_)
{
auto it = std::find_if(
all_flags.begin(),
all_flags.end(),
[true_flag](const flag_entry& entry) { return entry.getter == true_flag; }
);
ASSERT_NE(it, all_flags.end()); // no repeated flag
ASSERT_FALSE(contains(true_flag, ignore_flags_)); // ignore flags cannot be set to true
EXPECT_TRUE((value.*true_flag)()) << it->name;
all_flags.erase(it);
}
for (flag_getter true_flag: flags_)
{
auto it = std::find_if(
all_flags.begin(),
all_flags.end(),
[true_flag](const flag_entry& entry) { return entry.getter == true_flag; }
);
ASSERT_NE(it, all_flags.end()); // no repeated flag
ASSERT_FALSE(contains(true_flag, ignore_flags_)); // ignore flags cannot be set to true
EXPECT_TRUE((value.*true_flag)()) << it->name;
all_flags.erase(it);
}
for (const auto& entry: all_flags)
{
if (!contains(entry.getter, ignore_flags_))
{
EXPECT_FALSE((value.*entry.getter)()) << entry.name;
}
}
for (const auto& entry: all_flags)
{
if (!contains(entry.getter, ignore_flags_))
{
EXPECT_FALSE((value.*entry.getter)()) << entry.name;
}
}
}
void boost::mysql::test::validate_meta(
const std::vector<field_metadata>& actual,
const std::vector<meta_validator>& expected
const std::vector<field_metadata>& actual,
const std::vector<meta_validator>& expected
)
{
ASSERT_EQ(actual.size(), expected.size());
for (std::size_t i = 0; i < actual.size(); ++i)
{
expected[i].validate(actual[i]);
}
ASSERT_EQ(actual.size(), expected.size());
for (std::size_t i = 0; i < actual.size(); ++i)
{
expected[i].validate(actual[i]);
}
}

View File

@ -18,36 +18,36 @@ namespace test {
class meta_validator
{
public:
using flag_getter = bool (field_metadata::*)() const noexcept;
meta_validator(std::string table, std::string field, field_type type,
std::vector<flag_getter> flags={}, unsigned decimals=0,
std::vector<flag_getter> ignore_flags={}):
table_(std::move(table)), org_table_(table_), field_(std::move(field)), org_field_(field_),
decimals_(decimals), type_(type), flags_(std::move(flags)),
ignore_flags_(std::move(ignore_flags))
{
}
meta_validator(std::string table, std::string org_table,
std::string field, std::string org_field, field_type type,
std::vector<flag_getter> flags={}, unsigned decimals=0,
std::vector<flag_getter> ignore_flags={}):
table_(std::move(table)), org_table_(std::move(org_table)),
field_(std::move(field)), org_field_(std::move(org_field)),
decimals_(decimals), type_(type), flags_(std::move(flags)),
ignore_flags_(std::move(ignore_flags))
{
}
void validate(const field_metadata& value) const;
field_type type() const noexcept { return type_; }
using flag_getter = bool (field_metadata::*)() const noexcept;
meta_validator(std::string table, std::string field, field_type type,
std::vector<flag_getter> flags={}, unsigned decimals=0,
std::vector<flag_getter> ignore_flags={}):
table_(std::move(table)), org_table_(table_), field_(std::move(field)), org_field_(field_),
decimals_(decimals), type_(type), flags_(std::move(flags)),
ignore_flags_(std::move(ignore_flags))
{
}
meta_validator(std::string table, std::string org_table,
std::string field, std::string org_field, field_type type,
std::vector<flag_getter> flags={}, unsigned decimals=0,
std::vector<flag_getter> ignore_flags={}):
table_(std::move(table)), org_table_(std::move(org_table)),
field_(std::move(field)), org_field_(std::move(org_field)),
decimals_(decimals), type_(type), flags_(std::move(flags)),
ignore_flags_(std::move(ignore_flags))
{
}
void validate(const field_metadata& value) const;
field_type type() const noexcept { return type_; }
private:
std::string table_;
std::string org_table_;
std::string field_;
std::string org_field_;
unsigned decimals_;
field_type type_;
std::vector<flag_getter> flags_;
std::vector<flag_getter> ignore_flags_;
std::string table_;
std::string org_table_;
std::string field_;
std::string org_field_;
unsigned decimals_;
field_type type_;
std::vector<flag_getter> flags_;
std::vector<flag_getter> ignore_flags_;
};
void validate_meta(const std::vector<field_metadata>& actual, const std::vector<meta_validator>& expected);

File diff suppressed because it is too large Load Diff

View File

@ -50,46 +50,46 @@ struct no_result {};
template <typename T>
struct network_result
{
error_code err;
std::optional<error_info> info; // some async initiators (futures) don't support this
T value;
error_code err;
std::optional<error_info> info; // some async initiators (futures) don't support this
T value;
network_result() = default;
network_result() = default;
network_result(error_code ec, error_info info, T&& value = {}):
err(ec), info(std::move(info)), value(std::move(value)) {}
network_result(error_code ec, error_info info, T&& value = {}):
err(ec), info(std::move(info)), value(std::move(value)) {}
network_result(error_code ec, T&& value = {}):
err(ec), value(std::move(value)) {}
network_result(error_code ec, T&& value = {}):
err(ec), value(std::move(value)) {}
void validate_no_error() const
{
ASSERT_EQ(err, error_code());
if (info)
{
EXPECT_EQ(*info, error_info());
}
}
void validate_no_error() const
{
ASSERT_EQ(err, error_code());
if (info)
{
EXPECT_EQ(*info, error_info());
}
}
void validate_error(
error_code expected_errc,
const std::vector<std::string>& expected_msg
) const
{
EXPECT_EQ(err, expected_errc);
if (info)
{
validate_string_contains(info->message(), expected_msg);
}
}
void validate_error(
error_code expected_errc,
const std::vector<std::string>& expected_msg
) const
{
EXPECT_EQ(err, expected_errc);
if (info)
{
validate_string_contains(info->message(), expected_msg);
}
}
void validate_error(
errc expected_errc,
const std::vector<std::string>& expected_msg
) const
{
validate_error(detail::make_error_code(expected_errc), expected_msg);
}
void validate_error(
errc expected_errc,
const std::vector<std::string>& expected_msg
) const
{
validate_error(detail::make_error_code(expected_errc), expected_msg);
}
};
using value_list_it = std::forward_list<value>::const_iterator;
@ -98,24 +98,24 @@ template <typename Stream>
class network_functions
{
public:
using connection_type = connection<Stream>;
using prepared_statement_type = prepared_statement<Stream>;
using resultset_type = resultset<Stream>;
using connection_type = connection<Stream>;
using prepared_statement_type = prepared_statement<Stream>;
using resultset_type = resultset<Stream>;
virtual ~network_functions() = default;
virtual const char* name() const = 0;
virtual network_result<no_result> handshake(connection_type&, const connection_params&) = 0;
virtual network_result<resultset_type> query(connection_type&, std::string_view query) = 0;
virtual network_result<prepared_statement_type> prepare_statement(
connection_type&, std::string_view statement) = 0;
virtual network_result<resultset_type> execute_statement(
prepared_statement_type&, value_list_it params_first, value_list_it params_last) = 0;
virtual network_result<resultset_type> execute_statement(
prepared_statement_type&, const std::vector<value>&) = 0;
virtual network_result<no_result> close_statement(prepared_statement_type&) = 0;
virtual network_result<const row*> fetch_one(resultset_type&) = 0;
virtual network_result<std::vector<owning_row>> fetch_many(resultset_type&, std::size_t count) = 0;
virtual network_result<std::vector<owning_row>> fetch_all(resultset_type&) = 0;
virtual ~network_functions() = default;
virtual const char* name() const = 0;
virtual network_result<no_result> handshake(connection_type&, const connection_params&) = 0;
virtual network_result<resultset_type> query(connection_type&, std::string_view query) = 0;
virtual network_result<prepared_statement_type> prepare_statement(
connection_type&, std::string_view statement) = 0;
virtual network_result<resultset_type> execute_statement(
prepared_statement_type&, value_list_it params_first, value_list_it params_last) = 0;
virtual network_result<resultset_type> execute_statement(
prepared_statement_type&, const std::vector<value>&) = 0;
virtual network_result<no_result> close_statement(prepared_statement_type&) = 0;
virtual network_result<const row*> fetch_one(resultset_type&) = 0;
virtual network_result<std::vector<owning_row>> fetch_many(resultset_type&, std::size_t count) = 0;
virtual network_result<std::vector<owning_row>> fetch_all(resultset_type&) = 0;
};
template <typename Stream>

View File

@ -20,35 +20,35 @@ namespace
template <typename Stream>
struct PrepareStatementTest : public NetworkTest<Stream>
{
auto do_prepare(std::string_view stmt)
{
return this->GetParam().net->prepare_statement(this->conn, stmt);
}
auto do_prepare(std::string_view stmt)
{
return this->GetParam().net->prepare_statement(this->conn, stmt);
}
void OkNoParams()
{
auto stmt = do_prepare("SELECT * FROM empty_table");
stmt.validate_no_error();
ASSERT_TRUE(stmt.value.valid());
EXPECT_GT(stmt.value.id(), 0u);
EXPECT_EQ(stmt.value.num_params(), 0);
}
void OkNoParams()
{
auto stmt = do_prepare("SELECT * FROM empty_table");
stmt.validate_no_error();
ASSERT_TRUE(stmt.value.valid());
EXPECT_GT(stmt.value.id(), 0u);
EXPECT_EQ(stmt.value.num_params(), 0);
}
void OkWithParams()
{
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
stmt.validate_no_error();
ASSERT_TRUE(stmt.value.valid());
EXPECT_GT(stmt.value.id(), 0u);
EXPECT_EQ(stmt.value.num_params(), 2);
}
void OkWithParams()
{
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
stmt.validate_no_error();
ASSERT_TRUE(stmt.value.valid());
EXPECT_GT(stmt.value.id(), 0u);
EXPECT_EQ(stmt.value.num_params(), 2);
}
void InvalidStatement()
{
auto stmt = do_prepare("SELECT * FROM bad_table WHERE id IN (?, ?)");
stmt.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
EXPECT_FALSE(stmt.value.valid());
}
void InvalidStatement()
{
auto stmt = do_prepare("SELECT * FROM bad_table WHERE id IN (?, ?)");
stmt.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
EXPECT_FALSE(stmt.value.valid());
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(PrepareStatementTest)

View File

@ -17,219 +17,219 @@ namespace
template <typename Stream>
struct PreparedStatementLifecycleTest : NetworkTest<Stream>
{
std::int64_t get_table_size(const std::string& table)
{
return std::get<std::int64_t>(
this->conn.query("SELECT COUNT(*) FROM " + table).fetch_all().at(0).values().at(0));
}
std::int64_t get_table_size(const std::string& table)
{
return std::get<std::int64_t>(
this->conn.query("SELECT COUNT(*) FROM " + table).fetch_all().at(0).values().at(0));
}
value get_updates_table_value(const std::string& field_varchar="f0")
{
return this->conn.query("SELECT field_int FROM updates_table WHERE field_varchar = '" + field_varchar + "'")
.fetch_all().at(0).values().at(0);
}
value get_updates_table_value(const std::string& field_varchar="f0")
{
return this->conn.query("SELECT field_int FROM updates_table WHERE field_varchar = '" + field_varchar + "'")
.fetch_all().at(0).values().at(0);
}
void SelectWithParametersMultipleExecutions()
{
auto* net = this->GetParam().net;
void SelectWithParametersMultipleExecutions()
{
auto* net = this->GetParam().net;
// Prepare a statement
auto stmt = net->prepare_statement(
this->conn,
"SELECT * FROM two_rows_table WHERE id = ? OR field_varchar = ?"
);
stmt.validate_no_error();
// Prepare a statement
auto stmt = net->prepare_statement(
this->conn,
"SELECT * FROM two_rows_table WHERE id = ? OR field_varchar = ?"
);
stmt.validate_no_error();
// Execute it. Only one row will be returned (because of the id)
auto result = net->execute_statement(stmt.value, makevalues(1, "non_existent"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_FALSE(result.value.complete());
this->validate_2fields_meta(result.value, "two_rows_table");
// Execute it. Only one row will be returned (because of the id)
auto result = net->execute_statement(stmt.value, makevalues(1, "non_existent"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_FALSE(result.value.complete());
this->validate_2fields_meta(result.value, "two_rows_table");
auto rows = net->fetch_all(result.value);
rows.validate_no_error();
ASSERT_EQ(rows.value.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows.value[0]), (makerow(1, "f0")));
EXPECT_TRUE(result.value.complete());
auto rows = net->fetch_all(result.value);
rows.validate_no_error();
ASSERT_EQ(rows.value.size(), 1);
EXPECT_EQ(static_cast<const row&>(rows.value[0]), (makerow(1, "f0")));
EXPECT_TRUE(result.value.complete());
// Execute it again, but with different values. This time, two rows are returned
result = net->execute_statement(stmt.value, makevalues(1, "f1"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_FALSE(result.value.complete());
this->validate_2fields_meta(result.value, "two_rows_table");
// Execute it again, but with different values. This time, two rows are returned
result = net->execute_statement(stmt.value, makevalues(1, "f1"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_FALSE(result.value.complete());
this->validate_2fields_meta(result.value, "two_rows_table");
rows = net->fetch_all(result.value);
rows.validate_no_error();
ASSERT_EQ(rows.value.size(), 2);
EXPECT_EQ(static_cast<const row&>(rows.value[0]), (makerow(1, "f0")));
EXPECT_EQ(static_cast<const row&>(rows.value[1]), (makerow(2, "f1")));
EXPECT_TRUE(result.value.complete());
rows = net->fetch_all(result.value);
rows.validate_no_error();
ASSERT_EQ(rows.value.size(), 2);
EXPECT_EQ(static_cast<const row&>(rows.value[0]), (makerow(1, "f0")));
EXPECT_EQ(static_cast<const row&>(rows.value[1]), (makerow(2, "f1")));
EXPECT_TRUE(result.value.complete());
// Close it
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
// Close it
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
void InsertWithParametersMultipleExecutions()
{
auto* net = this->GetParam().net;
void InsertWithParametersMultipleExecutions()
{
auto* net = this->GetParam().net;
// Get the number of rows before insertion
auto rows_before = get_table_size("inserts_table");
// Get the number of rows before insertion
auto rows_before = get_table_size("inserts_table");
// Prepare a statement
auto stmt = net->prepare_statement(
this->conn,
"INSERT INTO inserts_table (field_varchar) VALUES (?)"
);
stmt.validate_no_error();
// Prepare a statement
auto stmt = net->prepare_statement(
this->conn,
"INSERT INTO inserts_table (field_varchar) VALUES (?)"
);
stmt.validate_no_error();
// Insert one value
auto result = net->execute_statement(stmt.value, makevalues("value0"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Insert one value
auto result = net->execute_statement(stmt.value, makevalues("value0"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Insert another one
result = net->execute_statement(stmt.value, makevalues("value1"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Insert another one
result = net->execute_statement(stmt.value, makevalues("value1"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Validate we did something
auto rows_after = get_table_size("inserts_table");
EXPECT_EQ(rows_after, rows_before + 2);
// Validate we did something
auto rows_after = get_table_size("inserts_table");
EXPECT_EQ(rows_after, rows_before + 2);
// Close it
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
// Close it
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
void UpdateWithParametersMultipleExecutions()
{
auto* net = this->GetParam().net;
void UpdateWithParametersMultipleExecutions()
{
auto* net = this->GetParam().net;
// Prepare a statement
auto stmt = net->prepare_statement(
this->conn,
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
);
stmt.validate_no_error();
// Prepare a statement
auto stmt = net->prepare_statement(
this->conn,
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
);
stmt.validate_no_error();
// Set field_int to something
auto result = net->execute_statement(stmt.value, makevalues(200, "f0"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Set field_int to something
auto result = net->execute_statement(stmt.value, makevalues(200, "f0"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Verify that took effect
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(200)));
// Verify that took effect
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(200)));
// Set field_int to something different
result = net->execute_statement(stmt.value, makevalues(250, "f0"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Set field_int to something different
result = net->execute_statement(stmt.value, makevalues(250, "f0"));
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_TRUE(result.value.fields().empty());
// Verify that took effect
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(250)));
// Verify that took effect
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(250)));
// Close the statement
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
// Close the statement
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
void MultipleStatements()
{
auto* net = this->GetParam().net;
void MultipleStatements()
{
auto* net = this->GetParam().net;
// Prepare an update
auto stmt_update = net->prepare_statement(
this->conn,
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
);
stmt_update.validate_no_error();
// Prepare an update
auto stmt_update = net->prepare_statement(
this->conn,
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
);
stmt_update.validate_no_error();
// Prepare a select
auto stmt_select = net->prepare_statement(
this->conn,
"SELECT field_int FROM updates_table WHERE field_varchar = ?"
);
stmt_select.validate_no_error();
// Prepare a select
auto stmt_select = net->prepare_statement(
this->conn,
"SELECT field_int FROM updates_table WHERE field_varchar = ?"
);
stmt_select.validate_no_error();
// They have different IDs
EXPECT_NE(stmt_update.value.id(), stmt_select.value.id());
// They have different IDs
EXPECT_NE(stmt_update.value.id(), stmt_select.value.id());
// Execute update
auto update_result = net->execute_statement(stmt_update.value, makevalues(210, "f0"));
update_result.validate_no_error();
EXPECT_TRUE(update_result.value.complete());
// Execute update
auto update_result = net->execute_statement(stmt_update.value, makevalues(210, "f0"));
update_result.validate_no_error();
EXPECT_TRUE(update_result.value.complete());
// Execute select
auto select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
select_result.validate_no_error();
auto rows = net->fetch_all(select_result.value);
rows.validate_no_error();
EXPECT_EQ(rows.value.size(), 1);
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(210));
// Execute select
auto select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
select_result.validate_no_error();
auto rows = net->fetch_all(select_result.value);
rows.validate_no_error();
EXPECT_EQ(rows.value.size(), 1);
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(210));
// Execute update again
update_result = net->execute_statement(stmt_update.value, makevalues(220, "f0"));
update_result.validate_no_error();
EXPECT_TRUE(update_result.value.complete());
// Execute update again
update_result = net->execute_statement(stmt_update.value, makevalues(220, "f0"));
update_result.validate_no_error();
EXPECT_TRUE(update_result.value.complete());
// Update no longer needed, close it
auto close_result = net->close_statement(stmt_update.value);
close_result.validate_no_error();
// Update no longer needed, close it
auto close_result = net->close_statement(stmt_update.value);
close_result.validate_no_error();
// Execute select again
select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
select_result.validate_no_error();
rows = net->fetch_all(select_result.value);
rows.validate_no_error();
EXPECT_EQ(rows.value.size(), 1);
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(220));
// Execute select again
select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
select_result.validate_no_error();
rows = net->fetch_all(select_result.value);
rows.validate_no_error();
EXPECT_EQ(rows.value.size(), 1);
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(220));
// Close select
close_result = net->close_statement(stmt_select.value);
close_result.validate_no_error();
}
// Close select
close_result = net->close_statement(stmt_select.value);
close_result.validate_no_error();
}
void InsertWithNullValues()
{
auto* net = this->GetParam().net;
void InsertWithNullValues()
{
auto* net = this->GetParam().net;
// Statement to perform the updates
auto stmt = net->prepare_statement(
this->conn,
"UPDATE updates_table SET field_int = ? WHERE field_varchar = 'fnull'"
);
stmt.validate_no_error();
// Statement to perform the updates
auto stmt = net->prepare_statement(
this->conn,
"UPDATE updates_table SET field_int = ? WHERE field_varchar = 'fnull'"
);
stmt.validate_no_error();
// Set the value we will be updating to something non-NULL
auto result = net->execute_statement(stmt.value, makevalues(42));
result.validate_no_error();
// Set the value we will be updating to something non-NULL
auto result = net->execute_statement(stmt.value, makevalues(42));
result.validate_no_error();
// Verify it took effect
ASSERT_EQ(get_updates_table_value("fnull"), value(std::int32_t(42)));
// Verify it took effect
ASSERT_EQ(get_updates_table_value("fnull"), value(std::int32_t(42)));
// Update the value to NULL
result = net->execute_statement(stmt.value, makevalues(nullptr));
result.validate_no_error();
// Update the value to NULL
result = net->execute_statement(stmt.value, makevalues(nullptr));
result.validate_no_error();
// Verify it took effect
ASSERT_EQ(get_updates_table_value("fnull"), value(nullptr));
// Verify it took effect
ASSERT_EQ(get_updates_table_value("fnull"), value(nullptr));
// Close statement
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
// Close statement
auto close_result = net->close_statement(stmt.value);
close_result.validate_no_error();
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(PreparedStatementLifecycleTest)

View File

@ -26,72 +26,72 @@ namespace
template <typename Stream>
struct QueryTest : public NetworkTest<Stream>
{
auto do_query(std::string_view sql)
{
return this->GetParam().net->query(this->conn, sql);
}
auto do_query(std::string_view sql)
{
return this->GetParam().net->query(this->conn, sql);
}
void InsertQueryOk()
{
const char* sql = "INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
auto result = do_query(sql);
result.validate_no_error();
EXPECT_TRUE(result.value.fields().empty());
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_EQ(result.value.affected_rows(), 1);
EXPECT_EQ(result.value.warning_count(), 0);
EXPECT_GT(result.value.last_insert_id(), 0);
EXPECT_EQ(result.value.info(), "");
}
void InsertQueryOk()
{
const char* sql = "INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
auto result = do_query(sql);
result.validate_no_error();
EXPECT_TRUE(result.value.fields().empty());
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_EQ(result.value.affected_rows(), 1);
EXPECT_EQ(result.value.warning_count(), 0);
EXPECT_GT(result.value.last_insert_id(), 0);
EXPECT_EQ(result.value.info(), "");
}
void InsertQueryFailed()
{
const char* sql = "INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
auto result = do_query(sql);
result.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
EXPECT_FALSE(result.value.valid());
}
void InsertQueryFailed()
{
const char* sql = "INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
auto result = do_query(sql);
result.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
EXPECT_FALSE(result.value.valid());
}
void UpdateQueryOk()
{
const char* sql = "UPDATE updates_table SET field_int = field_int+1";
auto result = do_query(sql);
result.validate_no_error();
EXPECT_TRUE(result.value.fields().empty());
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_EQ(result.value.affected_rows(), 2);
EXPECT_EQ(result.value.warning_count(), 0);
EXPECT_EQ(result.value.last_insert_id(), 0);
EXPECT_THAT(std::string(result.value.info()), HasSubstr("Rows matched"));
}
void UpdateQueryOk()
{
const char* sql = "UPDATE updates_table SET field_int = field_int+1";
auto result = do_query(sql);
result.validate_no_error();
EXPECT_TRUE(result.value.fields().empty());
EXPECT_TRUE(result.value.valid());
EXPECT_TRUE(result.value.complete());
EXPECT_EQ(result.value.affected_rows(), 2);
EXPECT_EQ(result.value.warning_count(), 0);
EXPECT_EQ(result.value.last_insert_id(), 0);
EXPECT_THAT(std::string(result.value.info()), HasSubstr("Rows matched"));
}
void SelectOk()
{
auto result = do_query("SELECT * FROM empty_table");
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_FALSE(result.value.complete());
this->validate_2fields_meta(result.value, "empty_table");
}
void SelectOk()
{
auto result = do_query("SELECT * FROM empty_table");
result.validate_no_error();
EXPECT_TRUE(result.value.valid());
EXPECT_FALSE(result.value.complete());
this->validate_2fields_meta(result.value, "empty_table");
}
void SelectQueryFailed()
{
auto result = do_query("SELECT field_varchar, field_bad FROM one_row_table");
result.validate_error(errc::bad_field_error, {"unknown column", "field_bad"});
EXPECT_FALSE(result.value.valid());
}
void SelectQueryFailed()
{
auto result = do_query("SELECT field_varchar, field_bad FROM one_row_table");
result.validate_error(errc::bad_field_error, {"unknown column", "field_bad"});
EXPECT_FALSE(result.value.valid());
}
// Some system-level query tests (TODO: this does not feel right here)
void QueryAndFetch_AliasedTableAndField_MetadataCorrect()
{
auto result = do_query("SELECT field_varchar AS field_alias FROM empty_table table_alias");
meta_validator validator ("table_alias", "empty_table", "field_alias",
"field_varchar", field_type::varchar);
result.validate_no_error();
validate_meta(result.value.fields(), {validator});
}
// Some system-level query tests (TODO: this does not feel right here)
void QueryAndFetch_AliasedTableAndField_MetadataCorrect()
{
auto result = do_query("SELECT field_varchar AS field_alias FROM empty_table table_alias");
meta_validator validator ("table_alias", "empty_table", "field_alias",
"field_varchar", field_type::varchar);
result.validate_no_error();
validate_meta(result.value.fields(), {validator});
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(QueryTest)

View File

@ -28,31 +28,31 @@ template <typename Stream>
class resultset_generator
{
public:
virtual ~resultset_generator() {}
virtual const char* name() const = 0;
virtual resultset<Stream> generate(connection<Stream>&, std::string_view) = 0;
virtual ~resultset_generator() {}
virtual const char* name() const = 0;
virtual resultset<Stream> generate(connection<Stream>&, std::string_view) = 0;
};
template <typename Stream>
class text_resultset_generator : public resultset_generator<Stream>
{
public:
const char* name() const override { return "text"; }
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
{
return conn.query(query);
}
const char* name() const override { return "text"; }
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
{
return conn.query(query);
}
};
template <typename Stream>
class binary_resultset_generator : public resultset_generator<Stream>
{
public:
const char* name() const override { return "binary"; }
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
{
return conn.prepare_statement(query).execute(boost::mysql::no_statement_params);
}
const char* name() const override { return "binary"; }
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
{
return conn.prepare_statement(query).execute(boost::mysql::no_statement_params);
}
};
template <typename Stream> text_resultset_generator<Stream> text_obj;
@ -62,233 +62,233 @@ template <typename Stream> binary_resultset_generator<Stream> binary_obj;
template <typename Stream>
struct resultset_testcase : network_testcase_with_ssl<Stream>
{
resultset_generator<Stream>* gen;
resultset_generator<Stream>* gen;
resultset_testcase(network_testcase_with_ssl<Stream> base, resultset_generator<Stream>* gen):
network_testcase_with_ssl<Stream>(base), gen(gen) {}
resultset_testcase(network_testcase_with_ssl<Stream> base, resultset_generator<Stream>* gen):
network_testcase_with_ssl<Stream>(base), gen(gen) {}
std::string name() const
{
return network_testcase_with_ssl<Stream>::name() + '_' + gen->name();
}
std::string name() const
{
return network_testcase_with_ssl<Stream>::name() + '_' + gen->name();
}
static std::vector<resultset_testcase<Stream>> make_all()
{
resultset_generator<Stream>* all_resultset_generators [] = {
&text_obj<Stream>,
&binary_obj<Stream>
};
static std::vector<resultset_testcase<Stream>> make_all()
{
resultset_generator<Stream>* all_resultset_generators [] = {
&text_obj<Stream>,
&binary_obj<Stream>
};
std::vector<resultset_testcase<Stream>> res;
for (auto* gen: all_resultset_generators)
{
for (auto base: network_testcase_with_ssl<Stream>::make_all())
{
res.push_back(resultset_testcase<Stream>(base, gen));
}
}
return res;
}
std::vector<resultset_testcase<Stream>> res;
for (auto* gen: all_resultset_generators)
{
for (auto base: network_testcase_with_ssl<Stream>::make_all())
{
res.push_back(resultset_testcase<Stream>(base, gen));
}
}
return res;
}
};
template <typename Stream>
struct ResultsetTest : public NetworkTest<Stream, resultset_testcase<Stream>, true>
{
auto do_generate(std::string_view query) { return this->GetParam().gen->generate(this->conn, query); }
auto do_fetch_one(resultset<Stream>& r) { return this->GetParam().net->fetch_one(r); }
auto do_fetch_many(resultset<Stream>& r, std::size_t count) { return this->GetParam().net->fetch_many(r, count); }
auto do_fetch_all(resultset<Stream>& r) { return this->GetParam().net->fetch_all(r); }
auto do_generate(std::string_view query) { return this->GetParam().gen->generate(this->conn, query); }
auto do_fetch_one(resultset<Stream>& r) { return this->GetParam().net->fetch_one(r); }
auto do_fetch_many(resultset<Stream>& r, std::size_t count) { return this->GetParam().net->fetch_many(r, count); }
auto do_fetch_all(resultset<Stream>& r) { return this->GetParam().net->fetch_all(r); }
// FetchOne
void FetchOne_NoResults()
{
auto result = do_generate("SELECT * FROM empty_table");
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
EXPECT_EQ(result.fields().size(), 2);
// FetchOne
void FetchOne_NoResults()
{
auto result = do_generate("SELECT * FROM empty_table");
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
EXPECT_EQ(result.fields().size(), 2);
// Already in the end of the resultset, we receive the EOF
auto row_result = do_fetch_one(result);
row_result.validate_no_error();
EXPECT_EQ(row_result.value, nullptr);
this->validate_eof(result);
// Already in the end of the resultset, we receive the EOF
auto row_result = do_fetch_one(result);
row_result.validate_no_error();
EXPECT_EQ(row_result.value, nullptr);
this->validate_eof(result);
// Fetching again just returns null
row_result = do_fetch_one(result);
row_result.validate_no_error();
EXPECT_EQ(row_result.value, nullptr);
this->validate_eof(result);
}
// Fetching again just returns null
row_result = do_fetch_one(result);
row_result.validate_no_error();
EXPECT_EQ(row_result.value, nullptr);
this->validate_eof(result);
}
void FetchOne_OneRow()
{
auto result = do_generate("SELECT * FROM one_row_table");
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
EXPECT_EQ(result.fields().size(), 2);
void FetchOne_OneRow()
{
auto result = do_generate("SELECT * FROM one_row_table");
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
EXPECT_EQ(result.fields().size(), 2);
// Fetch only row
auto row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_NE(row_result.value, nullptr);
this->validate_2fields_meta(result, "one_row_table");
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch only row
auto row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_NE(row_result.value, nullptr);
this->validate_2fields_meta(result, "one_row_table");
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch next: end of resultset
row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_EQ(row_result.value, nullptr);
this->validate_eof(result);
}
// Fetch next: end of resultset
row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_EQ(row_result.value, nullptr);
this->validate_eof(result);
}
void FetchOne_TwoRows()
{
auto result = do_generate("SELECT * FROM two_rows_table");
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
EXPECT_EQ(result.fields().size(), 2);
void FetchOne_TwoRows()
{
auto result = do_generate("SELECT * FROM two_rows_table");
EXPECT_TRUE(result.valid());
EXPECT_FALSE(result.complete());
EXPECT_EQ(result.fields().size(), 2);
// Fetch first row
auto row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_NE(row_result.value, nullptr);
this->validate_2fields_meta(result, "two_rows_table");
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch first row
auto row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_NE(row_result.value, nullptr);
this->validate_2fields_meta(result, "two_rows_table");
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
EXPECT_FALSE(result.complete());
// Fetch next row
row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_NE(row_result.value, nullptr);
this->validate_2fields_meta(result, "two_rows_table");
EXPECT_EQ(row_result.value->values(), makevalues(2, "f1"));
EXPECT_FALSE(result.complete());
// Fetch next row
row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_NE(row_result.value, nullptr);
this->validate_2fields_meta(result, "two_rows_table");
EXPECT_EQ(row_result.value->values(), makevalues(2, "f1"));
EXPECT_FALSE(result.complete());
// Fetch next: end of resultset
row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_EQ(row_result.value, nullptr);
this->validate_eof(result);
}
// Fetch next: end of resultset
row_result = do_fetch_one(result);
row_result.validate_no_error();
ASSERT_EQ(row_result.value, nullptr);
this->validate_eof(result);
}
// There seems to be no real case where fetch can fail (other than net fails)
// There seems to be no real case where fetch can fail (other than net fails)
// FetchMany
void FetchMany_NoResults()
{
auto result = do_generate("SELECT * FROM empty_table");
// FetchMany
void FetchMany_NoResults()
{
auto result = do_generate("SELECT * FROM empty_table");
// Fetch many, but there are no results
auto rows_result = do_fetch_many(result, 10);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
EXPECT_TRUE(result.complete());
this->validate_eof(result);
// Fetch many, but there are no results
auto rows_result = do_fetch_many(result, 10);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
EXPECT_TRUE(result.complete());
this->validate_eof(result);
// Fetch again, should return OK and empty
rows_result = do_fetch_many(result, 10);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
EXPECT_TRUE(result.complete());
this->validate_eof(result);
}
// Fetch again, should return OK and empty
rows_result = do_fetch_many(result, 10);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
EXPECT_TRUE(result.complete());
this->validate_eof(result);
}
void FetchMany_MoreRowsThanCount()
{
auto result = do_generate("SELECT * FROM three_rows_table");
void FetchMany_MoreRowsThanCount()
{
auto result = do_generate("SELECT * FROM three_rows_table");
// Fetch 2, one remaining
auto rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
// Fetch 2, one remaining
auto rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
// Fetch another two (completes the resultset)
rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_TRUE(result.complete());
this->validate_eof(result);
EXPECT_EQ(rows_result.value, (makerows(2, 3, "f2")));
}
// Fetch another two (completes the resultset)
rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_TRUE(result.complete());
this->validate_eof(result);
EXPECT_EQ(rows_result.value, (makerows(2, 3, "f2")));
}
void FetchMany_LessRowsThanCount()
{
auto result = do_generate("SELECT * FROM two_rows_table");
void FetchMany_LessRowsThanCount()
{
auto result = do_generate("SELECT * FROM two_rows_table");
// Fetch 3, resultset exhausted
auto rows_result = do_fetch_many(result, 3);
rows_result.validate_no_error();
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
this->validate_eof(result);
}
// Fetch 3, resultset exhausted
auto rows_result = do_fetch_many(result, 3);
rows_result.validate_no_error();
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
this->validate_eof(result);
}
void FetchMany_SameRowsAsCount()
{
auto result = do_generate("SELECT * FROM two_rows_table");
void FetchMany_SameRowsAsCount()
{
auto result = do_generate("SELECT * FROM two_rows_table");
// Fetch 2, 0 remaining but resultset not exhausted
auto rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
// Fetch 2, 0 remaining but resultset not exhausted
auto rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
// Fetch again, exhausts the resultset
rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_EQ(rows_result.value.size(), 0);
this->validate_eof(result);
}
// Fetch again, exhausts the resultset
rows_result = do_fetch_many(result, 2);
rows_result.validate_no_error();
EXPECT_EQ(rows_result.value.size(), 0);
this->validate_eof(result);
}
void FetchMany_CountEqualsOne()
{
auto result = do_generate("SELECT * FROM one_row_table");
void FetchMany_CountEqualsOne()
{
auto result = do_generate("SELECT * FROM one_row_table");
// Fetch 1, 1 remaining
auto rows_result = do_fetch_many(result, 1);
rows_result.validate_no_error();
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
}
// Fetch 1, 1 remaining
auto rows_result = do_fetch_many(result, 1);
rows_result.validate_no_error();
EXPECT_FALSE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
}
// FetchAll
void FetchAll_NoResults()
{
auto result = do_generate("SELECT * FROM empty_table");
// FetchAll
void FetchAll_NoResults()
{
auto result = do_generate("SELECT * FROM empty_table");
// Fetch many, but there are no results
auto rows_result = do_fetch_all(result);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
EXPECT_TRUE(result.complete());
// Fetch many, but there are no results
auto rows_result = do_fetch_all(result);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
EXPECT_TRUE(result.complete());
// Fetch again, should return OK and empty
rows_result = do_fetch_all(result);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
this->validate_eof(result);
}
// Fetch again, should return OK and empty
rows_result = do_fetch_all(result);
rows_result.validate_no_error();
EXPECT_TRUE(rows_result.value.empty());
this->validate_eof(result);
}
void FetchAll_OneRow()
{
auto result = do_generate("SELECT * FROM one_row_table");
void FetchAll_OneRow()
{
auto result = do_generate("SELECT * FROM one_row_table");
auto rows_result = do_fetch_all(result);
rows_result.validate_no_error();
EXPECT_TRUE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
}
auto rows_result = do_fetch_all(result);
rows_result.validate_no_error();
EXPECT_TRUE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
}
void FetchAll_SeveralRows()
{
auto result = do_generate("SELECT * FROM two_rows_table");
void FetchAll_SeveralRows()
{
auto result = do_generate("SELECT * FROM two_rows_table");
auto rows_result = do_fetch_all(result);
rows_result.validate_no_error();
this->validate_eof(result);
EXPECT_TRUE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
}
auto rows_result = do_fetch_all(result);
rows_result.validate_no_error();
this->validate_eof(result);
EXPECT_TRUE(result.complete());
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
}
};
BOOST_MYSQL_NETWORK_TEST_SUITE(ResultsetTest)

View File

@ -21,159 +21,159 @@ namespace
// mysql_native_password
struct MysqlNativePasswordTest : public Test
{
auth_calculator calc;
std::uint8_t challenge_buffer [20] {
0x79, 0x64, 0x3d, 0x12, 0x1d, 0x71, 0x74, 0x47,
0x5f, 0x48, 0x3e, 0x3e, 0x0b, 0x62, 0x0a, 0x03,
0x3d, 0x27, 0x3a, 0x4c
}; // Values snooped using Wireshark
std::uint8_t expected_buffer [20] {
0xf1, 0xb2, 0xfb, 0x1c, 0x8d, 0xe7, 0x5d, 0xb8,
0xeb, 0xa8, 0x12, 0x6a, 0xd1, 0x0f, 0xe9, 0xb1,
0x10, 0x50, 0xd4, 0x28
};
std::string_view challenge = makesv(challenge_buffer);
std::string_view expected = makesv(expected_buffer);
auth_calculator calc;
std::uint8_t challenge_buffer [20] {
0x79, 0x64, 0x3d, 0x12, 0x1d, 0x71, 0x74, 0x47,
0x5f, 0x48, 0x3e, 0x3e, 0x0b, 0x62, 0x0a, 0x03,
0x3d, 0x27, 0x3a, 0x4c
}; // Values snooped using Wireshark
std::uint8_t expected_buffer [20] {
0xf1, 0xb2, 0xfb, 0x1c, 0x8d, 0xe7, 0x5d, 0xb8,
0xeb, 0xa8, 0x12, 0x6a, 0xd1, 0x0f, 0xe9, 0xb1,
0x10, 0x50, 0xd4, 0x28
};
std::string_view challenge = makesv(challenge_buffer);
std::string_view expected = makesv(expected_buffer);
};
TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslFalse_ReturnsExpectedHash)
{
auto err = calc.calculate("mysql_native_password", "root", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
auto err = calc.calculate("mysql_native_password", "root", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
}
TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslTrue_ReturnsExpectedHash)
{
auto err = calc.calculate("mysql_native_password", "root", challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
auto err = calc.calculate("mysql_native_password", "root", challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
}
TEST_F(MysqlNativePasswordTest, EmptyPasswordSslFalse_ReturnsEmpty)
{
auto err = calc.calculate("mysql_native_password", "", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
auto err = calc.calculate("mysql_native_password", "", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
}
TEST_F(MysqlNativePasswordTest, EmptyPasswordSslTrue_ReturnsEmpty)
{
auto err = calc.calculate("mysql_native_password", "", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
auto err = calc.calculate("mysql_native_password", "", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
}
TEST_F(MysqlNativePasswordTest, BadChallengeLength_Fail)
{
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "", true)),
make_error_code(errc::protocol_value_error));
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "bad_challenge", true)),
make_error_code(errc::protocol_value_error));
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "", true)),
make_error_code(errc::protocol_value_error));
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "bad_challenge", true)),
make_error_code(errc::protocol_value_error));
}
// caching_sha2_password
struct CachingSha2PasswordTest : public Test
{
auth_calculator calc;
std::uint8_t challenge_buffer [20] {
0x3e, 0x3b, 0x4, 0x55, 0x4, 0x70, 0x16, 0x3a,
0x4c, 0x15, 0x35, 0x3, 0x15, 0x76, 0x73, 0x22,
0x46, 0x8, 0x18, 0x1
}; // Values snooped using the MySQL Python connector
std::uint8_t expected_buffer [32] {
0xa1, 0xc1, 0xe1, 0xe9, 0x1b, 0xb6, 0x54, 0x4b,
0xa7, 0x37, 0x4b, 0x9c, 0x56, 0x6d, 0x69, 0x3e,
0x6, 0xca, 0x7, 0x2, 0x98, 0xac, 0xd1, 0x6,
0x18, 0xc6, 0x90, 0x38, 0x9d, 0x88, 0xe1, 0x20
};
std::string_view challenge = makesv(challenge_buffer);
std::string_view expected = makesv(expected_buffer);
std::string_view cleartext_challenge { "\4" };
auth_calculator calc;
std::uint8_t challenge_buffer [20] {
0x3e, 0x3b, 0x4, 0x55, 0x4, 0x70, 0x16, 0x3a,
0x4c, 0x15, 0x35, 0x3, 0x15, 0x76, 0x73, 0x22,
0x46, 0x8, 0x18, 0x1
}; // Values snooped using the MySQL Python connector
std::uint8_t expected_buffer [32] {
0xa1, 0xc1, 0xe1, 0xe9, 0x1b, 0xb6, 0x54, 0x4b,
0xa7, 0x37, 0x4b, 0x9c, 0x56, 0x6d, 0x69, 0x3e,
0x6, 0xca, 0x7, 0x2, 0x98, 0xac, 0xd1, 0x6,
0x18, 0xc6, 0x90, 0x38, 0x9d, 0x88, 0xe1, 0x20
};
std::string_view challenge = makesv(challenge_buffer);
std::string_view expected = makesv(expected_buffer);
std::string_view cleartext_challenge { "\4" };
};
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslFalse_ReturnsExpectedHash)
{
auto err = calc.calculate("caching_sha2_password", "hola", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "hola", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslTrue_ReturnsExpectedHash)
{
auto err = calc.calculate("caching_sha2_password", "hola", challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "hola", challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), expected);
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslFalse_Fail)
{
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, false);
EXPECT_EQ(err, make_error_code(errc::auth_plugin_requires_ssl));
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, false);
EXPECT_EQ(err, make_error_code(errc::auth_plugin_requires_ssl));
}
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslTrue_ReturnsPassword)
{
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), std::string("hola") + '\0');
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), std::string("hola") + '\0');
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslFalse_ReturnsEmpty)
{
auto err = calc.calculate("caching_sha2_password", "", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "", challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslTrue_ReturnsEmpty)
{
auto err = calc.calculate("caching_sha2_password", "", challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "", challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslFalse_ReturnsEmpty)
{
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, false);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslTrue_ReturnsEmpty)
{
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, true);
EXPECT_EQ(err, error_code());
EXPECT_EQ(calc.response(), "");
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
}
TEST_F(CachingSha2PasswordTest, BadChallengeLength_Fail)
{
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "", true)),
make_error_code(errc::protocol_value_error));
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "bad_challenge", true)),
make_error_code(errc::protocol_value_error));
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "", true)),
make_error_code(errc::protocol_value_error));
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "bad_challenge", true)),
make_error_code(errc::protocol_value_error));
}
// Bad authentication plugin
TEST(AuthCalculator, UnknownAuthPlugin_Fail)
{
auth_calculator calc;
EXPECT_EQ((calc.calculate("bad_plugin", "password", "challenge", true)),
make_error_code(errc::unknown_auth_plugin));
EXPECT_EQ((calc.calculate("", "password", "challenge", true)),
make_error_code(errc::unknown_auth_plugin));
auth_calculator calc;
EXPECT_EQ((calc.calculate("bad_plugin", "password", "challenge", true)),
make_error_code(errc::unknown_auth_plugin));
EXPECT_EQ((calc.calculate("", "password", "challenge", true)),
make_error_code(errc::unknown_auth_plugin));
}

View File

@ -24,241 +24,241 @@ namespace
using boost::mysql::operator<<;
std::vector<boost::mysql::field_metadata> make_meta(
const std::vector<protocol_field_type>& types
const std::vector<protocol_field_type>& types
)
{
std::vector<boost::mysql::field_metadata> res;
for (const auto type: types)
{
column_definition_packet coldef;
coldef.type = type;
res.emplace_back(coldef);
}
return res;
std::vector<boost::mysql::field_metadata> res;
for (const auto type: types)
{
column_definition_packet coldef;
coldef.type = type;
res.emplace_back(coldef);
}
return res;
}
// for deserialize_binary_value
struct BinaryValueParam : named_param
{
std::string name;
std::vector<std::uint8_t> from;
value expected;
protocol_field_type type;
std::uint16_t flags;
std::string name;
std::vector<std::uint8_t> from;
value expected;
protocol_field_type type;
std::uint16_t flags;
template <typename T>
BinaryValueParam(
std::string name,
std::vector<std::uint8_t> from,
T&& expected_value,
protocol_field_type type,
std::uint16_t flags=0
):
name(std::move(name)),
from(std::move(from)),
expected(std::forward<T>(expected_value)),
type(type),
flags(flags)
{
}
template <typename T>
BinaryValueParam(
std::string name,
std::vector<std::uint8_t> from,
T&& expected_value,
protocol_field_type type,
std::uint16_t flags=0
):
name(std::move(name)),
from(std::move(from)),
expected(std::forward<T>(expected_value)),
type(type),
flags(flags)
{
}
};
struct DeserializeBinaryValueTest : public TestWithParam<BinaryValueParam> {};
TEST_P(DeserializeBinaryValueTest, CorrectFormat_SetsOutputValueReturnsTrue)
{
column_definition_packet coldef;
coldef.type = GetParam().type;
coldef.flags.value = GetParam().flags;
boost::mysql::field_metadata meta (coldef);
value actual_value;
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
auto err = deserialize_binary_value(ctx, meta, actual_value);
EXPECT_EQ(err, errc::ok);
EXPECT_EQ(actual_value, GetParam().expected);
column_definition_packet coldef;
coldef.type = GetParam().type;
coldef.flags.value = GetParam().flags;
boost::mysql::field_metadata meta (coldef);
value actual_value;
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
auto err = deserialize_binary_value(ctx, meta, actual_value);
EXPECT_EQ(err, errc::ok);
EXPECT_EQ(actual_value, GetParam().expected);
}
INSTANTIATE_TEST_SUITE_P(StringTypes, DeserializeBinaryValueTest, Values(
BinaryValueParam("varchar", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::var_string),
BinaryValueParam("char", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::string),
BinaryValueParam("varbinary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::var_string, column_flags::binary),
BinaryValueParam("binary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::binary),
BinaryValueParam("text_blob", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::blob, column_flags::blob),
BinaryValueParam("enum", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::enum_),
BinaryValueParam("set", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::set),
BinaryValueParam("varchar", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::var_string),
BinaryValueParam("char", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::string),
BinaryValueParam("varbinary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::var_string, column_flags::binary),
BinaryValueParam("binary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::binary),
BinaryValueParam("text_blob", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::blob, column_flags::blob),
BinaryValueParam("enum", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::enum_),
BinaryValueParam("set", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
protocol_field_type::string, column_flags::set),
BinaryValueParam("bit", {0x02, 0x02, 0x01}, "\2\1", protocol_field_type::bit),
BinaryValueParam("decimal", {0x02, 0x31, 0x30}, "10", protocol_field_type::newdecimal),
BinaryValueParam("geomtry", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::geometry)
BinaryValueParam("bit", {0x02, 0x02, 0x01}, "\2\1", protocol_field_type::bit),
BinaryValueParam("decimal", {0x02, 0x31, 0x30}, "10", protocol_field_type::newdecimal),
BinaryValueParam("geomtry", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::geometry)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(IntTypes, DeserializeBinaryValueTest, Values(
BinaryValueParam("tinyint_unsigned", {0x14}, std::uint32_t(20),
protocol_field_type::tiny, column_flags::unsigned_),
BinaryValueParam("tinyint_signed", {0xec}, std::int32_t(-20), protocol_field_type::tiny),
BinaryValueParam("tinyint_unsigned", {0x14}, std::uint32_t(20),
protocol_field_type::tiny, column_flags::unsigned_),
BinaryValueParam("tinyint_signed", {0xec}, std::int32_t(-20), protocol_field_type::tiny),
BinaryValueParam("smallint_unsigned", {0x14, 0x00}, std::uint32_t(20),
protocol_field_type::short_, column_flags::unsigned_),
BinaryValueParam("smallint_signed", {0xec, 0xff}, std::int32_t(-20), protocol_field_type::short_),
BinaryValueParam("smallint_unsigned", {0x14, 0x00}, std::uint32_t(20),
protocol_field_type::short_, column_flags::unsigned_),
BinaryValueParam("smallint_signed", {0xec, 0xff}, std::int32_t(-20), protocol_field_type::short_),
BinaryValueParam("mediumint_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
protocol_field_type::int24, column_flags::unsigned_),
BinaryValueParam("mediumint_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::int24),
BinaryValueParam("mediumint_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
protocol_field_type::int24, column_flags::unsigned_),
BinaryValueParam("mediumint_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::int24),
BinaryValueParam("int_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
protocol_field_type::long_, column_flags::unsigned_),
BinaryValueParam("int_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::long_),
BinaryValueParam("int_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
protocol_field_type::long_, column_flags::unsigned_),
BinaryValueParam("int_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::long_),
BinaryValueParam("bigint_unsigned", {0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, std::uint64_t(20),
protocol_field_type::longlong, column_flags::unsigned_),
BinaryValueParam("bigint_signed", {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, std::int64_t(-20),
protocol_field_type::longlong)
BinaryValueParam("bigint_unsigned", {0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, std::uint64_t(20),
protocol_field_type::longlong, column_flags::unsigned_),
BinaryValueParam("bigint_signed", {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, std::int64_t(-20),
protocol_field_type::longlong)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(FloatingPointTypes, DeserializeBinaryValueTest, Values(
BinaryValueParam("float", {0x66, 0x66, 0x86, 0xc0}, -4.2f, protocol_field_type::float_),
BinaryValueParam("double", {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0}, -4.2, protocol_field_type::double_)
BinaryValueParam("float", {0x66, 0x66, 0x86, 0xc0}, -4.2f, protocol_field_type::float_),
BinaryValueParam("double", {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0}, -4.2, protocol_field_type::double_)
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(TimeTypes, DeserializeBinaryValueTest, Values(
BinaryValueParam("date", {0x04, 0xda, 0x07, 0x03, 0x1c}, makedate(2010, 3, 28), protocol_field_type::date),
BinaryValueParam("datetime", {0x0b, 0xda, 0x07, 0x05, 0x02, 0x17, 0x01, 0x32, 0xa0, 0x86, 0x01, 0x00},
makedt(2010, 5, 2, 23, 1, 50, 100000), protocol_field_type::datetime),
BinaryValueParam("timestamp", {0x0b, 0xda, 0x07, 0x05, 0x02, 0x17, 0x01, 0x32, 0xa0, 0x86, 0x01, 0x00},
makedt(2010, 5, 2, 23, 1, 50, 100000), protocol_field_type::timestamp),
BinaryValueParam("time", { 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xa0, 0x86, 0x01, 0x00},
maket(120, 2, 3, 100000), protocol_field_type::time),
BinaryValueParam("year", {0xe3, 0x07}, std::uint32_t(2019), protocol_field_type::year, column_flags::unsigned_)
BinaryValueParam("date", {0x04, 0xda, 0x07, 0x03, 0x1c}, makedate(2010, 3, 28), protocol_field_type::date),
BinaryValueParam("datetime", {0x0b, 0xda, 0x07, 0x05, 0x02, 0x17, 0x01, 0x32, 0xa0, 0x86, 0x01, 0x00},
makedt(2010, 5, 2, 23, 1, 50, 100000), protocol_field_type::datetime),
BinaryValueParam("timestamp", {0x0b, 0xda, 0x07, 0x05, 0x02, 0x17, 0x01, 0x32, 0xa0, 0x86, 0x01, 0x00},
makedt(2010, 5, 2, 23, 1, 50, 100000), protocol_field_type::timestamp),
BinaryValueParam("time", { 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xa0, 0x86, 0x01, 0x00},
maket(120, 2, 3, 100000), protocol_field_type::time),
BinaryValueParam("year", {0xe3, 0x07}, std::uint32_t(2019), protocol_field_type::year, column_flags::unsigned_)
), test_name_generator);
// for deserialize_binary_row
struct BinaryRowParam : named_param
{
std::string name;
std::vector<std::uint8_t> from;
std::vector<value> expected;
std::vector<protocol_field_type> types;
std::string name;
std::vector<std::uint8_t> from;
std::vector<value> expected;
std::vector<protocol_field_type> types;
BinaryRowParam(
std::string name,
std::vector<std::uint8_t> from,
std::vector<value> expected,
std::vector<protocol_field_type> types
):
name(std::move(name)),
from(std::move(from)),
expected(std::move(expected)),
types(std::move(types))
{
assert(expected.size() == types.size());
}
BinaryRowParam(
std::string name,
std::vector<std::uint8_t> from,
std::vector<value> expected,
std::vector<protocol_field_type> types
):
name(std::move(name)),
from(std::move(from)),
expected(std::move(expected)),
types(std::move(types))
{
assert(expected.size() == types.size());
}
};
struct DeserializeBinaryRowTest : public TestWithParam<BinaryRowParam> {};
TEST_P(DeserializeBinaryRowTest, CorrectFormat_SetsOutputValueReturnsTrue)
{
auto meta = make_meta(GetParam().types);
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
auto meta = make_meta(GetParam().types);
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
std::vector<value> actual;
auto err = deserialize_binary_row(ctx, meta, actual);
EXPECT_EQ(err, error_code());
EXPECT_EQ(actual, GetParam().expected);
std::vector<value> actual;
auto err = deserialize_binary_row(ctx, meta, actual);
EXPECT_EQ(err, error_code());
EXPECT_EQ(actual, GetParam().expected);
}
INSTANTIATE_TEST_SUITE_P(Default, DeserializeBinaryRowTest, testing::Values(
BinaryRowParam("one_value", {0x00, 0x00, 0x14}, makevalues(std::int32_t(20)), {protocol_field_type::tiny}),
BinaryRowParam("one_null", {0x00, 0x04}, makevalues(nullptr), {protocol_field_type::tiny}),
BinaryRowParam("two_values", {0x00, 0x00, 0x03, 0x6d, 0x69, 0x6e, 0x6d, 0x07},
makevalues("min", std::int32_t(1901)), {protocol_field_type::var_string, protocol_field_type::short_}),
BinaryRowParam("one_value_one_null", {0x00, 0x08, 0x03, 0x6d, 0x61, 0x78},
makevalues("max", nullptr), {protocol_field_type::var_string, protocol_field_type::tiny}),
BinaryRowParam("two_nulls", {0x00, 0x0c},
makevalues(nullptr, nullptr), {protocol_field_type::tiny, protocol_field_type::tiny}),
BinaryRowParam("six_nulls", {0x00, 0xfc}, std::vector<value>(6, value(nullptr)),
std::vector<protocol_field_type>(6, protocol_field_type::tiny)),
BinaryRowParam("seven_nulls", {0x00, 0xfc, 0x01}, std::vector<value>(7, value(nullptr)),
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
BinaryRowParam("several_values", {
0x00, 0x90, 0x00, 0xfd, 0x14, 0x00, 0xc3, 0xf5, 0x48,
0x40, 0x02, 0x61, 0x62, 0x04, 0xe2, 0x07, 0x0a,
0x05, 0x71, 0x99, 0x6d, 0xe2, 0x93, 0x4d, 0xf5,
0x3d
}, makevalues(
std::int32_t(-3),
std::int32_t(20),
nullptr,
3.14f,
"ab",
nullptr,
makedate(2018, 10, 5),
3.10e-10
), {
protocol_field_type::tiny,
protocol_field_type::short_,
protocol_field_type::long_,
protocol_field_type::float_,
protocol_field_type::string,
protocol_field_type::long_,
protocol_field_type::date,
protocol_field_type::double_
}
)
BinaryRowParam("one_value", {0x00, 0x00, 0x14}, makevalues(std::int32_t(20)), {protocol_field_type::tiny}),
BinaryRowParam("one_null", {0x00, 0x04}, makevalues(nullptr), {protocol_field_type::tiny}),
BinaryRowParam("two_values", {0x00, 0x00, 0x03, 0x6d, 0x69, 0x6e, 0x6d, 0x07},
makevalues("min", std::int32_t(1901)), {protocol_field_type::var_string, protocol_field_type::short_}),
BinaryRowParam("one_value_one_null", {0x00, 0x08, 0x03, 0x6d, 0x61, 0x78},
makevalues("max", nullptr), {protocol_field_type::var_string, protocol_field_type::tiny}),
BinaryRowParam("two_nulls", {0x00, 0x0c},
makevalues(nullptr, nullptr), {protocol_field_type::tiny, protocol_field_type::tiny}),
BinaryRowParam("six_nulls", {0x00, 0xfc}, std::vector<value>(6, value(nullptr)),
std::vector<protocol_field_type>(6, protocol_field_type::tiny)),
BinaryRowParam("seven_nulls", {0x00, 0xfc, 0x01}, std::vector<value>(7, value(nullptr)),
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
BinaryRowParam("several_values", {
0x00, 0x90, 0x00, 0xfd, 0x14, 0x00, 0xc3, 0xf5, 0x48,
0x40, 0x02, 0x61, 0x62, 0x04, 0xe2, 0x07, 0x0a,
0x05, 0x71, 0x99, 0x6d, 0xe2, 0x93, 0x4d, 0xf5,
0x3d
}, makevalues(
std::int32_t(-3),
std::int32_t(20),
nullptr,
3.14f,
"ab",
nullptr,
makedate(2018, 10, 5),
3.10e-10
), {
protocol_field_type::tiny,
protocol_field_type::short_,
protocol_field_type::long_,
protocol_field_type::float_,
protocol_field_type::string,
protocol_field_type::long_,
protocol_field_type::date,
protocol_field_type::double_
}
)
), test_name_generator);
// errc cases for deserialize_binary_row
struct BinaryRowErrorParam : named_param
{
std::string name;
std::vector<std::uint8_t> from;
errc expected;
std::vector<protocol_field_type> types;
std::string name;
std::vector<std::uint8_t> from;
errc expected;
std::vector<protocol_field_type> types;
BinaryRowErrorParam(
std::string name,
std::vector<std::uint8_t> from,
errc expected,
std::vector<protocol_field_type> types
):
name(std::move(name)),
from(std::move(from)),
expected(expected),
types(std::move(types))
{
}
BinaryRowErrorParam(
std::string name,
std::vector<std::uint8_t> from,
errc expected,
std::vector<protocol_field_type> types
):
name(std::move(name)),
from(std::move(from)),
expected(expected),
types(std::move(types))
{
}
};
struct DeserializeBinaryRowErrorTest : public TestWithParam<BinaryRowErrorParam> {};
TEST_P(DeserializeBinaryRowErrorTest, ErrorCondition_ReturnsErrorCode)
{
auto meta = make_meta(GetParam().types);
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
auto meta = make_meta(GetParam().types);
const auto& buffer = GetParam().from;
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
std::vector<value> actual;
auto err = deserialize_binary_row(ctx, meta, actual);
EXPECT_EQ(err, make_error_code(GetParam().expected));
std::vector<value> actual;
auto err = deserialize_binary_row(ctx, meta, actual);
EXPECT_EQ(err, make_error_code(GetParam().expected));
}
INSTANTIATE_TEST_SUITE_P(Default, DeserializeBinaryRowErrorTest, testing::Values(
BinaryRowErrorParam("no_space_null_bitmap_1", {0x00}, errc::incomplete_message, {protocol_field_type::tiny}),
BinaryRowErrorParam("no_space_null_bitmap_2", {0x00, 0xfc}, errc::incomplete_message,
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
BinaryRowErrorParam("no_space_value_single", {0x00, 0x00}, errc::incomplete_message, {protocol_field_type::tiny}),
BinaryRowErrorParam("no_space_value_last", {0x00, 0x00, 0x01}, errc::incomplete_message,
std::vector<protocol_field_type>(2, protocol_field_type::tiny)),
BinaryRowErrorParam("no_space_value_middle", {0x00, 0x00, 0x01}, errc::incomplete_message,
std::vector<protocol_field_type>(3, protocol_field_type::tiny)),
BinaryRowErrorParam("extra_bytes", {0x00, 0x00, 0x01, 0x02}, errc::extra_bytes, {protocol_field_type::tiny})
BinaryRowErrorParam("no_space_null_bitmap_1", {0x00}, errc::incomplete_message, {protocol_field_type::tiny}),
BinaryRowErrorParam("no_space_null_bitmap_2", {0x00, 0xfc}, errc::incomplete_message,
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
BinaryRowErrorParam("no_space_value_single", {0x00, 0x00}, errc::incomplete_message, {protocol_field_type::tiny}),
BinaryRowErrorParam("no_space_value_last", {0x00, 0x00, 0x01}, errc::incomplete_message,
std::vector<protocol_field_type>(2, protocol_field_type::tiny)),
BinaryRowErrorParam("no_space_value_middle", {0x00, 0x00, 0x01}, errc::incomplete_message,
std::vector<protocol_field_type>(3, protocol_field_type::tiny)),
BinaryRowErrorParam("extra_bytes", {0x00, 0x00, 0x01, 0x02}, errc::extra_bytes, {protocol_field_type::tiny})
), test_name_generator);

View File

@ -13,64 +13,64 @@ using namespace testing;
TEST(Capabilities, Has_BitSet_ReturnsTrue)
{
capabilities caps (CLIENT_COMPRESS);
EXPECT_TRUE(caps.has(CLIENT_COMPRESS));
capabilities caps (CLIENT_COMPRESS);
EXPECT_TRUE(caps.has(CLIENT_COMPRESS));
}
TEST(Capabilities, Has_BitNotSet_ReturnsFalse)
{
capabilities caps (CLIENT_COMPRESS);
EXPECT_FALSE(caps.has(CLIENT_SSL));
capabilities caps (CLIENT_COMPRESS);
EXPECT_FALSE(caps.has(CLIENT_SSL));
}
TEST(Capabilities, Has_MultipleBitsSet_ReturnsTrueForSetBits)
{
capabilities caps (
CLIENT_CONNECT_WITH_DB |
CLIENT_SSL |
CLIENT_COMPRESS);
for (int i = 0; i < 32; ++i)
{
std::uint32_t cap_bit = 1 << i;
bool is_set =
cap_bit == CLIENT_CONNECT_WITH_DB ||
cap_bit == CLIENT_SSL ||
cap_bit == CLIENT_COMPRESS;
EXPECT_EQ(caps.has(cap_bit), is_set);
}
capabilities caps (
CLIENT_CONNECT_WITH_DB |
CLIENT_SSL |
CLIENT_COMPRESS);
for (int i = 0; i < 32; ++i)
{
std::uint32_t cap_bit = 1 << i;
bool is_set =
cap_bit == CLIENT_CONNECT_WITH_DB ||
cap_bit == CLIENT_SSL ||
cap_bit == CLIENT_COMPRESS;
EXPECT_EQ(caps.has(cap_bit), is_set);
}
}
struct CapabilitiesHasAllTest : public Test
{
capabilities rhs {CLIENT_CONNECT_WITH_DB | CLIENT_SSL | CLIENT_COMPRESS};
capabilities rhs {CLIENT_CONNECT_WITH_DB | CLIENT_SSL | CLIENT_COMPRESS};
};
TEST_F(CapabilitiesHasAllTest, HasAll_HasNone_ReturnsFalse)
{
capabilities lhs (0);
EXPECT_FALSE(lhs.has_all(rhs));
capabilities lhs (0);
EXPECT_FALSE(lhs.has_all(rhs));
}
TEST_F(CapabilitiesHasAllTest, HasAll_HasSomeButNotAll_ReturnsFalse)
{
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS);
EXPECT_FALSE(lhs.has_all(rhs));
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS);
EXPECT_FALSE(lhs.has_all(rhs));
}
TEST_F(CapabilitiesHasAllTest, HasAll_HasSomeButNotAllPlusUnrelated_ReturnsFalse)
{
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS | CLIENT_TRANSACTIONS);
EXPECT_FALSE(lhs.has_all(rhs));
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS | CLIENT_TRANSACTIONS);
EXPECT_FALSE(lhs.has_all(rhs));
}
TEST_F(CapabilitiesHasAllTest, HasAll_HasOnlyTheRequestedOnes_ReturnsTrue)
{
capabilities lhs (rhs);
EXPECT_TRUE(lhs.has_all(rhs));
capabilities lhs (rhs);
EXPECT_TRUE(lhs.has_all(rhs));
}
TEST_F(CapabilitiesHasAllTest, HasAll_HasTheRequestedOnesAndOthers_ReturnsTrue)
{
capabilities lhs = rhs | capabilities(CLIENT_TRANSACTIONS);
EXPECT_TRUE(lhs.has_all(rhs));
capabilities lhs = rhs | capabilities(CLIENT_TRANSACTIONS);
EXPECT_TRUE(lhs.has_all(rhs));
}

View File

@ -26,369 +26,369 @@ namespace
class MockStream
{
public:
MOCK_METHOD2(read_buffer, std::size_t(boost::asio::mutable_buffer, error_code&));
MOCK_METHOD2(write_buffer, std::size_t(boost::asio::const_buffer, error_code&));
MOCK_METHOD2(read_buffer, std::size_t(boost::asio::mutable_buffer, error_code&));
MOCK_METHOD2(write_buffer, std::size_t(boost::asio::const_buffer, error_code&));
void set_default_behavior()
{
ON_CALL(*this, read_buffer).WillByDefault(DoAll(
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
Return(0)
));
ON_CALL(*this, write_buffer).WillByDefault(DoAll(
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
Return(0)
));
}
void set_default_behavior()
{
ON_CALL(*this, read_buffer).WillByDefault(DoAll(
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
Return(0)
));
ON_CALL(*this, write_buffer).WillByDefault(DoAll(
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
Return(0)
));
}
template <typename MutableBufferSequence>
std::size_t read_some(MutableBufferSequence mb, error_code& ec)
{
if (buffer_size(mb) == 0)
{
ec.clear();
return 0;
}
size_t res = 0;
for (auto it = buffer_sequence_begin(mb); it != buffer_sequence_end(mb); ++it)
res += read_buffer(*it, ec);
return res;
}
template <typename MutableBufferSequence>
std::size_t read_some(MutableBufferSequence mb, error_code& ec)
{
if (buffer_size(mb) == 0)
{
ec.clear();
return 0;
}
size_t res = 0;
for (auto it = buffer_sequence_begin(mb); it != buffer_sequence_end(mb); ++it)
res += read_buffer(*it, ec);
return res;
}
template <typename MutableBufferSequence>
std::size_t read_some(MutableBufferSequence mb)
{
error_code ec;
auto res = read_some(mb, ec);
if (res)
{
throw boost::system::system_error(ec);
}
return res;
}
template <typename MutableBufferSequence>
std::size_t read_some(MutableBufferSequence mb)
{
error_code ec;
auto res = read_some(mb, ec);
if (res)
{
throw boost::system::system_error(ec);
}
return res;
}
template <typename ConstBufferSequence>
std::size_t write_some(ConstBufferSequence cb, error_code& ec)
{
if (buffer_size(cb) == 0)
{
ec.clear();
return 0;
}
size_t res = 0;
for (auto it = buffer_sequence_begin(cb); it != buffer_sequence_end(cb); ++it)
{
auto written = write_buffer(*it, ec);
res += written;
if (written < it->size()) break;
}
return res;
}
template <typename ConstBufferSequence>
std::size_t write_some(ConstBufferSequence cb, error_code& ec)
{
if (buffer_size(cb) == 0)
{
ec.clear();
return 0;
}
size_t res = 0;
for (auto it = buffer_sequence_begin(cb); it != buffer_sequence_end(cb); ++it)
{
auto written = write_buffer(*it, ec);
res += written;
if (written < it->size()) break;
}
return res;
}
template <typename ConstBufferSequence>
std::size_t write_some(ConstBufferSequence mb)
{
error_code ec;
auto res = write_some(mb, ec);
if (res)
{
throw boost::system::system_error(ec);
}
return res;
}
template <typename ConstBufferSequence>
std::size_t write_some(ConstBufferSequence mb)
{
error_code ec;
auto res = write_some(mb, ec);
if (res)
{
throw boost::system::system_error(ec);
}
return res;
}
using lowest_layer_type = MockStream;
using executor_type = boost::asio::system_executor;
using lowest_layer_type = MockStream;
using executor_type = boost::asio::system_executor;
MockStream& lowest_layer() { return *this; }
executor_type get_executor() { return boost::asio::system_executor(); }
MockStream& lowest_layer() { return *this; }
executor_type get_executor() { return boost::asio::system_executor(); }
};
struct MysqlChannelFixture : public Test
{
using MockChannel = channel<NiceMock<MockStream>>;
NiceMock<MockStream> stream;
MockChannel chan {stream};
error_code code;
InSequence seq;
using MockChannel = channel<NiceMock<MockStream>>;
NiceMock<MockStream> stream;
MockChannel chan {stream};
error_code code;
InSequence seq;
MysqlChannelFixture()
{
stream.set_default_behavior();
}
MysqlChannelFixture()
{
stream.set_default_behavior();
}
};
struct MysqlChannelReadTest : public MysqlChannelFixture
{
std::vector<uint8_t> buffer { 0xab, 0xac, 0xad, 0xae }; // simulate buffer was not empty, to verify we clear it
std::vector<uint8_t> bytes_to_read;
std::size_t index {0};
std::vector<uint8_t> buffer { 0xab, 0xac, 0xad, 0xae }; // simulate buffer was not empty, to verify we clear it
std::vector<uint8_t> bytes_to_read;
std::size_t index {0};
void verify_buffer(const std::vector<uint8_t>& expected)
{
EXPECT_EQ(buffer, expected);
}
void verify_buffer(const std::vector<uint8_t>& expected)
{
EXPECT_EQ(buffer, expected);
}
static auto buffer_copier(const std::vector<uint8_t>& buffer)
{
return [buffer](boost::asio::mutable_buffer b, error_code& ec) {
assert(b.size() >= buffer.size());
memcpy(b.data(), buffer.data(), buffer.size());
ec.clear();
return buffer.size();
};
}
static auto buffer_copier(const std::vector<uint8_t>& buffer)
{
return [buffer](boost::asio::mutable_buffer b, error_code& ec) {
assert(b.size() >= buffer.size());
memcpy(b.data(), buffer.data(), buffer.size());
ec.clear();
return buffer.size();
};
}
auto make_read_handler()
{
return [this](boost::asio::mutable_buffer b, error_code& ec) {
std::size_t to_copy = std::min(b.size(), bytes_to_read.size() - index);
memcpy(b.data(), bytes_to_read.data() + index, to_copy);
index += to_copy;
ec.clear();
return to_copy;
};
}
auto make_read_handler()
{
return [this](boost::asio::mutable_buffer b, error_code& ec) {
std::size_t to_copy = std::min(b.size(), bytes_to_read.size() - index);
memcpy(b.data(), bytes_to_read.data() + index, to_copy);
index += to_copy;
ec.clear();
return to_copy;
};
}
static auto read_failer(error_code error)
{
return [error](boost::asio::mutable_buffer, error_code& ec) {
ec = error;
return size_t(0);
};
}
static auto read_failer(error_code error)
{
return [error](boost::asio::mutable_buffer, error_code& ec) {
ec = error;
return size_t(0);
};
}
};
TEST_F(MysqlChannelReadTest, SyncRead_AllReadsSuccessful_ReadHeaderPopulatesBuffer)
{
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {
0x03, 0x00, 0x00, 0x00,
0xfe, 0x03, 0x02
};
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0xfe, 0x03, 0x02});
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {
0x03, 0x00, 0x00, 0x00,
0xfe, 0x03, 0x02
};
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0xfe, 0x03, 0x02});
}
TEST_F(MysqlChannelReadTest, SyncRead_MoreThan16M_JoinsPackets)
{
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x00});
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x01});
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
concat(bytes_to_read, {0x04, 0x00, 0x00, 0x02});
concat(bytes_to_read, {0x20, 0x20, 0x20, 0x20});
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer(std::vector<uint8_t>(0xffffff * 2 + 4, 0x20));
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x00});
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x01});
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
concat(bytes_to_read, {0x04, 0x00, 0x00, 0x02});
concat(bytes_to_read, {0x20, 0x20, 0x20, 0x20});
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer(std::vector<uint8_t>(0xffffff * 2 + 4, 0x20));
}
TEST_F(MysqlChannelReadTest, SyncRead_EmptyPacket_LeavesBufferEmpty)
{
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
concat(bytes_to_read, {0x00, 0x00, 0x00, 0x00});
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer(std::vector<uint8_t>{});
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
concat(bytes_to_read, {0x00, 0x00, 0x00, 0x00});
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer(std::vector<uint8_t>{});
}
TEST_F(MysqlChannelReadTest, SyncRead_ShortReads_InvokesReadAgain)
{
EXPECT_CALL(stream, read_buffer)
.WillOnce(Invoke(buffer_copier({0x04})))
.WillOnce(Invoke(buffer_copier({ 0x00, 0x00, 0x00})))
.WillOnce(Invoke(buffer_copier({0x01, 0x02})))
.WillOnce(Invoke(buffer_copier({0x03, 0x04})));
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0x01, 0x02, 0x03, 0x04});
EXPECT_CALL(stream, read_buffer)
.WillOnce(Invoke(buffer_copier({0x04})))
.WillOnce(Invoke(buffer_copier({ 0x00, 0x00, 0x00})))
.WillOnce(Invoke(buffer_copier({0x01, 0x02})))
.WillOnce(Invoke(buffer_copier({0x03, 0x04})));
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0x01, 0x02, 0x03, 0x04});
}
TEST_F(MysqlChannelReadTest, SyncRead_ReadErrorInHeader_ReturnsFailureErrorCode)
{
auto expected_error = make_error_code(boost::system::errc::not_supported);
EXPECT_CALL(stream, read_buffer)
.WillOnce(Invoke(read_failer(expected_error)));
chan.read(buffer, code);
EXPECT_EQ(code, expected_error);
auto expected_error = make_error_code(boost::system::errc::not_supported);
EXPECT_CALL(stream, read_buffer)
.WillOnce(Invoke(read_failer(expected_error)));
chan.read(buffer, code);
EXPECT_EQ(code, expected_error);
}
TEST_F(MysqlChannelReadTest, SyncRead_ReadErrorInPacket_ReturnsFailureErrorCode)
{
auto expected_error = make_error_code(boost::system::errc::not_supported);
EXPECT_CALL(stream, read_buffer)
.WillOnce(Invoke(buffer_copier({0xff, 0xff, 0xff, 0x00})))
.WillOnce(Invoke(read_failer(expected_error)));
chan.read(buffer, code);
EXPECT_EQ(code, expected_error);
auto expected_error = make_error_code(boost::system::errc::not_supported);
EXPECT_CALL(stream, read_buffer)
.WillOnce(Invoke(buffer_copier({0xff, 0xff, 0xff, 0x00})))
.WillOnce(Invoke(read_failer(expected_error)));
chan.read(buffer, code);
EXPECT_EQ(code, expected_error);
}
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberMismatch_ReturnsAppropriateErrorCode)
{
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {0xff, 0xff, 0xff, 0x05};
chan.read(buffer, code);
EXPECT_EQ(code, make_error_code(errc::sequence_number_mismatch));
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {0xff, 0xff, 0xff, 0x05};
chan.read(buffer, code);
EXPECT_EQ(code, make_error_code(errc::sequence_number_mismatch));
}
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberNotZero_RespectsCurrentSequenceNumber)
{
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {
0x03, 0x00, 0x00, 0x21,
0xfe, 0x03, 0x02
};
chan.reset_sequence_number(0x21);
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0xfe, 0x03, 0x02});
EXPECT_EQ(chan.sequence_number(), 0x22);
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {
0x03, 0x00, 0x00, 0x21,
0xfe, 0x03, 0x02
};
chan.reset_sequence_number(0x21);
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0xfe, 0x03, 0x02});
EXPECT_EQ(chan.sequence_number(), 0x22);
}
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberFF_SequenceNumberWraps)
{
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {
0x03, 0x00, 0x00, 0xff,
0xfe, 0x03, 0x02
};
chan.reset_sequence_number(0xff);
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0xfe, 0x03, 0x02});
EXPECT_EQ(chan.sequence_number(), 0);
ON_CALL(stream, read_buffer)
.WillByDefault(Invoke(make_read_handler()));
bytes_to_read = {
0x03, 0x00, 0x00, 0xff,
0xfe, 0x03, 0x02
};
chan.reset_sequence_number(0xff);
chan.read(buffer, code);
EXPECT_EQ(code, error_code());
verify_buffer({0xfe, 0x03, 0x02});
EXPECT_EQ(chan.sequence_number(), 0);
}
struct MysqlChannelWriteTest : public MysqlChannelFixture
{
std::vector<uint8_t> bytes_written;
std::vector<uint8_t> bytes_written;
void verify_buffer(const std::vector<uint8_t>& expected)
{
EXPECT_EQ(bytes_written, expected);
}
void verify_buffer(const std::vector<uint8_t>& expected)
{
EXPECT_EQ(bytes_written, expected);
}
auto make_write_handler(std::size_t max_bytes_written = 0xffffffff)
{
return [this, max_bytes_written](boost::asio::const_buffer buff, error_code& ec) {
auto actual_size = std::min(buff.size(), max_bytes_written);
concat(bytes_written, boost::asio::buffer(buff.data(), actual_size));
ec.clear();
return actual_size;
};
}
auto make_write_handler(std::size_t max_bytes_written = 0xffffffff)
{
return [this, max_bytes_written](boost::asio::const_buffer buff, error_code& ec) {
auto actual_size = std::min(buff.size(), max_bytes_written);
concat(bytes_written, boost::asio::buffer(buff.data(), actual_size));
ec.clear();
return actual_size;
};
}
static auto write_failer(boost::system::errc::errc_t error)
{
return [error](boost::asio::const_buffer, error_code& ec) {
ec = make_error_code(error);
return 0;
};
}
static auto write_failer(boost::system::errc::errc_t error)
{
return [error](boost::asio::const_buffer, error_code& ec) {
ec = make_error_code(error);
return 0;
};
}
};
TEST_F(MysqlChannelWriteTest, SyncWrite_AllWritesSuccessful_WritesHeaderAndBuffer)
{
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0x00, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0x00, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
}
TEST_F(MysqlChannelWriteTest, SyncWrite_MoreThan16M_SplitsInPackets)
{
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>(2*0xffffff + 4, 0xab)), code);
std::vector<uint8_t> expected_buffer {0xff, 0xff, 0xff, 0x00};
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
concat(expected_buffer, {0xff, 0xff, 0xff, 0x01});
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
concat(expected_buffer, {0x04, 0x00, 0x00, 0x02});
concat(expected_buffer, std::vector<std::uint8_t>(4, 0xab));
verify_buffer(expected_buffer);
EXPECT_EQ(code, error_code());
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>(2*0xffffff + 4, 0xab)), code);
std::vector<uint8_t> expected_buffer {0xff, 0xff, 0xff, 0x00};
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
concat(expected_buffer, {0xff, 0xff, 0xff, 0x01});
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
concat(expected_buffer, {0x04, 0x00, 0x00, 0x02});
concat(expected_buffer, std::vector<std::uint8_t>(4, 0xab));
verify_buffer(expected_buffer);
EXPECT_EQ(code, error_code());
}
TEST_F(MysqlChannelWriteTest, SyncWrite_EmptyPacket_WritesHeader)
{
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.reset_sequence_number(2);
chan.write(buffer(std::vector<uint8_t>{}), code);
verify_buffer({0x00, 0x00, 0x00, 0x02});
EXPECT_EQ(code, error_code());
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.reset_sequence_number(2);
chan.write(buffer(std::vector<uint8_t>{}), code);
verify_buffer({0x00, 0x00, 0x00, 0x02});
EXPECT_EQ(code, error_code());
}
TEST_F(MysqlChannelWriteTest, SyncWrite_ShortWrites_WritesHeaderAndBuffer)
{
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler(2)));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0x00, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler(2)));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0x00, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
}
TEST_F(MysqlChannelWriteTest, SyncWrite_WriteErrorInHeader_ReturnsErrorCode)
{
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(write_failer(boost::system::errc::broken_pipe)));
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(write_failer(boost::system::errc::broken_pipe)));
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
}
TEST_F(MysqlChannelWriteTest, SyncWrite_WriteErrorInPacket_ReturnsErrorCode)
{
EXPECT_CALL(stream, write_buffer)
.WillOnce(Return(4))
.WillOnce(Invoke(write_failer(boost::system::errc::broken_pipe)));
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
EXPECT_CALL(stream, write_buffer)
.WillOnce(Return(4))
.WillOnce(Invoke(write_failer(boost::system::errc::broken_pipe)));
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
}
TEST_F(MysqlChannelWriteTest, SyncWrite_SequenceNumberNotZero_RespectsSequenceNumber)
{
chan.reset_sequence_number(0xab);
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0xab, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
EXPECT_EQ(chan.sequence_number(), 0xac);
chan.reset_sequence_number(0xab);
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0xab, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
EXPECT_EQ(chan.sequence_number(), 0xac);
}
TEST_F(MysqlChannelWriteTest, SyncWrite_SequenceIsFF_WrapsSequenceNumber)
{
chan.reset_sequence_number(0xff);
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0xff, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
EXPECT_EQ(chan.sequence_number(), 0);
chan.reset_sequence_number(0xff);
ON_CALL(stream, write_buffer)
.WillByDefault(Invoke(make_write_handler()));
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
verify_buffer({
0x03, 0x00, 0x00, 0xff, // header
0xaa, 0xab, 0xac // body
});
EXPECT_EQ(code, error_code());
EXPECT_EQ(chan.sequence_number(), 0);
}
} // anon namespace

View File

@ -16,166 +16,166 @@ namespace
{
INSTANTIATE_TEST_SUITE_P(PacketHeader, FullSerializationTest, ::testing::Values(
serialization_testcase(packet_header{int3(3), int1(0)}, {0x03, 0x00, 0x00, 0x00}, "small_packet_seqnum_0"),
serialization_testcase(packet_header{int3(9), int1(2)}, {0x09, 0x00, 0x00, 0x02}, "small_packet_seqnum_not_0"),
serialization_testcase(packet_header{int3(0xcacbcc), int1(0xfa)}, {0xcc, 0xcb, 0xca, 0xfa}, "big_packet_seqnum_0"),
serialization_testcase(packet_header{int3(0xffffff), int1(0xff)}, {0xff, 0xff, 0xff, 0xff}, "max_packet_max_seqnum")
serialization_testcase(packet_header{int3(3), int1(0)}, {0x03, 0x00, 0x00, 0x00}, "small_packet_seqnum_0"),
serialization_testcase(packet_header{int3(9), int1(2)}, {0x09, 0x00, 0x00, 0x02}, "small_packet_seqnum_not_0"),
serialization_testcase(packet_header{int3(0xcacbcc), int1(0xfa)}, {0xcc, 0xcb, 0xca, 0xfa}, "big_packet_seqnum_0"),
serialization_testcase(packet_header{int3(0xffffff), int1(0xff)}, {0xff, 0xff, 0xff, 0xff}, "max_packet_max_seqnum")
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(OkPacket, DeserializeTest, ::testing::Values(
serialization_testcase(ok_packet{
int_lenenc(4), // affected rows
int_lenenc(0), // last insert ID
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT | SERVER_QUERY_NO_INDEX_USED)), // server status
int2(0), // warnings
string_lenenc("Rows matched: 5 Changed: 4 Warnings: 0")
}, {
0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x52, 0x6f, 0x77, 0x73,
0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x3a, 0x20, 0x35, 0x20, 0x20, 0x43, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x64, 0x3a, 0x20, 0x34, 0x20, 0x20, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67,
0x73, 0x3a, 0x20, 0x30
}, "successful_update"),
serialization_testcase(ok_packet{
int_lenenc(4), // affected rows
int_lenenc(0), // last insert ID
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT | SERVER_QUERY_NO_INDEX_USED)), // server status
int2(0), // warnings
string_lenenc("Rows matched: 5 Changed: 4 Warnings: 0")
}, {
0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x52, 0x6f, 0x77, 0x73,
0x20, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x65, 0x64, 0x3a, 0x20, 0x35, 0x20, 0x20, 0x43, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x64, 0x3a, 0x20, 0x34, 0x20, 0x20, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67,
0x73, 0x3a, 0x20, 0x30
}, "successful_update"),
serialization_testcase(ok_packet{
int_lenenc(1), // affected rows
int_lenenc(6), // last insert ID
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
int2(0), // warnings
string_lenenc("") // no message
},{
0x01, 0x06, 0x02, 0x00, 0x00, 0x00
}, "successful_insert"),
serialization_testcase(ok_packet{
int_lenenc(1), // affected rows
int_lenenc(6), // last insert ID
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
int2(0), // warnings
string_lenenc("") // no message
},{
0x01, 0x06, 0x02, 0x00, 0x00, 0x00
}, "successful_insert"),
serialization_testcase(ok_packet{
int_lenenc(0), // affected rows
int_lenenc(0), // last insert ID
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
int2(0), // warnings
string_lenenc("") // no message
}, {
0x00, 0x00, 0x02, 0x00, 0x00, 0x00
}, "successful_login")
serialization_testcase(ok_packet{
int_lenenc(0), // affected rows
int_lenenc(0), // last insert ID
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
int2(0), // warnings
string_lenenc("") // no message
}, {
0x00, 0x00, 0x02, 0x00, 0x00, 0x00
}, "successful_login")
), test_name_generator);
INSTANTIATE_TEST_SUITE_P(ErrPacket, DeserializeTest, ::testing::Values(
serialization_testcase(err_packet{
int2(1049), // eror code
string_fixed<1>{0x23}, // sql state marker
string_fixed<5>{'4', '2', '0', '0', '0'}, // sql state
string_eof("Unknown database 'a'") // err msg
}, {
0x19, 0x04, 0x23, 0x34, 0x32, 0x30, 0x30, 0x30, 0x55, 0x6e, 0x6b,
0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x64, 0x61, 0x74,
0x61, 0x62, 0x61, 0x73, 0x65, 0x20, 0x27, 0x61, 0x27
}, "wrong_use_database"),
serialization_testcase(err_packet{
int2(1049), // eror code
string_fixed<1>{0x23}, // sql state marker
string_fixed<5>{'4', '2', '0', '0', '0'}, // sql state
string_eof("Unknown database 'a'") // err msg
}, {
0x19, 0x04, 0x23, 0x34, 0x32, 0x30, 0x30, 0x30, 0x55, 0x6e, 0x6b,
0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x64, 0x61, 0x74,
0x61, 0x62, 0x61, 0x73, 0x65, 0x20, 0x27, 0x61, 0x27
}, "wrong_use_database"),
serialization_testcase(err_packet{
int2(1146), // eror code
string_fixed<1>{0x23}, // sql state marker
string_fixed<5>{'4', '2', 'S', '0', '2'}, // sql state
string_eof("Table 'awesome.unknown' doesn't exist") // err msg
}, {
0x7a, 0x04, 0x23, 0x34, 0x32, 0x53, 0x30, 0x32,
0x54, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x27, 0x61,
0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x2e, 0x75,
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x27, 0x20,
0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20,
0x65, 0x78, 0x69, 0x73, 0x74
}, "unknown_table"),
serialization_testcase(err_packet{
int2(1146), // eror code
string_fixed<1>{0x23}, // sql state marker
string_fixed<5>{'4', '2', 'S', '0', '2'}, // sql state
string_eof("Table 'awesome.unknown' doesn't exist") // err msg
}, {
0x7a, 0x04, 0x23, 0x34, 0x32, 0x53, 0x30, 0x32,
0x54, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x27, 0x61,
0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x2e, 0x75,
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x27, 0x20,
0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20,
0x65, 0x78, 0x69, 0x73, 0x74
}, "unknown_table"),
serialization_testcase(err_packet{
int2(1045), // error code
string_fixed<1>{0x23}, // SQL state marker
string_fixed<5>{'2', '8', '0', '0', '0'}, // SQL state
string_eof("Access denied for user 'root'@'localhost' (using password: YES)")
}, {
0x15, 0x04, 0x23, 0x32, 0x38, 0x30, 0x30, 0x30,
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x64,
0x65, 0x6e, 0x69, 0x65, 0x64, 0x20, 0x66, 0x6f,
0x72, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x27,
0x72, 0x6f, 0x6f, 0x74, 0x27, 0x40, 0x27, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
0x27, 0x20, 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67,
0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x3a, 0x20, 0x59, 0x45, 0x53, 0x29
}, "failed_login")
serialization_testcase(err_packet{
int2(1045), // error code
string_fixed<1>{0x23}, // SQL state marker
string_fixed<5>{'2', '8', '0', '0', '0'}, // SQL state
string_eof("Access denied for user 'root'@'localhost' (using password: YES)")
}, {
0x15, 0x04, 0x23, 0x32, 0x38, 0x30, 0x30, 0x30,
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x64,
0x65, 0x6e, 0x69, 0x65, 0x64, 0x20, 0x66, 0x6f,
0x72, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x27,
0x72, 0x6f, 0x6f, 0x74, 0x27, 0x40, 0x27, 0x6c,
0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
0x27, 0x20, 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67,
0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
0x64, 0x3a, 0x20, 0x59, 0x45, 0x53, 0x29
}, "failed_login")
), test_name_generator);
// Column definition
INSTANTIATE_TEST_SUITE_P(ColumnDefinition, DeserializeSpaceTest, testing::Values(
serialization_testcase(column_definition_packet{
string_lenenc("def"), //catalog
string_lenenc("awesome"), // schema (database)
string_lenenc("test_table"), // table
string_lenenc("test_table"), // physical table
string_lenenc("id"), // field name
string_lenenc("id"), // physical field name
collation::binary,
int4(11), // length
protocol_field_type::long_,
int2(
column_flags::not_null |
column_flags::pri_key |
column_flags::auto_increment |
column_flags::part_key
),
int1(0) // decimals
}, {
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
0x6c, 0x65, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64,
0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03,
0x03, 0x42, 0x00, 0x00, 0x00
}, "numeric_auto_increment_primary_key"),
serialization_testcase(column_definition_packet{
string_lenenc("def"), //catalog
string_lenenc("awesome"), // schema (database)
string_lenenc("child"), // table
string_lenenc("child_table"), // physical table
string_lenenc("field_alias"), // field name
string_lenenc("field_varchar"), // physical field name
collation::utf8_general_ci,
int4(765), // length
protocol_field_type::var_string,
int2(), // no column flags
int1(0) // decimals
}, {
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
0x73, 0x6f, 0x6d, 0x65, 0x05, 0x63, 0x68, 0x69,
0x6c, 0x64, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64,
0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0b, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x61, 0x6c, 0x69,
0x61, 0x73, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x5f, 0x76, 0x61, 0x72, 0x63, 0x68, 0x61, 0x72,
0x0c, 0x21, 0x00, 0xfd, 0x02, 0x00, 0x00, 0xfd,
0x00, 0x00, 0x00, 0x00, 0x00
}, "varchar_field_aliased_field_and_table_names_join"),
serialization_testcase(column_definition_packet{
string_lenenc("def"), //catalog
string_lenenc("awesome"), // schema (database)
string_lenenc("test_table"), // table
string_lenenc("test_table"), // physical table
string_lenenc("id"), // field name
string_lenenc("id"), // physical field name
collation::binary,
int4(11), // length
protocol_field_type::long_,
int2(
column_flags::not_null |
column_flags::pri_key |
column_flags::auto_increment |
column_flags::part_key
),
int1(0) // decimals
}, {
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
0x6c, 0x65, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64,
0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03,
0x03, 0x42, 0x00, 0x00, 0x00
}, "numeric_auto_increment_primary_key"),
serialization_testcase(column_definition_packet{
string_lenenc("def"), //catalog
string_lenenc("awesome"), // schema (database)
string_lenenc("child"), // table
string_lenenc("child_table"), // physical table
string_lenenc("field_alias"), // field name
string_lenenc("field_varchar"), // physical field name
collation::utf8_general_ci,
int4(765), // length
protocol_field_type::var_string,
int2(), // no column flags
int1(0) // decimals
}, {
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
0x73, 0x6f, 0x6d, 0x65, 0x05, 0x63, 0x68, 0x69,
0x6c, 0x64, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64,
0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0b, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x61, 0x6c, 0x69,
0x61, 0x73, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x5f, 0x76, 0x61, 0x72, 0x63, 0x68, 0x61, 0x72,
0x0c, 0x21, 0x00, 0xfd, 0x02, 0x00, 0x00, 0xfd,
0x00, 0x00, 0x00, 0x00, 0x00
}, "varchar_field_aliased_field_and_table_names_join"),
serialization_testcase(column_definition_packet{
string_lenenc("def"), //catalog
string_lenenc("awesome"), // schema (database)
string_lenenc("test_table"), // table
string_lenenc("test_table"), // physical table
string_lenenc("field_float"), // field name
string_lenenc("field_float"), // physical field name
collation::binary, // binary
int4(12), // length
protocol_field_type::float_,
int2(), // no column flags
int1(31) // decimals
}, {
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
0x6c, 0x65, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x0b, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x6f,
0x61, 0x74, 0x0c, 0x3f, 0x00, 0x0c, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x1f, 0x00, 0x00
}, "float_field")
serialization_testcase(column_definition_packet{
string_lenenc("def"), //catalog
string_lenenc("awesome"), // schema (database)
string_lenenc("test_table"), // table
string_lenenc("test_table"), // physical table
string_lenenc("field_float"), // field name
string_lenenc("field_float"), // physical field name
collation::binary, // binary
int4(12), // length
protocol_field_type::float_,
int2(), // no column flags
int1(31) // decimals
}, {
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
0x6c, 0x65, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64,
0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x0b, 0x66,
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x6f,
0x61, 0x74, 0x0c, 0x3f, 0x00, 0x0c, 0x00, 0x00,
0x00, 0x04, 0x00, 0x00, 0x1f, 0x00, 0x00
}, "float_field")
), test_name_generator);

Some files were not shown because too many files have changed in this diff Show More