mirror of
https://github.com/boostorg/mysql.git
synced 2025-05-12 14:11:41 +00:00
Replaced tabs for spaces
This commit is contained in:
parent
16ef83b136
commit
a54c83ef6a
@ -15,7 +15,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
|||||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
set(_MYSQL_TESTING_ENABLED ON)
|
set(_MYSQL_TESTING_ENABLED ON)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ find_package(Threads REQUIRED)
|
|||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
if (BOOST_MYSQL_VALGRIND_TESTS)
|
if (BOOST_MYSQL_VALGRIND_TESTS)
|
||||||
find_package(Mysqlvalgrind REQUIRED)
|
find_package(Mysqlvalgrind REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Date
|
# Date
|
||||||
@ -58,35 +58,35 @@ add_library(
|
|||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
mysql_asio
|
mysql_asio
|
||||||
INTERFACE
|
INTERFACE
|
||||||
Boost::system
|
Boost::system
|
||||||
Threads::Threads
|
Threads::Threads
|
||||||
OpenSSL::Crypto
|
OpenSSL::Crypto
|
||||||
OpenSSL::SSL
|
OpenSSL::SSL
|
||||||
)
|
)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
mysql_asio
|
mysql_asio
|
||||||
INTERFACE
|
INTERFACE
|
||||||
include
|
include
|
||||||
${date_SOURCE_DIR}/include
|
${date_SOURCE_DIR}/include
|
||||||
)
|
)
|
||||||
target_compile_features(
|
target_compile_features(
|
||||||
mysql_asio
|
mysql_asio
|
||||||
INTERFACE
|
INTERFACE
|
||||||
cxx_std_17
|
cxx_std_17
|
||||||
)
|
)
|
||||||
|
|
||||||
if (BOOST_MYSQL_VALGRIND_TESTS)
|
if (BOOST_MYSQL_VALGRIND_TESTS)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
mysql_asio
|
mysql_asio
|
||||||
INTERFACE
|
INTERFACE
|
||||||
Mysqlvalgrind::Mysqlvalgrind
|
Mysqlvalgrind::Mysqlvalgrind
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Examples and tests
|
# Examples and tests
|
||||||
if(_MYSQL_TESTING_ENABLED)
|
if(_MYSQL_TESTING_ENABLED)
|
||||||
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/test_utils.cmake)
|
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/test_utils.cmake)
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
endif()
|
endif()
|
||||||
|
1
TODO.txt
1
TODO.txt
@ -1,5 +1,4 @@
|
|||||||
Sanitize
|
Sanitize
|
||||||
Tabs to spaces
|
|
||||||
Bad serialize(value.value) somewhere
|
Bad serialize(value.value) somewhere
|
||||||
Copy operations for handshake
|
Copy operations for handshake
|
||||||
Test zero dates
|
Test zero dates
|
||||||
|
@ -12,31 +12,31 @@
|
|||||||
|
|
||||||
cp ci/*.pem /tmp # Copy SSL certs/keys to a known location
|
cp ci/*.pem /tmp # Copy SSL certs/keys to a known location
|
||||||
if [ $TRAVIS_OS_NAME == "osx" ]; then
|
if [ $TRAVIS_OS_NAME == "osx" ]; then
|
||||||
brew update
|
brew update
|
||||||
brew install $DATABASE
|
brew install $DATABASE
|
||||||
cp ci/unix-ci.cnf ~/.my.cnf # This location is checked by both MySQL and MariaDB
|
cp ci/unix-ci.cnf ~/.my.cnf # This location is checked by both MySQL and MariaDB
|
||||||
sudo mkdir -p /var/run/mysqld/
|
sudo mkdir -p /var/run/mysqld/
|
||||||
sudo chmod 777 /var/run/mysqld/
|
sudo chmod 777 /var/run/mysqld/
|
||||||
mysql.server start # Note that running this with sudo fails
|
mysql.server start # Note that running this with sudo fails
|
||||||
if [ $DATABASE == "mariadb" ]; then
|
if [ $DATABASE == "mariadb" ]; then
|
||||||
sudo mysql -u root < ci/root_user_setup.sql
|
sudo mysql -u root < ci/root_user_setup.sql
|
||||||
else
|
else
|
||||||
export BOOST_MYSQL_HAS_SHA256=1
|
export BOOST_MYSQL_HAS_SHA256=1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
sudo cp ci/unix-ci.cnf /etc/mysql/conf.d/
|
sudo cp ci/unix-ci.cnf /etc/mysql/conf.d/
|
||||||
sudo service mysql restart
|
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
|
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
|
mkdir -p /tmp/cmake-latest
|
||||||
bash cmake-latest.sh --prefix=/tmp/cmake-latest --skip-license
|
bash cmake-latest.sh --prefix=/tmp/cmake-latest --skip-license
|
||||||
export PATH=/tmp/cmake-latest/bin:$PATH
|
export PATH=/tmp/cmake-latest/bin:$PATH
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
|
cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
|
||||||
$(if [ $USE_VALGRIND ]; then echo -DBOOST_MYSQL_VALGRIND_TESTS=ON; fi) \
|
$(if [ $USE_VALGRIND ]; then echo -DBOOST_MYSQL_VALGRIND_TESTS=ON; fi) \
|
||||||
$CMAKE_OPTIONS \
|
$CMAKE_OPTIONS \
|
||||||
..
|
..
|
||||||
make -j6 CTEST_OUTPUT_ON_FAILURE=1 all test
|
make -j6 CTEST_OUTPUT_ON_FAILURE=1 all test
|
||||||
|
@ -26,29 +26,29 @@ if(Mysqlvalgrind_FOUND AND NOT TARGET Mysqlvalgrind::Mysqlvalgrind)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (Mysqlvalgrind_FOUND AND NOT COMMAND MysqlValgrind_AddTest)
|
if (Mysqlvalgrind_FOUND AND NOT COMMAND MysqlValgrind_AddTest)
|
||||||
function(Mysqlvalgrind_AddMemcheckTest)
|
function(Mysqlvalgrind_AddMemcheckTest)
|
||||||
set(options "")
|
set(options "")
|
||||||
set(oneValueArgs NAME)
|
set(oneValueArgs NAME)
|
||||||
set(multiValueArgs COMMAND)
|
set(multiValueArgs COMMAND)
|
||||||
cmake_parse_arguments(
|
cmake_parse_arguments(
|
||||||
AddMemcheckTest
|
AddMemcheckTest
|
||||||
"${options}"
|
"${options}"
|
||||||
"${oneValueArgs}"
|
"${oneValueArgs}"
|
||||||
"${multiValueArgs}"
|
"${multiValueArgs}"
|
||||||
${ARGN}
|
${ARGN}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_test(
|
add_test(
|
||||||
NAME ${AddMemcheckTest_NAME}
|
NAME ${AddMemcheckTest_NAME}
|
||||||
COMMAND
|
COMMAND
|
||||||
${Mysqlvalgrind_EXECUTABLE}
|
${Mysqlvalgrind_EXECUTABLE}
|
||||||
--leak-check=full
|
--leak-check=full
|
||||||
--error-limit=yes
|
--error-limit=yes
|
||||||
--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions.txt
|
--suppressions=${CMAKE_SOURCE_DIR}/tools/valgrind_suppressions.txt
|
||||||
--error-exitcode=1
|
--error-exitcode=1
|
||||||
--gen-suppressions=all
|
--gen-suppressions=all
|
||||||
${AddMemcheckTest_COMMAND}
|
${AddMemcheckTest_COMMAND}
|
||||||
)
|
)
|
||||||
endfunction()
|
endfunction()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -9,31 +9,31 @@ find_package(Python3 REQUIRED)
|
|||||||
|
|
||||||
# Adds a test fixture that consists of running a SQL file in the MySQL server
|
# Adds a test fixture that consists of running a SQL file in the MySQL server
|
||||||
function (_mysql_sql_setup_fixture)
|
function (_mysql_sql_setup_fixture)
|
||||||
set(options "")
|
set(options "")
|
||||||
set(oneValueArgs TEST_NAME FIXTURE_NAME SQL_FILE SKIP_VAR)
|
set(oneValueArgs TEST_NAME FIXTURE_NAME SQL_FILE SKIP_VAR)
|
||||||
set(multiValueArgs "")
|
set(multiValueArgs "")
|
||||||
cmake_parse_arguments(
|
cmake_parse_arguments(
|
||||||
SQLFIXT
|
SQLFIXT
|
||||||
"${options}"
|
"${options}"
|
||||||
"${oneValueArgs}"
|
"${oneValueArgs}"
|
||||||
"${multiValueArgs}"
|
"${multiValueArgs}"
|
||||||
${ARGN}
|
${ARGN}
|
||||||
)
|
)
|
||||||
|
|
||||||
# If this env var is defined we will skip setup
|
# If this env var is defined we will skip setup
|
||||||
# (just for development) (done in the Python script)
|
# (just for development) (done in the Python script)
|
||||||
if (SQLFIXT_SKIP_VAR)
|
if (SQLFIXT_SKIP_VAR)
|
||||||
set(ADDITIONAL_OPTIONS -s ${SQLFIXT_SKIP_VAR})
|
set(ADDITIONAL_OPTIONS -s ${SQLFIXT_SKIP_VAR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Actual test
|
# Actual test
|
||||||
add_test(
|
add_test(
|
||||||
NAME ${SQLFIXT_TEST_NAME}
|
NAME ${SQLFIXT_TEST_NAME}
|
||||||
COMMAND
|
COMMAND
|
||||||
${Python3_EXECUTABLE}
|
${Python3_EXECUTABLE}
|
||||||
${CMAKE_SOURCE_DIR}/tools/run_sql.py
|
${CMAKE_SOURCE_DIR}/tools/run_sql.py
|
||||||
${SQLFIXT_SQL_FILE}
|
${SQLFIXT_SQL_FILE}
|
||||||
${ADDITIONAL_OPTIONS}
|
${ADDITIONAL_OPTIONS}
|
||||||
)
|
)
|
||||||
set_tests_properties(${SQLFIXT_TEST_NAME} PROPERTIES FIXTURES_SETUP ${SQLFIXT_FIXTURE_NAME})
|
set_tests_properties(${SQLFIXT_TEST_NAME} PROPERTIES FIXTURES_SETUP ${SQLFIXT_FIXTURE_NAME})
|
||||||
endfunction()
|
endfunction()
|
@ -8,26 +8,26 @@
|
|||||||
# Utility function to set warnings and other compile properties of
|
# Utility function to set warnings and other compile properties of
|
||||||
# our test targets
|
# our test targets
|
||||||
function(_mysql_common_target_settings TARGET_NAME)
|
function(_mysql_common_target_settings TARGET_NAME)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
if (WIN32 AND CMAKE_SYSTEM_VERSION)
|
if (WIN32 AND CMAKE_SYSTEM_VERSION)
|
||||||
set(WINNT_VERSION ${CMAKE_SYSTEM_VERSION})
|
set(WINNT_VERSION ${CMAKE_SYSTEM_VERSION})
|
||||||
string(REPLACE "." "" WINNT_VERSION ${WINNT_VERSION})
|
string(REPLACE "." "" WINNT_VERSION ${WINNT_VERSION})
|
||||||
string(REGEX REPLACE "([0-9])" "0\\1" WINNT_VERSION ${WINNT_VERSION})
|
string(REGEX REPLACE "([0-9])" "0\\1" WINNT_VERSION ${WINNT_VERSION})
|
||||||
|
|
||||||
set(WINNT_VERSION "0x${WINNT_VERSION}")
|
set(WINNT_VERSION "0x${WINNT_VERSION}")
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
${TARGET_NAME}
|
${TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
_CRT_SECURE_NO_WARNINGS
|
_CRT_SECURE_NO_WARNINGS
|
||||||
_WIN32_WINNT=${WINNT_VERSION} # Silence warnings in Windows
|
_WIN32_WINNT=${WINNT_VERSION} # Silence warnings in Windows
|
||||||
_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING # Warnings in C++17 for Asio
|
_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING # Warnings in C++17 for Asio
|
||||||
)
|
)
|
||||||
# For some reason, Appveyor setup needs this to link against coroutine
|
# For some reason, Appveyor setup needs this to link against coroutine
|
||||||
target_link_directories(${TARGET_NAME} PRIVATE ${Boost_LIBRARY_DIRS})
|
target_link_directories(${TARGET_NAME} PRIVATE ${Boost_LIBRARY_DIRS})
|
||||||
target_compile_options(${TARGET_NAME} PRIVATE /bigobj) # Prevent failures on Windows
|
target_compile_options(${TARGET_NAME} PRIVATE /bigobj) # Prevent failures on Windows
|
||||||
else()
|
else()
|
||||||
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
|
target_compile_options(${TARGET_NAME} PRIVATE -Wall -Wextra -pedantic -Werror)
|
||||||
endif()
|
endif()
|
||||||
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF) # disable extensions
|
set_target_properties(${TARGET_NAME} PROPERTIES CXX_EXTENSIONS OFF) # disable extensions
|
||||||
endfunction()
|
endfunction()
|
||||||
|
@ -9,39 +9,39 @@ find_package(Boost REQUIRED COMPONENTS coroutine)
|
|||||||
|
|
||||||
# Compile the example
|
# Compile the example
|
||||||
function (_mysql_build_example EXECUTABLE_NAME CPPFILE)
|
function (_mysql_build_example EXECUTABLE_NAME CPPFILE)
|
||||||
add_executable(
|
add_executable(
|
||||||
${EXECUTABLE_NAME}
|
${EXECUTABLE_NAME}
|
||||||
${CPPFILE}
|
${CPPFILE}
|
||||||
)
|
)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${EXECUTABLE_NAME}
|
${EXECUTABLE_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
mysql_asio
|
mysql_asio
|
||||||
Boost::coroutine
|
Boost::coroutine
|
||||||
)
|
)
|
||||||
_mysql_common_target_settings(${EXECUTABLE_NAME})
|
_mysql_common_target_settings(${EXECUTABLE_NAME})
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Run it as a test
|
# Run it as a test
|
||||||
function (_mysql_test_example EXECUTABLE_NAME)
|
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}")
|
set(TEST_NAME "mysql_${EXECUTABLE_NAME}")
|
||||||
add_test(
|
add_test(
|
||||||
NAME ${TEST_NAME}
|
NAME ${TEST_NAME}
|
||||||
COMMAND ${EXECUTABLE_PATH} example_user example_password
|
COMMAND ${EXECUTABLE_PATH} example_user example_password
|
||||||
)
|
)
|
||||||
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
|
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Run it as a test using Valgrind
|
# Run it as a test using Valgrind
|
||||||
function (_mysql_memcheck_example EXECUTABLE_NAME)
|
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")
|
set(TEST_NAME "mysql_${EXECUTABLE_NAME}_memcheck")
|
||||||
Mysqlvalgrind_AddMemcheckTest(
|
Mysqlvalgrind_AddMemcheckTest(
|
||||||
NAME ${TEST_NAME}
|
NAME ${TEST_NAME}
|
||||||
COMMAND ${EXECUTABLE_PATH} example_user example_password
|
COMMAND ${EXECUTABLE_PATH} example_user example_password
|
||||||
)
|
)
|
||||||
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
|
set_tests_properties(${TEST_NAME} PROPERTIES FIXTURES_REQUIRED mysql_examples_fixture)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# The list of all the examples we have
|
# The list of all the examples we have
|
||||||
@ -62,9 +62,9 @@ set(MYSQL_EXAMPLES_NOMEMCHECK
|
|||||||
|
|
||||||
# Run setup
|
# Run setup
|
||||||
_mysql_sql_setup_fixture(
|
_mysql_sql_setup_fixture(
|
||||||
TEST_NAME mysql_examples_setup
|
TEST_NAME mysql_examples_setup
|
||||||
FIXTURE_NAME mysql_examples_fixture
|
FIXTURE_NAME mysql_examples_fixture
|
||||||
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/db_setup.sql
|
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/db_setup.sql
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build and run examples
|
# Build and run examples
|
||||||
@ -73,10 +73,10 @@ foreach(EXAMPLE_NAME ${MYSQL_EXAMPLES})
|
|||||||
|
|
||||||
_mysql_build_example(${EXECUTABLE_NAME} "${EXAMPLE_NAME}.cpp")
|
_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)
|
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()
|
else()
|
||||||
_mysql_test_example(${EXECUTABLE_NAME})
|
_mysql_test_example(${EXECUTABLE_NAME})
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
@ -15,30 +15,30 @@ USE mysql_asio_examples;
|
|||||||
|
|
||||||
-- Tables
|
-- Tables
|
||||||
CREATE TABLE company(
|
CREATE TABLE company(
|
||||||
id CHAR(10) NOT NULL PRIMARY KEY,
|
id CHAR(10) NOT NULL PRIMARY KEY,
|
||||||
name VARCHAR(100) NOT NULL
|
name VARCHAR(100) NOT NULL
|
||||||
);
|
);
|
||||||
CREATE TABLE employee(
|
CREATE TABLE employee(
|
||||||
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
first_name VARCHAR(100) NOT NULL,
|
first_name VARCHAR(100) NOT NULL,
|
||||||
last_name VARCHAR(100) NOT NULL,
|
last_name VARCHAR(100) NOT NULL,
|
||||||
salary DOUBLE,
|
salary DOUBLE,
|
||||||
company_id CHAR(10) NOT NULL,
|
company_id CHAR(10) NOT NULL,
|
||||||
FOREIGN KEY (company_id) REFERENCES company(id)
|
FOREIGN KEY (company_id) REFERENCES company(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO company (name, id) VALUES
|
INSERT INTO company (name, id) VALUES
|
||||||
("Award Winning Company, Inc.", "AWC"),
|
("Award Winning Company, Inc.", "AWC"),
|
||||||
("Sector Global Leader Plc", "SGL"),
|
("Sector Global Leader Plc", "SGL"),
|
||||||
("High Growth Startup, Ltd", "HGS")
|
("High Growth Startup, Ltd", "HGS")
|
||||||
;
|
;
|
||||||
INSERT INTO employee (first_name, last_name, salary, company_id) VALUES
|
INSERT INTO employee (first_name, last_name, salary, company_id) VALUES
|
||||||
("Efficient", "Developer", 30000, "AWC"),
|
("Efficient", "Developer", 30000, "AWC"),
|
||||||
("Lazy", "Manager", 80000, "AWC"),
|
("Lazy", "Manager", 80000, "AWC"),
|
||||||
("Good", "Team Player", 35000, "HGS"),
|
("Good", "Team Player", 35000, "HGS"),
|
||||||
("Enormous", "Slacker", 45000, "SGL"),
|
("Enormous", "Slacker", 45000, "SGL"),
|
||||||
("Coffee", "Drinker", 30000, "HGS"),
|
("Coffee", "Drinker", 30000, "HGS"),
|
||||||
("Underpaid", "Intern", 15000, "AWC")
|
("Underpaid", "Intern", 15000, "AWC")
|
||||||
;
|
;
|
||||||
|
|
||||||
-- User
|
-- User
|
||||||
|
@ -24,78 +24,78 @@
|
|||||||
|
|
||||||
void main_impl(int argc, char** argv)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection params (host, port, user, password, database)
|
// Connection params (host, port, user, password, database)
|
||||||
boost::asio::ip::tcp::endpoint ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port);
|
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");
|
boost::mysql::connection_params params (argv[1], argv[2], "mysql_asio_examples");
|
||||||
|
|
||||||
// TCP and MySQL level connect
|
// TCP and MySQL level connect
|
||||||
boost::asio::io_context ctx;
|
boost::asio::io_context ctx;
|
||||||
boost::mysql::tcp_connection conn (ctx);
|
boost::mysql::tcp_connection conn (ctx);
|
||||||
conn.next_layer().connect(ep);
|
conn.next_layer().connect(ep);
|
||||||
conn.handshake(params);
|
conn.handshake(params);
|
||||||
|
|
||||||
// Issue the query
|
// Issue the query
|
||||||
const char* sql = R"(
|
const char* sql = R"(
|
||||||
SELECT comp.name AS company_name, emp.id AS employee_id
|
SELECT comp.name AS company_name, emp.id AS employee_id
|
||||||
FROM employee emp
|
FROM employee emp
|
||||||
JOIN company comp ON (comp.id = emp.company_id)
|
JOIN company comp ON (comp.id = emp.company_id)
|
||||||
)";
|
)";
|
||||||
boost::mysql::tcp_resultset result = conn.query(sql);
|
boost::mysql::tcp_resultset result = conn.query(sql);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resultsets allow you to access metadata about the fields in the query
|
* Resultsets allow you to access metadata about the fields in the query
|
||||||
* using the fields() function, which returns a vector of field_metadata
|
* 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).
|
* (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,
|
* You can retrieve the field name, type, number of decimals,
|
||||||
* suggested display width, whether the field is part of a key...
|
* suggested display width, whether the field is part of a key...
|
||||||
*/
|
*/
|
||||||
assert(result.fields().size() == 2);
|
assert(result.fields().size() == 2);
|
||||||
|
|
||||||
[[maybe_unused]] const boost::mysql::field_metadata& company_name = result.fields()[0];
|
[[maybe_unused]] const boost::mysql::field_metadata& company_name = result.fields()[0];
|
||||||
assert(company_name.database() == "mysql_asio_examples"); // database name
|
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.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.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.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.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.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_primary_key()); // field is not a primary key
|
||||||
assert(!company_name.is_auto_increment()); // field is not AUTO_INCREMENT
|
assert(!company_name.is_auto_increment()); // field is not AUTO_INCREMENT
|
||||||
assert(company_name.is_not_null()); // field may not be NULL
|
assert(company_name.is_not_null()); // field may not be NULL
|
||||||
|
|
||||||
[[maybe_unused]] const boost::mysql::field_metadata& employee_id = result.fields()[1];
|
[[maybe_unused]] const boost::mysql::field_metadata& employee_id = result.fields()[1];
|
||||||
assert(employee_id.database() == "mysql_asio_examples");// database name
|
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.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.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.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.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.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_primary_key()); // field is a primary key
|
||||||
assert(employee_id.is_auto_increment()); // we declared the field as AUTO_INCREMENT
|
assert(employee_id.is_auto_increment()); // we declared the field as AUTO_INCREMENT
|
||||||
assert(employee_id.is_not_null()); // field cannot be NULL
|
assert(employee_id.is_not_null()); // field cannot be NULL
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,128 +24,128 @@
|
|||||||
|
|
||||||
void main_impl(int argc, char** argv)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection parameters
|
// Connection parameters
|
||||||
boost::asio::ip::tcp::endpoint ep (
|
boost::asio::ip::tcp::endpoint ep (
|
||||||
boost::asio::ip::address_v4::loopback(), // host
|
boost::asio::ip::address_v4::loopback(), // host
|
||||||
boost::mysql::default_port // port
|
boost::mysql::default_port // port
|
||||||
);
|
);
|
||||||
boost::mysql::connection_params params (
|
boost::mysql::connection_params params (
|
||||||
argv[1], // username
|
argv[1], // username
|
||||||
argv[2], // password
|
argv[2], // password
|
||||||
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
|
"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
|
// Declare the connection object and authenticate to the server
|
||||||
boost::mysql::tcp_connection conn (ctx);
|
boost::mysql::tcp_connection conn (ctx);
|
||||||
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
|
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
|
||||||
conn.handshake(params); // Authenticates to the MySQL server
|
conn.handshake(params); // Authenticates to the MySQL server
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We can tell MySQL to prepare a statement using connection::prepare_statement.
|
* 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,
|
* 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
|
* identified by question marks. Parameters are optional: you can prepare a statement
|
||||||
* with no parameters.
|
* with no parameters.
|
||||||
*
|
*
|
||||||
* Prepared statements are stored in the server on a per-connection basis.
|
* 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.
|
* 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
|
* 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).
|
* templatized on the stream type of the connection (tcp_prepared_statement in our case).
|
||||||
*
|
*
|
||||||
* We prepare two statements, a SELECT and an UPDATE.
|
* We prepare two statements, a SELECT and an UPDATE.
|
||||||
*/
|
*/
|
||||||
const char* salary_getter_sql = "SELECT salary FROM employee WHERE first_name = ?";
|
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);
|
boost::mysql::tcp_prepared_statement salary_getter = conn.prepare_statement(salary_getter_sql);
|
||||||
assert(salary_getter.num_params() == 1); // num_params() returns the number of parameters (question marks)
|
assert(salary_getter.num_params() == 1); // num_params() returns the number of parameters (question marks)
|
||||||
|
|
||||||
const char* salary_updater_sql = "UPDATE employee SET salary = ? WHERE first_name = ?";
|
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);
|
boost::mysql::tcp_prepared_statement salary_updater = conn.prepare_statement(salary_updater_sql);
|
||||||
assert(salary_updater.num_params() == 2);
|
assert(salary_updater.num_params() == 2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Once a statement has been prepared, it can be executed as many times as
|
* 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
|
* desired, by calling prepared_statement::execute(). execute takes as input a
|
||||||
* (possibly empty) collection of mysql::value's and returns a resultset.
|
* (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 returned resultset works the same as the one returned by connection::query().
|
||||||
*
|
*
|
||||||
* The parameters passed to execute() are replaced in order of declaration:
|
* The parameters passed to execute() are replaced in order of declaration:
|
||||||
* the first question mark will be replaced by the first passed parameter,
|
* 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
|
* the second question mark by the second parameter and so on. The number
|
||||||
* of passed parameters must match exactly the number of parameters for
|
* of passed parameters must match exactly the number of parameters for
|
||||||
* the prepared statement.
|
* the prepared statement.
|
||||||
*
|
*
|
||||||
* Any collection providing member functions begin() and end() returning
|
* Any collection providing member functions begin() and end() returning
|
||||||
* forward iterators to mysql::value's is acceptable. We use mysql::make_values(),
|
* 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.
|
* which creates a std::array with the passed in values converted to mysql::value's.
|
||||||
* An iterator version of execute() is also available.
|
* An iterator version of execute() is also available.
|
||||||
*/
|
*/
|
||||||
boost::mysql::tcp_resultset result = salary_getter.execute(boost::mysql::make_values("Efficient"));
|
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
|
std::vector<boost::mysql::owning_row> salaries = result.fetch_all(); // Get all the results
|
||||||
assert(salaries.size() == 1);
|
assert(salaries.size() == 1);
|
||||||
[[maybe_unused]] auto salary = std::get<double>(salaries[0].values().at(0)); // First row, first column
|
[[maybe_unused]] auto salary = std::get<double>(salaries[0].values().at(0)); // First row, first column
|
||||||
assert(salary == 30000);
|
assert(salary == 30000);
|
||||||
std::cout << "The salary before the payrise was: " << salary << std::endl;
|
std::cout << "The salary before the payrise was: " << salary << std::endl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the update. In this case, we must pass in two parameters.
|
* Run the update. In this case, we must pass in two parameters.
|
||||||
* Note that MySQL is flexible in the types passed as parameters.
|
* Note that MySQL is flexible in the types passed as parameters.
|
||||||
* In this case, we are sending the value 35000, which gets converted
|
* In this case, we are sending the value 35000, which gets converted
|
||||||
* into a mysql::value with type std::int32_t, while the 'salary'
|
* into a mysql::value with type std::int32_t, while the 'salary'
|
||||||
* column is declared as a DOUBLE. The MySQL server will do
|
* column is declared as a DOUBLE. The MySQL server will do
|
||||||
* the right thing for us.
|
* the right thing for us.
|
||||||
*/
|
*/
|
||||||
salary_updater.execute(boost::mysql::make_values(35000, "Efficient"));
|
salary_updater.execute(boost::mysql::make_values(35000, "Efficient"));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute the select again. We can execute a prepared statement
|
* Execute the select again. We can execute a prepared statement
|
||||||
* as many times as we want. We do NOT need to call
|
* as many times as we want. We do NOT need to call
|
||||||
* connection::prepare_statement() again.
|
* connection::prepare_statement() again.
|
||||||
*/
|
*/
|
||||||
result = salary_getter.execute(boost::mysql::make_values("Efficient"));
|
result = salary_getter.execute(boost::mysql::make_values("Efficient"));
|
||||||
salaries = result.fetch_all();
|
salaries = result.fetch_all();
|
||||||
assert(salaries.size() == 1);
|
assert(salaries.size() == 1);
|
||||||
salary = std::get<double>(salaries[0].values().at(0));
|
salary = std::get<double>(salaries[0].values().at(0));
|
||||||
assert(salary == 35000); // Our update took place, and the dev got his pay rise
|
assert(salary == 35000); // Our update took place, and the dev got his pay rise
|
||||||
std::cout << "The salary after the payrise was: " << salary << std::endl;
|
std::cout << "The salary after the payrise was: " << salary << std::endl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the statements. Closing a statement deallocates it from the server.
|
* Close the statements. Closing a statement deallocates it from the server.
|
||||||
* Once a statement is closed, trying to execute it will return an error.
|
* Once a statement is closed, trying to execute it will return an error.
|
||||||
*
|
*
|
||||||
* Closing statements implies communicating with the server and can thus fail.
|
* Closing statements implies communicating with the server and can thus fail.
|
||||||
*
|
*
|
||||||
* Statements are automatically deallocated once the connection is closed.
|
* Statements are automatically deallocated once the connection is closed.
|
||||||
* If you are re-using connection objects and preparing statements over time,
|
* If you are re-using connection objects and preparing statements over time,
|
||||||
* you should close() your statements to prevent excessive resource usage.
|
* you should close() your statements to prevent excessive resource usage.
|
||||||
* If you are not re-using the connections, or are preparing your statements
|
* 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.
|
* just once at application startup, there is no need to perform this step.
|
||||||
*/
|
*/
|
||||||
salary_updater.close();
|
salary_updater.close();
|
||||||
salary_getter.close();
|
salary_getter.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,126 +48,126 @@ using boost::mysql::owning_row;
|
|||||||
|
|
||||||
void print_employee(const boost::mysql::row& employee)
|
void print_employee(const boost::mysql::row& employee)
|
||||||
{
|
{
|
||||||
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
||||||
std::cout << "Employee '"
|
std::cout << "Employee '"
|
||||||
<< employee.values()[0] << " " // first_name (type std::string_view)
|
<< employee.values()[0] << " " // first_name (type std::string_view)
|
||||||
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
||||||
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
||||||
}
|
}
|
||||||
|
|
||||||
void die_on_error(
|
void die_on_error(
|
||||||
const error_code& err,
|
const error_code& err,
|
||||||
const boost::mysql::error_info& info = boost::mysql::error_info()
|
const boost::mysql::error_info& info = boost::mysql::error_info()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err << ": " << info.message() << std::endl;
|
std::cerr << "Error: " << err << ": " << info.message() << std::endl;
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class application
|
class application
|
||||||
{
|
{
|
||||||
boost::asio::ip::tcp::endpoint ep; // Physical endpoint to connect to
|
boost::asio::ip::tcp::endpoint ep; // Physical endpoint to connect to
|
||||||
boost::mysql::connection_params conn_params; // MySQL credentials and other connection config
|
boost::mysql::connection_params conn_params; // MySQL credentials and other connection config
|
||||||
boost::asio::io_context ctx; // boost::asio context
|
boost::asio::io_context ctx; // boost::asio context
|
||||||
boost::mysql::tcp_connection connection; // Represents the connection to the MySQL server
|
boost::mysql::tcp_connection connection; // Represents the connection to the MySQL server
|
||||||
boost::mysql::tcp_resultset resultset; // A result from a query
|
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::mysql::error_info additional_info; // Will be populated with additional information about any errors
|
||||||
public:
|
public:
|
||||||
application(const char* username, const char* password) :
|
application(const char* username, const char* password) :
|
||||||
ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port),
|
ep (boost::asio::ip::address_v4::loopback(), boost::mysql::default_port),
|
||||||
conn_params(username, password, "mysql_asio_examples"),
|
conn_params(username, password, "mysql_asio_examples"),
|
||||||
connection(ctx)
|
connection(ctx)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() { connect(); }
|
void start() { connect(); }
|
||||||
|
|
||||||
void connect()
|
void connect()
|
||||||
{
|
{
|
||||||
connection.next_layer().async_connect(ep, [this](error_code err) {
|
connection.next_layer().async_connect(ep, [this](error_code err) {
|
||||||
die_on_error(err);
|
die_on_error(err);
|
||||||
connection.async_handshake(conn_params, [this](error_code err) {
|
connection.async_handshake(conn_params, [this](error_code err) {
|
||||||
die_on_error(err, additional_info);
|
die_on_error(err, additional_info);
|
||||||
query_employees();
|
query_employees();
|
||||||
}, &additional_info);
|
}, &additional_info);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void query_employees()
|
void query_employees()
|
||||||
{
|
{
|
||||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
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) {
|
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
|
||||||
die_on_error(err, additional_info);
|
die_on_error(err, additional_info);
|
||||||
resultset = std::move(result);
|
resultset = std::move(result);
|
||||||
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
|
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
|
||||||
die_on_error(err, additional_info);
|
die_on_error(err, additional_info);
|
||||||
for (const auto& employee: rows)
|
for (const auto& employee: rows)
|
||||||
{
|
{
|
||||||
print_employee(employee);
|
print_employee(employee);
|
||||||
}
|
}
|
||||||
update_slacker();
|
update_slacker();
|
||||||
}, &additional_info);
|
}, &additional_info);
|
||||||
}, &additional_info);
|
}, &additional_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_slacker()
|
void update_slacker()
|
||||||
{
|
{
|
||||||
const char* sql = "UPDATE employee SET salary = 15000 WHERE last_name = '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) {
|
connection.async_query(sql, [this](error_code err, [[maybe_unused]] tcp_resultset&& result) {
|
||||||
die_on_error(err, additional_info);
|
die_on_error(err, additional_info);
|
||||||
assert(result.fields().size() == 0);
|
assert(result.fields().size() == 0);
|
||||||
query_intern();
|
query_intern();
|
||||||
}, &additional_info);
|
}, &additional_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void query_intern()
|
void query_intern()
|
||||||
{
|
{
|
||||||
const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'";
|
const char* sql = "SELECT salary FROM employee WHERE last_name = 'Slacker'";
|
||||||
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
|
connection.async_query(sql, [this](error_code err, tcp_resultset&& result) {
|
||||||
die_on_error(err, additional_info);
|
die_on_error(err, additional_info);
|
||||||
resultset = std::move(result);
|
resultset = std::move(result);
|
||||||
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
|
resultset.async_fetch_all([this](error_code err, const std::vector<owning_row>& rows) {
|
||||||
die_on_error(err, additional_info);
|
die_on_error(err, additional_info);
|
||||||
assert(rows.size() == 1);
|
assert(rows.size() == 1);
|
||||||
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
|
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
|
||||||
assert(salary == 15000);
|
assert(salary == 15000);
|
||||||
}, &additional_info);
|
}, &additional_info);
|
||||||
}, &additional_info);
|
}, &additional_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& context() { return ctx; }
|
auto& context() { return ctx; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void main_impl(int argc, char** argv)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
application app (argv[1], argv[2]);
|
application app (argv[1], argv[2]);
|
||||||
app.start(); // starts the async chain
|
app.start(); // starts the async chain
|
||||||
app.context().run(); // run the asio::io_context until the async chain finishes
|
app.context().run(); // run the asio::io_context until the async chain finishes
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,117 +45,117 @@ using boost::mysql::error_info;
|
|||||||
|
|
||||||
void print_employee(const boost::mysql::row& employee)
|
void print_employee(const boost::mysql::row& employee)
|
||||||
{
|
{
|
||||||
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
||||||
std::cout << "Employee '"
|
std::cout << "Employee '"
|
||||||
<< employee.values()[0] << " " // first_name (type std::string_view)
|
<< employee.values()[0] << " " // first_name (type std::string_view)
|
||||||
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
||||||
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws an exception if an operation failed
|
// Throws an exception if an operation failed
|
||||||
void check_error(
|
void check_error(
|
||||||
const error_code& err,
|
const error_code& err,
|
||||||
const error_info& info = {}
|
const error_info& info = {}
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
throw boost::system::system_error(err, info.message());
|
throw boost::system::system_error(err, info.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void main_impl(int argc, char** argv)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context and connections
|
// Context and connections
|
||||||
boost::asio::io_context ctx;
|
boost::asio::io_context ctx;
|
||||||
boost::mysql::tcp_connection conn (ctx);
|
boost::mysql::tcp_connection conn (ctx);
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint ep (
|
boost::asio::ip::tcp::endpoint ep (
|
||||||
boost::asio::ip::address_v4::loopback(), // host
|
boost::asio::ip::address_v4::loopback(), // host
|
||||||
boost::mysql::default_port // port
|
boost::mysql::default_port // port
|
||||||
);
|
);
|
||||||
boost::mysql::connection_params params (
|
boost::mysql::connection_params params (
|
||||||
argv[1], // username
|
argv[1], // username
|
||||||
argv[2], // password
|
argv[2], // password
|
||||||
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
|
"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
|
* 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).
|
* (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().
|
* 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
|
* It will suspend every time we call one of the asyncrhonous functions, saving
|
||||||
* all information it needs for resuming. When the asynchronous operation completes,
|
* all information it needs for resuming. When the asynchronous operation completes,
|
||||||
* the coroutine will resume in the point it was left.
|
* the coroutine will resume in the point it was left.
|
||||||
*
|
*
|
||||||
* The return type of a coroutine is the second argument to the handler signature
|
* 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
|
* for the asynchronous operation. For example, connection::query has a handler
|
||||||
* signature of void(error_code, resultset<Stream>), so the coroutine return
|
* signature of void(error_code, resultset<Stream>), so the coroutine return
|
||||||
* type is resultset<Stream>.
|
* type is resultset<Stream>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
boost::asio::spawn(ctx.get_executor(), [&conn, ep, params](boost::asio::yield_context yield) {
|
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
|
// This error_code and error_info will be filled if an
|
||||||
// operation fails. We will check them for every operation we perform.
|
// operation fails. We will check them for every operation we perform.
|
||||||
boost::mysql::error_code ec;
|
boost::mysql::error_code ec;
|
||||||
boost::mysql::error_info additional_info;
|
boost::mysql::error_info additional_info;
|
||||||
|
|
||||||
// TCP connect
|
// TCP connect
|
||||||
conn.next_layer().async_connect(ep, yield[ec]);
|
conn.next_layer().async_connect(ep, yield[ec]);
|
||||||
check_error(ec);
|
check_error(ec);
|
||||||
|
|
||||||
// MySQL handshake
|
// MySQL handshake
|
||||||
conn.async_handshake(params, yield[ec], &additional_info);
|
conn.async_handshake(params, yield[ec], &additional_info);
|
||||||
check_error(ec, additional_info);
|
check_error(ec, additional_info);
|
||||||
|
|
||||||
// Issue the query to the server
|
// Issue the query to the server
|
||||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
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);
|
boost::mysql::tcp_resultset result = conn.async_query(sql, yield[ec], &additional_info);
|
||||||
check_error(ec, additional_info);
|
check_error(ec, additional_info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
|
* 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
|
* 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
|
* 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 remain valid until the next call to async_fetch_one(). When no more
|
||||||
* rows are available, async_fetch_one returns nullptr.
|
* rows are available, async_fetch_one returns nullptr.
|
||||||
*/
|
*/
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
const boost::mysql::row* row = result.async_fetch_one(yield[ec], &additional_info);
|
const boost::mysql::row* row = result.async_fetch_one(yield[ec], &additional_info);
|
||||||
check_error(ec, additional_info);
|
check_error(ec, additional_info);
|
||||||
if (!row) break; // No more rows available
|
if (!row) break; // No more rows available
|
||||||
print_employee(*row);
|
print_employee(*row);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't forget to call run()! Otherwise, your program
|
// Don't forget to call run()! Otherwise, your program
|
||||||
// will not spawn the coroutine and will do nothing.
|
// will not spawn the coroutine and will do nothing.
|
||||||
ctx.run();
|
ctx.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,11 +46,11 @@ using boost::asio::use_future;
|
|||||||
|
|
||||||
void print_employee(const boost::mysql::row& employee)
|
void print_employee(const boost::mysql::row& employee)
|
||||||
{
|
{
|
||||||
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
||||||
std::cout << "Employee '"
|
std::cout << "Employee '"
|
||||||
<< employee.values()[0] << " " // first_name (type std::string_view)
|
<< employee.values()[0] << " " // first_name (type std::string_view)
|
||||||
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
||||||
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,92 +63,92 @@ void print_employee(const boost::mysql::row& employee)
|
|||||||
*/
|
*/
|
||||||
class application
|
class application
|
||||||
{
|
{
|
||||||
boost::asio::io_context ctx_;
|
boost::asio::io_context ctx_;
|
||||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard_;
|
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard_;
|
||||||
std::thread runner_;
|
std::thread runner_;
|
||||||
public:
|
public:
|
||||||
application(): guard_(ctx_.get_executor()), runner_([this] { ctx_.run(); }) {}
|
application(): guard_(ctx_.get_executor()), runner_([this] { ctx_.run(); }) {}
|
||||||
application(const application&) = delete;
|
application(const application&) = delete;
|
||||||
application(application&&) = delete;
|
application(application&&) = delete;
|
||||||
application& operator=(const application&) = delete;
|
application& operator=(const application&) = delete;
|
||||||
application& operator=(application&&) = delete;
|
application& operator=(application&&) = delete;
|
||||||
~application()
|
~application()
|
||||||
{
|
{
|
||||||
guard_.reset();
|
guard_.reset();
|
||||||
runner_.join();
|
runner_.join();
|
||||||
}
|
}
|
||||||
boost::asio::io_context& context() { return ctx_; }
|
boost::asio::io_context& context() { return ctx_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void main_impl(int argc, char** argv)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context and connections
|
// Context and connections
|
||||||
application app; // boost::asio::io_context and a thread that calls run()
|
application app; // boost::asio::io_context and a thread that calls run()
|
||||||
boost::mysql::tcp_connection conn (app.context());
|
boost::mysql::tcp_connection conn (app.context());
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint ep (
|
boost::asio::ip::tcp::endpoint ep (
|
||||||
boost::asio::ip::address_v4::loopback(), // host
|
boost::asio::ip::address_v4::loopback(), // host
|
||||||
boost::mysql::default_port // port
|
boost::mysql::default_port // port
|
||||||
);
|
);
|
||||||
boost::mysql::connection_params params (
|
boost::mysql::connection_params params (
|
||||||
argv[1], // username
|
argv[1], // username
|
||||||
argv[2], // password
|
argv[2], // password
|
||||||
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
|
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// TCP connect
|
// TCP connect
|
||||||
std::future<void> fut = conn.next_layer().async_connect(ep, use_future);
|
std::future<void> fut = conn.next_layer().async_connect(ep, use_future);
|
||||||
fut.get();
|
fut.get();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the MySQL handshake. Calling async_handshake triggers the
|
* Perform the MySQL handshake. Calling async_handshake triggers the
|
||||||
* operation, and calling future::get() blocks the current thread until
|
* operation, and calling future::get() blocks the current thread until
|
||||||
* it completes. get() will throw an exception if the operation fails.
|
* it completes. get() will throw an exception if the operation fails.
|
||||||
*/
|
*/
|
||||||
fut = conn.async_handshake(params, use_future);
|
fut = conn.async_handshake(params, use_future);
|
||||||
fut.get();
|
fut.get();
|
||||||
|
|
||||||
// Issue the query to the server
|
// Issue the query to the server
|
||||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
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);
|
std::future<boost::mysql::tcp_resultset> resultset_fut = conn.async_query(sql, use_future);
|
||||||
boost::mysql::tcp_resultset result = resultset_fut.get();
|
boost::mysql::tcp_resultset result = resultset_fut.get();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all rows in the resultset. We will employ resultset::async_fetch_one(),
|
* 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
|
* 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
|
* 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 remain valid until the next call to async_fetch_one(). When no more
|
||||||
* rows are available, async_fetch_one returns nullptr.
|
* rows are available, async_fetch_one returns nullptr.
|
||||||
*/
|
*/
|
||||||
while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get())
|
while (const boost::mysql::row* current_row = result.async_fetch_one(use_future).get())
|
||||||
{
|
{
|
||||||
print_employee(*current_row);
|
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)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,102 +31,102 @@
|
|||||||
*/
|
*/
|
||||||
void print_employee(const boost::mysql::row& employee)
|
void print_employee(const boost::mysql::row& employee)
|
||||||
{
|
{
|
||||||
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
||||||
std::cout << "Employee '"
|
std::cout << "Employee '"
|
||||||
<< employee.values()[0] << " " // first_name (type std::string_view)
|
<< employee.values()[0] << " " // first_name (type std::string_view)
|
||||||
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
||||||
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
||||||
}
|
}
|
||||||
|
|
||||||
void main_impl(int argc, char** argv)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3)
|
if (argc != 3)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password>\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection parameters that tell us where and how to connect to the MySQL server.
|
* Connection parameters that tell us where and how to connect to the MySQL server.
|
||||||
* There are two types of parameters:
|
* There are two types of parameters:
|
||||||
* - TCP-level connection parameters, identifying the host and port to connect to.
|
* - TCP-level connection parameters, identifying the host and port to connect to.
|
||||||
* - MySQL level parameters: database credentials and schema to use.
|
* - MySQL level parameters: database credentials and schema to use.
|
||||||
*/
|
*/
|
||||||
boost::asio::ip::tcp::endpoint ep (
|
boost::asio::ip::tcp::endpoint ep (
|
||||||
boost::asio::ip::address_v4::loopback(), // host
|
boost::asio::ip::address_v4::loopback(), // host
|
||||||
boost::mysql::default_port // port
|
boost::mysql::default_port // port
|
||||||
);
|
);
|
||||||
boost::mysql::connection_params params (
|
boost::mysql::connection_params params (
|
||||||
argv[1], // username
|
argv[1], // username
|
||||||
argv[2], // password
|
argv[2], // password
|
||||||
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
|
"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.
|
// Note: by default, SSL will be used if the server supports it.
|
||||||
// connection_params accepts an optional ssl_options argument
|
// connection_params accepts an optional ssl_options argument
|
||||||
// determining whether to use SSL or not. See ssl_options and ssl_mode
|
// determining whether to use SSL or not. See ssl_options and ssl_mode
|
||||||
// documentation for further details on SSL.
|
// 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.
|
* Represents a single connection over TCP to a MySQL server.
|
||||||
* Before being able to use it, you have to connect to the server by:
|
* Before being able to use it, you have to connect to the server by:
|
||||||
* - Establishing the TCP-level session.
|
* - Establishing the TCP-level session.
|
||||||
* - Authenticating to the MySQL server.
|
* - Authenticating to the MySQL server.
|
||||||
*/
|
*/
|
||||||
boost::mysql::tcp_connection conn (ctx);
|
boost::mysql::tcp_connection conn (ctx);
|
||||||
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
|
conn.next_layer().connect(ep); // next_level() returns a boost::asio::ip::tcp::socket
|
||||||
conn.handshake(params); // Authenticates to the MySQL server
|
conn.handshake(params); // Authenticates to the MySQL server
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To issue a SQL query to the database server, use tcp_connection::query, which takes
|
* 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.
|
* the SQL to be executed as parameter and returns a resultset object.
|
||||||
*
|
*
|
||||||
* Resultset objects represent the result of a query, in tabular format.
|
* 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,
|
* 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.
|
* 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 use fetch_all, which returns all the received rows as a std::vector.
|
||||||
*
|
*
|
||||||
* We will get all employees working for 'High Growth Startup'.
|
* 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'";
|
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||||
boost::mysql::tcp_resultset result = conn.query(sql);
|
boost::mysql::tcp_resultset result = conn.query(sql);
|
||||||
|
|
||||||
// Get all the rows in the resultset
|
// Get all the rows in the resultset
|
||||||
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
|
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
|
||||||
for (const auto& employee: employees)
|
for (const auto& employee: employees)
|
||||||
{
|
{
|
||||||
print_employee(employee);
|
print_employee(employee);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can issue any SQL statement, not only SELECTs. In this case, the returned
|
// We can issue any SQL statement, not only SELECTs. In this case, the returned
|
||||||
// resultset will have no fields and no rows
|
// resultset will have no fields and no rows
|
||||||
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
|
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
|
||||||
result = conn.query(sql);
|
result = conn.query(sql);
|
||||||
assert(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields
|
assert(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields
|
||||||
|
|
||||||
// Check we have updated our poor intern salary
|
// Check we have updated our poor intern salary
|
||||||
result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'");
|
result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'");
|
||||||
auto rows = result.fetch_all();
|
auto rows = result.fetch_all();
|
||||||
assert(rows.size() == 1);
|
assert(rows.size() == 1);
|
||||||
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
|
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
|
||||||
assert(salary == 10000);
|
assert(salary == 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@
|
|||||||
|
|
||||||
void print_employee(const boost::mysql::row& employee)
|
void print_employee(const boost::mysql::row& employee)
|
||||||
{
|
{
|
||||||
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
using boost::mysql::operator<<; // Required for mysql::value objects to be streamable, due to ADL rules
|
||||||
std::cout << "Employee '"
|
std::cout << "Employee '"
|
||||||
<< employee.values()[0] << " " // first_name (type std::string_view)
|
<< employee.values()[0] << " " // first_name (type std::string_view)
|
||||||
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
<< employee.values()[1] << "' earns " // last_name (type std::string_view)
|
||||||
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
<< employee.values()[2] << " dollars yearly\n"; // salary (type double)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UNIX sockets are only available in, er, UNIX systems. Typedefs for
|
// 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)
|
void main_impl(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc != 3 && argc != 4)
|
if (argc != 3 && argc != 4)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: " << argv[0] << " <username> <password> [<socket-path>]\n";
|
std::cerr << "Usage: " << argv[0] << " <username> <password> [<socket-path>]\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* socket_path = "/var/run/mysqld/mysqld.sock";
|
const char* socket_path = "/var/run/mysqld/mysqld.sock";
|
||||||
if (argc == 4)
|
if (argc == 4)
|
||||||
{
|
{
|
||||||
socket_path = argv[3];
|
socket_path = argv[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection parameters that tell us where and how to connect to the MySQL server.
|
* Connection parameters that tell us where and how to connect to the MySQL server.
|
||||||
* There are two types of parameters:
|
* There are two types of parameters:
|
||||||
* - UNIX-level connection parameters, identifying the UNIX socket to connect to.
|
* - UNIX-level connection parameters, identifying the UNIX socket to connect to.
|
||||||
* - MySQL level parameters: database credentials and schema to use.
|
* - MySQL level parameters: database credentials and schema to use.
|
||||||
*/
|
*/
|
||||||
boost::asio::local::stream_protocol::endpoint ep (socket_path);
|
boost::asio::local::stream_protocol::endpoint ep (socket_path);
|
||||||
boost::mysql::connection_params params (
|
boost::mysql::connection_params params (
|
||||||
argv[1], // username
|
argv[1], // username
|
||||||
argv[2], // password
|
argv[2], // password
|
||||||
"mysql_asio_examples" // database to use; leave empty or omit the parameter for no database
|
"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.
|
// Note: by default, SSL will be used if the server supports it.
|
||||||
// connection_params accepts an optional ssl_options argument
|
// connection_params accepts an optional ssl_options argument
|
||||||
// determining whether to use SSL or not. See ssl_options and ssl_mode
|
// determining whether to use SSL or not. See ssl_options and ssl_mode
|
||||||
// documentation for further details on SSL.
|
// 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
|
// Connection to the MySQL server, over a UNIX socket
|
||||||
boost::mysql::unix_connection conn (ctx);
|
boost::mysql::unix_connection conn (ctx);
|
||||||
conn.next_layer().connect(ep); // next_level() returns a boost::asio::local::stream_protocol::socket
|
conn.next_layer().connect(ep); // next_level() returns a boost::asio::local::stream_protocol::socket
|
||||||
conn.handshake(params); // Authenticates to the MySQL server
|
conn.handshake(params); // Authenticates to the MySQL server
|
||||||
|
|
||||||
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
const char* sql = "SELECT first_name, last_name, salary FROM employee WHERE company_id = 'HGS'";
|
||||||
boost::mysql::unix_resultset result = conn.query(sql);
|
boost::mysql::unix_resultset result = conn.query(sql);
|
||||||
|
|
||||||
// Get all the rows in the resultset
|
// Get all the rows in the resultset
|
||||||
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
|
std::vector<boost::mysql::owning_row> employees = result.fetch_all();
|
||||||
for (const auto& employee: employees)
|
for (const auto& employee: employees)
|
||||||
{
|
{
|
||||||
print_employee(employee);
|
print_employee(employee);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can issue any SQL statement, not only SELECTs. In this case, the returned
|
// We can issue any SQL statement, not only SELECTs. In this case, the returned
|
||||||
// resultset will have no fields and no rows
|
// resultset will have no fields and no rows
|
||||||
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
|
sql = "UPDATE employee SET salary = 10000 WHERE first_name = 'Underpaid'";
|
||||||
result = conn.query(sql);
|
result = conn.query(sql);
|
||||||
assert(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields
|
assert(result.fields().size() == 0); // fields() returns a vector containing metadata about the query fields
|
||||||
|
|
||||||
// Check we have updated our poor intern salary
|
// Check we have updated our poor intern salary
|
||||||
result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'");
|
result = conn.query("SELECT salary FROM employee WHERE first_name = 'Underpaid'");
|
||||||
auto rows = result.fetch_all();
|
auto rows = result.fetch_all();
|
||||||
assert(rows.size() == 1);
|
assert(rows.size() == 1);
|
||||||
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
|
[[maybe_unused]] auto salary = std::get<double>(rows[0].values()[0]);
|
||||||
assert(salary == 10000);
|
assert(salary == 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void main_impl(int, char**)
|
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
|
#endif
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
main_impl(argc, argv);
|
main_impl(argc, argv);
|
||||||
}
|
}
|
||||||
catch (const boost::system::system_error& err)
|
catch (const boost::system::system_error& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
std::cerr << "Error: " << err.what() << ", error code: " << err.code() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
catch (const std::exception& err)
|
catch (const std::exception& err)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << err.what() << std::endl;
|
std::cerr << "Error: " << err.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,112 +57,112 @@ namespace mysql {
|
|||||||
* Otherwise, results are undefined.
|
* Otherwise, results are undefined.
|
||||||
*/
|
*/
|
||||||
template <
|
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
|
class connection
|
||||||
{
|
{
|
||||||
using channel_type = detail::channel<Stream>;
|
using channel_type = detail::channel<Stream>;
|
||||||
|
|
||||||
Stream next_layer_;
|
Stream next_layer_;
|
||||||
channel_type channel_;
|
channel_type channel_;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* \brief Initializing constructor.
|
* \brief Initializing constructor.
|
||||||
* \details Creates a Stream object by forwarding any passed in arguments to its constructor.
|
* \details Creates a Stream object by forwarding any passed in arguments to its constructor.
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
connection(Args&&... args) :
|
connection(Args&&... args) :
|
||||||
next_layer_(std::forward<Args>(args)...),
|
next_layer_(std::forward<Args>(args)...),
|
||||||
channel_(next_layer_)
|
channel_(next_layer_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the underlying Stream object.
|
/// Retrieves the underlying Stream object.
|
||||||
Stream& next_layer() { return next_layer_; }
|
Stream& next_layer() { return next_layer_; }
|
||||||
|
|
||||||
/// Retrieves the underlying Stream object.
|
/// Retrieves the underlying Stream object.
|
||||||
const Stream& next_layer() const { return next_layer_; }
|
const Stream& next_layer() const { return next_layer_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns whether the connection uses SSL or not.
|
* \brief Returns whether the connection uses SSL or not.
|
||||||
* \details Will always return false for connections that haven't been
|
* \details Will always return false for connections that haven't been
|
||||||
* established yet (handshake not run yet). If the handshake fails,
|
* established yet (handshake not run yet). If the handshake fails,
|
||||||
* the return value is undefined.
|
* the return value is undefined.
|
||||||
*
|
*
|
||||||
* This function can be used to determine
|
* This function can be used to determine
|
||||||
* whether you are using a SSL connection or not when using
|
* whether you are using a SSL connection or not when using
|
||||||
* optional SSL (ssl_mode::enable).
|
* optional SSL (ssl_mode::enable).
|
||||||
*/
|
*/
|
||||||
bool uses_ssl() const noexcept { return channel_.ssl_active(); }
|
bool uses_ssl() const noexcept { return channel_.ssl_active(); }
|
||||||
|
|
||||||
/// Performs the MySQL-level handshake (synchronous with error code version).
|
/// Performs the MySQL-level handshake (synchronous with error code version).
|
||||||
void handshake(const connection_params& params, error_code& ec, error_info& info);
|
void handshake(const connection_params& params, error_code& ec, error_info& info);
|
||||||
|
|
||||||
/// Performs the MySQL-level handshake (synchronous with exceptions version).
|
/// Performs the MySQL-level handshake (synchronous with exceptions version).
|
||||||
void handshake(const connection_params& params);
|
void handshake(const connection_params& params);
|
||||||
|
|
||||||
/// Handler signature for handshake.
|
/// Handler signature for handshake.
|
||||||
using handshake_signature = void(error_code);
|
using handshake_signature = void(error_code);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Performs the MySQL-level handshake (asynchronous version).
|
* \brief Performs the MySQL-level handshake (asynchronous version).
|
||||||
* \details The strings pointed to by params should be kept alive by the caller
|
* \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.
|
* until the operation completes, as no copy is made by the library.
|
||||||
*/
|
*/
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
|
||||||
async_handshake(const connection_params& params, CompletionToken&& token, error_info* info = nullptr);
|
async_handshake(const connection_params& params, CompletionToken&& token, error_info* info = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Executes a SQL text query (sync with error code version).
|
* \brief Executes a SQL text query (sync with error code version).
|
||||||
* \details Does not perform the actual retrieval of the data; use the various
|
* \details Does not perform the actual retrieval of the data; use the various
|
||||||
* fetch functions within resultset to achieve that.
|
* fetch functions within resultset to achieve that.
|
||||||
* \see resultset
|
* \see resultset
|
||||||
*
|
*
|
||||||
* Note that query_string may contain any valid SQL, not just SELECT statements.
|
* 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.
|
* 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
|
* \warning After query() has returned, you should read the entire resultset
|
||||||
* before calling any function that involves communication with the server over this
|
* before calling any function that involves communication with the server over this
|
||||||
* connection (like connection::query, connection::prepare_statement or
|
* connection (like connection::query, connection::prepare_statement or
|
||||||
* prepared_statement::execute). Otherwise, the results are undefined.
|
* prepared_statement::execute). Otherwise, the results are undefined.
|
||||||
*/
|
*/
|
||||||
resultset<Stream> query(std::string_view query_string, error_code&, error_info&);
|
resultset<Stream> query(std::string_view query_string, error_code&, error_info&);
|
||||||
|
|
||||||
/// Executes a SQL text query (sync with exceptions version).
|
/// Executes a SQL text query (sync with exceptions version).
|
||||||
resultset<Stream> query(std::string_view query_string);
|
resultset<Stream> query(std::string_view query_string);
|
||||||
|
|
||||||
/// Handler signature for query.
|
/// Handler signature for query.
|
||||||
using query_signature = void(error_code, resultset<Stream>);
|
using query_signature = void(error_code, resultset<Stream>);
|
||||||
|
|
||||||
/// Executes a SQL text query (async version).
|
/// Executes a SQL text query (async version).
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, query_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, query_signature)
|
||||||
async_query(std::string_view query_string, CompletionToken&& token, error_info* info=nullptr);
|
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).
|
* \brief Prepares a statement in the server (sync with error code version).
|
||||||
* \details Instructs the server to create a prepared statement. The passed
|
* \details Instructs the server to create a prepared statement. The passed
|
||||||
* in statement should be a SQL statement with question marks (?) as placeholders
|
* in statement should be a SQL statement with question marks (?) as placeholders
|
||||||
* for the statement parameters. See the MySQL documentation on prepared statements
|
* for the statement parameters. See the MySQL documentation on prepared statements
|
||||||
* for more info.
|
* for more info.
|
||||||
*
|
*
|
||||||
* Prepared statements are only valid while the connection object on which
|
* Prepared statements are only valid while the connection object on which
|
||||||
* prepare_statement was called is alive and open. See prepared_statement docs
|
* prepare_statement was called is alive and open. See prepared_statement docs
|
||||||
* for more info.
|
* for more info.
|
||||||
*/
|
*/
|
||||||
prepared_statement<Stream> prepare_statement(std::string_view statement, error_code&, error_info&);
|
prepared_statement<Stream> prepare_statement(std::string_view statement, error_code&, error_info&);
|
||||||
|
|
||||||
/// Prepares a statement (sync with exceptions version).
|
/// Prepares a statement (sync with exceptions version).
|
||||||
prepared_statement<Stream> prepare_statement(std::string_view statement);
|
prepared_statement<Stream> prepare_statement(std::string_view statement);
|
||||||
|
|
||||||
/// Handler signature for prepare_statement.
|
/// Handler signature for prepare_statement.
|
||||||
using prepare_statement_signature = void(error_code, prepared_statement<Stream>);
|
using prepare_statement_signature = void(error_code, prepared_statement<Stream>);
|
||||||
|
|
||||||
/// Prepares a statement (async version).
|
/// Prepares a statement (async version).
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature)
|
||||||
async_prepare_statement(std::string_view statement, CompletionToken&& token, error_info* info=nullptr);
|
async_prepare_statement(std::string_view statement, CompletionToken&& token, error_info* info=nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,9 +26,9 @@ namespace mysql {
|
|||||||
*/
|
*/
|
||||||
enum class ssl_mode
|
enum class ssl_mode
|
||||||
{
|
{
|
||||||
disable, ///< Never use TLS
|
disable, ///< Never use TLS
|
||||||
enable, ///< Use TLS if the server supports it, fall back to non-encrypted connection if it does not.
|
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.
|
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
|
class ssl_options
|
||||||
{
|
{
|
||||||
ssl_mode mode_;
|
ssl_mode mode_;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* \brief Default and initialization constructor.
|
* \brief Default and initialization constructor.
|
||||||
* \details By default, SSL is enabled for the connection
|
* \details By default, SSL is enabled for the connection
|
||||||
* if the server supports is (ssl_mode::enable).
|
* if the server supports is (ssl_mode::enable).
|
||||||
*/
|
*/
|
||||||
explicit ssl_options(ssl_mode mode=ssl_mode::enable) noexcept:
|
explicit ssl_options(ssl_mode mode=ssl_mode::enable) noexcept:
|
||||||
mode_(mode) {}
|
mode_(mode) {}
|
||||||
|
|
||||||
/// Retrieves the TLS mode to be used for the connection.
|
/// Retrieves the TLS mode to be used for the connection.
|
||||||
ssl_mode mode() const noexcept { return mode_; }
|
ssl_mode mode() const noexcept { return mode_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -60,57 +60,57 @@ public:
|
|||||||
*/
|
*/
|
||||||
class connection_params
|
class connection_params
|
||||||
{
|
{
|
||||||
std::string_view username_;
|
std::string_view username_;
|
||||||
std::string_view password_;
|
std::string_view password_;
|
||||||
std::string_view database_;
|
std::string_view database_;
|
||||||
collation connection_collation_;
|
collation connection_collation_;
|
||||||
ssl_options ssl_;
|
ssl_options ssl_;
|
||||||
public:
|
public:
|
||||||
/// Initializing constructor
|
/// Initializing constructor
|
||||||
connection_params(
|
connection_params(
|
||||||
std::string_view username, ///< Username to authenticate as
|
std::string_view username, ///< Username to authenticate as
|
||||||
std::string_view password, ///< Password for that username, possibly empty.
|
std::string_view password, ///< Password for that username, possibly empty.
|
||||||
std::string_view db = "", ///< Database to use, or empty string for no database.
|
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.
|
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.
|
const ssl_options& opts = ssl_options() ///< The TLS options to use with this connection.
|
||||||
) :
|
) :
|
||||||
username_(username),
|
username_(username),
|
||||||
password_(password),
|
password_(password),
|
||||||
database_(db),
|
database_(db),
|
||||||
connection_collation_(connection_col),
|
connection_collation_(connection_col),
|
||||||
ssl_(opts)
|
ssl_(opts)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the username.
|
/// Retrieves the username.
|
||||||
std::string_view username() const noexcept { return username_; }
|
std::string_view username() const noexcept { return username_; }
|
||||||
|
|
||||||
/// Sets the username.
|
/// Sets the username.
|
||||||
void set_username(std::string_view value) noexcept { username_ = value; }
|
void set_username(std::string_view value) noexcept { username_ = value; }
|
||||||
|
|
||||||
/// Retrieves the password.
|
/// Retrieves the password.
|
||||||
std::string_view password() const noexcept { return password_; }
|
std::string_view password() const noexcept { return password_; }
|
||||||
|
|
||||||
/// Sets the password
|
/// Sets the password
|
||||||
void set_password(std::string_view value) noexcept { password_ = value; }
|
void set_password(std::string_view value) noexcept { password_ = value; }
|
||||||
|
|
||||||
/// Retrieves the database.
|
/// Retrieves the database.
|
||||||
std::string_view database() const noexcept { return database_; }
|
std::string_view database() const noexcept { return database_; }
|
||||||
|
|
||||||
/// Sets the database
|
/// Sets the database
|
||||||
void set_database(std::string_view value) noexcept { database_ = value; }
|
void set_database(std::string_view value) noexcept { database_ = value; }
|
||||||
|
|
||||||
/// Retrieves the connection collation.
|
/// Retrieves the connection collation.
|
||||||
collation connection_collation() const noexcept { return connection_collation_; }
|
collation connection_collation() const noexcept { return connection_collation_; }
|
||||||
|
|
||||||
/// Sets the connection collation
|
/// Sets the connection collation
|
||||||
void set_connection_collation(collation value) noexcept { connection_collation_ = value; }
|
void set_connection_collation(collation value) noexcept { connection_collation_ = value; }
|
||||||
|
|
||||||
/// Retrieves SSL options
|
/// Retrieves SSL options
|
||||||
const ssl_options& ssl() const noexcept { return ssl_; }
|
const ssl_options& ssl() const noexcept { return ssl_; }
|
||||||
|
|
||||||
/// Sets SSL options
|
/// Sets SSL options
|
||||||
void set_ssl(const ssl_options& value) noexcept { ssl_ = value; }
|
void set_ssl(const ssl_options& value) noexcept { ssl_ = value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // mysql
|
} // mysql
|
||||||
|
@ -19,36 +19,36 @@ namespace detail {
|
|||||||
|
|
||||||
struct authentication_plugin
|
struct authentication_plugin
|
||||||
{
|
{
|
||||||
using calculator_signature = error_code (*)(
|
using calculator_signature = error_code (*)(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool use_ssl,
|
bool use_ssl,
|
||||||
std::string& output
|
std::string& output
|
||||||
);
|
);
|
||||||
|
|
||||||
std::string_view name;
|
std::string_view name;
|
||||||
calculator_signature calculator;
|
calculator_signature calculator;
|
||||||
};
|
};
|
||||||
|
|
||||||
class auth_calculator
|
class auth_calculator
|
||||||
{
|
{
|
||||||
const authentication_plugin* plugin_ {nullptr};
|
const authentication_plugin* plugin_ {nullptr};
|
||||||
std::string response_;
|
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:
|
public:
|
||||||
inline error_code calculate(
|
inline error_code calculate(
|
||||||
std::string_view plugin_name,
|
std::string_view plugin_name,
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool use_ssl
|
bool use_ssl
|
||||||
);
|
);
|
||||||
std::string_view response() const noexcept { return response_; }
|
std::string_view response() const noexcept { return response_; }
|
||||||
std::string_view plugin_name() const noexcept
|
std::string_view plugin_name() const noexcept
|
||||||
{
|
{
|
||||||
assert(plugin_);
|
assert(plugin_);
|
||||||
return plugin_->name;
|
return plugin_->name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -27,10 +27,10 @@ namespace caching_sha2_password {
|
|||||||
// auth without an SSL connection, but that requires the server public key,
|
// auth without an SSL connection, but that requires the server public key,
|
||||||
// and we do not implement that.
|
// and we do not implement that.
|
||||||
inline error_code compute_response(
|
inline error_code compute_response(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool use_ssl,
|
bool use_ssl,
|
||||||
std::string& output
|
std::string& output
|
||||||
);
|
);
|
||||||
|
|
||||||
} // caching_sha2_password
|
} // caching_sha2_password
|
||||||
|
@ -16,18 +16,18 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
constexpr authentication_plugin mysql_native_password_plugin {
|
constexpr authentication_plugin mysql_native_password_plugin {
|
||||||
"mysql_native_password",
|
"mysql_native_password",
|
||||||
&mysql_native_password::compute_response
|
&mysql_native_password::compute_response
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr authentication_plugin caching_sha2_password_plugin {
|
constexpr authentication_plugin caching_sha2_password_plugin {
|
||||||
"caching_sha2_password",
|
"caching_sha2_password",
|
||||||
&caching_sha2_password::compute_response
|
&caching_sha2_password::compute_response
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::array<const authentication_plugin*, 2> all_authentication_plugins {
|
constexpr std::array<const authentication_plugin*, 2> all_authentication_plugins {
|
||||||
&mysql_native_password_plugin,
|
&mysql_native_password_plugin,
|
||||||
&caching_sha2_password_plugin
|
&caching_sha2_password_plugin
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -36,44 +36,44 @@ constexpr std::array<const authentication_plugin*, 2> all_authentication_plugins
|
|||||||
|
|
||||||
inline const boost::mysql::detail::authentication_plugin*
|
inline const boost::mysql::detail::authentication_plugin*
|
||||||
boost::mysql::detail::auth_calculator::find_plugin(
|
boost::mysql::detail::auth_calculator::find_plugin(
|
||||||
std::string_view name
|
std::string_view name
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto it = std::find_if(
|
auto it = std::find_if(
|
||||||
all_authentication_plugins.begin(),
|
all_authentication_plugins.begin(),
|
||||||
all_authentication_plugins.end(),
|
all_authentication_plugins.end(),
|
||||||
[name](const authentication_plugin* plugin) { return plugin->name == name; }
|
[name](const authentication_plugin* plugin) { return plugin->name == name; }
|
||||||
);
|
);
|
||||||
return it == std::end(all_authentication_plugins) ? nullptr : *it;
|
return it == std::end(all_authentication_plugins) ? nullptr : *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::error_code
|
inline boost::mysql::error_code
|
||||||
boost::mysql::detail::auth_calculator::calculate(
|
boost::mysql::detail::auth_calculator::calculate(
|
||||||
std::string_view plugin_name,
|
std::string_view plugin_name,
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool use_ssl
|
bool use_ssl
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
plugin_ = find_plugin(plugin_name);
|
plugin_ = find_plugin(plugin_name);
|
||||||
if (plugin_)
|
if (plugin_)
|
||||||
{
|
{
|
||||||
// Blank password: we should just return an empty auth string
|
// Blank password: we should just return an empty auth string
|
||||||
if (password.empty())
|
if (password.empty())
|
||||||
{
|
{
|
||||||
response_ = "";
|
response_ = "";
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return plugin_->calculator(password, challenge, use_ssl, response_);
|
return plugin_->calculator(password, challenge, use_ssl, response_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return make_error_code(errc::unknown_auth_plugin);
|
return make_error_code(errc::unknown_auth_plugin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,33 +23,33 @@ constexpr std::string_view perform_full_auth = "\4";
|
|||||||
// challenge must point to challenge_length bytes of data
|
// challenge must point to challenge_length bytes of data
|
||||||
// output must point to response_length bytes of data
|
// output must point to response_length bytes of data
|
||||||
inline void compute_auth_string(
|
inline void compute_auth_string(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
const void* challenge,
|
const void* challenge,
|
||||||
void* output
|
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
|
// SHA(SHA(password_sha) concat challenge) XOR password_sha
|
||||||
// hash1 = SHA(pass)
|
// hash1 = SHA(pass)
|
||||||
using sha_buffer = std::uint8_t [response_length];
|
using sha_buffer = std::uint8_t [response_length];
|
||||||
sha_buffer password_sha;
|
sha_buffer password_sha;
|
||||||
SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha);
|
SHA256(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha);
|
||||||
|
|
||||||
// SHA(password_sha) concat challenge = buffer
|
// SHA(password_sha) concat challenge = buffer
|
||||||
std::uint8_t buffer [response_length + challenge_length];
|
std::uint8_t buffer [response_length + challenge_length];
|
||||||
SHA256(password_sha, response_length, buffer);
|
SHA256(password_sha, response_length, buffer);
|
||||||
std::memcpy(buffer + response_length, challenge, challenge_length);
|
std::memcpy(buffer + response_length, challenge, challenge_length);
|
||||||
|
|
||||||
// SHA(SHA(password_sha) concat challenge) = SHA(buffer) = salted_password
|
// SHA(SHA(password_sha) concat challenge) = SHA(buffer) = salted_password
|
||||||
sha_buffer salted_password;
|
sha_buffer salted_password;
|
||||||
SHA256(buffer, sizeof(buffer), salted_password);
|
SHA256(buffer, sizeof(buffer), salted_password);
|
||||||
|
|
||||||
// salted_password XOR password_sha
|
// salted_password XOR password_sha
|
||||||
for (unsigned i = 0; i < response_length; ++i)
|
for (unsigned i = 0; i < response_length; ++i)
|
||||||
{
|
{
|
||||||
static_cast<std::uint8_t*>(output)[i] = salted_password[i] ^ password_sha[i];
|
static_cast<std::uint8_t*>(output)[i] = salted_password[i] ^ password_sha[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // caching_sha2_password
|
} // caching_sha2_password
|
||||||
@ -61,39 +61,39 @@ inline void compute_auth_string(
|
|||||||
|
|
||||||
inline boost::mysql::error_code
|
inline boost::mysql::error_code
|
||||||
boost::mysql::detail::caching_sha2_password::compute_response(
|
boost::mysql::detail::caching_sha2_password::compute_response(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool use_ssl,
|
bool use_ssl,
|
||||||
std::string& output
|
std::string& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (challenge == perform_full_auth)
|
if (challenge == perform_full_auth)
|
||||||
{
|
{
|
||||||
if (!use_ssl)
|
if (!use_ssl)
|
||||||
{
|
{
|
||||||
return make_error_code(errc::auth_plugin_requires_ssl);
|
return make_error_code(errc::auth_plugin_requires_ssl);
|
||||||
}
|
}
|
||||||
output = password;
|
output = password;
|
||||||
output.push_back(0);
|
output.push_back(0);
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Check challenge size
|
// Check challenge size
|
||||||
if (challenge.size() != challenge_length)
|
if (challenge.size() != challenge_length)
|
||||||
{
|
{
|
||||||
return make_error_code(errc::protocol_value_error);
|
return make_error_code(errc::protocol_value_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the calculation
|
// Do the calculation
|
||||||
output.resize(response_length);
|
output.resize(response_length);
|
||||||
compute_auth_string(
|
compute_auth_string(
|
||||||
password,
|
password,
|
||||||
challenge.data(),
|
challenge.data(),
|
||||||
output.data()
|
output.data()
|
||||||
);
|
);
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,29 +23,29 @@ constexpr std::size_t response_length = 20;
|
|||||||
// output must point to response_length bytes of data
|
// output must point to response_length bytes of data
|
||||||
// SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
|
// SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
|
||||||
inline void compute_auth_string(
|
inline void compute_auth_string(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
const void* challenge,
|
const void* challenge,
|
||||||
void* output
|
void* output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// SHA1 (password)
|
// SHA1 (password)
|
||||||
using sha1_buffer = unsigned char [SHA_DIGEST_LENGTH];
|
using sha1_buffer = unsigned char [SHA_DIGEST_LENGTH];
|
||||||
sha1_buffer password_sha1;
|
sha1_buffer password_sha1;
|
||||||
SHA1(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha1);
|
SHA1(reinterpret_cast<const unsigned char*>(password.data()), password.size(), password_sha1);
|
||||||
|
|
||||||
// Add server challenge (salt)
|
// Add server challenge (salt)
|
||||||
unsigned char salted_buffer [challenge_length + SHA_DIGEST_LENGTH];
|
unsigned char salted_buffer [challenge_length + SHA_DIGEST_LENGTH];
|
||||||
memcpy(salted_buffer, challenge, challenge_length);
|
memcpy(salted_buffer, challenge, challenge_length);
|
||||||
SHA1(password_sha1, sizeof(password_sha1), salted_buffer + 20);
|
SHA1(password_sha1, sizeof(password_sha1), salted_buffer + 20);
|
||||||
sha1_buffer salted_sha1;
|
sha1_buffer salted_sha1;
|
||||||
SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1);
|
SHA1(salted_buffer, sizeof(salted_buffer), salted_sha1);
|
||||||
|
|
||||||
// XOR
|
// XOR
|
||||||
static_assert(response_length == SHA_DIGEST_LENGTH);
|
static_assert(response_length == SHA_DIGEST_LENGTH);
|
||||||
for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
for (std::size_t i = 0; i < SHA_DIGEST_LENGTH; ++i)
|
||||||
{
|
{
|
||||||
static_cast<std::uint8_t*>(output)[i] = password_sha1[i] ^ salted_sha1[i];
|
static_cast<std::uint8_t*>(output)[i] = password_sha1[i] ^ salted_sha1[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // mysql_native_password
|
} // mysql_native_password
|
||||||
@ -56,26 +56,26 @@ inline void compute_auth_string(
|
|||||||
|
|
||||||
inline boost::mysql::error_code
|
inline boost::mysql::error_code
|
||||||
boost::mysql::detail::mysql_native_password::compute_response(
|
boost::mysql::detail::mysql_native_password::compute_response(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool, // use_ssl
|
bool, // use_ssl
|
||||||
std::string& output
|
std::string& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Check challenge size
|
// Check challenge size
|
||||||
if (challenge.size() != challenge_length)
|
if (challenge.size() != challenge_length)
|
||||||
{
|
{
|
||||||
return make_error_code(errc::protocol_value_error);
|
return make_error_code(errc::protocol_value_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the calculation
|
// Do the calculation
|
||||||
output.resize(response_length);
|
output.resize(response_length);
|
||||||
compute_auth_string(
|
compute_auth_string(
|
||||||
password,
|
password,
|
||||||
challenge.data(),
|
challenge.data(),
|
||||||
output.data()
|
output.data()
|
||||||
);
|
);
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@ namespace mysql_native_password {
|
|||||||
// Authorization for this plugin is always challenge (nonce) -> response
|
// Authorization for this plugin is always challenge (nonce) -> response
|
||||||
// (hashed password).
|
// (hashed password).
|
||||||
inline error_code compute_response(
|
inline error_code compute_response(
|
||||||
std::string_view password,
|
std::string_view password,
|
||||||
std::string_view challenge,
|
std::string_view challenge,
|
||||||
bool use_ssl,
|
bool use_ssl,
|
||||||
std::string& output
|
std::string& output
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) DEDUCED
|
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) DEDUCED
|
||||||
#else
|
#else
|
||||||
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) \
|
#define BOOST_MYSQL_INITFN_RESULT_TYPE(ct, sig) \
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(ct, sig)
|
BOOST_ASIO_INITFN_RESULT_TYPE(ct, sig)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_ASYNC_RESULT_MACRO_HPP_ */
|
#endif /* INCLUDE_BOOST_MYSQL_DETAIL_AUXILIAR_ASYNC_RESULT_MACRO_HPP_ */
|
||||||
|
@ -22,38 +22,38 @@ struct get_handler_arg;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct get_handler_arg<void(error_code, T)>
|
struct get_handler_arg<void(error_code, T)>
|
||||||
{
|
{
|
||||||
using type = T;
|
using type = T;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_handler_arg<void(error_code)>
|
struct get_handler_arg<void(error_code)>
|
||||||
{
|
{
|
||||||
using type = void;
|
using type = void;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename HandlerType, typename HandlerArg>
|
template <typename HandlerType, typename HandlerArg>
|
||||||
constexpr bool is_handler_signature_ok()
|
constexpr bool is_handler_signature_ok()
|
||||||
{
|
{
|
||||||
if constexpr (std::is_same_v<HandlerArg, void>)
|
if constexpr (std::is_same_v<HandlerArg, void>)
|
||||||
{
|
{
|
||||||
return std::is_invocable_v<HandlerType, error_code>;
|
return std::is_invocable_v<HandlerType, error_code>;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return std::is_invocable_v<HandlerType, error_code, HandlerArg>;
|
return std::is_invocable_v<HandlerType, error_code, HandlerArg>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompletionToken, typename HandlerSignature>
|
template <typename CompletionToken, typename HandlerSignature>
|
||||||
constexpr void check_completion_token()
|
constexpr void check_completion_token()
|
||||||
{
|
{
|
||||||
using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using handler_arg = typename detail::get_handler_arg<HandlerSignature>::type;
|
using handler_arg = typename detail::get_handler_arg<HandlerSignature>::type;
|
||||||
static_assert(
|
static_assert(
|
||||||
is_handler_signature_ok<handler_type, handler_arg>(),
|
is_handler_signature_ok<handler_type, handler_arg>(),
|
||||||
"Invalid CompletionToken type. Check that CompletionToken fullfills the CompletionToken "
|
"Invalid CompletionToken type. Check that CompletionToken fullfills the CompletionToken "
|
||||||
"requirements or that the callback signature you passed in is correct"
|
"requirements or that the callback signature you passed in is correct"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -17,16 +17,16 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename TLeft, typename TRight>
|
template <typename TLeft, typename TRight>
|
||||||
inline bool container_equals(
|
inline bool container_equals(
|
||||||
const std::vector<TLeft>& lhs,
|
const std::vector<TLeft>& lhs,
|
||||||
const std::vector<TRight>& rhs
|
const std::vector<TRight>& rhs
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (lhs.size() != rhs.size()) return false;
|
if (lhs.size() != rhs.size()) return false;
|
||||||
return std::equal(
|
return std::equal(
|
||||||
lhs.begin(),
|
lhs.begin(),
|
||||||
lhs.end(),
|
lhs.end(),
|
||||||
rhs.begin()
|
rhs.begin()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -18,9 +18,9 @@ namespace detail {
|
|||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
std::string stringize(const Types&... inputs)
|
std::string stringize(const Types&... inputs)
|
||||||
{
|
{
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
(ss << ... << inputs);
|
(ss << ... << inputs);
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -17,13 +17,13 @@ namespace detail {
|
|||||||
template <typename T, typename Head, typename... Tail>
|
template <typename T, typename Head, typename... Tail>
|
||||||
struct is_one_of
|
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>
|
template <typename T, typename Head>
|
||||||
struct is_one_of<T, 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>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline void valgrind_make_mem_defined(
|
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
|
#ifdef BOOST_MYSQL_VALGRIND_TESTS
|
||||||
VALGRIND_MAKE_MEM_DEFINED(buff.data(), buff.size());
|
VALGRIND_MAKE_MEM_DEFINED(buff.data(), buff.size());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void close_statement(
|
void close_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
error_code& code,
|
error_code& code,
|
||||||
error_info& info
|
error_info& info
|
||||||
);
|
);
|
||||||
|
|
||||||
using close_signature = empty_signature;
|
using close_signature = empty_signature;
|
||||||
@ -27,10 +27,10 @@ using close_signature = empty_signature;
|
|||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_signature)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, close_signature)
|
||||||
async_close_statement(
|
async_close_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -20,9 +20,9 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
using deserialize_row_fn = error_code (*)(
|
using deserialize_row_fn = error_code (*)(
|
||||||
deserialization_context&,
|
deserialization_context&,
|
||||||
const std::vector<field_metadata>&,
|
const std::vector<field_metadata>&,
|
||||||
std::vector<value>&
|
std::vector<value>&
|
||||||
);
|
);
|
||||||
|
|
||||||
using empty_signature = void(error_code);
|
using empty_signature = void(error_code);
|
||||||
|
@ -18,12 +18,12 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename StreamType, typename Serializable>
|
template <typename StreamType, typename Serializable>
|
||||||
void execute_generic(
|
void execute_generic(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const Serializable& request,
|
const Serializable& request,
|
||||||
resultset<StreamType>& output,
|
resultset<StreamType>& output,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
);
|
);
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
@ -32,11 +32,11 @@ using execute_generic_signature = void(error_code, resultset<StreamType>);
|
|||||||
template <typename StreamType, typename Serializable, typename CompletionToken>
|
template <typename StreamType, typename Serializable, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
|
||||||
async_execute_generic(
|
async_execute_generic(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
const Serializable& request,
|
const Serializable& request,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -19,20 +19,20 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void execute_query(
|
void execute_query(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
std::string_view query,
|
std::string_view query,
|
||||||
resultset<StreamType>& output,
|
resultset<StreamType>& output,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
);
|
);
|
||||||
|
|
||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
|
||||||
async_execute_query(
|
async_execute_query(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::string_view query,
|
std::string_view query,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,24 +19,24 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename StreamType, typename ForwardIterator>
|
template <typename StreamType, typename ForwardIterator>
|
||||||
void execute_statement(
|
void execute_statement(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
ForwardIterator params_begin,
|
ForwardIterator params_begin,
|
||||||
ForwardIterator params_end,
|
ForwardIterator params_end,
|
||||||
resultset<StreamType>& output,
|
resultset<StreamType>& output,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
);
|
);
|
||||||
|
|
||||||
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
|
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, execute_generic_signature<StreamType>)
|
||||||
async_execute_statement(
|
async_execute_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
ForwardIterator params_begin,
|
ForwardIterator params_begin,
|
||||||
ForwardIterator params_end,
|
ForwardIterator params_end,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -21,10 +21,10 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void hanshake(
|
void hanshake(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
);
|
);
|
||||||
|
|
||||||
using handshake_signature = empty_signature;
|
using handshake_signature = empty_signature;
|
||||||
@ -32,10 +32,10 @@ using handshake_signature = empty_signature;
|
|||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, handshake_signature)
|
||||||
async_handshake(
|
async_handshake(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -12,47 +12,47 @@
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void boost::mysql::detail::close_statement(
|
void boost::mysql::detail::close_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
error_code& code,
|
error_code& code,
|
||||||
error_info&
|
error_info&
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Compose the close message
|
// Compose the close message
|
||||||
com_stmt_close_packet packet {int4(statement_id)};
|
com_stmt_close_packet packet {int4(statement_id)};
|
||||||
|
|
||||||
// Serialize it
|
// Serialize it
|
||||||
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
|
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
|
||||||
|
|
||||||
// Send it. No response is sent back
|
// Send it. No response is sent back
|
||||||
chan.reset_sequence_number();
|
chan.reset_sequence_number();
|
||||||
chan.write(boost::asio::buffer(chan.shared_buffer()), code);
|
chan.write(boost::asio::buffer(chan.shared_buffer()), code);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::close_signature
|
boost::mysql::detail::close_signature
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_close_statement(
|
boost::mysql::detail::async_close_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info*
|
error_info*
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Compose the close message
|
// Compose the close message
|
||||||
com_stmt_close_packet packet {int4(statement_id)};
|
com_stmt_close_packet packet {int4(statement_id)};
|
||||||
|
|
||||||
// Serialize it
|
// Serialize it
|
||||||
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
|
serialize_message(packet, chan.current_capabilities(), chan.shared_buffer());
|
||||||
|
|
||||||
// Send it. No response is sent back
|
// Send it. No response is sent back
|
||||||
chan.reset_sequence_number();
|
chan.reset_sequence_number();
|
||||||
return chan.async_write(
|
return chan.async_write(
|
||||||
boost::asio::buffer(chan.shared_buffer()),
|
boost::asio::buffer(chan.shared_buffer()),
|
||||||
std::forward<CompletionToken>(token)
|
std::forward<CompletionToken>(token)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,123 +18,123 @@ namespace detail {
|
|||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
class execute_processor
|
class execute_processor
|
||||||
{
|
{
|
||||||
deserialize_row_fn deserializer_;
|
deserialize_row_fn deserializer_;
|
||||||
channel<StreamType>& channel_;
|
channel<StreamType>& channel_;
|
||||||
bytestring buffer_;
|
bytestring buffer_;
|
||||||
std::size_t field_count_ {};
|
std::size_t field_count_ {};
|
||||||
ok_packet ok_packet_;
|
ok_packet ok_packet_;
|
||||||
std::vector<field_metadata> fields_;
|
std::vector<field_metadata> fields_;
|
||||||
std::vector<bytestring> field_buffers_;
|
std::vector<bytestring> field_buffers_;
|
||||||
public:
|
public:
|
||||||
execute_processor(deserialize_row_fn deserializer, channel<StreamType>& chan):
|
execute_processor(deserialize_row_fn deserializer, channel<StreamType>& chan):
|
||||||
deserializer_(deserializer), channel_(chan) {};
|
deserializer_(deserializer), channel_(chan) {};
|
||||||
|
|
||||||
template <typename Serializable>
|
template <typename Serializable>
|
||||||
void process_request(
|
void process_request(
|
||||||
const Serializable& request
|
const Serializable& request
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Serialize the request
|
// Serialize the request
|
||||||
capabilities caps = channel_.current_capabilities();
|
capabilities caps = channel_.current_capabilities();
|
||||||
serialize_message(request, caps, buffer_);
|
serialize_message(request, caps, buffer_);
|
||||||
|
|
||||||
// Prepare the channel
|
// Prepare the channel
|
||||||
channel_.reset_sequence_number();
|
channel_.reset_sequence_number();
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_response(
|
void process_response(
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Response may be: ok_packet, err_packet, local infile request (not implemented)
|
// 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
|
// If it is none of this, then the message type itself is the beginning of
|
||||||
// a length-encoded int containing the field count
|
// a length-encoded int containing the field count
|
||||||
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
|
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
|
||||||
std::uint8_t msg_type;
|
std::uint8_t msg_type;
|
||||||
std::tie(err, msg_type) = deserialize_message_type(ctx);
|
std::tie(err, msg_type) = deserialize_message_type(ctx);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
if (msg_type == ok_packet_header)
|
if (msg_type == ok_packet_header)
|
||||||
{
|
{
|
||||||
err = deserialize_message(ok_packet_, ctx);
|
err = deserialize_message(ok_packet_, ctx);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
field_count_ = 0;
|
field_count_ = 0;
|
||||||
}
|
}
|
||||||
else if (msg_type == error_packet_header)
|
else if (msg_type == error_packet_header)
|
||||||
{
|
{
|
||||||
err = process_error_packet(ctx, info);
|
err = process_error_packet(ctx, info);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Resultset with metadata. First packet is an int_lenenc with
|
// Resultset with metadata. First packet is an int_lenenc with
|
||||||
// the number of field definitions to expect. Message type is part
|
// the number of field definitions to expect. Message type is part
|
||||||
// of this packet, so we must rewind the context
|
// of this packet, so we must rewind the context
|
||||||
ctx.rewind(1);
|
ctx.rewind(1);
|
||||||
int_lenenc num_fields;
|
int_lenenc num_fields;
|
||||||
err = deserialize_message(num_fields, ctx);
|
err = deserialize_message(num_fields, ctx);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// For platforms where size_t is shorter than uint64_t,
|
// For platforms where size_t is shorter than uint64_t,
|
||||||
// perform range check
|
// perform range check
|
||||||
if (num_fields.value > std::numeric_limits<std::size_t>::max())
|
if (num_fields.value > std::numeric_limits<std::size_t>::max())
|
||||||
{
|
{
|
||||||
err = make_error_code(errc::protocol_value_error);
|
err = make_error_code(errc::protocol_value_error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have fields, as field_count is indicative of
|
// Ensure we have fields, as field_count is indicative of
|
||||||
// a resultset with fields
|
// a resultset with fields
|
||||||
field_count_ = static_cast<std::size_t>(num_fields.value);
|
field_count_ = static_cast<std::size_t>(num_fields.value);
|
||||||
if (field_count_ == 0)
|
if (field_count_ == 0)
|
||||||
{
|
{
|
||||||
err = make_error_code(errc::protocol_value_error);
|
err = make_error_code(errc::protocol_value_error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fields_.reserve(field_count_);
|
fields_.reserve(field_count_);
|
||||||
field_buffers_.reserve(field_count_);
|
field_buffers_.reserve(field_count_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code process_field_definition()
|
error_code process_field_definition()
|
||||||
{
|
{
|
||||||
column_definition_packet field_definition;
|
column_definition_packet field_definition;
|
||||||
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
|
deserialization_context ctx (boost::asio::buffer(buffer_), channel_.current_capabilities());
|
||||||
auto err = deserialize_message(field_definition, ctx);
|
auto err = deserialize_message(field_definition, ctx);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
|
|
||||||
// Add it to our array
|
// Add it to our array
|
||||||
fields_.push_back(field_definition);
|
fields_.push_back(field_definition);
|
||||||
field_buffers_.push_back(std::move(buffer_));
|
field_buffers_.push_back(std::move(buffer_));
|
||||||
buffer_ = bytestring();
|
buffer_ = bytestring();
|
||||||
|
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
resultset<StreamType> create_resultset() &&
|
resultset<StreamType> create_resultset() &&
|
||||||
{
|
{
|
||||||
if (field_count_ == 0)
|
if (field_count_ == 0)
|
||||||
{
|
{
|
||||||
return resultset<StreamType>(
|
return resultset<StreamType>(
|
||||||
channel_,
|
channel_,
|
||||||
std::move(buffer_),
|
std::move(buffer_),
|
||||||
ok_packet_
|
ok_packet_
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return resultset<StreamType>(
|
return resultset<StreamType>(
|
||||||
channel_,
|
channel_,
|
||||||
resultset_metadata(std::move(field_buffers_), std::move(fields_)),
|
resultset_metadata(std::move(field_buffers_), std::move(fields_)),
|
||||||
deserializer_
|
deserializer_
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& get_channel() { return channel_; }
|
auto& get_channel() { return channel_; }
|
||||||
auto& get_buffer() { return buffer_; }
|
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
|
} // detail
|
||||||
@ -143,164 +143,164 @@ public:
|
|||||||
|
|
||||||
template <typename StreamType, typename Serializable>
|
template <typename StreamType, typename Serializable>
|
||||||
void boost::mysql::detail::execute_generic(
|
void boost::mysql::detail::execute_generic(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const Serializable& request,
|
const Serializable& request,
|
||||||
resultset<StreamType>& output,
|
resultset<StreamType>& output,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Compose a com_query message, reset seq num
|
// Compose a com_query message, reset seq num
|
||||||
execute_processor<StreamType> processor (deserializer, channel);
|
execute_processor<StreamType> processor (deserializer, channel);
|
||||||
processor.process_request(request);
|
processor.process_request(request);
|
||||||
|
|
||||||
// Send it
|
// Send it
|
||||||
channel.write(boost::asio::buffer(processor.get_buffer()), err);
|
channel.write(boost::asio::buffer(processor.get_buffer()), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Read the response
|
// Read the response
|
||||||
channel.read(processor.get_buffer(), err);
|
channel.read(processor.get_buffer(), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
|
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
|
||||||
processor.process_response(err, info);
|
processor.process_response(err, info);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Read all of the field definitions (zero if empty resultset)
|
// Read all of the field definitions (zero if empty resultset)
|
||||||
for (std::uint64_t i = 0; i < processor.field_count(); ++i)
|
for (std::uint64_t i = 0; i < processor.field_count(); ++i)
|
||||||
{
|
{
|
||||||
// Read the field definition packet
|
// Read the field definition packet
|
||||||
channel.read(processor.get_buffer(), err);
|
channel.read(processor.get_buffer(), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Process the message
|
// Process the message
|
||||||
err = processor.process_field_definition();
|
err = processor.process_field_definition();
|
||||||
if (err) return;
|
if (err) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No EOF packet is expected here, as we require deprecate EOF capabilities
|
// No EOF packet is expected here, as we require deprecate EOF capabilities
|
||||||
output = std::move(processor).create_resultset();
|
output = std::move(processor).create_resultset();
|
||||||
err.clear();
|
err.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename StreamType, typename Serializable, typename CompletionToken>
|
template <typename StreamType, typename Serializable, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::execute_generic_signature<StreamType>
|
boost::mysql::detail::execute_generic_signature<StreamType>
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_execute_generic(
|
boost::mysql::detail::async_execute_generic(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
const Serializable& request,
|
const Serializable& request,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using HandlerSignature = execute_generic_signature<StreamType>;
|
using HandlerSignature = execute_generic_signature<StreamType>;
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
||||||
using ResultsetType = resultset<StreamType>;
|
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
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
std::shared_ptr<execute_processor<StreamType>> processor_;
|
std::shared_ptr<execute_processor<StreamType>> processor_;
|
||||||
std::uint64_t remaining_fields_ {0};
|
std::uint64_t remaining_fields_ {0};
|
||||||
error_info* output_info_;
|
error_info* output_info_;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const Serializable& request,
|
const Serializable& request,
|
||||||
error_info* output_info
|
error_info* output_info
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
||||||
processor_(std::make_shared<execute_processor<StreamType>>(deserializer, channel)),
|
processor_(std::make_shared<execute_processor<StreamType>>(deserializer, channel)),
|
||||||
output_info_(output_info)
|
output_info_(output_info)
|
||||||
{
|
{
|
||||||
processor_->process_request(request);
|
processor_->process_request(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code err,
|
error_code err,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Error checking
|
// Error checking
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
this->complete(cont, err, ResultsetType());
|
this->complete(cont, err, ResultsetType());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-error path
|
// Non-error path
|
||||||
error_info info;
|
error_info info;
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
// The request message has already been composed in the ctor. Send it
|
// The request message has already been composed in the ctor. Send it
|
||||||
yield processor_->get_channel().async_write(
|
yield processor_->get_channel().async_write(
|
||||||
boost::asio::buffer(processor_->get_buffer()),
|
boost::asio::buffer(processor_->get_buffer()),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Read the response
|
// Read the response
|
||||||
yield processor_->get_channel().async_read(
|
yield processor_->get_channel().async_read(
|
||||||
processor_->get_buffer(),
|
processor_->get_buffer(),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
|
// Response may be: ok_packet, err_packet, local infile request (not implemented), or response with fields
|
||||||
processor_->process_response(err, info);
|
processor_->process_response(err, info);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
conditional_assign(output_info_, std::move(info));
|
conditional_assign(output_info_, std::move(info));
|
||||||
this->complete(cont, err, ResultsetType());
|
this->complete(cont, err, ResultsetType());
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
remaining_fields_ = processor_->field_count();
|
remaining_fields_ = processor_->field_count();
|
||||||
|
|
||||||
// Read all of the field definitions
|
// Read all of the field definitions
|
||||||
while (remaining_fields_ > 0)
|
while (remaining_fields_ > 0)
|
||||||
{
|
{
|
||||||
// Read the field definition packet
|
// Read the field definition packet
|
||||||
yield processor_->get_channel().async_read(
|
yield processor_->get_channel().async_read(
|
||||||
processor_->get_buffer(),
|
processor_->get_buffer(),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Process the message
|
// Process the message
|
||||||
err = processor_->process_field_definition();
|
err = processor_->process_field_definition();
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
this->complete(cont, err, ResultsetType());
|
this->complete(cont, err, ResultsetType());
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
remaining_fields_--;
|
remaining_fields_--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No EOF packet is expected here, as we require deprecate EOF capabilities
|
// No EOF packet is expected here, as we require deprecate EOF capabilities
|
||||||
this->complete(
|
this->complete(
|
||||||
cont,
|
cont,
|
||||||
error_code(),
|
error_code(),
|
||||||
ResultsetType(std::move(*processor_).create_resultset())
|
ResultsetType(std::move(*processor_).create_resultset())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
std::move(initiator.completion_handler),
|
std::move(initiator.completion_handler),
|
||||||
deserializer,
|
deserializer,
|
||||||
chan,
|
chan,
|
||||||
request,
|
request,
|
||||||
info
|
info
|
||||||
)(error_code(), false);
|
)(error_code(), false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/asio/unyield.hpp>
|
#include <boost/asio/unyield.hpp>
|
||||||
|
@ -13,45 +13,45 @@
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void boost::mysql::detail::execute_query(
|
void boost::mysql::detail::execute_query(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
std::string_view query,
|
std::string_view query,
|
||||||
resultset<StreamType>& output,
|
resultset<StreamType>& output,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
com_query_packet request { string_eof(query) };
|
com_query_packet request { string_eof(query) };
|
||||||
execute_generic(
|
execute_generic(
|
||||||
&deserialize_text_row,
|
&deserialize_text_row,
|
||||||
channel,
|
channel,
|
||||||
request,
|
request,
|
||||||
output,
|
output,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::execute_generic_signature<StreamType>
|
boost::mysql::detail::execute_generic_signature<StreamType>
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_execute_query(
|
boost::mysql::detail::async_execute_query(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::string_view query,
|
std::string_view query,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
com_query_packet request { string_eof(query) };
|
com_query_packet request { string_eof(query) };
|
||||||
return async_execute_generic(
|
return async_execute_generic(
|
||||||
&deserialize_text_row,
|
&deserialize_text_row,
|
||||||
chan,
|
chan,
|
||||||
request,
|
request,
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/asio/unyield.hpp>
|
#include <boost/asio/unyield.hpp>
|
||||||
|
@ -17,19 +17,19 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
com_stmt_execute_packet<ForwardIterator> make_stmt_execute_packet(
|
com_stmt_execute_packet<ForwardIterator> make_stmt_execute_packet(
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
ForwardIterator params_begin,
|
ForwardIterator params_begin,
|
||||||
ForwardIterator params_end
|
ForwardIterator params_end
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return com_stmt_execute_packet<ForwardIterator> {
|
return com_stmt_execute_packet<ForwardIterator> {
|
||||||
int4(statement_id),
|
int4(statement_id),
|
||||||
int1(0), // flags
|
int1(0), // flags
|
||||||
int4(1), // iteration count
|
int4(1), // iteration count
|
||||||
int1(1), // new params flag: set
|
int1(1), // new params flag: set
|
||||||
params_begin,
|
params_begin,
|
||||||
params_end
|
params_end
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -38,46 +38,46 @@ com_stmt_execute_packet<ForwardIterator> make_stmt_execute_packet(
|
|||||||
|
|
||||||
template <typename StreamType, typename ForwardIterator>
|
template <typename StreamType, typename ForwardIterator>
|
||||||
void boost::mysql::detail::execute_statement(
|
void boost::mysql::detail::execute_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
ForwardIterator params_begin,
|
ForwardIterator params_begin,
|
||||||
ForwardIterator params_end,
|
ForwardIterator params_end,
|
||||||
resultset<StreamType>& output,
|
resultset<StreamType>& output,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
execute_generic(
|
execute_generic(
|
||||||
&deserialize_binary_row,
|
&deserialize_binary_row,
|
||||||
chan,
|
chan,
|
||||||
make_stmt_execute_packet(statement_id, params_begin, params_end),
|
make_stmt_execute_packet(statement_id, params_begin, params_end),
|
||||||
output,
|
output,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
|
template <typename StreamType, typename ForwardIterator, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::execute_generic_signature<StreamType>
|
boost::mysql::detail::execute_generic_signature<StreamType>
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_execute_statement(
|
boost::mysql::detail::async_execute_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::uint32_t statement_id,
|
std::uint32_t statement_id,
|
||||||
ForwardIterator params_begin,
|
ForwardIterator params_begin,
|
||||||
ForwardIterator params_end,
|
ForwardIterator params_end,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return async_execute_generic(
|
return async_execute_generic(
|
||||||
&deserialize_binary_row,
|
&deserialize_binary_row,
|
||||||
chan,
|
chan,
|
||||||
make_stmt_execute_packet(statement_id, params_begin, params_end),
|
make_stmt_execute_packet(statement_id, params_begin, params_end),
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,36 +20,36 @@ namespace detail {
|
|||||||
|
|
||||||
inline std::uint8_t get_collation_first_byte(collation value)
|
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)
|
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(
|
inline error_code deserialize_handshake(
|
||||||
boost::asio::const_buffer buffer,
|
boost::asio::const_buffer buffer,
|
||||||
handshake_packet& output,
|
handshake_packet& output,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
deserialization_context ctx (boost::asio::buffer(buffer), capabilities());
|
deserialization_context ctx (boost::asio::buffer(buffer), capabilities());
|
||||||
auto [err, msg_type] = deserialize_message_type(ctx);
|
auto [err, msg_type] = deserialize_message_type(ctx);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
if (msg_type == handshake_protocol_version_9)
|
if (msg_type == handshake_protocol_version_9)
|
||||||
{
|
{
|
||||||
return make_error_code(errc::server_unsupported);
|
return make_error_code(errc::server_unsupported);
|
||||||
}
|
}
|
||||||
else if (msg_type == error_packet_header)
|
else if (msg_type == error_packet_header)
|
||||||
{
|
{
|
||||||
return process_error_packet(ctx, info);
|
return process_error_packet(ctx, info);
|
||||||
}
|
}
|
||||||
else if (msg_type != handshake_protocol_version_10)
|
else if (msg_type != handshake_protocol_version_10)
|
||||||
{
|
{
|
||||||
return make_error_code(errc::protocol_value_error);
|
return make_error_code(errc::protocol_value_error);
|
||||||
}
|
}
|
||||||
return deserialize_message(output, ctx);
|
return deserialize_message(output, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When receiving an auth response from the server, several things can happen:
|
// 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.
|
// happens just for caching_sha2_password.
|
||||||
enum class auth_result
|
enum class auth_result
|
||||||
{
|
{
|
||||||
complete,
|
complete,
|
||||||
send_more_data,
|
send_more_data,
|
||||||
wait_for_ok,
|
wait_for_ok,
|
||||||
invalid
|
invalid
|
||||||
};
|
};
|
||||||
|
|
||||||
class handshake_processor
|
class handshake_processor
|
||||||
{
|
{
|
||||||
connection_params params_;
|
connection_params params_;
|
||||||
capabilities negotiated_caps_;
|
capabilities negotiated_caps_;
|
||||||
auth_calculator auth_calc_;
|
auth_calculator auth_calc_;
|
||||||
public:
|
public:
|
||||||
handshake_processor(const connection_params& params): params_(params) {};
|
handshake_processor(const connection_params& params): params_(params) {};
|
||||||
capabilities negotiated_capabilities() const noexcept { return negotiated_caps_; }
|
capabilities negotiated_capabilities() const noexcept { return negotiated_caps_; }
|
||||||
const connection_params& params() const noexcept { return params_; }
|
const connection_params& params() const noexcept { return params_; }
|
||||||
bool use_ssl() const noexcept { return negotiated_caps_.has(CLIENT_SSL); }
|
bool use_ssl() const noexcept { return negotiated_caps_.has(CLIENT_SSL); }
|
||||||
|
|
||||||
// Initial greeting processing
|
// Initial greeting processing
|
||||||
error_code process_capabilities(const handshake_packet& handshake)
|
error_code process_capabilities(const handshake_packet& handshake)
|
||||||
{
|
{
|
||||||
auto ssl = params_.ssl().mode();
|
auto ssl = params_.ssl().mode();
|
||||||
capabilities server_caps (handshake.capability_falgs.value);
|
capabilities server_caps (handshake.capability_falgs.value);
|
||||||
capabilities required_caps = mandatory_capabilities |
|
capabilities required_caps = mandatory_capabilities |
|
||||||
conditional_capability(!params_.database().empty(), CLIENT_CONNECT_WITH_DB) |
|
conditional_capability(!params_.database().empty(), CLIENT_CONNECT_WITH_DB) |
|
||||||
conditional_capability(ssl == ssl_mode::require, CLIENT_SSL);
|
conditional_capability(ssl == ssl_mode::require, CLIENT_SSL);
|
||||||
if (!server_caps.has_all(required_caps))
|
if (!server_caps.has_all(required_caps))
|
||||||
{
|
{
|
||||||
return make_error_code(errc::server_unsupported);
|
return make_error_code(errc::server_unsupported);
|
||||||
}
|
}
|
||||||
negotiated_caps_ = server_caps & (required_caps | optional_capabilities |
|
negotiated_caps_ = server_caps & (required_caps | optional_capabilities |
|
||||||
conditional_capability(ssl == ssl_mode::enable, CLIENT_SSL));
|
conditional_capability(ssl == ssl_mode::enable, CLIENT_SSL));
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
error_code process_handshake(bytestring& buffer, error_info& info)
|
error_code process_handshake(bytestring& buffer, error_info& info)
|
||||||
{
|
{
|
||||||
// Deserialize server greeting
|
// Deserialize server greeting
|
||||||
handshake_packet handshake;
|
handshake_packet handshake;
|
||||||
auto err = deserialize_handshake(boost::asio::buffer(buffer), handshake, info);
|
auto err = deserialize_handshake(boost::asio::buffer(buffer), handshake, info);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
|
|
||||||
// Check capabilities
|
// Check capabilities
|
||||||
err = process_capabilities(handshake);
|
err = process_capabilities(handshake);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
|
|
||||||
// Compute auth response
|
// Compute auth response
|
||||||
return auth_calc_.calculate(
|
return auth_calc_.calculate(
|
||||||
handshake.auth_plugin_name.value,
|
handshake.auth_plugin_name.value,
|
||||||
params_.password(),
|
params_.password(),
|
||||||
handshake.auth_plugin_data.value,
|
handshake.auth_plugin_data.value,
|
||||||
use_ssl()
|
use_ssl()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response to that initial greeting
|
// Response to that initial greeting
|
||||||
void compose_ssl_request(bytestring& buffer)
|
void compose_ssl_request(bytestring& buffer)
|
||||||
{
|
{
|
||||||
ssl_request sslreq {
|
ssl_request sslreq {
|
||||||
int4(negotiated_capabilities().get()),
|
int4(negotiated_capabilities().get()),
|
||||||
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
|
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
|
||||||
int1(get_collation_first_byte(params_.connection_collation())),
|
int1(get_collation_first_byte(params_.connection_collation())),
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Serialize and send
|
// Serialize and send
|
||||||
serialize_message(sslreq, negotiated_caps_, buffer);
|
serialize_message(sslreq, negotiated_caps_, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compose_handshake_response(bytestring& buffer)
|
void compose_handshake_response(bytestring& buffer)
|
||||||
{
|
{
|
||||||
// Compose response
|
// Compose response
|
||||||
handshake_response_packet response {
|
handshake_response_packet response {
|
||||||
int4(negotiated_caps_.get()),
|
int4(negotiated_caps_.get()),
|
||||||
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
|
int4(static_cast<std::uint32_t>(MAX_PACKET_SIZE)),
|
||||||
int1(get_collation_first_byte(params_.connection_collation())),
|
int1(get_collation_first_byte(params_.connection_collation())),
|
||||||
string_null(params_.username()),
|
string_null(params_.username()),
|
||||||
string_lenenc(auth_calc_.response()),
|
string_lenenc(auth_calc_.response()),
|
||||||
string_null(params_.database()),
|
string_null(params_.database()),
|
||||||
string_null(auth_calc_.plugin_name())
|
string_null(auth_calc_.plugin_name())
|
||||||
};
|
};
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
serialize_message(response, negotiated_caps_, buffer);
|
serialize_message(response, negotiated_caps_, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server handshake response
|
// Server handshake response
|
||||||
error_code process_handshake_server_response(
|
error_code process_handshake_server_response(
|
||||||
bytestring& buffer,
|
bytestring& buffer,
|
||||||
auth_result& result,
|
auth_result& result,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
deserialization_context ctx (boost::asio::buffer(buffer), negotiated_caps_);
|
deserialization_context ctx (boost::asio::buffer(buffer), negotiated_caps_);
|
||||||
auto [err, msg_type] = deserialize_message_type(ctx);
|
auto [err, msg_type] = deserialize_message_type(ctx);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
if (msg_type == ok_packet_header)
|
if (msg_type == ok_packet_header)
|
||||||
{
|
{
|
||||||
// Auth success via fast auth path
|
// Auth success via fast auth path
|
||||||
result = auth_result::complete;
|
result = auth_result::complete;
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
else if (msg_type == error_packet_header)
|
else if (msg_type == error_packet_header)
|
||||||
{
|
{
|
||||||
return process_error_packet(ctx, info);
|
return process_error_packet(ctx, info);
|
||||||
}
|
}
|
||||||
else if (msg_type == auth_switch_request_header)
|
else if (msg_type == auth_switch_request_header)
|
||||||
{
|
{
|
||||||
// We have received an auth switch request. Deserialize it
|
// We have received an auth switch request. Deserialize it
|
||||||
auth_switch_request_packet auth_sw;
|
auth_switch_request_packet auth_sw;
|
||||||
err = deserialize_message(auth_sw, ctx);
|
err = deserialize_message(auth_sw, ctx);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
|
|
||||||
// Compute response
|
// Compute response
|
||||||
auth_switch_response_packet auth_sw_res;
|
auth_switch_response_packet auth_sw_res;
|
||||||
err = auth_calc_.calculate(
|
err = auth_calc_.calculate(
|
||||||
auth_sw.plugin_name.value,
|
auth_sw.plugin_name.value,
|
||||||
params_.password(),
|
params_.password(),
|
||||||
auth_sw.auth_plugin_data.value,
|
auth_sw.auth_plugin_data.value,
|
||||||
use_ssl()
|
use_ssl()
|
||||||
);
|
);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
auth_sw_res.auth_plugin_data.value = auth_calc_.response();
|
auth_sw_res.auth_plugin_data.value = auth_calc_.response();
|
||||||
|
|
||||||
// Serialize
|
// Serialize
|
||||||
serialize_message(auth_sw_res, negotiated_caps_, buffer);
|
serialize_message(auth_sw_res, negotiated_caps_, buffer);
|
||||||
|
|
||||||
result = auth_result::send_more_data;
|
result = auth_result::send_more_data;
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
else if (msg_type == auth_more_data_header)
|
else if (msg_type == auth_more_data_header)
|
||||||
{
|
{
|
||||||
// We have received an auth more data request. Deserialize it
|
// We have received an auth more data request. Deserialize it
|
||||||
auth_more_data_packet more_data;
|
auth_more_data_packet more_data;
|
||||||
err = deserialize_message(more_data, ctx);
|
err = deserialize_message(more_data, ctx);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
|
|
||||||
std::string_view challenge = more_data.auth_plugin_data.value;
|
std::string_view challenge = more_data.auth_plugin_data.value;
|
||||||
if (challenge == fast_auth_complete_challenge)
|
if (challenge == fast_auth_complete_challenge)
|
||||||
{
|
{
|
||||||
result = auth_result::wait_for_ok;
|
result = auth_result::wait_for_ok;
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute response
|
// Compute response
|
||||||
err = auth_calc_.calculate(
|
err = auth_calc_.calculate(
|
||||||
auth_calc_.plugin_name(),
|
auth_calc_.plugin_name(),
|
||||||
params_.password(),
|
params_.password(),
|
||||||
challenge,
|
challenge,
|
||||||
use_ssl()
|
use_ssl()
|
||||||
);
|
);
|
||||||
if (err) return err;
|
if (err) return err;
|
||||||
|
|
||||||
serialize_message(
|
serialize_message(
|
||||||
auth_switch_response_packet {string_eof(auth_calc_.response())},
|
auth_switch_response_packet {string_eof(auth_calc_.response())},
|
||||||
negotiated_caps_,
|
negotiated_caps_,
|
||||||
buffer
|
buffer
|
||||||
);
|
);
|
||||||
|
|
||||||
result = auth_result::send_more_data;
|
result = auth_result::send_more_data;
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return make_error_code(errc::protocol_value_error);
|
return make_error_code(errc::protocol_value_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -238,192 +238,192 @@ public:
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void boost::mysql::detail::hanshake(
|
void boost::mysql::detail::hanshake(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Set up processor
|
// Set up processor
|
||||||
handshake_processor processor (params);
|
handshake_processor processor (params);
|
||||||
|
|
||||||
// Read server greeting
|
// Read server greeting
|
||||||
channel.read(channel.shared_buffer(), err);
|
channel.read(channel.shared_buffer(), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Process server greeting (handshake)
|
// Process server greeting (handshake)
|
||||||
err = processor.process_handshake(channel.shared_buffer(), info);
|
err = processor.process_handshake(channel.shared_buffer(), info);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Setup SSL if required
|
// Setup SSL if required
|
||||||
if (processor.use_ssl())
|
if (processor.use_ssl())
|
||||||
{
|
{
|
||||||
// Send SSL request
|
// Send SSL request
|
||||||
processor.compose_ssl_request(channel.shared_buffer());
|
processor.compose_ssl_request(channel.shared_buffer());
|
||||||
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
|
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// SSL handshake
|
// SSL handshake
|
||||||
channel.ssl_handshake(err);
|
channel.ssl_handshake(err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handshake response
|
// Handshake response
|
||||||
processor.compose_handshake_response(channel.shared_buffer());
|
processor.compose_handshake_response(channel.shared_buffer());
|
||||||
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
|
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
auth_result auth_outcome = auth_result::invalid;
|
auth_result auth_outcome = auth_result::invalid;
|
||||||
while (auth_outcome != auth_result::complete)
|
while (auth_outcome != auth_result::complete)
|
||||||
{
|
{
|
||||||
// Receive response
|
// Receive response
|
||||||
channel.read(channel.shared_buffer(), err);
|
channel.read(channel.shared_buffer(), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Process it
|
// Process it
|
||||||
err = processor.process_handshake_server_response(channel.shared_buffer(), auth_outcome, info);
|
err = processor.process_handshake_server_response(channel.shared_buffer(), auth_outcome, info);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
if (auth_outcome == auth_result::send_more_data)
|
if (auth_outcome == auth_result::send_more_data)
|
||||||
{
|
{
|
||||||
// We received an auth switch request and we have the response ready to be sent
|
// We received an auth switch request and we have the response ready to be sent
|
||||||
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
|
channel.write(boost::asio::buffer(channel.shared_buffer()), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
channel.set_current_capabilities(processor.negotiated_capabilities());
|
channel.set_current_capabilities(processor.negotiated_capabilities());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::handshake_signature
|
boost::mysql::detail::handshake_signature
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_handshake(
|
boost::mysql::detail::async_handshake(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using HandlerSignature = handshake_signature;
|
using HandlerSignature = handshake_signature;
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
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
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
channel<StreamType>& channel_;
|
channel<StreamType>& channel_;
|
||||||
handshake_processor processor_;
|
handshake_processor processor_;
|
||||||
error_info info_;
|
error_info info_;
|
||||||
error_info* output_info_;
|
error_info* output_info_;
|
||||||
auth_result auth_state_ {auth_result::invalid};
|
auth_result auth_state_ {auth_result::invalid};
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
error_info* output_info
|
error_info* output_info
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
||||||
channel_(channel),
|
channel_(channel),
|
||||||
processor_(params),
|
processor_(params),
|
||||||
output_info_(output_info)
|
output_info_(output_info)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void complete(bool cont, error_code code)
|
void complete(bool cont, error_code code)
|
||||||
{
|
{
|
||||||
channel_.set_current_capabilities(processor_.negotiated_capabilities());
|
channel_.set_current_capabilities(processor_.negotiated_capabilities());
|
||||||
conditional_assign(output_info_, std::move(info_));
|
conditional_assign(output_info_, std::move(info_));
|
||||||
BaseType::complete(cont, code);
|
BaseType::complete(cont, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code err,
|
error_code err,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Error checking
|
// Error checking
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
complete(cont, err);
|
complete(cont, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-error path
|
// Non-error path
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
// Read server greeting
|
// Read server greeting
|
||||||
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
|
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
|
||||||
|
|
||||||
// Process server greeting
|
// Process server greeting
|
||||||
err = processor_.process_handshake(channel_.shared_buffer(), info_);
|
err = processor_.process_handshake(channel_.shared_buffer(), info_);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
complete(cont, err);
|
complete(cont, err);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSL
|
// SSL
|
||||||
if (processor_.use_ssl())
|
if (processor_.use_ssl())
|
||||||
{
|
{
|
||||||
// Send SSL request
|
// Send SSL request
|
||||||
processor_.compose_ssl_request(channel_.shared_buffer());
|
processor_.compose_ssl_request(channel_.shared_buffer());
|
||||||
yield channel_.async_write(
|
yield channel_.async_write(
|
||||||
boost::asio::buffer(channel_.shared_buffer()),
|
boost::asio::buffer(channel_.shared_buffer()),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
|
|
||||||
// SSL handshake
|
// SSL handshake
|
||||||
yield channel_.async_ssl_handshake(std::move(*this));
|
yield channel_.async_ssl_handshake(std::move(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose and send handshake response
|
// Compose and send handshake response
|
||||||
processor_.compose_handshake_response(channel_.shared_buffer());
|
processor_.compose_handshake_response(channel_.shared_buffer());
|
||||||
yield channel_.async_write(boost::asio::buffer(channel_.shared_buffer()), std::move(*this));
|
yield channel_.async_write(boost::asio::buffer(channel_.shared_buffer()), std::move(*this));
|
||||||
|
|
||||||
while (auth_state_ != auth_result::complete)
|
while (auth_state_ != auth_result::complete)
|
||||||
{
|
{
|
||||||
// Receive response
|
// Receive response
|
||||||
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
|
yield channel_.async_read(channel_.shared_buffer(), std::move(*this));
|
||||||
|
|
||||||
// Process it
|
// Process it
|
||||||
err = processor_.process_handshake_server_response(
|
err = processor_.process_handshake_server_response(
|
||||||
channel_.shared_buffer(),
|
channel_.shared_buffer(),
|
||||||
auth_state_,
|
auth_state_,
|
||||||
info_
|
info_
|
||||||
);
|
);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
complete(cont, err);
|
complete(cont, err);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth_state_ == auth_result::send_more_data)
|
if (auth_state_ == auth_result::send_more_data)
|
||||||
{
|
{
|
||||||
// We received an auth switch response and we have the response ready to be sent
|
// We received an auth switch response and we have the response ready to be sent
|
||||||
yield channel_.async_write(
|
yield channel_.async_write(
|
||||||
boost::asio::buffer(channel_.shared_buffer()),
|
boost::asio::buffer(channel_.shared_buffer()),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(cont, error_code());
|
complete(cont, error_code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
std::move(initiator.completion_handler),
|
std::move(initiator.completion_handler),
|
||||||
chan,
|
chan,
|
||||||
params,
|
params,
|
||||||
info
|
info
|
||||||
)(error_code(), false);
|
)(error_code(), false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/asio/unyield.hpp>
|
#include <boost/asio/unyield.hpp>
|
||||||
|
@ -17,47 +17,47 @@ namespace detail {
|
|||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
class prepare_statement_processor
|
class prepare_statement_processor
|
||||||
{
|
{
|
||||||
channel<StreamType>& channel_;
|
channel<StreamType>& channel_;
|
||||||
com_stmt_prepare_ok_packet response_;
|
com_stmt_prepare_ok_packet response_;
|
||||||
public:
|
public:
|
||||||
prepare_statement_processor(channel<StreamType>& chan): channel_(chan) {}
|
prepare_statement_processor(channel<StreamType>& chan): channel_(chan) {}
|
||||||
void process_request(std::string_view statement)
|
void process_request(std::string_view statement)
|
||||||
{
|
{
|
||||||
com_stmt_prepare_packet packet { string_eof(statement) };
|
com_stmt_prepare_packet packet { string_eof(statement) };
|
||||||
serialize_message(packet, channel_.current_capabilities(), channel_.shared_buffer());
|
serialize_message(packet, channel_.current_capabilities(), channel_.shared_buffer());
|
||||||
channel_.reset_sequence_number();
|
channel_.reset_sequence_number();
|
||||||
}
|
}
|
||||||
void process_response(error_code& err, error_info& info)
|
void process_response(error_code& err, error_info& info)
|
||||||
{
|
{
|
||||||
deserialization_context ctx (
|
deserialization_context ctx (
|
||||||
boost::asio::buffer(channel_.shared_buffer()),
|
boost::asio::buffer(channel_.shared_buffer()),
|
||||||
channel_.current_capabilities()
|
channel_.current_capabilities()
|
||||||
);
|
);
|
||||||
std::uint8_t msg_type = 0;
|
std::uint8_t msg_type = 0;
|
||||||
std::tie(err, msg_type) = deserialize_message_type(ctx);
|
std::tie(err, msg_type) = deserialize_message_type(ctx);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
if (msg_type == error_packet_header)
|
if (msg_type == error_packet_header)
|
||||||
{
|
{
|
||||||
err = process_error_packet(ctx, info);
|
err = process_error_packet(ctx, info);
|
||||||
}
|
}
|
||||||
else if (msg_type != 0)
|
else if (msg_type != 0)
|
||||||
{
|
{
|
||||||
err = make_error_code(errc::protocol_value_error);
|
err = make_error_code(errc::protocol_value_error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
err = deserialize_message(response_, ctx);
|
err = deserialize_message(response_, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto& get_buffer() noexcept { return channel_.shared_buffer(); }
|
auto& get_buffer() noexcept { return channel_.shared_buffer(); }
|
||||||
auto& get_channel() noexcept { return channel_; }
|
auto& get_channel() noexcept { return channel_; }
|
||||||
const auto& get_response() const noexcept { return response_; }
|
const auto& get_response() const noexcept { return response_; }
|
||||||
|
|
||||||
unsigned get_num_metadata_packets() const noexcept
|
unsigned get_num_metadata_packets() const noexcept
|
||||||
{
|
{
|
||||||
return response_.num_columns.value + response_.num_params.value;
|
return response_.num_columns.value + response_.num_params.value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -66,145 +66,145 @@ public:
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void boost::mysql::detail::prepare_statement(
|
void boost::mysql::detail::prepare_statement(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info,
|
error_info& info,
|
||||||
prepared_statement<StreamType>& output
|
prepared_statement<StreamType>& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Prepare message
|
// Prepare message
|
||||||
prepare_statement_processor<StreamType> processor (channel);
|
prepare_statement_processor<StreamType> processor (channel);
|
||||||
processor.process_request(statement);
|
processor.process_request(statement);
|
||||||
|
|
||||||
// Write message
|
// Write message
|
||||||
processor.get_channel().write(boost::asio::buffer(processor.get_buffer()), err);
|
processor.get_channel().write(boost::asio::buffer(processor.get_buffer()), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Read response
|
// Read response
|
||||||
processor.get_channel().read(processor.get_buffer(), err);
|
processor.get_channel().read(processor.get_buffer(), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Process response
|
// Process response
|
||||||
processor.process_response(err, info);
|
processor.process_response(err, info);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
// Server sends now one packet per parameter and field.
|
// Server sends now one packet per parameter and field.
|
||||||
// We ignore these for now. TODO: do sth useful with these
|
// We ignore these for now. TODO: do sth useful with these
|
||||||
for (unsigned i = 0; i < processor.get_num_metadata_packets(); ++i)
|
for (unsigned i = 0; i < processor.get_num_metadata_packets(); ++i)
|
||||||
{
|
{
|
||||||
processor.get_channel().read(processor.get_buffer(), err);
|
processor.get_channel().read(processor.get_buffer(), err);
|
||||||
if (err) return;
|
if (err) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose response
|
// Compose response
|
||||||
output = prepared_statement<StreamType>(channel, processor.get_response());
|
output = prepared_statement<StreamType>(channel, processor.get_response());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::prepare_statement_signature<StreamType>
|
boost::mysql::detail::prepare_statement_signature<StreamType>
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_prepare_statement(
|
boost::mysql::detail::async_prepare_statement(
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using HandlerSignature = prepare_statement_signature<StreamType>;
|
using HandlerSignature = prepare_statement_signature<StreamType>;
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
||||||
using PreparedStatementType = prepared_statement<StreamType>;
|
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
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
prepare_statement_processor<StreamType> processor_;
|
prepare_statement_processor<StreamType> processor_;
|
||||||
unsigned remaining_meta_;
|
unsigned remaining_meta_;
|
||||||
error_info* output_info_;
|
error_info* output_info_;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
error_info* output_info
|
error_info* output_info
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
||||||
processor_(channel),
|
processor_(channel),
|
||||||
remaining_meta_(0),
|
remaining_meta_(0),
|
||||||
output_info_(output_info)
|
output_info_(output_info)
|
||||||
{
|
{
|
||||||
processor_.process_request(statement);
|
processor_.process_request(statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code err,
|
error_code err,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Error checking
|
// Error checking
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
this->complete(cont, err, PreparedStatementType());
|
this->complete(cont, err, PreparedStatementType());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular coroutine body; if there has been an error, we don't get here
|
// Regular coroutine body; if there has been an error, we don't get here
|
||||||
error_info info;
|
error_info info;
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
// Write message (already serialized at this point)
|
// Write message (already serialized at this point)
|
||||||
yield processor_.get_channel().async_write(
|
yield processor_.get_channel().async_write(
|
||||||
boost::asio::buffer(processor_.get_buffer()),
|
boost::asio::buffer(processor_.get_buffer()),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Read response
|
// Read response
|
||||||
yield processor_.get_channel().async_read(
|
yield processor_.get_channel().async_read(
|
||||||
processor_.get_buffer(),
|
processor_.get_buffer(),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Process response
|
// Process response
|
||||||
processor_.process_response(err, info);
|
processor_.process_response(err, info);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
detail::conditional_assign(output_info_, std::move(info));
|
detail::conditional_assign(output_info_, std::move(info));
|
||||||
this->complete(cont, err, PreparedStatementType());
|
this->complete(cont, err, PreparedStatementType());
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server sends now one packet per parameter and field.
|
// Server sends now one packet per parameter and field.
|
||||||
// We ignore these for now. TODO: do sth useful with these
|
// We ignore these for now. TODO: do sth useful with these
|
||||||
remaining_meta_ = processor_.get_num_metadata_packets();
|
remaining_meta_ = processor_.get_num_metadata_packets();
|
||||||
for (; remaining_meta_ > 0; --remaining_meta_)
|
for (; remaining_meta_ > 0; --remaining_meta_)
|
||||||
{
|
{
|
||||||
yield processor_.get_channel().async_read(
|
yield processor_.get_channel().async_read(
|
||||||
processor_.get_buffer(),
|
processor_.get_buffer(),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose response
|
// Compose response
|
||||||
this->complete(
|
this->complete(
|
||||||
cont,
|
cont,
|
||||||
err,
|
err,
|
||||||
PreparedStatementType(processor_.get_channel(), processor_.get_response())
|
PreparedStatementType(processor_.get_channel(), processor_.get_response())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
std::move(initiator.completion_handler),
|
std::move(initiator.completion_handler),
|
||||||
chan,
|
chan,
|
||||||
statement,
|
statement,
|
||||||
info
|
info
|
||||||
)(error_code(), false);
|
)(error_code(), false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/asio/unyield.hpp>
|
#include <boost/asio/unyield.hpp>
|
||||||
|
@ -16,44 +16,44 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline read_row_result process_read_message(
|
inline read_row_result process_read_message(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
capabilities current_capabilities,
|
capabilities current_capabilities,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
const bytestring& buffer,
|
const bytestring& buffer,
|
||||||
std::vector<value>& output_values,
|
std::vector<value>& output_values,
|
||||||
ok_packet& output_ok_packet,
|
ok_packet& output_ok_packet,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assert(deserializer);
|
assert(deserializer);
|
||||||
|
|
||||||
// Message type: row, error or eof?
|
// Message type: row, error or eof?
|
||||||
std::uint8_t msg_type;
|
std::uint8_t msg_type;
|
||||||
deserialization_context ctx (boost::asio::buffer(buffer), current_capabilities);
|
deserialization_context ctx (boost::asio::buffer(buffer), current_capabilities);
|
||||||
std::tie(err, msg_type) = deserialize_message_type(ctx);
|
std::tie(err, msg_type) = deserialize_message_type(ctx);
|
||||||
if (err) return read_row_result::error;
|
if (err) return read_row_result::error;
|
||||||
if (msg_type == eof_packet_header)
|
if (msg_type == eof_packet_header)
|
||||||
{
|
{
|
||||||
// end of resultset
|
// end of resultset
|
||||||
err = deserialize_message(output_ok_packet, ctx);
|
err = deserialize_message(output_ok_packet, ctx);
|
||||||
if (err) return read_row_result::error;
|
if (err) return read_row_result::error;
|
||||||
return read_row_result::eof;
|
return read_row_result::eof;
|
||||||
}
|
}
|
||||||
else if (msg_type == error_packet_header)
|
else if (msg_type == error_packet_header)
|
||||||
{
|
{
|
||||||
// An error occurred during the generation of the rows
|
// An error occurred during the generation of the rows
|
||||||
err = process_error_packet(ctx, info);
|
err = process_error_packet(ctx, info);
|
||||||
return read_row_result::error;
|
return read_row_result::error;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// An actual row
|
// An actual row
|
||||||
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
|
ctx.rewind(1); // keep the 'message type' byte, as it is part of the actual message
|
||||||
err = deserializer(ctx, meta, output_values);
|
err = deserializer(ctx, meta, output_values);
|
||||||
if (err) return read_row_result::error;
|
if (err) return read_row_result::error;
|
||||||
return read_row_result::row;
|
return read_row_result::row;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -63,127 +63,127 @@ inline read_row_result process_read_message(
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
|
boost::mysql::detail::read_row_result boost::mysql::detail::read_row(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
bytestring& buffer,
|
bytestring& buffer,
|
||||||
std::vector<value>& output_values,
|
std::vector<value>& output_values,
|
||||||
ok_packet& output_ok_packet,
|
ok_packet& output_ok_packet,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Read a packet
|
// Read a packet
|
||||||
channel.read(buffer, err);
|
channel.read(buffer, err);
|
||||||
if (err) return read_row_result::error;
|
if (err) return read_row_result::error;
|
||||||
|
|
||||||
return process_read_message(
|
return process_read_message(
|
||||||
deserializer,
|
deserializer,
|
||||||
channel.current_capabilities(),
|
channel.current_capabilities(),
|
||||||
meta,
|
meta,
|
||||||
buffer,
|
buffer,
|
||||||
output_values,
|
output_values,
|
||||||
output_ok_packet,
|
output_ok_packet,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
boost::mysql::detail::read_row_signature
|
boost::mysql::detail::read_row_signature
|
||||||
)
|
)
|
||||||
boost::mysql::detail::async_read_row(
|
boost::mysql::detail::async_read_row(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& chan,
|
channel<StreamType>& chan,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
bytestring& buffer,
|
bytestring& buffer,
|
||||||
std::vector<value>& output_values,
|
std::vector<value>& output_values,
|
||||||
ok_packet& output_ok_packet,
|
ok_packet& output_ok_packet,
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using HandlerSignature = read_row_signature;
|
using HandlerSignature = read_row_signature;
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
||||||
|
|
||||||
struct Op: BaseType, boost::asio::coroutine
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
deserialize_row_fn deserializer_;
|
deserialize_row_fn deserializer_;
|
||||||
channel<StreamType>& channel_;
|
channel<StreamType>& channel_;
|
||||||
const std::vector<field_metadata>& meta_;
|
const std::vector<field_metadata>& meta_;
|
||||||
bytestring& buffer_;
|
bytestring& buffer_;
|
||||||
std::vector<value>& output_values_;
|
std::vector<value>& output_values_;
|
||||||
ok_packet& output_ok_packet_;
|
ok_packet& output_ok_packet_;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
bytestring& buffer,
|
bytestring& buffer,
|
||||||
std::vector<value>& output_values,
|
std::vector<value>& output_values,
|
||||||
ok_packet& output_ok_packet
|
ok_packet& output_ok_packet
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
BaseType(std::move(handler), channel.next_layer().get_executor()),
|
||||||
deserializer_(deserializer),
|
deserializer_(deserializer),
|
||||||
channel_(channel),
|
channel_(channel),
|
||||||
meta_(meta),
|
meta_(meta),
|
||||||
buffer_(buffer),
|
buffer_(buffer),
|
||||||
output_values_(output_values),
|
output_values_(output_values),
|
||||||
output_ok_packet_(output_ok_packet)
|
output_ok_packet_(output_ok_packet)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_result(bool cont)
|
void process_result(bool cont)
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
error_info info;
|
error_info info;
|
||||||
auto result = process_read_message(
|
auto result = process_read_message(
|
||||||
deserializer_,
|
deserializer_,
|
||||||
channel_.current_capabilities(),
|
channel_.current_capabilities(),
|
||||||
meta_,
|
meta_,
|
||||||
buffer_,
|
buffer_,
|
||||||
output_values_,
|
output_values_,
|
||||||
output_ok_packet_,
|
output_ok_packet_,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
this->complete(cont, err, info, result);
|
this->complete(cont, err, info, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code err,
|
error_code err,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
yield channel_.async_read(buffer_, std::move(*this));
|
yield channel_.async_read(buffer_, std::move(*this));
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
this->complete(cont, err, error_info(), read_row_result::error);
|
this->complete(cont, err, error_info(), read_row_result::error);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
process_result(cont);
|
process_result(cont);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
|
boost::asio::async_completion<CompletionToken, HandlerSignature> initiator(token);
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
std::move(initiator.completion_handler),
|
std::move(initiator.completion_handler),
|
||||||
deserializer,
|
deserializer,
|
||||||
chan,
|
chan,
|
||||||
meta,
|
meta,
|
||||||
buffer,
|
buffer,
|
||||||
output_values,
|
output_values,
|
||||||
output_ok_packet
|
output_ok_packet
|
||||||
)(error_code(), false);
|
)(error_code(), false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/asio/unyield.hpp>
|
#include <boost/asio/unyield.hpp>
|
||||||
|
@ -17,11 +17,11 @@ namespace detail {
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void prepare_statement(
|
void prepare_statement(
|
||||||
::boost::mysql::detail::channel<StreamType>& channel,
|
::boost::mysql::detail::channel<StreamType>& channel,
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info,
|
error_info& info,
|
||||||
prepared_statement<StreamType>& output
|
prepared_statement<StreamType>& output
|
||||||
);
|
);
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
@ -30,10 +30,10 @@ using prepare_statement_signature = void(error_code, prepared_statement<StreamTy
|
|||||||
template <typename StreamType, typename CompletionToken>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature<StreamType>)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, prepare_statement_signature<StreamType>)
|
||||||
async_prepare_statement(
|
async_prepare_statement(
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -19,21 +19,21 @@ namespace detail {
|
|||||||
|
|
||||||
enum class read_row_result
|
enum class read_row_result
|
||||||
{
|
{
|
||||||
error,
|
error,
|
||||||
row,
|
row,
|
||||||
eof
|
eof
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
read_row_result read_row(
|
read_row_result read_row(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
bytestring& buffer,
|
bytestring& buffer,
|
||||||
std::vector<value>& output_values,
|
std::vector<value>& output_values,
|
||||||
ok_packet& output_ok_packet,
|
ok_packet& output_ok_packet,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
);
|
);
|
||||||
|
|
||||||
using read_row_signature = void(error_code, error_info, read_row_result);
|
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>
|
template <typename StreamType, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, read_row_signature)
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, read_row_signature)
|
||||||
async_read_row(
|
async_read_row(
|
||||||
deserialize_row_fn deserializer,
|
deserialize_row_fn deserializer,
|
||||||
channel<StreamType>& channel,
|
channel<StreamType>& channel,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
bytestring& buffer,
|
bytestring& buffer,
|
||||||
std::vector<value>& output_values,
|
std::vector<value>& output_values,
|
||||||
ok_packet& output_ok_packet,
|
ok_packet& output_ok_packet,
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,15 +19,15 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline errc deserialize_binary_value(
|
inline errc deserialize_binary_value(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
const field_metadata& meta,
|
const field_metadata& meta,
|
||||||
value& output
|
value& output
|
||||||
);
|
);
|
||||||
|
|
||||||
inline error_code deserialize_binary_row(
|
inline error_code deserialize_binary_row(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
std::vector<value>& output
|
std::vector<value>& output
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -46,17 +46,17 @@ constexpr std::uint32_t CLIENT_REMEMBER_OPTIONS = (1UL << 31); // Don't reset th
|
|||||||
|
|
||||||
class capabilities
|
class capabilities
|
||||||
{
|
{
|
||||||
std::uint32_t value_;
|
std::uint32_t value_;
|
||||||
public:
|
public:
|
||||||
constexpr explicit capabilities(std::uint32_t value=0) noexcept : value_(value) {};
|
constexpr explicit capabilities(std::uint32_t value=0) noexcept : value_(value) {};
|
||||||
constexpr std::uint32_t get() const noexcept { return value_; }
|
constexpr std::uint32_t get() const noexcept { return value_; }
|
||||||
constexpr void set(std::uint32_t value) noexcept { value_ = 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(std::uint32_t cap) const noexcept { return value_ & cap; }
|
||||||
constexpr bool has_all(capabilities other) const noexcept { return (value_ & other.get()) == other.get(); }
|
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 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 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 {
|
constexpr capabilities mandatory_capabilities {
|
||||||
CLIENT_PROTOCOL_41 |
|
CLIENT_PROTOCOL_41 |
|
||||||
CLIENT_PLUGIN_AUTH |
|
CLIENT_PLUGIN_AUTH |
|
||||||
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
|
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA |
|
||||||
CLIENT_DEPRECATE_EOF |
|
CLIENT_DEPRECATE_EOF |
|
||||||
CLIENT_SECURE_CONNECTION
|
CLIENT_SECURE_CONNECTION
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr capabilities optional_capabilities {0};
|
constexpr capabilities optional_capabilities {0};
|
||||||
|
@ -27,79 +27,79 @@ namespace detail {
|
|||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
class channel
|
class channel
|
||||||
{
|
{
|
||||||
// TODO: static asserts for AsyncStream concept
|
// TODO: static asserts for AsyncStream concept
|
||||||
// TODO: actually we also require it to be SyncStream, name misleading
|
// TODO: actually we also require it to be SyncStream, name misleading
|
||||||
struct ssl_block
|
struct ssl_block
|
||||||
{
|
{
|
||||||
boost::asio::ssl::context ctx;
|
boost::asio::ssl::context ctx;
|
||||||
boost::asio::ssl::stream<AsyncStream&> stream;
|
boost::asio::ssl::stream<AsyncStream&> stream;
|
||||||
|
|
||||||
ssl_block(AsyncStream& base_stream):
|
ssl_block(AsyncStream& base_stream):
|
||||||
ctx(boost::asio::ssl::context::tls_client),
|
ctx(boost::asio::ssl::context::tls_client),
|
||||||
stream (base_stream, ctx) {}
|
stream (base_stream, ctx) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
AsyncStream& stream_;
|
AsyncStream& stream_;
|
||||||
std::optional<ssl_block> ssl_block_;
|
std::optional<ssl_block> ssl_block_;
|
||||||
std::uint8_t sequence_number_ {0};
|
std::uint8_t sequence_number_ {0};
|
||||||
std::array<std::uint8_t, 4> header_buffer_ {}; // for async ops
|
std::array<std::uint8_t, 4> header_buffer_ {}; // for async ops
|
||||||
bytestring shared_buff_; // for async ops
|
bytestring shared_buff_; // for async ops
|
||||||
capabilities current_caps_;
|
capabilities current_caps_;
|
||||||
|
|
||||||
bool process_sequence_number(std::uint8_t got);
|
bool process_sequence_number(std::uint8_t got);
|
||||||
std::uint8_t next_sequence_number() { return sequence_number_++; }
|
std::uint8_t next_sequence_number() { return sequence_number_++; }
|
||||||
|
|
||||||
error_code process_header_read(std::uint32_t& size_to_read); // reads from 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 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>
|
template <typename BufferSeq>
|
||||||
std::size_t read_impl(BufferSeq&& buff, error_code& ec);
|
std::size_t read_impl(BufferSeq&& buff, error_code& ec);
|
||||||
|
|
||||||
template <typename BufferSeq>
|
template <typename BufferSeq>
|
||||||
std::size_t write_impl(BufferSeq&& buff, error_code& ec);
|
std::size_t write_impl(BufferSeq&& buff, error_code& ec);
|
||||||
|
|
||||||
template <typename BufferSeq, typename CompletionToken>
|
template <typename BufferSeq, typename CompletionToken>
|
||||||
auto async_read_impl(BufferSeq&& buff, CompletionToken&& token);
|
auto async_read_impl(BufferSeq&& buff, CompletionToken&& token);
|
||||||
|
|
||||||
template <typename BufferSeq, typename CompletionToken>
|
template <typename BufferSeq, typename CompletionToken>
|
||||||
auto async_write_impl(BufferSeq&& buff, CompletionToken&& token);
|
auto async_write_impl(BufferSeq&& buff, CompletionToken&& token);
|
||||||
public:
|
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>
|
template <typename Allocator>
|
||||||
void read(basic_bytestring<Allocator>& buffer, error_code& code);
|
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>
|
template <typename Allocator, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
|
||||||
async_read(basic_bytestring<Allocator>& buffer, CompletionToken&& token);
|
async_read(basic_bytestring<Allocator>& buffer, CompletionToken&& token);
|
||||||
|
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
|
||||||
async_write(boost::asio::const_buffer buffer, CompletionToken&& token);
|
async_write(boost::asio::const_buffer buffer, CompletionToken&& token);
|
||||||
|
|
||||||
void ssl_handshake(error_code& ec);
|
void ssl_handshake(error_code& ec);
|
||||||
|
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code))
|
||||||
async_ssl_handshake(CompletionToken&& token);
|
async_ssl_handshake(CompletionToken&& token);
|
||||||
|
|
||||||
void reset_sequence_number(std::uint8_t value = 0) { sequence_number_ = value; }
|
void reset_sequence_number(std::uint8_t value = 0) { sequence_number_ = value; }
|
||||||
std::uint8_t sequence_number() const { return sequence_number_; }
|
std::uint8_t sequence_number() const { return sequence_number_; }
|
||||||
|
|
||||||
using stream_type = AsyncStream;
|
using stream_type = AsyncStream;
|
||||||
stream_type& next_layer() { return stream_; }
|
stream_type& next_layer() { return stream_; }
|
||||||
|
|
||||||
capabilities current_capabilities() const noexcept { return current_caps_; }
|
capabilities current_capabilities() const noexcept { return current_caps_; }
|
||||||
void set_current_capabilities(capabilities value) noexcept { current_caps_ = value; }
|
void set_current_capabilities(capabilities value) noexcept { current_caps_ = value; }
|
||||||
|
|
||||||
const bytestring& shared_buffer() const noexcept { return shared_buff_; }
|
const bytestring& shared_buffer() const noexcept { return shared_buff_; }
|
||||||
bytestring& shared_buffer() noexcept { return shared_buff_; }
|
bytestring& shared_buffer() noexcept { return shared_buff_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ChannelType>
|
template <typename ChannelType>
|
||||||
|
@ -20,110 +20,110 @@ namespace detail {
|
|||||||
// header
|
// header
|
||||||
struct packet_header
|
struct packet_header
|
||||||
{
|
{
|
||||||
int3 packet_size;
|
int3 packet_size;
|
||||||
int1 sequence_number;
|
int1 sequence_number;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<packet_header>
|
struct get_struct_fields<packet_header>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&packet_header::packet_size,
|
&packet_header::packet_size,
|
||||||
&packet_header::sequence_number
|
&packet_header::sequence_number
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ok packet
|
// ok packet
|
||||||
struct ok_packet
|
struct ok_packet
|
||||||
{
|
{
|
||||||
// header: int<1> header 0x00 or 0xFE the OK packet header
|
// header: int<1> header 0x00 or 0xFE the OK packet header
|
||||||
int_lenenc affected_rows;
|
int_lenenc affected_rows;
|
||||||
int_lenenc last_insert_id;
|
int_lenenc last_insert_id;
|
||||||
int2 status_flags; // server_status_flags
|
int2 status_flags; // server_status_flags
|
||||||
int2 warnings;
|
int2 warnings;
|
||||||
// TODO: CLIENT_SESSION_TRACK
|
// TODO: CLIENT_SESSION_TRACK
|
||||||
string_lenenc info;
|
string_lenenc info;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<ok_packet>
|
struct get_struct_fields<ok_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&ok_packet::affected_rows,
|
&ok_packet::affected_rows,
|
||||||
&ok_packet::last_insert_id,
|
&ok_packet::last_insert_id,
|
||||||
&ok_packet::status_flags,
|
&ok_packet::status_flags,
|
||||||
&ok_packet::warnings,
|
&ok_packet::warnings,
|
||||||
&ok_packet::info
|
&ok_packet::info
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<ok_packet, serialization_tag::struct_with_fields> :
|
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
|
// err packet
|
||||||
struct err_packet
|
struct err_packet
|
||||||
{
|
{
|
||||||
// int<1> header 0xFF ERR packet header
|
// int<1> header 0xFF ERR packet header
|
||||||
int2 error_code;
|
int2 error_code;
|
||||||
string_fixed<1> sql_state_marker;
|
string_fixed<1> sql_state_marker;
|
||||||
string_fixed<5> sql_state;
|
string_fixed<5> sql_state;
|
||||||
string_eof error_message;
|
string_eof error_message;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<err_packet>
|
struct get_struct_fields<err_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&err_packet::error_code,
|
&err_packet::error_code,
|
||||||
&err_packet::sql_state_marker,
|
&err_packet::sql_state_marker,
|
||||||
&err_packet::sql_state,
|
&err_packet::sql_state,
|
||||||
&err_packet::error_message
|
&err_packet::error_message
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// col def
|
// col def
|
||||||
struct column_definition_packet
|
struct column_definition_packet
|
||||||
{
|
{
|
||||||
string_lenenc catalog; // always "def"
|
string_lenenc catalog; // always "def"
|
||||||
string_lenenc schema;
|
string_lenenc schema;
|
||||||
string_lenenc table; // virtual table
|
string_lenenc table; // virtual table
|
||||||
string_lenenc org_table; // physical table
|
string_lenenc org_table; // physical table
|
||||||
string_lenenc name; // virtual column name
|
string_lenenc name; // virtual column name
|
||||||
string_lenenc org_name; // physical column name
|
string_lenenc org_name; // physical column name
|
||||||
collation character_set;
|
collation character_set;
|
||||||
int4 column_length; // maximum length of the field
|
int4 column_length; // maximum length of the field
|
||||||
protocol_field_type type; // type of the column as defined in enum_field_types
|
protocol_field_type type; // type of the column as defined in enum_field_types
|
||||||
int2 flags; // Flags as defined in Column Definition Flags
|
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
|
int1 decimals; // max shown decimal digits. 0x00 for int/static strings; 0x1f for dynamic strings, double, float
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<column_definition_packet>
|
struct get_struct_fields<column_definition_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&column_definition_packet::catalog,
|
&column_definition_packet::catalog,
|
||||||
&column_definition_packet::schema,
|
&column_definition_packet::schema,
|
||||||
&column_definition_packet::table,
|
&column_definition_packet::table,
|
||||||
&column_definition_packet::org_table,
|
&column_definition_packet::org_table,
|
||||||
&column_definition_packet::name,
|
&column_definition_packet::name,
|
||||||
&column_definition_packet::org_name,
|
&column_definition_packet::org_name,
|
||||||
&column_definition_packet::character_set,
|
&column_definition_packet::character_set,
|
||||||
&column_definition_packet::column_length,
|
&column_definition_packet::column_length,
|
||||||
&column_definition_packet::type,
|
&column_definition_packet::type,
|
||||||
&column_definition_packet::flags,
|
&column_definition_packet::flags,
|
||||||
&column_definition_packet::decimals
|
&column_definition_packet::decimals
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<column_definition_packet, serialization_tag::struct_with_fields> :
|
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
|
// aux
|
||||||
|
@ -16,32 +16,32 @@ namespace detail {
|
|||||||
|
|
||||||
enum class protocol_field_type : std::uint8_t
|
enum class protocol_field_type : std::uint8_t
|
||||||
{
|
{
|
||||||
decimal = 0x00, // Apparently not sent
|
decimal = 0x00, // Apparently not sent
|
||||||
tiny = 0x01, // TINYINT
|
tiny = 0x01, // TINYINT
|
||||||
short_ = 0x02, // SMALLINT
|
short_ = 0x02, // SMALLINT
|
||||||
long_ = 0x03, // INT
|
long_ = 0x03, // INT
|
||||||
float_ = 0x04, // FLOAT
|
float_ = 0x04, // FLOAT
|
||||||
double_ = 0x05, // DOUBLE
|
double_ = 0x05, // DOUBLE
|
||||||
null = 0x06, // Apparently not sent
|
null = 0x06, // Apparently not sent
|
||||||
timestamp = 0x07, // TIMESTAMP
|
timestamp = 0x07, // TIMESTAMP
|
||||||
longlong = 0x08, // BIGINT
|
longlong = 0x08, // BIGINT
|
||||||
int24 = 0x09, // MEDIUMINT
|
int24 = 0x09, // MEDIUMINT
|
||||||
date = 0x0a, // DATE
|
date = 0x0a, // DATE
|
||||||
time = 0x0b, // TIME
|
time = 0x0b, // TIME
|
||||||
datetime = 0x0c, // DATETIME
|
datetime = 0x0c, // DATETIME
|
||||||
year = 0x0d, // YEAR
|
year = 0x0d, // YEAR
|
||||||
varchar = 0x0f, // Apparently not sent
|
varchar = 0x0f, // Apparently not sent
|
||||||
bit = 0x10, // BIT
|
bit = 0x10, // BIT
|
||||||
newdecimal = 0xf6, // DECIMAL
|
newdecimal = 0xf6, // DECIMAL
|
||||||
enum_ = 0xf7, // Apparently not sent
|
enum_ = 0xf7, // Apparently not sent
|
||||||
set = 0xf8, // Apperently not sent
|
set = 0xf8, // Apperently not sent
|
||||||
tiny_blob = 0xf9, // Apparently not sent
|
tiny_blob = 0xf9, // Apparently not sent
|
||||||
medium_blob = 0xfa,// Apparently not sent
|
medium_blob = 0xfa,// Apparently not sent
|
||||||
long_blob = 0xfb, // Apparently not sent
|
long_blob = 0xfb, // Apparently not sent
|
||||||
blob = 0xfc, // Used for all TEXT and BLOB types
|
blob = 0xfc, // Used for all TEXT and BLOB types
|
||||||
var_string = 0xfd, // Used for VARCHAR and VARBINARY
|
var_string = 0xfd, // Used for VARCHAR and VARBINARY
|
||||||
string = 0xfe, // Used for CHAR and BINARY, ENUM (enum flag set), SET (set flag set)
|
string = 0xfe, // Used for CHAR and BINARY, ENUM (enum flag set), SET (set flag set)
|
||||||
geometry = 0xff // GEOMETRY
|
geometry = 0xff // GEOMETRY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,34 +21,34 @@ namespace detail {
|
|||||||
|
|
||||||
class deserialization_context
|
class deserialization_context
|
||||||
{
|
{
|
||||||
const std::uint8_t* first_;
|
const std::uint8_t* first_;
|
||||||
const std::uint8_t* last_;
|
const std::uint8_t* last_;
|
||||||
capabilities capabilities_;
|
capabilities capabilities_;
|
||||||
public:
|
public:
|
||||||
deserialization_context(const std::uint8_t* first, const std::uint8_t* last, capabilities caps) noexcept:
|
deserialization_context(const std::uint8_t* first, const std::uint8_t* last, capabilities caps) noexcept:
|
||||||
first_(first), last_(last), capabilities_(caps) { assert(last_ >= first_); };
|
first_(first), last_(last), capabilities_(caps) { assert(last_ >= first_); };
|
||||||
deserialization_context(boost::asio::const_buffer buff, capabilities caps) noexcept:
|
deserialization_context(boost::asio::const_buffer buff, capabilities caps) noexcept:
|
||||||
deserialization_context(
|
deserialization_context(
|
||||||
static_cast<const std::uint8_t*>(buff.data()),
|
static_cast<const std::uint8_t*>(buff.data()),
|
||||||
static_cast<const std::uint8_t*>(buff.data()) + buff.size(),
|
static_cast<const std::uint8_t*>(buff.data()) + buff.size(),
|
||||||
caps
|
caps
|
||||||
) {};
|
) {};
|
||||||
const std::uint8_t* first() const noexcept { return first_; }
|
const std::uint8_t* first() const noexcept { return first_; }
|
||||||
const std::uint8_t* last() const noexcept { return last_; }
|
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 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 advance(std::size_t sz) noexcept { first_ += sz; assert(last_ >= first_); }
|
||||||
void rewind(std::size_t sz) noexcept { first_ -= sz; }
|
void rewind(std::size_t sz) noexcept { first_ -= sz; }
|
||||||
std::size_t size() const noexcept { return last_ - first_; }
|
std::size_t size() const noexcept { return last_ - first_; }
|
||||||
bool empty() 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; }
|
bool enough_size(std::size_t required_size) const noexcept { return size() >= required_size; }
|
||||||
capabilities get_capabilities() const noexcept { return capabilities_; }
|
capabilities get_capabilities() const noexcept { return capabilities_; }
|
||||||
errc copy(void* to, std::size_t sz) noexcept
|
errc copy(void* to, std::size_t sz) noexcept
|
||||||
{
|
{
|
||||||
if (!enough_size(sz)) return errc::incomplete_message;
|
if (!enough_size(sz)) return errc::incomplete_message;
|
||||||
memcpy(to, first_, sz);
|
memcpy(to, first_, sz);
|
||||||
advance(sz);
|
advance(sz);
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,144 +17,144 @@ namespace detail {
|
|||||||
// initial handshake
|
// initial handshake
|
||||||
struct handshake_packet
|
struct handshake_packet
|
||||||
{
|
{
|
||||||
// int<1> protocol version Always 10
|
// int<1> protocol version Always 10
|
||||||
string_null server_version;
|
string_null server_version;
|
||||||
int4 connection_id;
|
int4 connection_id;
|
||||||
string_lenenc auth_plugin_data; // not an actual protocol field, the merge of two fields
|
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
|
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
|
int1 character_set; // default server a_protocol_character_set, only the lower 8-bits
|
||||||
int2 status_flags; // server_status_flags
|
int2 status_flags; // server_status_flags
|
||||||
string_null auth_plugin_name;
|
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 <>
|
template <>
|
||||||
struct get_struct_fields<handshake_packet>
|
struct get_struct_fields<handshake_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&handshake_packet::server_version,
|
&handshake_packet::server_version,
|
||||||
&handshake_packet::connection_id,
|
&handshake_packet::connection_id,
|
||||||
&handshake_packet::auth_plugin_data,
|
&handshake_packet::auth_plugin_data,
|
||||||
&handshake_packet::capability_falgs,
|
&handshake_packet::capability_falgs,
|
||||||
&handshake_packet::character_set,
|
&handshake_packet::character_set,
|
||||||
&handshake_packet::status_flags,
|
&handshake_packet::status_flags,
|
||||||
&handshake_packet::auth_plugin_name
|
&handshake_packet::auth_plugin_name
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<handshake_packet, serialization_tag::struct_with_fields> :
|
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
|
// response
|
||||||
struct handshake_response_packet
|
struct handshake_response_packet
|
||||||
{
|
{
|
||||||
int4 client_flag; // capabilities
|
int4 client_flag; // capabilities
|
||||||
int4 max_packet_size;
|
int4 max_packet_size;
|
||||||
int1 character_set;
|
int1 character_set;
|
||||||
// string[23] filler filler to the size of the handhshake response packet. All 0s.
|
// string[23] filler filler to the size of the handhshake response packet. All 0s.
|
||||||
string_null username;
|
string_null username;
|
||||||
string_lenenc auth_response; // we require CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA
|
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 database; // only to be serialized if CLIENT_CONNECT_WITH_DB
|
||||||
string_null client_plugin_name; // we require CLIENT_PLUGIN_AUTH
|
string_null client_plugin_name; // we require CLIENT_PLUGIN_AUTH
|
||||||
// TODO: CLIENT_CONNECT_ATTRS
|
// TODO: CLIENT_CONNECT_ATTRS
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<handshake_response_packet>
|
struct get_struct_fields<handshake_response_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&handshake_response_packet::client_flag,
|
&handshake_response_packet::client_flag,
|
||||||
&handshake_response_packet::max_packet_size,
|
&handshake_response_packet::max_packet_size,
|
||||||
&handshake_response_packet::character_set,
|
&handshake_response_packet::character_set,
|
||||||
&handshake_response_packet::username,
|
&handshake_response_packet::username,
|
||||||
&handshake_response_packet::auth_response,
|
&handshake_response_packet::auth_response,
|
||||||
&handshake_response_packet::database,
|
&handshake_response_packet::database,
|
||||||
&handshake_response_packet::client_plugin_name
|
&handshake_response_packet::client_plugin_name
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<handshake_response_packet, serialization_tag::struct_with_fields> :
|
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 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 void serialize_(const handshake_response_packet& value, serialization_context& ctx) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
// SSL request
|
// SSL request
|
||||||
struct ssl_request
|
struct ssl_request
|
||||||
{
|
{
|
||||||
int4 client_flag;
|
int4 client_flag;
|
||||||
int4 max_packet_size;
|
int4 max_packet_size;
|
||||||
int1 character_set;
|
int1 character_set;
|
||||||
string_fixed<23> filler {};
|
string_fixed<23> filler {};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<ssl_request>
|
struct get_struct_fields<ssl_request>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&ssl_request::client_flag,
|
&ssl_request::client_flag,
|
||||||
&ssl_request::max_packet_size,
|
&ssl_request::max_packet_size,
|
||||||
&ssl_request::character_set,
|
&ssl_request::character_set,
|
||||||
&ssl_request::filler
|
&ssl_request::filler
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// auth switch request
|
// auth switch request
|
||||||
struct auth_switch_request_packet
|
struct auth_switch_request_packet
|
||||||
{
|
{
|
||||||
string_null plugin_name;
|
string_null plugin_name;
|
||||||
string_eof auth_plugin_data;
|
string_eof auth_plugin_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<auth_switch_request_packet>
|
struct get_struct_fields<auth_switch_request_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&auth_switch_request_packet::plugin_name,
|
&auth_switch_request_packet::plugin_name,
|
||||||
&auth_switch_request_packet::auth_plugin_data
|
&auth_switch_request_packet::auth_plugin_data
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// response
|
// response
|
||||||
struct auth_switch_response_packet
|
struct auth_switch_response_packet
|
||||||
{
|
{
|
||||||
string_eof auth_plugin_data;
|
string_eof auth_plugin_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<auth_switch_response_packet>
|
struct get_struct_fields<auth_switch_response_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&auth_switch_response_packet::auth_plugin_data
|
&auth_switch_response_packet::auth_plugin_data
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<auth_switch_request_packet, serialization_tag::struct_with_fields> :
|
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)
|
// more data (like auth switch request, but for the same plugin)
|
||||||
struct auth_more_data_packet
|
struct auth_more_data_packet
|
||||||
{
|
{
|
||||||
string_eof auth_plugin_data;
|
string_eof auth_plugin_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<auth_more_data_packet>
|
struct get_struct_fields<auth_more_data_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&auth_more_data_packet::auth_plugin_data
|
&auth_more_data_packet::auth_plugin_data
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,58 +18,58 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
using binary_protocol_value = std::variant<
|
using binary_protocol_value = std::variant<
|
||||||
int1,
|
int1,
|
||||||
int2,
|
int2,
|
||||||
int4,
|
int4,
|
||||||
int8,
|
int8,
|
||||||
int1_signed,
|
int1_signed,
|
||||||
int2_signed,
|
int2_signed,
|
||||||
int4_signed,
|
int4_signed,
|
||||||
int8_signed,
|
int8_signed,
|
||||||
string_lenenc,
|
string_lenenc,
|
||||||
float,
|
float,
|
||||||
double,
|
double,
|
||||||
date,
|
date,
|
||||||
datetime,
|
datetime,
|
||||||
time
|
time
|
||||||
>;
|
>;
|
||||||
|
|
||||||
template <typename SignedType, typename UnsignedType>
|
template <typename SignedType, typename UnsignedType>
|
||||||
binary_protocol_value get_int_deserializable_type(
|
binary_protocol_value get_int_deserializable_type(
|
||||||
const field_metadata& meta
|
const field_metadata& meta
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return meta.is_unsigned() ? binary_protocol_value(UnsignedType()) :
|
return meta.is_unsigned() ? binary_protocol_value(UnsignedType()) :
|
||||||
binary_protocol_value(SignedType());
|
binary_protocol_value(SignedType());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline binary_protocol_value get_deserializable_type(
|
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:
|
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::short_:
|
||||||
case protocol_field_type::year:
|
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::int24:
|
||||||
case protocol_field_type::long_:
|
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:
|
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_:
|
case protocol_field_type::float_:
|
||||||
return float();
|
return float();
|
||||||
case protocol_field_type::double_:
|
case protocol_field_type::double_:
|
||||||
return double();
|
return double();
|
||||||
case protocol_field_type::timestamp:
|
case protocol_field_type::timestamp:
|
||||||
case protocol_field_type::datetime:
|
case protocol_field_type::datetime:
|
||||||
return datetime();
|
return datetime();
|
||||||
case protocol_field_type::date:
|
case protocol_field_type::date:
|
||||||
return date();
|
return date();
|
||||||
case protocol_field_type::time:
|
case protocol_field_type::time:
|
||||||
return time();
|
return time();
|
||||||
// True string types
|
// True string types
|
||||||
case protocol_field_type::varchar:
|
case protocol_field_type::varchar:
|
||||||
case protocol_field_type::var_string:
|
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::newdecimal:
|
||||||
case protocol_field_type::geometry:
|
case protocol_field_type::geometry:
|
||||||
default:
|
default:
|
||||||
return string_lenenc();
|
return string_lenenc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -95,76 +95,76 @@ inline binary_protocol_value get_deserializable_type(
|
|||||||
} // boost
|
} // boost
|
||||||
|
|
||||||
inline boost::mysql::errc boost::mysql::detail::deserialize_binary_value(
|
inline boost::mysql::errc boost::mysql::detail::deserialize_binary_value(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
const field_metadata& meta,
|
const field_metadata& meta,
|
||||||
value& output
|
value& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto protocol_value = get_deserializable_type(meta);
|
auto protocol_value = get_deserializable_type(meta);
|
||||||
return std::visit([&output, &ctx](auto typed_protocol_value) {
|
return std::visit([&output, &ctx](auto typed_protocol_value) {
|
||||||
using type = decltype(typed_protocol_value);
|
using type = decltype(typed_protocol_value);
|
||||||
auto err = deserialize(typed_protocol_value, ctx);
|
auto err = deserialize(typed_protocol_value, ctx);
|
||||||
if (err == errc::ok)
|
if (err == errc::ok)
|
||||||
{
|
{
|
||||||
if constexpr (std::is_constructible_v<value, type>) // not a value holder
|
if constexpr (std::is_constructible_v<value, type>) // not a value holder
|
||||||
{
|
{
|
||||||
output = typed_protocol_value;
|
output = typed_protocol_value;
|
||||||
}
|
}
|
||||||
else if constexpr (is_one_of_v<type, int1, int2>)
|
else if constexpr (is_one_of_v<type, int1, int2>)
|
||||||
{
|
{
|
||||||
// regular promotion would make this int32_t. Force it be uint32_t
|
// regular promotion would make this int32_t. Force it be uint32_t
|
||||||
// TODO: check here
|
// TODO: check here
|
||||||
output = std::uint32_t(typed_protocol_value.value);
|
output = std::uint32_t(typed_protocol_value.value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
output = typed_protocol_value.value;
|
output = typed_protocol_value.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}, protocol_value);
|
}, protocol_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::error_code boost::mysql::detail::deserialize_binary_row(
|
inline boost::mysql::error_code boost::mysql::detail::deserialize_binary_row(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
std::vector<value>& output
|
std::vector<value>& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Skip packet header (it is not part of the message in the binary
|
// 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)
|
// 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
|
// The caller will have checked we have this byte already for us
|
||||||
assert(ctx.enough_size(1));
|
assert(ctx.enough_size(1));
|
||||||
ctx.advance(1);
|
ctx.advance(1);
|
||||||
|
|
||||||
// Number of fields
|
// Number of fields
|
||||||
auto num_fields = meta.size();
|
auto num_fields = meta.size();
|
||||||
output.resize(num_fields);
|
output.resize(num_fields);
|
||||||
|
|
||||||
// Null bitmap
|
// Null bitmap
|
||||||
null_bitmap_traits null_bitmap (binary_row_null_bitmap_offset, num_fields);
|
null_bitmap_traits null_bitmap (binary_row_null_bitmap_offset, num_fields);
|
||||||
const std::uint8_t* null_bitmap_begin = ctx.first();
|
const std::uint8_t* null_bitmap_begin = ctx.first();
|
||||||
if (!ctx.enough_size(null_bitmap.byte_count())) return make_error_code(errc::incomplete_message);
|
if (!ctx.enough_size(null_bitmap.byte_count())) return make_error_code(errc::incomplete_message);
|
||||||
ctx.advance(null_bitmap.byte_count());
|
ctx.advance(null_bitmap.byte_count());
|
||||||
|
|
||||||
// Actual values
|
// Actual values
|
||||||
for (std::vector<value>::size_type i = 0; i < output.size(); ++i)
|
for (std::vector<value>::size_type i = 0; i < output.size(); ++i)
|
||||||
{
|
{
|
||||||
if (null_bitmap.is_null(null_bitmap_begin, i))
|
if (null_bitmap.is_null(null_bitmap_begin, i))
|
||||||
{
|
{
|
||||||
output[i] = nullptr;
|
output[i] = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto err = deserialize_binary_value(ctx, meta[i], output[i]);
|
auto err = deserialize_binary_value(ctx, meta[i], output[i]);
|
||||||
if (err != errc::ok) return make_error_code(err);
|
if (err != errc::ok) return make_error_code(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for remaining bytes
|
// Check for remaining bytes
|
||||||
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
|
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
|
||||||
|
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,14 +20,14 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline std::uint32_t compute_size_to_write(
|
inline std::uint32_t compute_size_to_write(
|
||||||
std::size_t buffer_size,
|
std::size_t buffer_size,
|
||||||
std::size_t transferred_size
|
std::size_t transferred_size
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return static_cast<std::uint32_t>(std::min(
|
return static_cast<std::uint32_t>(std::min(
|
||||||
MAX_PACKET_SIZE,
|
MAX_PACKET_SIZE,
|
||||||
buffer_size - transferred_size
|
buffer_size - transferred_size
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -36,194 +36,194 @@ inline std::uint32_t compute_size_to_write(
|
|||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
bool boost::mysql::detail::channel<AsyncStream>::process_sequence_number(
|
bool boost::mysql::detail::channel<AsyncStream>::process_sequence_number(
|
||||||
std::uint8_t got
|
std::uint8_t got
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (got == sequence_number_)
|
if (got == sequence_number_)
|
||||||
{
|
{
|
||||||
++sequence_number_;
|
++sequence_number_;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
boost::mysql::error_code boost::mysql::detail::channel<AsyncStream>::process_header_read(
|
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;
|
packet_header header;
|
||||||
deserialization_context ctx (boost::asio::buffer(header_buffer_), capabilities(0)); // unaffected by capabilities
|
deserialization_context ctx (boost::asio::buffer(header_buffer_), capabilities(0)); // unaffected by capabilities
|
||||||
[[maybe_unused]] errc err = deserialize(header, ctx);
|
[[maybe_unused]] errc err = deserialize(header, ctx);
|
||||||
assert(err == errc::ok); // this should always succeed
|
assert(err == errc::ok); // this should always succeed
|
||||||
if (!process_sequence_number(header.sequence_number.value))
|
if (!process_sequence_number(header.sequence_number.value))
|
||||||
{
|
{
|
||||||
return make_error_code(errc::sequence_number_mismatch);
|
return make_error_code(errc::sequence_number_mismatch);
|
||||||
}
|
}
|
||||||
size_to_read = header.packet_size.value;
|
size_to_read = header.packet_size.value;
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
void boost::mysql::detail::channel<AsyncStream>::process_header_write(
|
void boost::mysql::detail::channel<AsyncStream>::process_header_write(
|
||||||
std::uint32_t size_to_write
|
std::uint32_t size_to_write
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
packet_header header;
|
packet_header header;
|
||||||
header.packet_size.value = size_to_write;
|
header.packet_size.value = size_to_write;
|
||||||
header.sequence_number.value = next_sequence_number();
|
header.sequence_number.value = next_sequence_number();
|
||||||
serialization_context ctx (capabilities(0), header_buffer_.data()); // capabilities not relevant here
|
serialization_context ctx (capabilities(0), header_buffer_.data()); // capabilities not relevant here
|
||||||
serialize(header, ctx);
|
serialize(header, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
template <typename BufferSeq>
|
template <typename BufferSeq>
|
||||||
std::size_t boost::mysql::detail::channel<AsyncStream>::read_impl(
|
std::size_t boost::mysql::detail::channel<AsyncStream>::read_impl(
|
||||||
BufferSeq&& buff,
|
BufferSeq&& buff,
|
||||||
error_code& ec
|
error_code& ec
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (ssl_active())
|
if (ssl_active())
|
||||||
{
|
{
|
||||||
return boost::asio::read(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
|
return boost::asio::read(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return boost::asio::read(stream_, std::forward<BufferSeq>(buff), ec);
|
return boost::asio::read(stream_, std::forward<BufferSeq>(buff), ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
template <typename BufferSeq>
|
template <typename BufferSeq>
|
||||||
std::size_t boost::mysql::detail::channel<AsyncStream>::write_impl(
|
std::size_t boost::mysql::detail::channel<AsyncStream>::write_impl(
|
||||||
BufferSeq&& buff,
|
BufferSeq&& buff,
|
||||||
error_code& ec
|
error_code& ec
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (ssl_active())
|
if (ssl_active())
|
||||||
{
|
{
|
||||||
return boost::asio::write(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
|
return boost::asio::write(ssl_block_->stream, std::forward<BufferSeq>(buff), ec);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return boost::asio::write(stream_, std::forward<BufferSeq>(buff), ec);
|
return boost::asio::write(stream_, std::forward<BufferSeq>(buff), ec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
template <typename BufferSeq, typename CompletionToken>
|
template <typename BufferSeq, typename CompletionToken>
|
||||||
auto boost::mysql::detail::channel<AsyncStream>::async_read_impl(
|
auto boost::mysql::detail::channel<AsyncStream>::async_read_impl(
|
||||||
BufferSeq&& buff,
|
BufferSeq&& buff,
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (ssl_active())
|
if (ssl_active())
|
||||||
{
|
{
|
||||||
return boost::asio::async_read(
|
return boost::asio::async_read(
|
||||||
ssl_block_->stream,
|
ssl_block_->stream,
|
||||||
std::forward<BufferSeq>(buff),
|
std::forward<BufferSeq>(buff),
|
||||||
std::forward<CompletionToken>(token)
|
std::forward<CompletionToken>(token)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return boost::asio::async_read(
|
return boost::asio::async_read(
|
||||||
stream_,
|
stream_,
|
||||||
std::forward<BufferSeq>(buff),
|
std::forward<BufferSeq>(buff),
|
||||||
std::forward<CompletionToken>(token)
|
std::forward<CompletionToken>(token)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
template <typename BufferSeq, typename CompletionToken>
|
template <typename BufferSeq, typename CompletionToken>
|
||||||
auto boost::mysql::detail::channel<AsyncStream>::async_write_impl(
|
auto boost::mysql::detail::channel<AsyncStream>::async_write_impl(
|
||||||
BufferSeq&& buff,
|
BufferSeq&& buff,
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (ssl_active())
|
if (ssl_active())
|
||||||
{
|
{
|
||||||
return boost::asio::async_write(
|
return boost::asio::async_write(
|
||||||
ssl_block_->stream,
|
ssl_block_->stream,
|
||||||
std::forward<BufferSeq>(buff),
|
std::forward<BufferSeq>(buff),
|
||||||
std::forward<CompletionToken>(token)
|
std::forward<CompletionToken>(token)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return boost::asio::async_write(
|
return boost::asio::async_write(
|
||||||
stream_,
|
stream_,
|
||||||
std::forward<BufferSeq>(buff),
|
std::forward<BufferSeq>(buff),
|
||||||
std::forward<CompletionToken>(token)
|
std::forward<CompletionToken>(token)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
template <typename Allocator>
|
template <typename Allocator>
|
||||||
void boost::mysql::detail::channel<AsyncStream>::read(
|
void boost::mysql::detail::channel<AsyncStream>::read(
|
||||||
basic_bytestring<Allocator>& buffer,
|
basic_bytestring<Allocator>& buffer,
|
||||||
error_code& code
|
error_code& code
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::size_t transferred_size = 0;
|
std::size_t transferred_size = 0;
|
||||||
std::uint32_t size_to_read = 0;
|
std::uint32_t size_to_read = 0;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
code.clear();
|
code.clear();
|
||||||
auto header_buff = boost::asio::buffer(header_buffer_);
|
auto header_buff = boost::asio::buffer(header_buffer_);
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// Read header
|
// Read header
|
||||||
read_impl(header_buff, code);
|
read_impl(header_buff, code);
|
||||||
valgrind_make_mem_defined(header_buff);
|
valgrind_make_mem_defined(header_buff);
|
||||||
if (code) return;
|
if (code) return;
|
||||||
|
|
||||||
// See how many bytes we should be reading
|
// See how many bytes we should be reading
|
||||||
code = process_header_read(size_to_read);
|
code = process_header_read(size_to_read);
|
||||||
if (code) return;
|
if (code) return;
|
||||||
|
|
||||||
// Read the rest of the message
|
// Read the rest of the message
|
||||||
buffer.resize(buffer.size() + size_to_read);
|
buffer.resize(buffer.size() + size_to_read);
|
||||||
auto read_buffer = boost::asio::buffer(buffer.data() + transferred_size, size_to_read);
|
auto read_buffer = boost::asio::buffer(buffer.data() + transferred_size, size_to_read);
|
||||||
read_impl(read_buffer, code);
|
read_impl(read_buffer, code);
|
||||||
valgrind_make_mem_defined(read_buffer);
|
valgrind_make_mem_defined(read_buffer);
|
||||||
if (code) return;
|
if (code) return;
|
||||||
transferred_size += size_to_read;
|
transferred_size += size_to_read;
|
||||||
|
|
||||||
} while (size_to_read == MAX_PACKET_SIZE);
|
} while (size_to_read == MAX_PACKET_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
void boost::mysql::detail::channel<AsyncStream>::write(
|
void boost::mysql::detail::channel<AsyncStream>::write(
|
||||||
boost::asio::const_buffer buffer,
|
boost::asio::const_buffer buffer,
|
||||||
error_code& code
|
error_code& code
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::size_t transferred_size = 0;
|
std::size_t transferred_size = 0;
|
||||||
auto bufsize = buffer.size();
|
auto bufsize = buffer.size();
|
||||||
auto first = static_cast<const std::uint8_t*>(buffer.data());
|
auto first = static_cast<const std::uint8_t*>(buffer.data());
|
||||||
|
|
||||||
// If the packet is empty, we should still write the header, saying
|
// If the packet is empty, we should still write the header, saying
|
||||||
// we are sending an empty packet.
|
// we are sending an empty packet.
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
auto size_to_write = compute_size_to_write(bufsize, transferred_size);
|
auto size_to_write = compute_size_to_write(bufsize, transferred_size);
|
||||||
process_header_write(size_to_write);
|
process_header_write(size_to_write);
|
||||||
write_impl(
|
write_impl(
|
||||||
std::array<boost::asio::const_buffer, 2> {
|
std::array<boost::asio::const_buffer, 2> {
|
||||||
boost::asio::buffer(header_buffer_),
|
boost::asio::buffer(header_buffer_),
|
||||||
boost::asio::buffer(first + transferred_size, size_to_write)
|
boost::asio::buffer(first + transferred_size, size_to_write)
|
||||||
},
|
},
|
||||||
code
|
code
|
||||||
);
|
);
|
||||||
if (code) return;
|
if (code) return;
|
||||||
transferred_size += size_to_write;
|
transferred_size += size_to_write;
|
||||||
} while (transferred_size < bufsize);
|
} while (transferred_size < bufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -231,185 +231,185 @@ template <typename AsyncStream>
|
|||||||
template <typename Allocator, typename CompletionToken>
|
template <typename Allocator, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||||
boost::mysql::detail::channel<AsyncStream>::async_read(
|
boost::mysql::detail::channel<AsyncStream>::async_read(
|
||||||
basic_bytestring<Allocator>& buffer,
|
basic_bytestring<Allocator>& buffer,
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using HandlerSignature = void(mysql::error_code);
|
using HandlerSignature = void(mysql::error_code);
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename AsyncStream::executor_type>;
|
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
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
channel<AsyncStream>& stream_;
|
channel<AsyncStream>& stream_;
|
||||||
basic_bytestring<Allocator>& buffer_;
|
basic_bytestring<Allocator>& buffer_;
|
||||||
std::size_t total_transferred_size_ = 0;
|
std::size_t total_transferred_size_ = 0;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
channel<AsyncStream>& stream,
|
channel<AsyncStream>& stream,
|
||||||
basic_bytestring<Allocator>& buffer
|
basic_bytestring<Allocator>& buffer
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), stream.next_layer().get_executor()),
|
BaseType(std::move(handler), stream.next_layer().get_executor()),
|
||||||
stream_(stream),
|
stream_(stream),
|
||||||
buffer_(buffer)
|
buffer_(buffer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code code,
|
error_code code,
|
||||||
std::size_t bytes_transferred,
|
std::size_t bytes_transferred,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Error checking
|
// Error checking
|
||||||
if (code)
|
if (code)
|
||||||
{
|
{
|
||||||
this->complete(cont, code);
|
this->complete(cont, code);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-error path
|
// Non-error path
|
||||||
std::uint32_t size_to_read = 0;
|
std::uint32_t size_to_read = 0;
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
yield stream_.async_read_impl(
|
yield stream_.async_read_impl(
|
||||||
boost::asio::buffer(stream_.header_buffer_),
|
boost::asio::buffer(stream_.header_buffer_),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
valgrind_make_mem_defined(boost::asio::buffer(stream_.header_buffer_));
|
valgrind_make_mem_defined(boost::asio::buffer(stream_.header_buffer_));
|
||||||
|
|
||||||
code = stream_.process_header_read(size_to_read);
|
code = stream_.process_header_read(size_to_read);
|
||||||
if (code)
|
if (code)
|
||||||
{
|
{
|
||||||
this->complete(cont, code);
|
this->complete(cont, code);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_.resize(buffer_.size() + size_to_read);
|
buffer_.resize(buffer_.size() + size_to_read);
|
||||||
|
|
||||||
yield stream_.async_read_impl(
|
yield stream_.async_read_impl(
|
||||||
boost::asio::buffer(buffer_.data() + total_transferred_size_, size_to_read),
|
boost::asio::buffer(buffer_.data() + total_transferred_size_, size_to_read),
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
valgrind_make_mem_defined(
|
valgrind_make_mem_defined(
|
||||||
boost::asio::buffer(buffer_.data() + total_transferred_size_, bytes_transferred)
|
boost::asio::buffer(buffer_.data() + total_transferred_size_, bytes_transferred)
|
||||||
);
|
);
|
||||||
|
|
||||||
total_transferred_size_ += bytes_transferred;
|
total_transferred_size_ += bytes_transferred;
|
||||||
} while (bytes_transferred == MAX_PACKET_SIZE);
|
} while (bytes_transferred == MAX_PACKET_SIZE);
|
||||||
|
|
||||||
this->complete(cont, error_code());
|
this->complete(cont, error_code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
|
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename AsyncStream>
|
template <typename AsyncStream>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||||
boost::mysql::detail::channel<AsyncStream>::async_write(
|
boost::mysql::detail::channel<AsyncStream>::async_write(
|
||||||
boost::asio::const_buffer buffer,
|
boost::asio::const_buffer buffer,
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using HandlerSignature = void(mysql::error_code);
|
using HandlerSignature = void(mysql::error_code);
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename AsyncStream::executor_type>;
|
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
|
struct Op : BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
channel<AsyncStream>& stream_;
|
channel<AsyncStream>& stream_;
|
||||||
boost::asio::const_buffer buffer_;
|
boost::asio::const_buffer buffer_;
|
||||||
std::size_t total_transferred_size_ = 0;
|
std::size_t total_transferred_size_ = 0;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
channel<AsyncStream>& stream,
|
channel<AsyncStream>& stream,
|
||||||
boost::asio::const_buffer buffer
|
boost::asio::const_buffer buffer
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), stream.next_layer().get_executor()),
|
BaseType(std::move(handler), stream.next_layer().get_executor()),
|
||||||
stream_(stream),
|
stream_(stream),
|
||||||
buffer_(buffer)
|
buffer_(buffer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code code,
|
error_code code,
|
||||||
std::size_t bytes_transferred,
|
std::size_t bytes_transferred,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Error handling
|
// Error handling
|
||||||
if (code)
|
if (code)
|
||||||
{
|
{
|
||||||
this->complete(cont, code);
|
this->complete(cont, code);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-error path
|
// Non-error path
|
||||||
std::uint32_t size_to_write;
|
std::uint32_t size_to_write;
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
// Force write the packet header on an empty packet, at least.
|
// Force write the packet header on an empty packet, at least.
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
size_to_write = compute_size_to_write(buffer_.size(), total_transferred_size_);
|
size_to_write = compute_size_to_write(buffer_.size(), total_transferred_size_);
|
||||||
stream_.process_header_write(size_to_write);
|
stream_.process_header_write(size_to_write);
|
||||||
|
|
||||||
yield stream_.async_write_impl(
|
yield stream_.async_write_impl(
|
||||||
std::array<boost::asio::const_buffer, 2> {
|
std::array<boost::asio::const_buffer, 2> {
|
||||||
boost::asio::buffer(stream_.header_buffer_),
|
boost::asio::buffer(stream_.header_buffer_),
|
||||||
boost::asio::buffer(buffer_ + total_transferred_size_, size_to_write)
|
boost::asio::buffer(buffer_ + total_transferred_size_, size_to_write)
|
||||||
},
|
},
|
||||||
std::move(*this)
|
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);
|
Op(std::move(initiator.completion_handler), *this, buffer)(error_code(), 0, false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
void boost::mysql::detail::channel<Stream>::ssl_handshake(
|
void boost::mysql::detail::channel<Stream>::ssl_handshake(
|
||||||
error_code& ec
|
error_code& ec
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
create_ssl_block();
|
create_ssl_block();
|
||||||
ssl_block_->stream.handshake(boost::asio::ssl::stream_base::client, ec);
|
ssl_block_->stream.handshake(boost::asio::ssl::stream_base::client, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::mysql::error_code))
|
||||||
boost::mysql::detail::channel<Stream>::async_ssl_handshake(
|
boost::mysql::detail::channel<Stream>::async_ssl_handshake(
|
||||||
CompletionToken&& token
|
CompletionToken&& token
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
create_ssl_block();
|
create_ssl_block();
|
||||||
return ssl_block_->stream.async_handshake(
|
return ssl_block_->stream.async_handshake(
|
||||||
boost::asio::ssl::stream_base::client,
|
boost::asio::ssl::stream_base::client,
|
||||||
std::forward<CompletionToken>(token)
|
std::forward<CompletionToken>(token)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <boost/asio/unyield.hpp>
|
#include <boost/asio/unyield.hpp>
|
||||||
|
@ -10,68 +10,68 @@
|
|||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::ok_packet,
|
boost::mysql::detail::ok_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
ok_packet& output,
|
ok_packet& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto err = deserialize_fields(
|
auto err = deserialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
output.affected_rows,
|
output.affected_rows,
|
||||||
output.last_insert_id,
|
output.last_insert_id,
|
||||||
output.status_flags,
|
output.status_flags,
|
||||||
output.warnings
|
output.warnings
|
||||||
);
|
);
|
||||||
if (err == errc::ok && ctx.enough_size(1)) // message is optional, may be omitted
|
if (err == errc::ok && ctx.enough_size(1)) // message is optional, may be omitted
|
||||||
{
|
{
|
||||||
err = deserialize(output.info, ctx);
|
err = deserialize(output.info, ctx);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::column_definition_packet,
|
boost::mysql::detail::column_definition_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
column_definition_packet& output,
|
column_definition_packet& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int_lenenc length_of_fixed_fields;
|
int_lenenc length_of_fixed_fields;
|
||||||
int2 final_padding;
|
int2 final_padding;
|
||||||
return deserialize_fields(
|
return deserialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
output.catalog,
|
output.catalog,
|
||||||
output.schema,
|
output.schema,
|
||||||
output.table,
|
output.table,
|
||||||
output.org_table,
|
output.org_table,
|
||||||
output.name,
|
output.name,
|
||||||
output.org_name,
|
output.org_name,
|
||||||
length_of_fixed_fields,
|
length_of_fixed_fields,
|
||||||
output.character_set,
|
output.character_set,
|
||||||
output.column_length,
|
output.column_length,
|
||||||
output.type,
|
output.type,
|
||||||
output.flags,
|
output.flags,
|
||||||
output.decimals,
|
output.decimals,
|
||||||
final_padding
|
final_padding
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::error_code boost::mysql::detail::process_error_packet(
|
inline boost::mysql::error_code boost::mysql::detail::process_error_packet(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
err_packet error_packet;
|
err_packet error_packet;
|
||||||
auto code = deserialize_message(error_packet, ctx);
|
auto code = deserialize_message(error_packet, ctx);
|
||||||
if (code) return code;
|
if (code) return code;
|
||||||
info.set_message(std::string(error_packet.error_message.value));
|
info.set_message(std::string(error_packet.error_message.value));
|
||||||
return make_error_code(static_cast<errc>(error_packet.error_code.value));
|
return make_error_code(static_cast<errc>(error_packet.error_code.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,137 +11,137 @@
|
|||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::handshake_packet,
|
boost::mysql::detail::handshake_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
handshake_packet& output,
|
handshake_packet& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
constexpr std::uint8_t auth1_length = 8;
|
constexpr std::uint8_t auth1_length = 8;
|
||||||
string_fixed<auth1_length> auth_plugin_data_part_1;
|
string_fixed<auth1_length> auth_plugin_data_part_1;
|
||||||
string_fixed<2> capability_flags_low;
|
string_fixed<2> capability_flags_low;
|
||||||
string_fixed<2> capability_flags_high;
|
string_fixed<2> capability_flags_high;
|
||||||
int1 filler; // should be 0
|
int1 filler; // should be 0
|
||||||
int1 auth_plugin_data_len; // TODO: double check this
|
int1 auth_plugin_data_len; // TODO: double check this
|
||||||
string_fixed<10> reserved;
|
string_fixed<10> reserved;
|
||||||
|
|
||||||
auto err = deserialize_fields(
|
auto err = deserialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
output.server_version,
|
output.server_version,
|
||||||
output.connection_id,
|
output.connection_id,
|
||||||
auth_plugin_data_part_1,
|
auth_plugin_data_part_1,
|
||||||
filler, // TODO: docs state fields below the filler are optional
|
filler, // TODO: docs state fields below the filler are optional
|
||||||
capability_flags_low,
|
capability_flags_low,
|
||||||
output.character_set,
|
output.character_set,
|
||||||
output.status_flags,
|
output.status_flags,
|
||||||
capability_flags_high
|
capability_flags_high
|
||||||
);
|
);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
|
|
||||||
// Compose capabilities
|
// Compose capabilities
|
||||||
auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&output.capability_falgs.value);
|
auto capabilities_begin = reinterpret_cast<std::uint8_t*>(&output.capability_falgs.value);
|
||||||
memcpy(capabilities_begin, capability_flags_low.data(), 2);
|
memcpy(capabilities_begin, capability_flags_low.data(), 2);
|
||||||
memcpy(capabilities_begin + 2, capability_flags_high.data(), 2);
|
memcpy(capabilities_begin + 2, capability_flags_high.data(), 2);
|
||||||
boost::endian::little_to_native_inplace(output.capability_falgs.value);
|
boost::endian::little_to_native_inplace(output.capability_falgs.value);
|
||||||
|
|
||||||
// Check minimum server capabilities to deserialize this frame
|
// Check minimum server capabilities to deserialize this frame
|
||||||
capabilities cap (output.capability_falgs.value);
|
capabilities cap (output.capability_falgs.value);
|
||||||
if (!cap.has(CLIENT_PLUGIN_AUTH)) return errc::server_unsupported;
|
if (!cap.has(CLIENT_PLUGIN_AUTH)) return errc::server_unsupported;
|
||||||
|
|
||||||
// Deserialize the rest of the frame
|
// Deserialize the rest of the frame
|
||||||
err = deserialize_fields(
|
err = deserialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
auth_plugin_data_len,
|
auth_plugin_data_len,
|
||||||
reserved
|
reserved
|
||||||
);
|
);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
auto auth2_length = static_cast<std::uint8_t>(
|
auto auth2_length = static_cast<std::uint8_t>(
|
||||||
std::max(13, auth_plugin_data_len.value - auth1_length));
|
std::max(13, auth_plugin_data_len.value - auth1_length));
|
||||||
err = ctx.copy(output.auth_plugin_data_buffer.data() + auth1_length, auth2_length);
|
err = ctx.copy(output.auth_plugin_data_buffer.data() + auth1_length, auth2_length);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
err = deserialize(output.auth_plugin_name, ctx);
|
err = deserialize(output.auth_plugin_name, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
|
|
||||||
// Compose auth_plugin_data
|
// Compose auth_plugin_data
|
||||||
memcpy(
|
memcpy(
|
||||||
output.auth_plugin_data_buffer.data(),
|
output.auth_plugin_data_buffer.data(),
|
||||||
auth_plugin_data_part_1.data(),
|
auth_plugin_data_part_1.data(),
|
||||||
auth1_length
|
auth1_length
|
||||||
);
|
);
|
||||||
output.auth_plugin_data.value = std::string_view(
|
output.auth_plugin_data.value = std::string_view(
|
||||||
output.auth_plugin_data_buffer.data(),
|
output.auth_plugin_data_buffer.data(),
|
||||||
auth1_length + auth2_length - 1 // discard trailing null byte
|
auth1_length + auth2_length - 1 // discard trailing null byte
|
||||||
);
|
);
|
||||||
|
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t
|
std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::handshake_response_packet,
|
boost::mysql::detail::handshake_response_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::get_size_(
|
>::get_size_(
|
||||||
const handshake_response_packet& value,
|
const handshake_response_packet& value,
|
||||||
const serialization_context& ctx
|
const serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
std::size_t res =
|
std::size_t res =
|
||||||
get_size(value.client_flag, ctx) +
|
get_size(value.client_flag, ctx) +
|
||||||
get_size(value.max_packet_size, ctx) +
|
get_size(value.max_packet_size, ctx) +
|
||||||
get_size(value.character_set, ctx) +
|
get_size(value.character_set, ctx) +
|
||||||
23 + // filler
|
23 + // filler
|
||||||
get_size(value.username, ctx) +
|
get_size(value.username, ctx) +
|
||||||
get_size(value.auth_response, ctx);
|
get_size(value.auth_response, ctx);
|
||||||
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
|
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
|
||||||
{
|
{
|
||||||
res += get_size(value.database, ctx);
|
res += get_size(value.database, ctx);
|
||||||
}
|
}
|
||||||
res += get_size(value.client_plugin_name, ctx);
|
res += get_size(value.client_plugin_name, ctx);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::handshake_response_packet,
|
boost::mysql::detail::handshake_response_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
const handshake_response_packet& value,
|
const handshake_response_packet& value,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
serialize(value.client_flag, ctx);
|
serialize(value.client_flag, ctx);
|
||||||
serialize(value.max_packet_size, ctx);
|
serialize(value.max_packet_size, ctx);
|
||||||
serialize(value.character_set, ctx);
|
serialize(value.character_set, ctx);
|
||||||
std::uint8_t buffer [23] {};
|
std::uint8_t buffer [23] {};
|
||||||
ctx.write(buffer, sizeof(buffer));
|
ctx.write(buffer, sizeof(buffer));
|
||||||
serialize(value.username, ctx);
|
serialize(value.username, ctx);
|
||||||
serialize(value.auth_response, ctx);
|
serialize(value.auth_response, ctx);
|
||||||
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
|
if (ctx.get_capabilities().has(CLIENT_CONNECT_WITH_DB))
|
||||||
{
|
{
|
||||||
serialize(value.database, ctx);
|
serialize(value.database, ctx);
|
||||||
}
|
}
|
||||||
serialize(value.client_plugin_name, ctx);
|
serialize(value.client_plugin_name, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::auth_switch_request_packet,
|
boost::mysql::detail::auth_switch_request_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
auth_switch_request_packet& output,
|
auth_switch_request_packet& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
auto err = deserialize_fields(ctx, output.plugin_name, output.auth_plugin_data);
|
auto err = deserialize_fields(ctx, output.plugin_name, output.auth_plugin_data);
|
||||||
auto& auth_data = output.auth_plugin_data.value;
|
auto& auth_data = output.auth_plugin_data.value;
|
||||||
// Discard an additional NULL at the end of auth data
|
// Discard an additional NULL at the end of auth data
|
||||||
if (!auth_data.empty())
|
if (!auth_data.empty())
|
||||||
{
|
{
|
||||||
assert(auth_data.back() == 0);
|
assert(auth_data.back() == 0);
|
||||||
auth_data = auth_data.substr(0, auth_data.size() - 1);
|
auth_data = auth_data.substr(0, auth_data.size() - 1);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,38 +16,38 @@ namespace detail {
|
|||||||
|
|
||||||
// Maps from an actual value to a protocol_field_type. Only value's type is used
|
// Maps from an actual value to a protocol_field_type. Only value's type is used
|
||||||
inline protocol_field_type get_protocol_field_type(
|
inline protocol_field_type get_protocol_field_type(
|
||||||
const value& input
|
const value& input
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
struct visitor
|
struct visitor
|
||||||
{
|
{
|
||||||
constexpr auto operator()(std::int32_t) const noexcept { return protocol_field_type::long_; }
|
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::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::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::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()(std::string_view) const noexcept { return protocol_field_type::varchar; }
|
||||||
constexpr auto operator()(float) const noexcept { return protocol_field_type::float_; }
|
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()(double) const noexcept { return protocol_field_type::double_; }
|
||||||
constexpr auto operator()(date) const noexcept { return protocol_field_type::date; }
|
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()(datetime) const noexcept { return protocol_field_type::datetime; }
|
||||||
constexpr auto operator()(time) const noexcept { return protocol_field_type::time; }
|
constexpr auto operator()(time) const noexcept { return protocol_field_type::time; }
|
||||||
constexpr auto operator()(std::nullptr_t) const noexcept { return protocol_field_type::null; }
|
constexpr auto operator()(std::nullptr_t) const noexcept { return protocol_field_type::null; }
|
||||||
};
|
};
|
||||||
return std::visit(visitor(), input);
|
return std::visit(visitor(), input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether to include the unsigned flag in the statement execute message
|
// Whether to include the unsigned flag in the statement execute message
|
||||||
// for a given value or not. Only value's type is used
|
// for a given value or not. Only value's type is used
|
||||||
inline bool is_unsigned(
|
inline bool is_unsigned(
|
||||||
const value& input
|
const value& input
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
// By default, return false; just for integer types explicitly unsigned return true
|
// By default, return false; just for integer types explicitly unsigned return true
|
||||||
return std::visit([](auto v) {
|
return std::visit([](auto v) {
|
||||||
using type = decltype(v);
|
using type = decltype(v);
|
||||||
return std::is_same_v<type, std::uint32_t> ||
|
return std::is_same_v<type, std::uint32_t> ||
|
||||||
std::is_same_v<type, std::uint64_t>;
|
std::is_same_v<type, std::uint64_t>;
|
||||||
}, input);
|
}, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs a mapping from T to a type that can be serialized
|
// 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
|
// Indicate no serialization is required
|
||||||
struct dummy_serializable
|
struct dummy_serializable
|
||||||
{
|
{
|
||||||
explicit dummy_serializable(std::nullptr_t) {}
|
explicit dummy_serializable(std::nullptr_t) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<dummy_serializable, serialization_tag::none> :
|
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>
|
template <typename T>
|
||||||
inline get_serializable_type_t<T> to_serializable_type(T input) noexcept
|
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(
|
inline std::size_t get_binary_value_size(
|
||||||
const value& input,
|
const value& input,
|
||||||
const serialization_context& ctx
|
const serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
return std::visit([&ctx](const auto& v) {
|
return std::visit([&ctx](const auto& v) {
|
||||||
return get_size(to_serializable_type(v), ctx);
|
return get_size(to_serializable_type(v), ctx);
|
||||||
}, input);
|
}, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void serialize_binary_value(
|
inline void serialize_binary_value(
|
||||||
const value& input,
|
const value& input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
std::visit([&ctx](const auto& v) {
|
std::visit([&ctx](const auto& v) {
|
||||||
serialize(to_serializable_type(v), ctx);
|
serialize(to_serializable_type(v), ctx);
|
||||||
}, input);
|
}, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -108,99 +108,99 @@ inline void serialize_binary_value(
|
|||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::com_stmt_prepare_ok_packet,
|
boost::mysql::detail::com_stmt_prepare_ok_packet,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
com_stmt_prepare_ok_packet& output,
|
com_stmt_prepare_ok_packet& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int1 reserved;
|
int1 reserved;
|
||||||
return deserialize_fields(
|
return deserialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
output.statement_id,
|
output.statement_id,
|
||||||
output.num_columns,
|
output.num_columns,
|
||||||
output.num_params,
|
output.num_params,
|
||||||
reserved,
|
reserved,
|
||||||
output.warning_count
|
output.warning_count
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
inline std::size_t
|
inline std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
|
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::get_size_(
|
>::get_size_(
|
||||||
const com_stmt_execute_packet<ForwardIterator>& value,
|
const com_stmt_execute_packet<ForwardIterator>& value,
|
||||||
const serialization_context& ctx
|
const serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
std::size_t res = 1 + // command ID
|
std::size_t res = 1 + // command ID
|
||||||
get_size(value.statement_id, ctx) +
|
get_size(value.statement_id, ctx) +
|
||||||
get_size(value.flags, ctx) +
|
get_size(value.flags, ctx) +
|
||||||
get_size(value.iteration_count, ctx);
|
get_size(value.iteration_count, ctx);
|
||||||
auto num_params = std::distance(value.params_begin, value.params_end);
|
auto num_params = std::distance(value.params_begin, value.params_end);
|
||||||
assert(num_params >= 0 && num_params <= 255);
|
assert(num_params >= 0 && num_params <= 255);
|
||||||
res += null_bitmap_traits(stmt_execute_null_bitmap_offset, num_params).byte_count();
|
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(value.new_params_bind_flag, ctx);
|
||||||
res += get_size(com_stmt_execute_param_meta_packet{}, ctx) * num_params;
|
res += get_size(com_stmt_execute_param_meta_packet{}, ctx) * num_params;
|
||||||
for (auto it = value.params_begin; it != value.params_end; ++it)
|
for (auto it = value.params_begin; it != value.params_end; ++it)
|
||||||
{
|
{
|
||||||
res += get_binary_value_size(*it, ctx);
|
res += get_binary_value_size(*it, ctx);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
inline void
|
inline void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
|
boost::mysql::detail::com_stmt_execute_packet<ForwardIterator>,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
const com_stmt_execute_packet<ForwardIterator>& input,
|
const com_stmt_execute_packet<ForwardIterator>& input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
serialize(int1(com_stmt_execute_packet<ForwardIterator>::command_id), ctx);
|
serialize(int1(com_stmt_execute_packet<ForwardIterator>::command_id), ctx);
|
||||||
serialize(input.statement_id, ctx);
|
serialize(input.statement_id, ctx);
|
||||||
serialize(input.flags, ctx);
|
serialize(input.flags, ctx);
|
||||||
serialize(input.iteration_count, ctx);
|
serialize(input.iteration_count, ctx);
|
||||||
|
|
||||||
// Number of parameters
|
// Number of parameters
|
||||||
auto num_params = std::distance(input.params_begin, input.params_end);
|
auto num_params = std::distance(input.params_begin, input.params_end);
|
||||||
assert(num_params >= 0 && num_params <= 255);
|
assert(num_params >= 0 && num_params <= 255);
|
||||||
|
|
||||||
// NULL bitmap (already size zero if num_params == 0)
|
// NULL bitmap (already size zero if num_params == 0)
|
||||||
null_bitmap_traits traits (stmt_execute_null_bitmap_offset, num_params);
|
null_bitmap_traits traits (stmt_execute_null_bitmap_offset, num_params);
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
|
std::memset(ctx.first(), 0, traits.byte_count()); // Initialize to zeroes
|
||||||
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
|
for (auto it = input.params_begin; it != input.params_end; ++it, ++i)
|
||||||
{
|
{
|
||||||
if (std::holds_alternative<std::nullptr_t>(*it))
|
if (std::holds_alternative<std::nullptr_t>(*it))
|
||||||
{
|
{
|
||||||
traits.set_null(ctx.first(), i);
|
traits.set_null(ctx.first(), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.advance(traits.byte_count());
|
ctx.advance(traits.byte_count());
|
||||||
|
|
||||||
// new parameters bind flag
|
// new parameters bind flag
|
||||||
serialize(input.new_params_bind_flag, ctx);
|
serialize(input.new_params_bind_flag, ctx);
|
||||||
|
|
||||||
// value metadata
|
// value metadata
|
||||||
com_stmt_execute_param_meta_packet meta;
|
com_stmt_execute_param_meta_packet meta;
|
||||||
for (auto it = input.params_begin; it != input.params_end; ++it)
|
for (auto it = input.params_begin; it != input.params_end; ++it)
|
||||||
{
|
{
|
||||||
meta.type = get_protocol_field_type(*it);
|
meta.type = get_protocol_field_type(*it);
|
||||||
meta.unsigned_flag.value = is_unsigned(*it) ? 0x80 : 0;
|
meta.unsigned_flag.value = is_unsigned(*it) ? 0x80 : 0;
|
||||||
serialize(meta, ctx);
|
serialize(meta, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual values
|
// actual values
|
||||||
for (auto it = input.params_begin; it != input.params_end; ++it)
|
for (auto it = input.params_begin; it != input.params_end; ++it)
|
||||||
{
|
{
|
||||||
serialize_binary_value(*it, ctx);
|
serialize_binary_value(*it, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@ namespace detail {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr std::size_t get_int_size()
|
constexpr std::size_t get_int_size()
|
||||||
{
|
{
|
||||||
static_assert(is_fixed_size_int<T>());
|
static_assert(is_fixed_size_int<T>());
|
||||||
return std::is_same<T, int3>::value ? 3 :
|
return std::is_same<T, int3>::value ? 3 :
|
||||||
std::is_same<T, int6>::value ? 6 : sizeof(T);
|
std::is_same<T, int6>::value ? 6 : sizeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers for structs
|
// Helpers for structs
|
||||||
@ -38,101 +38,101 @@ struct is_command : decltype(is_command_helper::get<T>(nullptr))
|
|||||||
|
|
||||||
template <std::size_t index, typename T>
|
template <std::size_t index, typename T>
|
||||||
errc deserialize_struct(
|
errc deserialize_struct(
|
||||||
[[maybe_unused]] T& output,
|
[[maybe_unused]] T& output,
|
||||||
[[maybe_unused]] deserialization_context& ctx
|
[[maybe_unused]] deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
constexpr auto fields = get_struct_fields<T>::value;
|
constexpr auto fields = get_struct_fields<T>::value;
|
||||||
if constexpr (index == std::tuple_size<decltype(fields)>::value)
|
if constexpr (index == std::tuple_size<decltype(fields)>::value)
|
||||||
{
|
{
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
constexpr auto pmem = std::get<index>(fields);
|
constexpr auto pmem = std::get<index>(fields);
|
||||||
errc err = deserialize(output.*pmem, ctx);
|
errc err = deserialize(output.*pmem, ctx);
|
||||||
if (err != errc::ok)
|
if (err != errc::ok)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return deserialize_struct<index+1>(output, ctx);
|
return deserialize_struct<index+1>(output, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t index, typename T>
|
template <std::size_t index, typename T>
|
||||||
void serialize_struct(
|
void serialize_struct(
|
||||||
[[maybe_unused]] const T& value,
|
[[maybe_unused]] const T& value,
|
||||||
[[maybe_unused]] serialization_context& ctx
|
[[maybe_unused]] serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
constexpr auto fields = get_struct_fields<T>::value;
|
constexpr auto fields = get_struct_fields<T>::value;
|
||||||
if constexpr (index < std::tuple_size<decltype(fields)>::value)
|
if constexpr (index < std::tuple_size<decltype(fields)>::value)
|
||||||
{
|
{
|
||||||
auto pmem = std::get<index>(fields);
|
auto pmem = std::get<index>(fields);
|
||||||
serialize(value.*pmem, ctx);
|
serialize(value.*pmem, ctx);
|
||||||
serialize_struct<index+1>(value, ctx);
|
serialize_struct<index+1>(value, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t index, typename T>
|
template <std::size_t index, typename T>
|
||||||
std::size_t get_size_struct(
|
std::size_t get_size_struct(
|
||||||
[[maybe_unused]] const T& input,
|
[[maybe_unused]] const T& input,
|
||||||
[[maybe_unused]] const serialization_context& ctx
|
[[maybe_unused]] const serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
constexpr auto fields = get_struct_fields<T>::value;
|
constexpr auto fields = get_struct_fields<T>::value;
|
||||||
if constexpr (index == std::tuple_size<decltype(fields)>::value)
|
if constexpr (index == std::tuple_size<decltype(fields)>::value)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
constexpr auto pmem = std::get<index>(fields);
|
constexpr auto pmem = std::get<index>(fields);
|
||||||
return get_size_struct<index+1>(input, ctx) +
|
return get_size_struct<index+1>(input, ctx) +
|
||||||
get_size(input.*pmem, ctx);
|
get_size(input.*pmem, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers for (de)serialize_fields
|
// Helpers for (de)serialize_fields
|
||||||
template <typename FirstType>
|
template <typename FirstType>
|
||||||
errc deserialize_fields_helper(deserialization_context& ctx, FirstType& field) noexcept
|
errc deserialize_fields_helper(deserialization_context& ctx, FirstType& field) noexcept
|
||||||
{
|
{
|
||||||
return deserialize(field, ctx);
|
return deserialize(field, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FirstType, typename... Types>
|
template <typename FirstType, typename... Types>
|
||||||
errc deserialize_fields_helper(
|
errc deserialize_fields_helper(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
FirstType& field,
|
FirstType& field,
|
||||||
Types&... fields_tail
|
Types&... fields_tail
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
errc err = deserialize(field, ctx);
|
errc err = deserialize(field, ctx);
|
||||||
if (err == errc::ok)
|
if (err == errc::ok)
|
||||||
{
|
{
|
||||||
err = deserialize_fields_helper(ctx, fields_tail...);
|
err = deserialize_fields_helper(ctx, fields_tail...);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FirstType>
|
template <typename FirstType>
|
||||||
void serialize_fields_helper(serialization_context& ctx, const FirstType& field) noexcept
|
void serialize_fields_helper(serialization_context& ctx, const FirstType& field) noexcept
|
||||||
{
|
{
|
||||||
serialize(field, ctx);
|
serialize(field, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FirstType, typename... Types>
|
template <typename FirstType, typename... Types>
|
||||||
void serialize_fields_helper(
|
void serialize_fields_helper(
|
||||||
serialization_context& ctx,
|
serialization_context& ctx,
|
||||||
const FirstType& field,
|
const FirstType& field,
|
||||||
const Types&... fields_tail
|
const Types&... fields_tail
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
serialize(field, ctx);
|
serialize(field, ctx);
|
||||||
serialize_fields_helper(ctx, fields_tail...);
|
serialize_fields_helper(ctx, fields_tail...);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -143,149 +143,149 @@ void serialize_fields_helper(
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr bool boost::mysql::detail::is_fixed_size_int()
|
constexpr bool boost::mysql::detail::is_fixed_size_int()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
std::is_integral<get_value_type_t<T>>::value &&
|
std::is_integral<get_value_type_t<T>>::value &&
|
||||||
std::is_base_of<value_holder<get_value_type_t<T>>, T>::value &&
|
std::is_base_of<value_holder<get_value_type_t<T>>, T>::value &&
|
||||||
!std::is_same<T, int_lenenc>::value;
|
!std::is_same<T, int_lenenc>::value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
boost::mysql::errc
|
boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::fixed_size_int
|
boost::mysql::detail::serialization_tag::fixed_size_int
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
T& output,
|
T& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) 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>();
|
constexpr auto size = get_int_size<T>();
|
||||||
if (!ctx.enough_size(size))
|
if (!ctx.enough_size(size))
|
||||||
{
|
{
|
||||||
return errc::incomplete_message;
|
return errc::incomplete_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&output.value, 0, sizeof(output.value));
|
memset(&output.value, 0, sizeof(output.value));
|
||||||
memcpy(&output.value, ctx.first(), size);
|
memcpy(&output.value, ctx.first(), size);
|
||||||
boost::endian::little_to_native_inplace(output);
|
boost::endian::little_to_native_inplace(output);
|
||||||
ctx.advance(size);
|
ctx.advance(size);
|
||||||
|
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void boost::mysql::detail::serialization_traits<
|
void boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::fixed_size_int
|
boost::mysql::detail::serialization_tag::fixed_size_int
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
T input,
|
T input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
boost::endian::native_to_little_inplace(input);
|
boost::endian::native_to_little_inplace(input);
|
||||||
ctx.write(&input.value, get_int_size<T>());
|
ctx.write(&input.value, get_int_size<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr std::size_t boost::mysql::detail::serialization_traits<
|
constexpr std::size_t boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::fixed_size_int
|
boost::mysql::detail::serialization_tag::fixed_size_int
|
||||||
>::get_size_(T, const serialization_context&) noexcept
|
>::get_size_(T, const serialization_context&) noexcept
|
||||||
{
|
{
|
||||||
return get_int_size<T>();
|
return get_int_size<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fixed size strings
|
// Fixed size strings
|
||||||
template <std::size_t N>
|
template <std::size_t N>
|
||||||
boost::mysql::errc boost::mysql::detail::serialization_traits<
|
boost::mysql::errc boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::string_fixed<N>,
|
boost::mysql::detail::string_fixed<N>,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
string_fixed<N>& output,
|
string_fixed<N>& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
if (!ctx.enough_size(N))
|
if (!ctx.enough_size(N))
|
||||||
{
|
{
|
||||||
return errc::incomplete_message;
|
return errc::incomplete_message;
|
||||||
}
|
}
|
||||||
memcpy(output.data(), ctx.first(), N);
|
memcpy(output.data(), ctx.first(), N);
|
||||||
ctx.advance(N);
|
ctx.advance(N);
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enums
|
// Enums
|
||||||
template <typename T>
|
template <typename T>
|
||||||
boost::mysql::errc
|
boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::enumeration
|
boost::mysql::detail::serialization_tag::enumeration
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
T& output,
|
T& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
serializable_type value;
|
serializable_type value;
|
||||||
errc err = deserialize(value, ctx);
|
errc err = deserialize(value, ctx);
|
||||||
if (err != errc::ok)
|
if (err != errc::ok)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
output = static_cast<T>(value.value);
|
output = static_cast<T>(value.value);
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t boost::mysql::detail::serialization_traits<
|
std::size_t boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::enumeration
|
boost::mysql::detail::serialization_tag::enumeration
|
||||||
>::get_size_(T, const serialization_context&) noexcept
|
>::get_size_(T, const serialization_context&) noexcept
|
||||||
{
|
{
|
||||||
return get_int_size<serializable_type>();
|
return get_int_size<serializable_type>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Floating points
|
// Floating points
|
||||||
template <typename T>
|
template <typename T>
|
||||||
boost::mysql::errc
|
boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::floating_point
|
boost::mysql::detail::serialization_tag::floating_point
|
||||||
>::deserialize_(T& output, deserialization_context& ctx) noexcept
|
>::deserialize_(T& output, deserialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
// Size check
|
// Size check
|
||||||
if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message;
|
if (!ctx.enough_size(sizeof(T))) return errc::incomplete_message;
|
||||||
|
|
||||||
// Endianness conversion
|
// Endianness conversion
|
||||||
// Boost.Endian support for floats start at 1.71. TODO: maybe update requirements and CI
|
// Boost.Endian support for floats start at 1.71. TODO: maybe update requirements and CI
|
||||||
#if BOOST_ENDIAN_BIG_BYTE
|
#if BOOST_ENDIAN_BIG_BYTE
|
||||||
char buf [sizeof(T)];
|
char buf [sizeof(T)];
|
||||||
std::memcpy(buf, ctx.first(), sizeof(T));
|
std::memcpy(buf, ctx.first(), sizeof(T));
|
||||||
std::reverse(buf, buf + sizeof(T));
|
std::reverse(buf, buf + sizeof(T));
|
||||||
std::memcpy(&output, buf, sizeof(T));
|
std::memcpy(&output, buf, sizeof(T));
|
||||||
#else
|
#else
|
||||||
std::memcpy(&output, ctx.first(), sizeof(T));
|
std::memcpy(&output, ctx.first(), sizeof(T));
|
||||||
#endif
|
#endif
|
||||||
ctx.advance(sizeof(T));
|
ctx.advance(sizeof(T));
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void
|
void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::floating_point
|
boost::mysql::detail::serialization_tag::floating_point
|
||||||
>::serialize_(T input, serialization_context& ctx) noexcept
|
>::serialize_(T input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
// Endianness conversion
|
// Endianness conversion
|
||||||
#if BOOST_ENDIAN_BIG_BYTE
|
#if BOOST_ENDIAN_BIG_BYTE
|
||||||
char buf [sizeof(T)];
|
char buf [sizeof(T)];
|
||||||
std::memcpy(buf, &input, sizeof(T));
|
std::memcpy(buf, &input, sizeof(T));
|
||||||
std::reverse(buf, buf + sizeof(T));
|
std::reverse(buf, buf + sizeof(T));
|
||||||
ctx.write(buf, sizeof(T));
|
ctx.write(buf, sizeof(T));
|
||||||
#else
|
#else
|
||||||
ctx.write(&input, sizeof(T));
|
ctx.write(&input, sizeof(T));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,110 +293,110 @@ boost::mysql::detail::serialization_traits<
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr bool boost::mysql::detail::is_struct_with_fields()
|
constexpr bool boost::mysql::detail::is_struct_with_fields()
|
||||||
{
|
{
|
||||||
return !std::is_same_v<
|
return !std::is_same_v<
|
||||||
std::decay_t<decltype(get_struct_fields<T>::value)>,
|
std::decay_t<decltype(get_struct_fields<T>::value)>,
|
||||||
not_a_struct_with_fields
|
not_a_struct_with_fields
|
||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
boost::mysql::errc
|
boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::deserialize_(T& output, deserialization_context& ctx) noexcept
|
>::deserialize_(T& output, deserialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
return deserialize_struct<0>(output, ctx);
|
return deserialize_struct<0>(output, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void
|
void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
[[maybe_unused]] const T& input,
|
[[maybe_unused]] const T& input,
|
||||||
[[maybe_unused]] serialization_context& ctx
|
[[maybe_unused]] serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
// For commands, add the command ID. Commands are only sent by the client,
|
// For commands, add the command ID. Commands are only sent by the client,
|
||||||
// so this is not considered in the deserialization functions.
|
// so this is not considered in the deserialization functions.
|
||||||
if constexpr (is_command<T>::value)
|
if constexpr (is_command<T>::value)
|
||||||
{
|
{
|
||||||
serialize(int1(T::command_id), ctx);
|
serialize(int1(T::command_id), ctx);
|
||||||
}
|
}
|
||||||
serialize_struct<0>(input, ctx);
|
serialize_struct<0>(input, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t
|
std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
T,
|
T,
|
||||||
boost::mysql::detail::serialization_tag::struct_with_fields
|
boost::mysql::detail::serialization_tag::struct_with_fields
|
||||||
>::get_size_(const T& input, const serialization_context& ctx) noexcept
|
>::get_size_(const T& input, const serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
std::size_t res = is_command<T>::value ? 1 : 0;
|
std::size_t res = is_command<T>::value ? 1 : 0;
|
||||||
res += get_size_struct<0>(input, ctx);
|
res += get_size_struct<0>(input, ctx);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Miscellanea
|
// Miscellanea
|
||||||
template <typename Serializable, typename Allocator>
|
template <typename Serializable, typename Allocator>
|
||||||
void boost::mysql::detail::serialize_message(
|
void boost::mysql::detail::serialize_message(
|
||||||
const Serializable& input,
|
const Serializable& input,
|
||||||
capabilities caps,
|
capabilities caps,
|
||||||
basic_bytestring<Allocator>& buffer
|
basic_bytestring<Allocator>& buffer
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
serialization_context ctx (caps);
|
serialization_context ctx (caps);
|
||||||
std::size_t size = get_size(input, ctx);
|
std::size_t size = get_size(input, ctx);
|
||||||
buffer.resize(size);
|
buffer.resize(size);
|
||||||
ctx.set_first(buffer.data());
|
ctx.set_first(buffer.data());
|
||||||
serialize(input, ctx);
|
serialize(input, ctx);
|
||||||
assert(ctx.first() == buffer.data() + buffer.size());
|
assert(ctx.first() == buffer.data() + buffer.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Deserializable>
|
template <typename Deserializable>
|
||||||
boost::mysql::error_code boost::mysql::detail::deserialize_message(
|
boost::mysql::error_code boost::mysql::detail::deserialize_message(
|
||||||
Deserializable& output,
|
Deserializable& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
auto err = deserialize(output, ctx);
|
auto err = deserialize(output, ctx);
|
||||||
if (err != errc::ok) return make_error_code(err);
|
if (err != errc::ok) return make_error_code(err);
|
||||||
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
|
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
boost::mysql::errc
|
boost::mysql::errc
|
||||||
boost::mysql::detail::deserialize_fields(
|
boost::mysql::detail::deserialize_fields(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
Types&... fields
|
Types&... fields
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
return deserialize_fields_helper(ctx, fields...);
|
return deserialize_fields_helper(ctx, fields...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
void boost::mysql::detail::serialize_fields(
|
void boost::mysql::detail::serialize_fields(
|
||||||
serialization_context& ctx,
|
serialization_context& ctx,
|
||||||
const Types&... fields
|
const Types&... fields
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
serialize_fields_helper(ctx, fields...);
|
serialize_fields_helper(ctx, fields...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr boost::mysql::detail::serialization_tag
|
constexpr boost::mysql::detail::serialization_tag
|
||||||
boost::mysql::detail::get_serialization_tag()
|
boost::mysql::detail::get_serialization_tag()
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
is_fixed_size_int<T>() ? serialization_tag::fixed_size_int :
|
is_fixed_size_int<T>() ? serialization_tag::fixed_size_int :
|
||||||
std::is_floating_point<T>::value ? serialization_tag::floating_point :
|
std::is_floating_point<T>::value ? serialization_tag::floating_point :
|
||||||
std::is_enum<T>::value ? serialization_tag::enumeration :
|
std::is_enum<T>::value ? serialization_tag::enumeration :
|
||||||
is_struct_with_fields<T>() ? serialization_tag::struct_with_fields :
|
is_struct_with_fields<T>() ? serialization_tag::struct_with_fields :
|
||||||
serialization_tag::none;
|
serialization_tag::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,112 +15,112 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline std::string_view get_string(
|
inline std::string_view get_string(
|
||||||
const std::uint8_t* from,
|
const std::uint8_t* from,
|
||||||
std::size_t size
|
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(
|
inline errc deserialize_binary_date(
|
||||||
date& output,
|
date& output,
|
||||||
std::uint8_t length,
|
std::uint8_t length,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int2 year;
|
int2 year;
|
||||||
int1 month;
|
int1 month;
|
||||||
int1 day;
|
int1 day;
|
||||||
|
|
||||||
if (length >= 4) // if length is zero, year, month and day are zero
|
if (length >= 4) // if length is zero, year, month and day are zero
|
||||||
{
|
{
|
||||||
auto err = deserialize_fields(ctx, year, month, day);
|
auto err = deserialize_fields(ctx, year, month, day);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: how does this handle zero dates?
|
// TODO: how does this handle zero dates?
|
||||||
::date::year_month_day ymd (::date::year(year.value), ::date::month(month.value), ::date::day(day.value));
|
::date::year_month_day ymd (::date::year(year.value), ::date::month(month.value), ::date::day(day.value));
|
||||||
output = date(ymd);
|
output = date(ymd);
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does not add the length prefix byte
|
// Does not add the length prefix byte
|
||||||
inline void serialize_binary_ymd(
|
inline void serialize_binary_ymd(
|
||||||
const ::date::year_month_day& ymd,
|
const ::date::year_month_day& ymd,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
serialize_fields(
|
serialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
int2(static_cast<std::uint16_t>(static_cast<int>(ymd.year()))),
|
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.month()))),
|
||||||
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.day())))
|
int1(static_cast<std::uint8_t>(static_cast<unsigned>(ymd.day())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct broken_datetime
|
struct broken_datetime
|
||||||
{
|
{
|
||||||
::date::year_month_day ymd;
|
::date::year_month_day ymd;
|
||||||
::date::time_of_day<std::chrono::microseconds> tod;
|
::date::time_of_day<std::chrono::microseconds> tod;
|
||||||
|
|
||||||
broken_datetime(const datetime& input) noexcept :
|
broken_datetime(const datetime& input) noexcept :
|
||||||
ymd(::date::floor<::date::days>(input)),
|
ymd(::date::floor<::date::days>(input)),
|
||||||
tod(input - ::date::floor<::date::days>(input))
|
tod(input - ::date::floor<::date::days>(input))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doesn't count the first length byte
|
// Doesn't count the first length byte
|
||||||
std::uint8_t binary_serialized_length() const noexcept
|
std::uint8_t binary_serialized_length() const noexcept
|
||||||
{
|
{
|
||||||
std::uint8_t res = 11; // base length
|
std::uint8_t res = 11; // base length
|
||||||
if (tod.subseconds().count() == 0)
|
if (tod.subseconds().count() == 0)
|
||||||
{
|
{
|
||||||
res -= 4;
|
res -= 4;
|
||||||
if (tod.seconds().count() == 0 &&
|
if (tod.seconds().count() == 0 &&
|
||||||
tod.minutes().count() == 0 &&
|
tod.minutes().count() == 0 &&
|
||||||
tod.hours().count() == 0)
|
tod.hours().count() == 0)
|
||||||
{
|
{
|
||||||
res -= 3;
|
res -= 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct broken_time
|
struct broken_time
|
||||||
{
|
{
|
||||||
::date::days days;
|
::date::days days;
|
||||||
std::chrono::hours hours;
|
std::chrono::hours hours;
|
||||||
std::chrono::minutes minutes;
|
std::chrono::minutes minutes;
|
||||||
std::chrono::seconds seconds;
|
std::chrono::seconds seconds;
|
||||||
std::chrono::microseconds microseconds;
|
std::chrono::microseconds microseconds;
|
||||||
|
|
||||||
broken_time(const time& input) noexcept :
|
broken_time(const time& input) noexcept :
|
||||||
days(std::chrono::duration_cast<::date::days>(input)),
|
days(std::chrono::duration_cast<::date::days>(input)),
|
||||||
hours(std::chrono::duration_cast<std::chrono::hours>(input % ::date::days(1))),
|
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))),
|
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))),
|
seconds(std::chrono::duration_cast<std::chrono::seconds>(input % std::chrono::minutes(1))),
|
||||||
microseconds(input % std::chrono::seconds(1))
|
microseconds(input % std::chrono::seconds(1))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doesn't count the first length byte
|
// Doesn't count the first length byte
|
||||||
std::uint8_t binary_serialized_length() const noexcept
|
std::uint8_t binary_serialized_length() const noexcept
|
||||||
{
|
{
|
||||||
std::uint8_t res = 12;
|
std::uint8_t res = 12;
|
||||||
if (microseconds.count() == 0)
|
if (microseconds.count() == 0)
|
||||||
{
|
{
|
||||||
res -= 4;
|
res -= 4;
|
||||||
if (seconds.count() == 0 &&
|
if (seconds.count() == 0 &&
|
||||||
minutes.count() == 0 &&
|
minutes.count() == 0 &&
|
||||||
hours.count() == 0 &&
|
hours.count() == 0 &&
|
||||||
days.count() == 0)
|
days.count() == 0)
|
||||||
{
|
{
|
||||||
res -= 8;
|
res -= 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -129,394 +129,394 @@ struct broken_time
|
|||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::int_lenenc,
|
boost::mysql::detail::int_lenenc,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
int_lenenc& output,
|
int_lenenc& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int1 first_byte;
|
int1 first_byte;
|
||||||
errc err = deserialize(first_byte, ctx);
|
errc err = deserialize(first_byte, ctx);
|
||||||
if (err != errc::ok)
|
if (err != errc::ok)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first_byte.value == 0xFC)
|
if (first_byte.value == 0xFC)
|
||||||
{
|
{
|
||||||
int2 value;
|
int2 value;
|
||||||
err = deserialize(value, ctx);
|
err = deserialize(value, ctx);
|
||||||
output.value = value.value;
|
output.value = value.value;
|
||||||
}
|
}
|
||||||
else if (first_byte.value == 0xFD)
|
else if (first_byte.value == 0xFD)
|
||||||
{
|
{
|
||||||
int3 value;
|
int3 value;
|
||||||
err = deserialize(value, ctx);
|
err = deserialize(value, ctx);
|
||||||
output.value = value.value;
|
output.value = value.value;
|
||||||
}
|
}
|
||||||
else if (first_byte.value == 0xFE)
|
else if (first_byte.value == 0xFE)
|
||||||
{
|
{
|
||||||
int8 value;
|
int8 value;
|
||||||
err = deserialize(value, ctx);
|
err = deserialize(value, ctx);
|
||||||
output.value = value.value;
|
output.value = value.value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
err = errc::ok;
|
err = errc::ok;
|
||||||
output.value = first_byte.value;
|
output.value = first_byte.value;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::int_lenenc,
|
boost::mysql::detail::int_lenenc,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
int_lenenc input,
|
int_lenenc input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
if (input.value < 251)
|
if (input.value < 251)
|
||||||
{
|
{
|
||||||
serialize(int1(static_cast<std::uint8_t>(input.value)), ctx);
|
serialize(int1(static_cast<std::uint8_t>(input.value)), ctx);
|
||||||
}
|
}
|
||||||
else if (input.value < 0x10000)
|
else if (input.value < 0x10000)
|
||||||
{
|
{
|
||||||
ctx.write(0xfc);
|
ctx.write(0xfc);
|
||||||
serialize(int2(static_cast<std::uint16_t>(input.value)), ctx);
|
serialize(int2(static_cast<std::uint16_t>(input.value)), ctx);
|
||||||
}
|
}
|
||||||
else if (input.value < 0x1000000)
|
else if (input.value < 0x1000000)
|
||||||
{
|
{
|
||||||
ctx.write(0xfd);
|
ctx.write(0xfd);
|
||||||
serialize(int3(static_cast<std::uint32_t>(input.value)), ctx);
|
serialize(int3(static_cast<std::uint32_t>(input.value)), ctx);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.write(0xfe);
|
ctx.write(0xfe);
|
||||||
serialize(int8(static_cast<std::uint64_t>(input.value)), ctx);
|
serialize(int8(static_cast<std::uint64_t>(input.value)), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t
|
inline std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::int_lenenc,
|
boost::mysql::detail::int_lenenc,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::get_size_(
|
>::get_size_(
|
||||||
int_lenenc input,
|
int_lenenc input,
|
||||||
const serialization_context&
|
const serialization_context&
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
if (input.value < 251) return 1;
|
if (input.value < 251) return 1;
|
||||||
else if (input.value < 0x10000) return 3;
|
else if (input.value < 0x10000) return 3;
|
||||||
else if (input.value < 0x1000000) return 4;
|
else if (input.value < 0x1000000) return 4;
|
||||||
else return 9;
|
else return 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::string_null,
|
boost::mysql::detail::string_null,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
string_null& output,
|
string_null& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
auto string_end = std::find(ctx.first(), ctx.last(), 0);
|
auto string_end = std::find(ctx.first(), ctx.last(), 0);
|
||||||
if (string_end == ctx.last())
|
if (string_end == ctx.last())
|
||||||
{
|
{
|
||||||
return errc::incomplete_message;
|
return errc::incomplete_message;
|
||||||
}
|
}
|
||||||
output.value = get_string(ctx.first(), string_end-ctx.first());
|
output.value = get_string(ctx.first(), string_end-ctx.first());
|
||||||
ctx.set_first(string_end + 1); // skip the null terminator
|
ctx.set_first(string_end + 1); // skip the null terminator
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::string_eof,
|
boost::mysql::detail::string_eof,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
string_eof& output,
|
string_eof& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
output.value = get_string(ctx.first(), ctx.last()-ctx.first());
|
output.value = get_string(ctx.first(), ctx.last()-ctx.first());
|
||||||
ctx.set_first(ctx.last());
|
ctx.set_first(ctx.last());
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::detail::string_lenenc,
|
boost::mysql::detail::string_lenenc,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
string_lenenc& output,
|
string_lenenc& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int_lenenc length;
|
int_lenenc length;
|
||||||
errc err = deserialize(length, ctx);
|
errc err = deserialize(length, ctx);
|
||||||
if (err != errc::ok)
|
if (err != errc::ok)
|
||||||
{
|
{
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
if (length.value > std::numeric_limits<std::size_t>::max())
|
if (length.value > std::numeric_limits<std::size_t>::max())
|
||||||
{
|
{
|
||||||
return errc::protocol_value_error;
|
return errc::protocol_value_error;
|
||||||
}
|
}
|
||||||
auto len = static_cast<std::size_t>(length.value);
|
auto len = static_cast<std::size_t>(length.value);
|
||||||
if (!ctx.enough_size(len))
|
if (!ctx.enough_size(len))
|
||||||
{
|
{
|
||||||
return errc::incomplete_message;
|
return errc::incomplete_message;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.value = get_string(ctx.first(), len);
|
output.value = get_string(ctx.first(), len);
|
||||||
ctx.advance(len);
|
ctx.advance(len);
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t
|
inline std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::date,
|
boost::mysql::date,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::get_size_(
|
>::get_size_(
|
||||||
const date&,
|
const date&,
|
||||||
const serialization_context&
|
const serialization_context&
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
// TODO: consider zero dates?
|
// TODO: consider zero dates?
|
||||||
return 5; // length, year, month, day
|
return 5; // length, year, month, day
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::date,
|
boost::mysql::date,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
const date& input,
|
const date& input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
// TODO: consider zero dates?
|
// TODO: consider zero dates?
|
||||||
serialize(int1(4), ctx); //
|
serialize(int1(4), ctx); //
|
||||||
serialize_binary_ymd(::date::year_month_day (input), ctx);
|
serialize_binary_ymd(::date::year_month_day (input), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::date,
|
boost::mysql::date,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
date& output,
|
date& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int1 length;
|
int1 length;
|
||||||
auto err = deserialize(length, ctx);
|
auto err = deserialize(length, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
return deserialize_binary_date(output, length.value, ctx);
|
return deserialize_binary_date(output, length.value, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
// datetime
|
// datetime
|
||||||
inline std::size_t
|
inline std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::datetime,
|
boost::mysql::datetime,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::get_size_(
|
>::get_size_(
|
||||||
const datetime& input,
|
const datetime& input,
|
||||||
const serialization_context&
|
const serialization_context&
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
broken_datetime dt (input);
|
broken_datetime dt (input);
|
||||||
return dt.binary_serialized_length() + 1; // extra length prefix byte
|
return dt.binary_serialized_length() + 1; // extra length prefix byte
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::datetime,
|
boost::mysql::datetime,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
const datetime& input,
|
const datetime& input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
broken_datetime brokendt (input);
|
broken_datetime brokendt (input);
|
||||||
auto length = brokendt.binary_serialized_length();
|
auto length = brokendt.binary_serialized_length();
|
||||||
serialize(int1(length), ctx);
|
serialize(int1(length), ctx);
|
||||||
if (length >= 4) // TODO: refactor these magic constants
|
if (length >= 4) // TODO: refactor these magic constants
|
||||||
{
|
{
|
||||||
serialize_binary_ymd(brokendt.ymd, ctx);
|
serialize_binary_ymd(brokendt.ymd, ctx);
|
||||||
}
|
}
|
||||||
if (length >= 7)
|
if (length >= 7)
|
||||||
{
|
{
|
||||||
serialize_fields(
|
serialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
int1(static_cast<std::uint8_t>(brokendt.tod.hours().count())),
|
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.minutes().count())),
|
||||||
int1(static_cast<std::uint8_t>(brokendt.tod.seconds().count()))
|
int1(static_cast<std::uint8_t>(brokendt.tod.seconds().count()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (length >= 11)
|
if (length >= 11)
|
||||||
{
|
{
|
||||||
auto micros = static_cast<std::uint32_t>(brokendt.tod.subseconds().count());
|
auto micros = static_cast<std::uint32_t>(brokendt.tod.subseconds().count());
|
||||||
serialize(int4(micros), ctx);
|
serialize(int4(micros), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::datetime,
|
boost::mysql::datetime,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
datetime& output,
|
datetime& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
int1 length;
|
int1 length;
|
||||||
date date_part;
|
date date_part;
|
||||||
int1 hours;
|
int1 hours;
|
||||||
int1 minutes;
|
int1 minutes;
|
||||||
int1 seconds;
|
int1 seconds;
|
||||||
int4 micros;
|
int4 micros;
|
||||||
|
|
||||||
// Deserialize length
|
// Deserialize length
|
||||||
auto err = deserialize(length, ctx);
|
auto err = deserialize(length, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
|
|
||||||
// Based on length, deserialize the rest of the fields
|
// Based on length, deserialize the rest of the fields
|
||||||
err = deserialize_binary_date(date_part, length.value, ctx);
|
err = deserialize_binary_date(date_part, length.value, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
if (length.value >= 7)
|
if (length.value >= 7)
|
||||||
{
|
{
|
||||||
err = deserialize_fields(ctx, hours, minutes, seconds);
|
err = deserialize_fields(ctx, hours, minutes, seconds);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
}
|
}
|
||||||
if (length.value >= 11)
|
if (length.value >= 11)
|
||||||
{
|
{
|
||||||
err = deserialize(micros, ctx);
|
err = deserialize(micros, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compose the final datetime. Doing time of day and date separately to avoid overflow
|
// 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) +
|
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);
|
std::chrono::seconds(seconds.value) + std::chrono::microseconds(micros.value);
|
||||||
output = date_part + time_of_day_part;
|
output = date_part + time_of_day_part;
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// time
|
// time
|
||||||
inline std::size_t
|
inline std::size_t
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::time,
|
boost::mysql::time,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::get_size_(
|
>::get_size_(
|
||||||
const time& input,
|
const time& input,
|
||||||
const serialization_context&
|
const serialization_context&
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
return broken_time(input).binary_serialized_length() + 1; // length byte
|
return broken_time(input).binary_serialized_length() + 1; // length byte
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::time,
|
boost::mysql::time,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::serialize_(
|
>::serialize_(
|
||||||
const time& input,
|
const time& input,
|
||||||
serialization_context& ctx
|
serialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
broken_time broken (input);
|
broken_time broken (input);
|
||||||
auto length = broken.binary_serialized_length();
|
auto length = broken.binary_serialized_length();
|
||||||
serialize(int1(length), ctx);
|
serialize(int1(length), ctx);
|
||||||
if (length >= 8) // TODO: magic constants
|
if (length >= 8) // TODO: magic constants
|
||||||
{
|
{
|
||||||
int1 is_negative (input.count() < 0 ? 1 : 0);
|
int1 is_negative (input.count() < 0 ? 1 : 0);
|
||||||
serialize_fields(
|
serialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
is_negative,
|
is_negative,
|
||||||
int4(static_cast<std::uint32_t>(std::abs(broken.days.count()))),
|
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.hours.count()))),
|
||||||
int1(static_cast<std::uint8_t>(std::abs(broken.minutes.count()))),
|
int1(static_cast<std::uint8_t>(std::abs(broken.minutes.count()))),
|
||||||
int1(static_cast<std::uint8_t>(std::abs(broken.seconds.count())))
|
int1(static_cast<std::uint8_t>(std::abs(broken.seconds.count())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (length >= 12)
|
if (length >= 12)
|
||||||
{
|
{
|
||||||
auto micros = static_cast<std::uint32_t>(std::abs(broken.microseconds.count()));
|
auto micros = static_cast<std::uint32_t>(std::abs(broken.microseconds.count()));
|
||||||
serialize(int4(micros), ctx);
|
serialize(int4(micros), ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline boost::mysql::errc
|
inline boost::mysql::errc
|
||||||
boost::mysql::detail::serialization_traits<
|
boost::mysql::detail::serialization_traits<
|
||||||
boost::mysql::time,
|
boost::mysql::time,
|
||||||
boost::mysql::detail::serialization_tag::none
|
boost::mysql::detail::serialization_tag::none
|
||||||
>::deserialize_(
|
>::deserialize_(
|
||||||
time& output,
|
time& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
) noexcept
|
) noexcept
|
||||||
{
|
{
|
||||||
// Length
|
// Length
|
||||||
int1 length;
|
int1 length;
|
||||||
auto err = deserialize(length, ctx);
|
auto err = deserialize(length, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
|
|
||||||
int1 is_negative (0);
|
int1 is_negative (0);
|
||||||
int4 days (0);
|
int4 days (0);
|
||||||
int1 hours (0);
|
int1 hours (0);
|
||||||
int1 minutes(0);
|
int1 minutes(0);
|
||||||
int1 seconds(0);
|
int1 seconds(0);
|
||||||
int4 microseconds(0);
|
int4 microseconds(0);
|
||||||
|
|
||||||
if (length.value >= 8)
|
if (length.value >= 8)
|
||||||
{
|
{
|
||||||
err = deserialize_fields(
|
err = deserialize_fields(
|
||||||
ctx,
|
ctx,
|
||||||
is_negative,
|
is_negative,
|
||||||
days,
|
days,
|
||||||
hours,
|
hours,
|
||||||
minutes,
|
minutes,
|
||||||
seconds
|
seconds
|
||||||
);
|
);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
}
|
}
|
||||||
if (length.value >= 12)
|
if (length.value >= 12)
|
||||||
{
|
{
|
||||||
err = deserialize(microseconds, ctx);
|
err = deserialize(microseconds, ctx);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
output = (is_negative.value ? -1 : 1) * (
|
output = (is_negative.value ? -1 : 1) * (
|
||||||
::date::days(days.value) +
|
::date::days(days.value) +
|
||||||
std::chrono::hours(hours.value) +
|
std::chrono::hours(hours.value) +
|
||||||
std::chrono::minutes(minutes.value) +
|
std::chrono::minutes(minutes.value) +
|
||||||
std::chrono::seconds(seconds.value) +
|
std::chrono::seconds(seconds.value) +
|
||||||
std::chrono::microseconds(microseconds.value)
|
std::chrono::microseconds(microseconds.value)
|
||||||
);
|
);
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::pair<boost::mysql::error_code, std::uint8_t>
|
inline std::pair<boost::mysql::error_code, std::uint8_t>
|
||||||
boost::mysql::detail::deserialize_message_type(
|
boost::mysql::detail::deserialize_message_type(
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int1 msg_type;
|
int1 msg_type;
|
||||||
std::pair<mysql::error_code, std::uint8_t> res {};
|
std::pair<mysql::error_code, std::uint8_t> res {};
|
||||||
auto err = deserialize(msg_type, ctx);
|
auto err = deserialize(msg_type, ctx);
|
||||||
if (err == errc::ok)
|
if (err == errc::ok)
|
||||||
{
|
{
|
||||||
res.second = msg_type.value;
|
res.second = msg_type.value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res.first = make_error_code(err);
|
res.first = make_error_code(err);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,90 +18,90 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline errc deserialize_text_value_impl(
|
inline errc deserialize_text_value_impl(
|
||||||
std::string_view from,
|
std::string_view from,
|
||||||
date& to
|
date& to
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
constexpr std::size_t size = 4 + 2 + 2 + 2; // year, month, day, separators
|
constexpr std::size_t size = 4 + 2 + 2 + 2; // year, month, day, separators
|
||||||
if (from.size() != size) return errc::protocol_value_error;
|
if (from.size() != size) return errc::protocol_value_error;
|
||||||
unsigned year, month, day;
|
unsigned year, month, day;
|
||||||
char buffer [size + 1] {};
|
char buffer [size + 1] {};
|
||||||
memcpy(buffer, from.data(), from.size());
|
memcpy(buffer, from.data(), from.size());
|
||||||
int parsed = sscanf(buffer, "%4u-%2u-%2u", &year, &month, &day);
|
int parsed = sscanf(buffer, "%4u-%2u-%2u", &year, &month, &day);
|
||||||
if (parsed != 3) return errc::protocol_value_error;
|
if (parsed != 3) return errc::protocol_value_error;
|
||||||
::date::year_month_day result (::date::year(year)/::date::month(month)/::date::day(day));
|
::date::year_month_day result (::date::year(year)/::date::month(month)/::date::day(day));
|
||||||
if (!result.ok()) return errc::protocol_value_error;
|
if (!result.ok()) return errc::protocol_value_error;
|
||||||
if (result > max_date || result < min_date) return errc::protocol_value_error;
|
if (result > max_date || result < min_date) return errc::protocol_value_error;
|
||||||
to = result;
|
to = result;
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline errc deserialize_text_value_impl(
|
inline errc deserialize_text_value_impl(
|
||||||
std::string_view from,
|
std::string_view from,
|
||||||
time& to,
|
time& to,
|
||||||
unsigned decimals
|
unsigned decimals
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// size check
|
// size check
|
||||||
constexpr std::size_t min_size = 2 + 2 + 2 + 2; // hours, mins, seconds, no micros
|
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
|
constexpr std::size_t max_size = min_size + 1 + 1 + 7; // hour extra character, sign and micros
|
||||||
decimals = std::min(decimals, 6u);
|
decimals = std::min(decimals, 6u);
|
||||||
if (from.size() < min_size || from.size() > max_size) return errc::protocol_value_error;
|
if (from.size() < min_size || from.size() > max_size) return errc::protocol_value_error;
|
||||||
|
|
||||||
// Parse it
|
// Parse it
|
||||||
int hours;
|
int hours;
|
||||||
unsigned minutes, seconds, micros = 0;
|
unsigned minutes, seconds, micros = 0;
|
||||||
char buffer [max_size + 1] {};
|
char buffer [max_size + 1] {};
|
||||||
memcpy(buffer, from.data(), from.size());
|
memcpy(buffer, from.data(), from.size());
|
||||||
int parsed = decimals ? sscanf(buffer, "%4d:%2u:%2u.%6u", &hours, &minutes, &seconds, µs) : // sign adds 1 char
|
int parsed = decimals ? sscanf(buffer, "%4d:%2u:%2u.%6u", &hours, &minutes, &seconds, µs) : // sign adds 1 char
|
||||||
sscanf(buffer, "%4d:%2u:%2u", &hours, &minutes, &seconds);
|
sscanf(buffer, "%4d:%2u:%2u", &hours, &minutes, &seconds);
|
||||||
if ((decimals && parsed != 4) || (!decimals && parsed != 3)) return errc::protocol_value_error;
|
if ((decimals && parsed != 4) || (!decimals && parsed != 3)) return errc::protocol_value_error;
|
||||||
micros *= static_cast<unsigned>(std::pow(10, 6 - decimals));
|
micros *= static_cast<unsigned>(std::pow(10, 6 - decimals));
|
||||||
hours = std::abs(hours);
|
hours = std::abs(hours);
|
||||||
bool is_negative = from[0] == '-';
|
bool is_negative = from[0] == '-';
|
||||||
|
|
||||||
// Sum it
|
// Sum it
|
||||||
auto res = std::chrono::hours(hours) + std::chrono::minutes(minutes) +
|
auto res = std::chrono::hours(hours) + std::chrono::minutes(minutes) +
|
||||||
std::chrono::seconds(seconds) + std::chrono::microseconds(micros);
|
std::chrono::seconds(seconds) + std::chrono::microseconds(micros);
|
||||||
if (is_negative)
|
if (is_negative)
|
||||||
{
|
{
|
||||||
res = -res;
|
res = -res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range check
|
// Range check
|
||||||
if (res > max_time || res < min_time) return errc::protocol_value_error;
|
if (res > max_time || res < min_time) return errc::protocol_value_error;
|
||||||
|
|
||||||
to = res;
|
to = res;
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline errc deserialize_text_value_impl(
|
inline errc deserialize_text_value_impl(
|
||||||
std::string_view from,
|
std::string_view from,
|
||||||
datetime& to,
|
datetime& to,
|
||||||
unsigned decimals
|
unsigned decimals
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Length check
|
// Length check
|
||||||
constexpr std::size_t min_size = 4 + 5*2 + 5; // year, month, day, hour, minute, seconds, separators
|
constexpr std::size_t min_size = 4 + 5*2 + 5; // year, month, day, hour, minute, seconds, separators
|
||||||
decimals = std::min(decimals, 6u);
|
decimals = std::min(decimals, 6u);
|
||||||
std::size_t expected_size = min_size + (decimals ? decimals + 1 : 0);
|
std::size_t expected_size = min_size + (decimals ? decimals + 1 : 0);
|
||||||
if (from.size() != expected_size) return errc::protocol_value_error;
|
if (from.size() != expected_size) return errc::protocol_value_error;
|
||||||
|
|
||||||
// Date part
|
// Date part
|
||||||
date dt;
|
date dt;
|
||||||
auto err = deserialize_text_value_impl(from.substr(0, 10), dt);
|
auto err = deserialize_text_value_impl(from.substr(0, 10), dt);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
|
|
||||||
// Time of day part
|
// Time of day part
|
||||||
time time_of_day;
|
time time_of_day;
|
||||||
err = deserialize_text_value_impl(from.substr(11), time_of_day, decimals);
|
err = deserialize_text_value_impl(from.substr(11), time_of_day, decimals);
|
||||||
if (err != errc::ok) return err;
|
if (err != errc::ok) return err;
|
||||||
constexpr auto max_time_of_day = std::chrono::hours(24) - std::chrono::microseconds(1);
|
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;
|
if (time_of_day < std::chrono::seconds(0) || time_of_day > max_time_of_day) return errc::protocol_value_error;
|
||||||
|
|
||||||
// Sum it up
|
// Sum it up
|
||||||
to = dt + time_of_day;
|
to = dt + time_of_day;
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -109,43 +109,43 @@ template <typename T>
|
|||||||
std::enable_if_t<std::is_arithmetic_v<T>, errc>
|
std::enable_if_t<std::is_arithmetic_v<T>, errc>
|
||||||
deserialize_text_value_impl(std::string_view from, T& to)
|
deserialize_text_value_impl(std::string_view from, T& to)
|
||||||
{
|
{
|
||||||
bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), to);
|
bool ok = boost::conversion::try_lexical_convert(from.data(), from.size(), to);
|
||||||
return ok ? errc::ok : errc::protocol_value_error;
|
return ok ? errc::ok : errc::protocol_value_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline errc deserialize_text_value_impl(std::string_view from, std::string_view& to)
|
inline errc deserialize_text_value_impl(std::string_view from, std::string_view& to)
|
||||||
{
|
{
|
||||||
to = from;
|
to = from;
|
||||||
return errc::ok;
|
return errc::ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
errc deserialize_text_value_to_variant(std::string_view from, value& to, Args&&... args)
|
errc deserialize_text_value_to_variant(std::string_view from, value& to, Args&&... args)
|
||||||
{
|
{
|
||||||
T value;
|
T value;
|
||||||
auto err = deserialize_text_value_impl(from, value, std::forward<Args>(args)...);
|
auto err = deserialize_text_value_impl(from, value, std::forward<Args>(args)...);
|
||||||
if (err == errc::ok)
|
if (err == errc::ok)
|
||||||
{
|
{
|
||||||
to = value;
|
to = value;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_next_field_null(
|
inline bool is_next_field_null(
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int1 type_byte;
|
int1 type_byte;
|
||||||
errc err = deserialize(type_byte, ctx);
|
errc err = deserialize(type_byte, ctx);
|
||||||
if (err == errc::ok)
|
if (err == errc::ok)
|
||||||
{
|
{
|
||||||
if (type_byte.value == 0xfb)
|
if (type_byte.value == 0xfb)
|
||||||
{
|
{
|
||||||
return true; // it was null, do not rewind
|
return true; // it was null, do not rewind
|
||||||
}
|
}
|
||||||
ctx.rewind(1); // it was not null, rewind (this byte is part of the actual message)
|
ctx.rewind(1); // it was not null, rewind (this byte is part of the actual message)
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -153,36 +153,36 @@ inline bool is_next_field_null(
|
|||||||
} // boost
|
} // boost
|
||||||
|
|
||||||
inline boost::mysql::errc boost::mysql::detail::deserialize_text_value(
|
inline boost::mysql::errc boost::mysql::detail::deserialize_text_value(
|
||||||
std::string_view from,
|
std::string_view from,
|
||||||
const field_metadata& meta,
|
const field_metadata& meta,
|
||||||
value& output
|
value& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
switch (meta.protocol_type())
|
switch (meta.protocol_type())
|
||||||
{
|
{
|
||||||
case protocol_field_type::tiny:
|
case protocol_field_type::tiny:
|
||||||
case protocol_field_type::short_:
|
case protocol_field_type::short_:
|
||||||
case protocol_field_type::int24:
|
case protocol_field_type::int24:
|
||||||
case protocol_field_type::long_:
|
case protocol_field_type::long_:
|
||||||
case protocol_field_type::year:
|
case protocol_field_type::year:
|
||||||
return meta.is_unsigned() ?
|
return meta.is_unsigned() ?
|
||||||
deserialize_text_value_to_variant<std::uint32_t>(from, output) :
|
deserialize_text_value_to_variant<std::uint32_t>(from, output) :
|
||||||
deserialize_text_value_to_variant<std::int32_t>(from, output);
|
deserialize_text_value_to_variant<std::int32_t>(from, output);
|
||||||
case protocol_field_type::longlong:
|
case protocol_field_type::longlong:
|
||||||
return meta.is_unsigned() ?
|
return meta.is_unsigned() ?
|
||||||
deserialize_text_value_to_variant<std::uint64_t>(from, output) :
|
deserialize_text_value_to_variant<std::uint64_t>(from, output) :
|
||||||
deserialize_text_value_to_variant<std::int64_t>(from, output);
|
deserialize_text_value_to_variant<std::int64_t>(from, output);
|
||||||
case protocol_field_type::float_:
|
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_:
|
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::timestamp:
|
||||||
case protocol_field_type::datetime:
|
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:
|
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:
|
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
|
// True string types
|
||||||
case protocol_field_type::varchar:
|
case protocol_field_type::varchar:
|
||||||
case protocol_field_type::var_string:
|
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::newdecimal:
|
||||||
case protocol_field_type::geometry:
|
case protocol_field_type::geometry:
|
||||||
default:
|
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(
|
boost::mysql::error_code boost::mysql::detail::deserialize_text_row(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
const std::vector<field_metadata>& fields,
|
const std::vector<field_metadata>& fields,
|
||||||
std::vector<value>& output
|
std::vector<value>& output
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
output.resize(fields.size());
|
output.resize(fields.size());
|
||||||
for (std::vector<value>::size_type i = 0; i < fields.size(); ++i)
|
for (std::vector<value>::size_type i = 0; i < fields.size(); ++i)
|
||||||
{
|
{
|
||||||
bool is_null = is_next_field_null(ctx);
|
bool is_null = is_next_field_null(ctx);
|
||||||
if (is_null)
|
if (is_null)
|
||||||
{
|
{
|
||||||
output[i] = nullptr;
|
output[i] = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string_lenenc value_str;
|
string_lenenc value_str;
|
||||||
errc err = deserialize(value_str, ctx);
|
errc err = deserialize(value_str, ctx);
|
||||||
if (err != errc::ok) return make_error_code(err);
|
if (err != errc::ok) return make_error_code(err);
|
||||||
err = deserialize_text_value(value_str.value, fields[i], output[i]);
|
err = deserialize_text_value(value_str.value, fields[i], output[i]);
|
||||||
if (err != errc::ok) return make_error_code(err);
|
if (err != errc::ok) return make_error_code(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
|
if (!ctx.empty()) return make_error_code(errc::extra_bytes);
|
||||||
return error_code();
|
return error_code();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,28 +17,28 @@ namespace detail {
|
|||||||
|
|
||||||
class null_bitmap_traits
|
class null_bitmap_traits
|
||||||
{
|
{
|
||||||
std::size_t offset_;
|
std::size_t offset_;
|
||||||
std::size_t num_fields_;
|
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 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 bit_pos(std::size_t field_pos) const noexcept { return (field_pos + offset_) % 8; }
|
||||||
public:
|
public:
|
||||||
constexpr null_bitmap_traits(std::size_t offset, std::size_t num_fields) noexcept:
|
constexpr null_bitmap_traits(std::size_t offset, std::size_t num_fields) noexcept:
|
||||||
offset_(offset), num_fields_ {num_fields} {};
|
offset_(offset), num_fields_ {num_fields} {};
|
||||||
constexpr std::size_t offset() const noexcept { return offset_; }
|
constexpr std::size_t offset() const noexcept { return offset_; }
|
||||||
constexpr std::size_t num_fields() const noexcept { return num_fields_; }
|
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; }
|
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
|
bool is_null(const std::uint8_t* null_bitmap_begin, std::size_t field_pos) const noexcept
|
||||||
{
|
{
|
||||||
assert(field_pos < num_fields_);
|
assert(field_pos < num_fields_);
|
||||||
return null_bitmap_begin[byte_pos(field_pos)] & (1 << bit_pos(field_pos));
|
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
|
void set_null(std::uint8_t* null_bitmap_begin, std::size_t field_pos) const noexcept
|
||||||
{
|
{
|
||||||
assert(field_pos < num_fields_);
|
assert(field_pos < num_fields_);
|
||||||
null_bitmap_begin[byte_pos(field_pos)] |= (1 << bit_pos(field_pos));
|
null_bitmap_begin[byte_pos(field_pos)] |= (1 << bit_pos(field_pos));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::size_t stmt_execute_null_bitmap_offset = 0;
|
constexpr std::size_t stmt_execute_null_bitmap_offset = 0;
|
||||||
|
@ -19,117 +19,117 @@ namespace detail {
|
|||||||
// prepare
|
// prepare
|
||||||
struct com_stmt_prepare_packet
|
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 <>
|
template <>
|
||||||
struct get_struct_fields<com_stmt_prepare_packet>
|
struct get_struct_fields<com_stmt_prepare_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&com_stmt_prepare_packet::statement
|
&com_stmt_prepare_packet::statement
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// response
|
// response
|
||||||
struct com_stmt_prepare_ok_packet
|
struct com_stmt_prepare_ok_packet
|
||||||
{
|
{
|
||||||
// int1 status: must be 0
|
// int1 status: must be 0
|
||||||
int4 statement_id;
|
int4 statement_id;
|
||||||
int2 num_columns;
|
int2 num_columns;
|
||||||
int2 num_params;
|
int2 num_params;
|
||||||
// int1 reserved_1: must be 0
|
// int1 reserved_1: must be 0
|
||||||
int2 warning_count;
|
int2 warning_count;
|
||||||
// TODO: int1 metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA
|
// TODO: int1 metadata_follows when CLIENT_OPTIONAL_RESULTSET_METADATA
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<com_stmt_prepare_ok_packet>
|
struct get_struct_fields<com_stmt_prepare_ok_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&com_stmt_prepare_ok_packet::statement_id,
|
&com_stmt_prepare_ok_packet::statement_id,
|
||||||
&com_stmt_prepare_ok_packet::num_columns,
|
&com_stmt_prepare_ok_packet::num_columns,
|
||||||
&com_stmt_prepare_ok_packet::num_params,
|
&com_stmt_prepare_ok_packet::num_params,
|
||||||
&com_stmt_prepare_ok_packet::warning_count
|
&com_stmt_prepare_ok_packet::warning_count
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<com_stmt_prepare_ok_packet, serialization_tag::struct_with_fields> :
|
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,
|
static inline errc deserialize_(com_stmt_prepare_ok_packet& output,
|
||||||
deserialization_context& ctx) noexcept;
|
deserialization_context& ctx) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
struct com_stmt_execute_packet
|
struct com_stmt_execute_packet
|
||||||
{
|
{
|
||||||
int4 statement_id;
|
int4 statement_id;
|
||||||
int1 flags;
|
int1 flags;
|
||||||
int4 iteration_count;
|
int4 iteration_count;
|
||||||
// if num_params > 0: NULL bitmap
|
// if num_params > 0: NULL bitmap
|
||||||
int1 new_params_bind_flag;
|
int1 new_params_bind_flag;
|
||||||
ForwardIterator params_begin;
|
ForwardIterator params_begin;
|
||||||
ForwardIterator params_end;
|
ForwardIterator params_end;
|
||||||
|
|
||||||
static constexpr std::uint8_t command_id = 0x17;
|
static constexpr std::uint8_t command_id = 0x17;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
struct get_struct_fields<com_stmt_execute_packet<ForwardIterator>>
|
struct get_struct_fields<com_stmt_execute_packet<ForwardIterator>>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&com_stmt_execute_packet<ForwardIterator>::statement_id,
|
&com_stmt_execute_packet<ForwardIterator>::statement_id,
|
||||||
&com_stmt_execute_packet<ForwardIterator>::flags,
|
&com_stmt_execute_packet<ForwardIterator>::flags,
|
||||||
&com_stmt_execute_packet<ForwardIterator>::iteration_count,
|
&com_stmt_execute_packet<ForwardIterator>::iteration_count,
|
||||||
&com_stmt_execute_packet<ForwardIterator>::new_params_bind_flag
|
&com_stmt_execute_packet<ForwardIterator>::new_params_bind_flag
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
struct serialization_traits<
|
struct serialization_traits<
|
||||||
com_stmt_execute_packet<ForwardIterator>,
|
com_stmt_execute_packet<ForwardIterator>,
|
||||||
serialization_tag::struct_with_fields
|
serialization_tag::struct_with_fields
|
||||||
> : noop_deserialize<com_stmt_execute_packet<ForwardIterator>>
|
> : noop_deserialize<com_stmt_execute_packet<ForwardIterator>>
|
||||||
{
|
{
|
||||||
static inline std::size_t get_size_(const com_stmt_execute_packet<ForwardIterator>& value,
|
static inline std::size_t get_size_(const com_stmt_execute_packet<ForwardIterator>& value,
|
||||||
const serialization_context& ctx) noexcept;
|
const serialization_context& ctx) noexcept;
|
||||||
static inline void serialize_(const com_stmt_execute_packet<ForwardIterator>& input,
|
static inline void serialize_(const com_stmt_execute_packet<ForwardIterator>& input,
|
||||||
serialization_context& ctx) noexcept;
|
serialization_context& ctx) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct com_stmt_execute_param_meta_packet
|
struct com_stmt_execute_param_meta_packet
|
||||||
{
|
{
|
||||||
protocol_field_type type;
|
protocol_field_type type;
|
||||||
int1 unsigned_flag;
|
int1 unsigned_flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct get_struct_fields<com_stmt_execute_param_meta_packet>
|
struct get_struct_fields<com_stmt_execute_param_meta_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&com_stmt_execute_param_meta_packet::type,
|
&com_stmt_execute_param_meta_packet::type,
|
||||||
&com_stmt_execute_param_meta_packet::unsigned_flag
|
&com_stmt_execute_param_meta_packet::unsigned_flag
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// close
|
// close
|
||||||
struct com_stmt_close_packet
|
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 <>
|
template <>
|
||||||
struct get_struct_fields<com_stmt_close_packet>
|
struct get_struct_fields<com_stmt_close_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&com_stmt_close_packet::statement_id
|
&com_stmt_close_packet::statement_id
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,17 +17,17 @@ namespace detail {
|
|||||||
|
|
||||||
struct com_query_packet
|
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 <>
|
template <>
|
||||||
struct get_struct_fields<com_query_packet>
|
struct get_struct_fields<com_query_packet>
|
||||||
{
|
{
|
||||||
static constexpr auto value = std::make_tuple(
|
static constexpr auto value = std::make_tuple(
|
||||||
&com_query_packet::query
|
&com_query_packet::query
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,11 +24,11 @@ namespace detail {
|
|||||||
|
|
||||||
enum class serialization_tag
|
enum class serialization_tag
|
||||||
{
|
{
|
||||||
none,
|
none,
|
||||||
fixed_size_int,
|
fixed_size_int,
|
||||||
floating_point,
|
floating_point,
|
||||||
enumeration,
|
enumeration,
|
||||||
struct_with_fields
|
struct_with_fields
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -40,19 +40,19 @@ struct serialization_traits;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
errc deserialize(T& output, deserialization_context& ctx) noexcept
|
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>
|
template <typename T>
|
||||||
void serialize(const T& input, serialization_context& ctx) noexcept
|
void serialize(const T& input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
serialization_traits<T>::serialize_(input, ctx);
|
serialization_traits<T>::serialize_(input, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t get_size(const T& input, const serialization_context& ctx) noexcept
|
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
|
// Fixed-size integers
|
||||||
@ -62,18 +62,18 @@ constexpr bool is_fixed_size_int();
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct serialization_traits<T, serialization_tag::fixed_size_int>
|
struct serialization_traits<T, serialization_tag::fixed_size_int>
|
||||||
{
|
{
|
||||||
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
||||||
static void serialize_(T input, serialization_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 constexpr std::size_t get_size_(T, const serialization_context&) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
// int_lenenc
|
// int_lenenc
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<int_lenenc, serialization_tag::none>
|
struct serialization_traits<int_lenenc, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline errc deserialize_(int_lenenc& output, deserialization_context& ctx) 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 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 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>
|
template <std::size_t N>
|
||||||
struct serialization_traits<string_fixed<N>, serialization_tag::none>
|
struct serialization_traits<string_fixed<N>, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static errc deserialize_(string_fixed<N>& output, deserialization_context& ctx) noexcept;
|
static errc deserialize_(string_fixed<N>& output, deserialization_context& ctx) noexcept;
|
||||||
static void serialize_(const string_fixed<N>& input, serialization_context& ctx) noexcept
|
static void serialize_(const string_fixed<N>& input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
ctx.write(input.data(), N);
|
ctx.write(input.data(), N);
|
||||||
}
|
}
|
||||||
static constexpr std::size_t get_size_(const string_fixed<N>&, const serialization_context&) noexcept
|
static constexpr std::size_t get_size_(const string_fixed<N>&, const serialization_context&) noexcept
|
||||||
{
|
{
|
||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -97,75 +97,75 @@ struct serialization_traits<string_fixed<N>, serialization_tag::none>
|
|||||||
template <>
|
template <>
|
||||||
struct serialization_traits<string_null, serialization_tag::none>
|
struct serialization_traits<string_null, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline errc deserialize_(string_null& output, deserialization_context& ctx) noexcept;
|
static inline errc deserialize_(string_null& output, deserialization_context& ctx) noexcept;
|
||||||
static inline void serialize_(string_null input, serialization_context& ctx) noexcept
|
static inline void serialize_(string_null input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
ctx.write(input.value.data(), input.value.size());
|
ctx.write(input.value.data(), input.value.size());
|
||||||
ctx.write(0); // null terminator
|
ctx.write(0); // null terminator
|
||||||
}
|
}
|
||||||
static inline std::size_t get_size_(string_null input, const serialization_context&) noexcept
|
static inline std::size_t get_size_(string_null input, const serialization_context&) noexcept
|
||||||
{
|
{
|
||||||
return input.value.size() + 1;
|
return input.value.size() + 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// string_eof
|
// string_eof
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<string_eof, serialization_tag::none>
|
struct serialization_traits<string_eof, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline errc deserialize_(string_eof& output, deserialization_context& ctx) noexcept;
|
static inline errc deserialize_(string_eof& output, deserialization_context& ctx) noexcept;
|
||||||
static inline void serialize_(string_eof input, serialization_context& ctx) noexcept
|
static inline void serialize_(string_eof input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
ctx.write(input.value.data(), input.value.size());
|
ctx.write(input.value.data(), input.value.size());
|
||||||
}
|
}
|
||||||
static inline std::size_t get_size_(string_eof input, const serialization_context&) noexcept
|
static inline std::size_t get_size_(string_eof input, const serialization_context&) noexcept
|
||||||
{
|
{
|
||||||
return input.value.size();
|
return input.value.size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// string_lenenc
|
// string_lenenc
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<string_lenenc, serialization_tag::none>
|
struct serialization_traits<string_lenenc, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline errc deserialize_(string_lenenc& output, deserialization_context& ctx) noexcept;
|
static inline errc deserialize_(string_lenenc& output, deserialization_context& ctx) noexcept;
|
||||||
static inline void serialize_(string_lenenc input, serialization_context& ctx) noexcept
|
static inline void serialize_(string_lenenc input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
serialize(int_lenenc(input.value.size()), ctx);
|
serialize(int_lenenc(input.value.size()), ctx);
|
||||||
ctx.write(input.value.data(), input.value.size());
|
ctx.write(input.value.data(), input.value.size());
|
||||||
}
|
}
|
||||||
static inline std::size_t get_size_(string_lenenc input, const serialization_context& ctx) noexcept
|
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();
|
return get_size(int_lenenc(input.value.size()), ctx) + input.value.size();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enums
|
// Enums
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct serialization_traits<T, serialization_tag::enumeration>
|
struct serialization_traits<T, serialization_tag::enumeration>
|
||||||
{
|
{
|
||||||
using underlying_type = std::underlying_type_t<T>;
|
using underlying_type = std::underlying_type_t<T>;
|
||||||
using serializable_type = value_holder<underlying_type>;
|
using serializable_type = value_holder<underlying_type>;
|
||||||
|
|
||||||
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
||||||
static void serialize_(T input, serialization_context& ctx) noexcept
|
static void serialize_(T input, serialization_context& ctx) noexcept
|
||||||
{
|
{
|
||||||
serialize(serializable_type(static_cast<underlying_type>(input)), ctx);
|
serialize(serializable_type(static_cast<underlying_type>(input)), ctx);
|
||||||
}
|
}
|
||||||
static std::size_t get_size_(T, const serialization_context&) noexcept;
|
static std::size_t get_size_(T, const serialization_context&) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Floating points
|
// Floating points
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct serialization_traits<T, serialization_tag::floating_point>
|
struct serialization_traits<T, serialization_tag::floating_point>
|
||||||
{
|
{
|
||||||
static_assert(std::numeric_limits<T>::is_iec559);
|
static_assert(std::numeric_limits<T>::is_iec559);
|
||||||
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
||||||
static void serialize_(T input, serialization_context& ctx) noexcept;
|
static void serialize_(T input, serialization_context& ctx) noexcept;
|
||||||
static std::size_t get_size_(T, const serialization_context&) noexcept
|
static std::size_t get_size_(T, const serialization_context&) noexcept
|
||||||
{
|
{
|
||||||
return sizeof(T);
|
return sizeof(T);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -173,25 +173,25 @@ struct serialization_traits<T, serialization_tag::floating_point>
|
|||||||
template <>
|
template <>
|
||||||
struct serialization_traits<date, serialization_tag::none>
|
struct serialization_traits<date, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline std::size_t get_size_(const date& input, const serialization_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 void serialize_(const date& input, serialization_context& ctx) noexcept;
|
||||||
static inline errc deserialize_(date& output, deserialization_context& ctx) noexcept;
|
static inline errc deserialize_(date& output, deserialization_context& ctx) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<datetime, serialization_tag::none>
|
struct serialization_traits<datetime, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline std::size_t get_size_(const datetime& input, const serialization_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 void serialize_(const datetime& input, serialization_context& ctx) noexcept;
|
||||||
static inline errc deserialize_(datetime& output, deserialization_context& ctx) noexcept;
|
static inline errc deserialize_(datetime& output, deserialization_context& ctx) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct serialization_traits<time, serialization_tag::none>
|
struct serialization_traits<time, serialization_tag::none>
|
||||||
{
|
{
|
||||||
static inline std::size_t get_size_(const time& input, const serialization_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 void serialize_(const time& input, serialization_context& ctx) noexcept;
|
||||||
static inline errc deserialize_(time& output, deserialization_context& ctx) noexcept;
|
static inline errc deserialize_(time& output, deserialization_context& ctx) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Structs and commands (messages)
|
// 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>
|
template <typename T>
|
||||||
struct get_struct_fields
|
struct get_struct_fields
|
||||||
{
|
{
|
||||||
static constexpr not_a_struct_with_fields value {};
|
static constexpr not_a_struct_with_fields value {};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -213,9 +213,9 @@ constexpr bool is_struct_with_fields();
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct serialization_traits<T, serialization_tag::struct_with_fields>
|
struct serialization_traits<T, serialization_tag::struct_with_fields>
|
||||||
{
|
{
|
||||||
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
static errc deserialize_(T& output, deserialization_context& ctx) noexcept;
|
||||||
static void serialize_(const T& input, serialization_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 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
|
// 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>
|
template <typename T>
|
||||||
struct noop_serialize
|
struct noop_serialize
|
||||||
{
|
{
|
||||||
static inline std::size_t get_size_(const T&, const serialization_context&) noexcept { return 0; }
|
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 void serialize_(const T&, serialization_context&) noexcept {}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct noop_deserialize
|
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
|
// Helper to serialize top-level messages
|
||||||
template <typename Serializable, typename Allocator>
|
template <typename Serializable, typename Allocator>
|
||||||
void serialize_message(
|
void serialize_message(
|
||||||
const Serializable& input,
|
const Serializable& input,
|
||||||
capabilities caps,
|
capabilities caps,
|
||||||
basic_bytestring<Allocator>& buffer
|
basic_bytestring<Allocator>& buffer
|
||||||
);
|
);
|
||||||
|
|
||||||
template <typename Deserializable>
|
template <typename Deserializable>
|
||||||
error_code deserialize_message(
|
error_code deserialize_message(
|
||||||
Deserializable& output,
|
Deserializable& output,
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
);
|
);
|
||||||
|
|
||||||
// Helpers for (de) serializing a set of fields
|
// 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;
|
void serialize_fields(serialization_context& ctx, const Types&... fields) noexcept;
|
||||||
|
|
||||||
inline std::pair<error_code, std::uint8_t> deserialize_message_type(
|
inline std::pair<error_code, std::uint8_t> deserialize_message_type(
|
||||||
deserialization_context& ctx
|
deserialization_context& ctx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,18 +19,18 @@ namespace detail {
|
|||||||
|
|
||||||
class serialization_context
|
class serialization_context
|
||||||
{
|
{
|
||||||
std::uint8_t* first_;
|
std::uint8_t* first_;
|
||||||
capabilities capabilities_;
|
capabilities capabilities_;
|
||||||
public:
|
public:
|
||||||
serialization_context(capabilities caps, std::uint8_t* first = nullptr) noexcept:
|
serialization_context(capabilities caps, std::uint8_t* first = nullptr) noexcept:
|
||||||
first_(first), capabilities_(caps) {};
|
first_(first), capabilities_(caps) {};
|
||||||
std::uint8_t* first() const noexcept { return first_; }
|
std::uint8_t* first() const noexcept { return first_; }
|
||||||
void set_first(std::uint8_t* new_first) noexcept { first_ = new_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 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; }
|
void advance(std::size_t size) noexcept { first_ += size; }
|
||||||
capabilities get_capabilities() const noexcept { return capabilities_; }
|
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(const void* buffer, std::size_t size) noexcept { memcpy(first_, buffer, size); advance(size); }
|
||||||
void write(std::uint8_t elm) noexcept { *first_ = elm; ++first_; }
|
void write(std::uint8_t elm) noexcept { *first_ = elm; ++first_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline errc deserialize_text_value(
|
inline errc deserialize_text_value(
|
||||||
std::string_view from,
|
std::string_view from,
|
||||||
const field_metadata& meta,
|
const field_metadata& meta,
|
||||||
value& output
|
value& output
|
||||||
);
|
);
|
||||||
|
|
||||||
inline error_code deserialize_text_row(
|
inline error_code deserialize_text_row(
|
||||||
deserialization_context& ctx,
|
deserialization_context& ctx,
|
||||||
const std::vector<field_metadata>& meta,
|
const std::vector<field_metadata>& meta,
|
||||||
std::vector<value>& output
|
std::vector<value>& output
|
||||||
);
|
);
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -18,20 +18,20 @@ namespace detail {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct value_holder
|
struct value_holder
|
||||||
{
|
{
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
static_assert(std::is_nothrow_default_constructible_v<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>
|
template <typename U>
|
||||||
explicit constexpr value_holder(U&& u)
|
explicit constexpr value_holder(U&& u)
|
||||||
noexcept(std::is_nothrow_constructible_v<T, decltype(u)>):
|
noexcept(std::is_nothrow_constructible_v<T, decltype(u)>):
|
||||||
value(std::forward<U>(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
|
// Operations on value holders
|
||||||
|
@ -30,20 +30,20 @@ namespace mysql {
|
|||||||
*/
|
*/
|
||||||
enum class errc : int
|
enum class errc : int
|
||||||
{
|
{
|
||||||
ok = 0, ///< No error.
|
ok = 0, ///< No error.
|
||||||
|
|
||||||
// Server returned errors
|
// Server returned errors
|
||||||
#include "boost/mysql/impl/server_error_enum.hpp"
|
#include "boost/mysql/impl/server_error_enum.hpp"
|
||||||
|
|
||||||
// Protocol errors
|
// Protocol errors
|
||||||
incomplete_message = 0x10000, ///< An incomplete message was received from the server.
|
incomplete_message = 0x10000, ///< An incomplete message was received from the server.
|
||||||
extra_bytes, ///< Unexpected extra bytes at the end of a message were received.
|
extra_bytes, ///< Unexpected extra bytes at the end of a message were received.
|
||||||
sequence_number_mismatch, ///< A sequence number mismatched happened.
|
sequence_number_mismatch, ///< A sequence number mismatched happened.
|
||||||
server_unsupported, ///< The server does not support the minimum required capabilities to establish the connection.
|
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.
|
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.
|
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.
|
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.
|
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
|
class error_info
|
||||||
{
|
{
|
||||||
std::string msg_;
|
std::string msg_;
|
||||||
public:
|
public:
|
||||||
/// Default constructor.
|
/// Default constructor.
|
||||||
error_info() = default;
|
error_info() = default;
|
||||||
|
|
||||||
/// Initialization constructor.
|
/// Initialization constructor.
|
||||||
error_info(std::string&& err) noexcept: msg_(std::move(err)) {}
|
error_info(std::string&& err) noexcept: msg_(std::move(err)) {}
|
||||||
|
|
||||||
/// Gets the error message.
|
/// Gets the error message.
|
||||||
const std::string& message() const noexcept { return msg_; }
|
const std::string& message() const noexcept { return msg_; }
|
||||||
|
|
||||||
/// Sets the error message.
|
/// Sets the error message.
|
||||||
void set_message(std::string&& err) { msg_ = std::move(err); }
|
void set_message(std::string&& err) { msg_ = std::move(err); }
|
||||||
|
|
||||||
/// Restores the object to its initial state.
|
/// Restores the object to its initial state.
|
||||||
void clear() noexcept { msg_.clear(); }
|
void clear() noexcept { msg_.clear(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,32 +23,32 @@ namespace mysql {
|
|||||||
*/
|
*/
|
||||||
enum class field_type
|
enum class field_type
|
||||||
{
|
{
|
||||||
tinyint, ///< TINYINT (signed and unsigned).
|
tinyint, ///< TINYINT (signed and unsigned).
|
||||||
smallint, ///< SMALLINT (signed and unsigned).
|
smallint, ///< SMALLINT (signed and unsigned).
|
||||||
mediumint, ///< MEDIUMINT (signed and unsigned).
|
mediumint, ///< MEDIUMINT (signed and unsigned).
|
||||||
int_, ///< Plain INT (signed and unsigned).
|
int_, ///< Plain INT (signed and unsigned).
|
||||||
bigint, ///< BIGINT (signed and unsigned).
|
bigint, ///< BIGINT (signed and unsigned).
|
||||||
float_, ///< FLOAT (warning: FLOAT(p) where p >= 24 creates a DOUBLE column).
|
float_, ///< FLOAT (warning: FLOAT(p) where p >= 24 creates a DOUBLE column).
|
||||||
double_, ///< DOUBLE
|
double_, ///< DOUBLE
|
||||||
decimal, ///< DECIMAL
|
decimal, ///< DECIMAL
|
||||||
bit, ///< BIT
|
bit, ///< BIT
|
||||||
year, ///< YEAR
|
year, ///< YEAR
|
||||||
time, ///< TIME
|
time, ///< TIME
|
||||||
date, ///< DATE
|
date, ///< DATE
|
||||||
datetime, ///< DATETIME
|
datetime, ///< DATETIME
|
||||||
timestamp, ///< TIMESTAMP
|
timestamp, ///< TIMESTAMP
|
||||||
char_, ///< CHAR (any length)
|
char_, ///< CHAR (any length)
|
||||||
varchar, ///< VARCHAR (any length)
|
varchar, ///< VARCHAR (any length)
|
||||||
binary, ///< BINARY (any length)
|
binary, ///< BINARY (any length)
|
||||||
varbinary, ///< VARBINARY (any length)
|
varbinary, ///< VARBINARY (any length)
|
||||||
text, ///< TINYTEXT, TEXT, MEDIUMTEXT and LONGTEXT
|
text, ///< TINYTEXT, TEXT, MEDIUMTEXT and LONGTEXT
|
||||||
blob, ///< TINYBLOB, BLOB, MEDIUMBLOB and LONGBLOB
|
blob, ///< TINYBLOB, BLOB, MEDIUMBLOB and LONGBLOB
|
||||||
enum_, ///< ENUM
|
enum_, ///< ENUM
|
||||||
set, ///< SET
|
set, ///< SET
|
||||||
geometry, ///< GEOMETRY
|
geometry, ///< GEOMETRY
|
||||||
|
|
||||||
unknown, ///< None of the known types; maybe a new MySQL type we have no knowledge of.
|
unknown, ///< None of the known types; maybe a new MySQL type we have no knowledge of.
|
||||||
_not_computed,
|
_not_computed,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // mysql
|
} // mysql
|
||||||
|
@ -16,147 +16,147 @@
|
|||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
void boost::mysql::connection<Stream>::handshake(
|
void boost::mysql::connection<Stream>::handshake(
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
error_code& code,
|
error_code& code,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
code.clear();
|
code.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
detail::hanshake(channel_, params, code, info);
|
detail::hanshake(channel_, params, code, info);
|
||||||
// TODO: should we close() the stream in case of error?
|
// TODO: should we close() the stream in case of error?
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
void boost::mysql::connection<Stream>::handshake(
|
void boost::mysql::connection<Stream>::handshake(
|
||||||
const connection_params& params
|
const connection_params& params
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
error_code code;
|
error_code code;
|
||||||
error_info info;
|
error_info info;
|
||||||
handshake(params, code, info);
|
handshake(params, code, info);
|
||||||
detail::check_error_code(code, info);
|
detail::check_error_code(code, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::connection<Stream>::handshake_signature
|
typename boost::mysql::connection<Stream>::handshake_signature
|
||||||
)
|
)
|
||||||
boost::mysql::connection<Stream>::async_handshake(
|
boost::mysql::connection<Stream>::async_handshake(
|
||||||
const connection_params& params,
|
const connection_params& params,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, handshake_signature>();
|
detail::check_completion_token<CompletionToken, handshake_signature>();
|
||||||
return detail::async_handshake(
|
return detail::async_handshake(
|
||||||
channel_,
|
channel_,
|
||||||
params,
|
params,
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query
|
// Query
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
|
boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
|
||||||
std::string_view query_string,
|
std::string_view query_string,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
err.clear();
|
err.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
resultset<Stream> res;
|
resultset<Stream> res;
|
||||||
detail::execute_query(channel_, query_string, res, err, info);
|
detail::execute_query(channel_, query_string, res, err, info);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
|
boost::mysql::resultset<Stream> boost::mysql::connection<Stream>::query(
|
||||||
std::string_view query_string
|
std::string_view query_string
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
resultset<Stream> res;
|
resultset<Stream> res;
|
||||||
error_code err;
|
error_code err;
|
||||||
error_info info;
|
error_info info;
|
||||||
detail::execute_query(channel_, query_string, res, err, info);
|
detail::execute_query(channel_, query_string, res, err, info);
|
||||||
detail::check_error_code(err, info);
|
detail::check_error_code(err, info);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::connection<Stream>::query_signature
|
typename boost::mysql::connection<Stream>::query_signature
|
||||||
)
|
)
|
||||||
boost::mysql::connection<Stream>::async_query(
|
boost::mysql::connection<Stream>::async_query(
|
||||||
std::string_view query_string,
|
std::string_view query_string,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, query_signature>();
|
detail::check_completion_token<CompletionToken, query_signature>();
|
||||||
return detail::async_execute_query(
|
return detail::async_execute_query(
|
||||||
channel_,
|
channel_,
|
||||||
query_string,
|
query_string,
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepare_statement(
|
boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepare_statement(
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
mysql::prepared_statement<Stream> res;
|
mysql::prepared_statement<Stream> res;
|
||||||
err.clear();
|
err.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
detail::prepare_statement(channel_, statement, err, info, res);
|
detail::prepare_statement(channel_, statement, err, info, res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepare_statement(
|
boost::mysql::prepared_statement<Stream> boost::mysql::connection<Stream>::prepare_statement(
|
||||||
std::string_view statement
|
std::string_view statement
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
mysql::prepared_statement<Stream> res;
|
mysql::prepared_statement<Stream> res;
|
||||||
error_code err;
|
error_code err;
|
||||||
error_info info;
|
error_info info;
|
||||||
detail::prepare_statement(channel_, statement, err, info, res);
|
detail::prepare_statement(channel_, statement, err, info, res);
|
||||||
detail::check_error_code(err, info);
|
detail::check_error_code(err, info);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::connection<Stream>::prepare_statement_signature
|
typename boost::mysql::connection<Stream>::prepare_statement_signature
|
||||||
)
|
)
|
||||||
boost::mysql::connection<Stream>::async_prepare_statement(
|
boost::mysql::connection<Stream>::async_prepare_statement(
|
||||||
std::string_view statement,
|
std::string_view statement,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, prepare_statement_signature>();
|
detail::check_completion_token<CompletionToken, prepare_statement_signature>();
|
||||||
return detail::async_prepare_statement(
|
return detail::async_prepare_statement(
|
||||||
channel_,
|
channel_,
|
||||||
statement,
|
statement,
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ namespace system {
|
|||||||
template <>
|
template <>
|
||||||
struct is_error_code_enum<mysql::errc>
|
struct is_error_code_enum<mysql::errc>
|
||||||
{
|
{
|
||||||
static constexpr bool value = true;
|
static constexpr bool value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // system
|
} // system
|
||||||
@ -27,61 +27,61 @@ namespace detail {
|
|||||||
|
|
||||||
inline const char* error_to_string(errc error) noexcept
|
inline const char* error_to_string(errc error) noexcept
|
||||||
{
|
{
|
||||||
switch (error)
|
switch (error)
|
||||||
{
|
{
|
||||||
case errc::ok: return "no 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::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::extra_bytes: return "Extra bytes at the end of the message";
|
||||||
case errc::sequence_number_mismatch: return "Mismatched sequence numbers";
|
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::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::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::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";
|
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
|
class mysql_error_category_t : public boost::system::error_category
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const char* name() const noexcept final override { return "mysql"; }
|
const char* name() const noexcept final override { return "mysql"; }
|
||||||
std::string message(int ev) const final override
|
std::string message(int ev) const final override
|
||||||
{
|
{
|
||||||
return error_to_string(static_cast<errc>(ev));
|
return error_to_string(static_cast<errc>(ev));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
inline mysql_error_category_t mysql_error_category;
|
inline mysql_error_category_t mysql_error_category;
|
||||||
|
|
||||||
inline boost::system::error_code make_error_code(errc error)
|
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)
|
inline void check_error_code(const error_code& code, const error_info& info)
|
||||||
{
|
{
|
||||||
if (code)
|
if (code)
|
||||||
{
|
{
|
||||||
throw boost::system::system_error(code, info.message());
|
throw boost::system::system_error(code, info.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void conditional_clear(error_info* info) noexcept
|
inline void conditional_clear(error_info* info) noexcept
|
||||||
{
|
{
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
info->clear();
|
info->clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void conditional_assign(error_info* to, error_info&& from)
|
inline void conditional_assign(error_info* to, error_info&& from)
|
||||||
{
|
{
|
||||||
if (to)
|
if (to)
|
||||||
{
|
{
|
||||||
*to = std::move(from);
|
*to = std::move(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -13,60 +13,60 @@ namespace mysql {
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
inline field_type compute_field_type_string(
|
inline field_type compute_field_type_string(
|
||||||
std::uint32_t flags
|
std::uint32_t flags
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (flags & column_flags::set) return field_type::set;
|
if (flags & column_flags::set) return field_type::set;
|
||||||
else if (flags & column_flags::enum_) return field_type::enum_;
|
else if (flags & column_flags::enum_) return field_type::enum_;
|
||||||
else if (flags & column_flags::binary) return field_type::binary;
|
else if (flags & column_flags::binary) return field_type::binary;
|
||||||
else return field_type::char_;
|
else return field_type::char_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline field_type compute_field_type_var_string(
|
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;
|
if (flags & column_flags::binary) return field_type::varbinary;
|
||||||
else return field_type::varchar;
|
else return field_type::varchar;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline field_type compute_field_type_blob(
|
inline field_type compute_field_type_blob(
|
||||||
std::uint32_t flags
|
std::uint32_t flags
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (flags & column_flags::binary) return field_type::blob;
|
if (flags & column_flags::binary) return field_type::blob;
|
||||||
else return field_type::text;
|
else return field_type::text;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline field_type compute_field_type(
|
inline field_type compute_field_type(
|
||||||
protocol_field_type protocol_type,
|
protocol_field_type protocol_type,
|
||||||
std::uint32_t flags
|
std::uint32_t flags
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
switch (protocol_type)
|
switch (protocol_type)
|
||||||
{
|
{
|
||||||
case protocol_field_type::decimal:
|
case protocol_field_type::decimal:
|
||||||
case protocol_field_type::newdecimal:
|
case protocol_field_type::newdecimal:
|
||||||
return field_type::decimal;
|
return field_type::decimal;
|
||||||
case protocol_field_type::geometry: return field_type::geometry;
|
case protocol_field_type::geometry: return field_type::geometry;
|
||||||
case protocol_field_type::tiny: return field_type::tinyint;
|
case protocol_field_type::tiny: return field_type::tinyint;
|
||||||
case protocol_field_type::short_: return field_type::smallint;
|
case protocol_field_type::short_: return field_type::smallint;
|
||||||
case protocol_field_type::int24: return field_type::mediumint;
|
case protocol_field_type::int24: return field_type::mediumint;
|
||||||
case protocol_field_type::long_: return field_type::int_;
|
case protocol_field_type::long_: return field_type::int_;
|
||||||
case protocol_field_type::longlong: return field_type::bigint;
|
case protocol_field_type::longlong: return field_type::bigint;
|
||||||
case protocol_field_type::float_: return field_type::float_;
|
case protocol_field_type::float_: return field_type::float_;
|
||||||
case protocol_field_type::double_: return field_type::double_;
|
case protocol_field_type::double_: return field_type::double_;
|
||||||
case protocol_field_type::bit: return field_type::bit;
|
case protocol_field_type::bit: return field_type::bit;
|
||||||
case protocol_field_type::date: return field_type::date;
|
case protocol_field_type::date: return field_type::date;
|
||||||
case protocol_field_type::datetime: return field_type::datetime;
|
case protocol_field_type::datetime: return field_type::datetime;
|
||||||
case protocol_field_type::timestamp: return field_type::timestamp;
|
case protocol_field_type::timestamp: return field_type::timestamp;
|
||||||
case protocol_field_type::time: return field_type::time;
|
case protocol_field_type::time: return field_type::time;
|
||||||
case protocol_field_type::year: return field_type::year;
|
case protocol_field_type::year: return field_type::year;
|
||||||
case protocol_field_type::string: return compute_field_type_string(flags);
|
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::var_string: return compute_field_type_var_string(flags);
|
||||||
case protocol_field_type::blob: return compute_field_type_blob(flags);
|
case protocol_field_type::blob: return compute_field_type_blob(flags);
|
||||||
default: return field_type::unknown;
|
default: return field_type::unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -75,12 +75,12 @@ inline field_type compute_field_type(
|
|||||||
|
|
||||||
inline boost::mysql::field_type boost::mysql::field_metadata::type() const noexcept
|
inline boost::mysql::field_type boost::mysql::field_metadata::type() const noexcept
|
||||||
{
|
{
|
||||||
if (field_type_ == field_type::_not_computed)
|
if (field_type_ == field_type::_not_computed)
|
||||||
{
|
{
|
||||||
field_type_ = detail::compute_field_type(msg_.type, msg_.flags.value);
|
field_type_ = detail::compute_field_type(msg_.type, msg_.flags.value);
|
||||||
assert(field_type_ != field_type::_not_computed);
|
assert(field_type_ != field_type::_not_computed);
|
||||||
}
|
}
|
||||||
return field_type_;
|
return field_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,160 +17,160 @@
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
void boost::mysql::prepared_statement<Stream>::check_num_params(
|
void boost::mysql::prepared_statement<Stream>::check_num_params(
|
||||||
ForwardIterator first,
|
ForwardIterator first,
|
||||||
ForwardIterator last,
|
ForwardIterator last,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
auto param_count = std::distance(first, last);
|
auto param_count = std::distance(first, last);
|
||||||
if (param_count != num_params())
|
if (param_count != num_params())
|
||||||
{
|
{
|
||||||
err = detail::make_error_code(errc::wrong_num_params);
|
err = detail::make_error_code(errc::wrong_num_params);
|
||||||
info.set_message(detail::stringize(
|
info.set_message(detail::stringize(
|
||||||
"prepared_statement::execute: expected ", num_params(), " params, but got ", param_count));
|
"prepared_statement::execute: expected ", num_params(), " params, but got ", param_count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
|
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
|
||||||
ForwardIterator params_first,
|
ForwardIterator params_first,
|
||||||
ForwardIterator params_last,
|
ForwardIterator params_last,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
|
|
||||||
mysql::resultset<Stream> res;
|
mysql::resultset<Stream> res;
|
||||||
err.clear();
|
err.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
|
|
||||||
// Verify we got passed the right number of params
|
// Verify we got passed the right number of params
|
||||||
check_num_params(params_first, params_last, err, info);
|
check_num_params(params_first, params_last, err, info);
|
||||||
if (!err)
|
if (!err)
|
||||||
{
|
{
|
||||||
detail::execute_statement(
|
detail::execute_statement(
|
||||||
*channel_,
|
*channel_,
|
||||||
stmt_msg_.statement_id.value,
|
stmt_msg_.statement_id.value,
|
||||||
params_first,
|
params_first,
|
||||||
params_last,
|
params_last,
|
||||||
res,
|
res,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
|
boost::mysql::resultset<Stream> boost::mysql::prepared_statement<Stream>::execute(
|
||||||
ForwardIterator params_first,
|
ForwardIterator params_first,
|
||||||
ForwardIterator params_last
|
ForwardIterator params_last
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
error_info info;
|
error_info info;
|
||||||
auto res = execute(params_first, params_last, err, info);
|
auto res = execute(params_first, params_last, err, info);
|
||||||
detail::check_error_code(err, info);
|
detail::check_error_code(err, info);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
template <typename ForwardIterator, typename CompletionToken>
|
template <typename ForwardIterator, typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::prepared_statement<StreamType>::execute_signature
|
typename boost::mysql::prepared_statement<StreamType>::execute_signature
|
||||||
)
|
)
|
||||||
boost::mysql::prepared_statement<StreamType>::async_execute(
|
boost::mysql::prepared_statement<StreamType>::async_execute(
|
||||||
ForwardIterator params_first,
|
ForwardIterator params_first,
|
||||||
ForwardIterator params_last,
|
ForwardIterator params_last,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, execute_signature>();
|
detail::check_completion_token<CompletionToken, execute_signature>();
|
||||||
|
|
||||||
// Check we got passed the right number of params
|
// Check we got passed the right number of params
|
||||||
error_code err;
|
error_code err;
|
||||||
error_info nonnull_info;
|
error_info nonnull_info;
|
||||||
check_num_params(params_first, params_last, err, nonnull_info);
|
check_num_params(params_first, params_last, err, nonnull_info);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
detail::conditional_assign(info, std::move(nonnull_info));
|
detail::conditional_assign(info, std::move(nonnull_info));
|
||||||
boost::asio::async_completion<CompletionToken, execute_signature> completion (token);
|
boost::asio::async_completion<CompletionToken, execute_signature> completion (token);
|
||||||
// TODO: is executor correctly preserved here?
|
// TODO: is executor correctly preserved here?
|
||||||
boost::asio::post(
|
boost::asio::post(
|
||||||
channel_->next_layer().get_executor(),
|
channel_->next_layer().get_executor(),
|
||||||
boost::beast::bind_front_handler(
|
boost::beast::bind_front_handler(
|
||||||
std::move(completion.completion_handler),
|
std::move(completion.completion_handler),
|
||||||
err,
|
err,
|
||||||
resultset<StreamType>()
|
resultset<StreamType>()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return completion.result.get();
|
return completion.result.get();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Actually execute the statement
|
// Actually execute the statement
|
||||||
return detail::async_execute_statement(
|
return detail::async_execute_statement(
|
||||||
*channel_,
|
*channel_,
|
||||||
stmt_msg_.statement_id.value,
|
stmt_msg_.statement_id.value,
|
||||||
params_first,
|
params_first,
|
||||||
params_last,
|
params_last,
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void boost::mysql::prepared_statement<StreamType>::close(
|
void boost::mysql::prepared_statement<StreamType>::close(
|
||||||
error_code& code,
|
error_code& code,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
code.clear();
|
code.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
detail::close_statement(*channel_, id(), code, info);
|
detail::close_statement(*channel_, id(), code, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
void boost::mysql::prepared_statement<StreamType>::close()
|
void boost::mysql::prepared_statement<StreamType>::close()
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
error_code code;
|
error_code code;
|
||||||
error_info info;
|
error_info info;
|
||||||
detail::close_statement(*channel_, id(), code, info);
|
detail::close_statement(*channel_, id(), code, info);
|
||||||
detail::check_error_code(code, info);
|
detail::check_error_code(code, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::prepared_statement<StreamType>::close_signature
|
typename boost::mysql::prepared_statement<StreamType>::close_signature
|
||||||
)
|
)
|
||||||
boost::mysql::prepared_statement<StreamType>::async_close(
|
boost::mysql::prepared_statement<StreamType>::async_close(
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, close_signature>();
|
detail::check_completion_token<CompletionToken, close_signature>();
|
||||||
return detail::async_close_statement(
|
return detail::async_close_statement(
|
||||||
*channel_,
|
*channel_,
|
||||||
id(),
|
id(),
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* INCLUDE_BOOST_MYSQL_IMPL_PREPARED_STATEMENT_HPP_ */
|
#endif /* INCLUDE_BOOST_MYSQL_IMPL_PREPARED_STATEMENT_HPP_ */
|
||||||
|
@ -17,322 +17,322 @@
|
|||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
const boost::mysql::row* boost::mysql::resultset<StreamType>::fetch_one(
|
const boost::mysql::row* boost::mysql::resultset<StreamType>::fetch_one(
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
|
|
||||||
err.clear();
|
err.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
|
|
||||||
if (complete())
|
if (complete())
|
||||||
{
|
{
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto result = detail::read_row(
|
auto result = detail::read_row(
|
||||||
deserializer_,
|
deserializer_,
|
||||||
*channel_,
|
*channel_,
|
||||||
meta_.fields(),
|
meta_.fields(),
|
||||||
buffer_,
|
buffer_,
|
||||||
current_row_.values(),
|
current_row_.values(),
|
||||||
ok_packet_,
|
ok_packet_,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
eof_received_ = result == detail::read_row_result::eof;
|
eof_received_ = result == detail::read_row_result::eof;
|
||||||
return result == detail::read_row_result::row ? ¤t_row_ : nullptr;
|
return result == detail::read_row_result::row ? ¤t_row_ : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
const boost::mysql::row* boost::mysql::resultset<StreamType>::fetch_one()
|
const boost::mysql::row* boost::mysql::resultset<StreamType>::fetch_one()
|
||||||
{
|
{
|
||||||
error_code code;
|
error_code code;
|
||||||
error_info info;
|
error_info info;
|
||||||
const row* res = fetch_one(code, info);
|
const row* res = fetch_one(code, info);
|
||||||
detail::check_error_code(code, info);
|
detail::check_error_code(code, info);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_many(
|
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_many(
|
||||||
std::size_t count,
|
std::size_t count,
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
error_info& info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
assert(valid());
|
assert(valid());
|
||||||
|
|
||||||
err.clear();
|
err.clear();
|
||||||
info.clear();
|
info.clear();
|
||||||
|
|
||||||
std::vector<mysql::owning_row> res;
|
std::vector<mysql::owning_row> res;
|
||||||
|
|
||||||
if (!complete()) // support calling fetch on already exhausted resultset
|
if (!complete()) // support calling fetch on already exhausted resultset
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < count; ++i)
|
for (std::size_t i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
detail::bytestring buff;
|
detail::bytestring buff;
|
||||||
std::vector<value> values;
|
std::vector<value> values;
|
||||||
|
|
||||||
auto result = detail::read_row(
|
auto result = detail::read_row(
|
||||||
deserializer_,
|
deserializer_,
|
||||||
*channel_,
|
*channel_,
|
||||||
meta_.fields(),
|
meta_.fields(),
|
||||||
buff,
|
buff,
|
||||||
values,
|
values,
|
||||||
ok_packet_,
|
ok_packet_,
|
||||||
err,
|
err,
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
eof_received_ = result == detail::read_row_result::eof;
|
eof_received_ = result == detail::read_row_result::eof;
|
||||||
if (result == detail::read_row_result::row)
|
if (result == detail::read_row_result::row)
|
||||||
{
|
{
|
||||||
res.emplace_back(std::move(values), std::move(buff));
|
res.emplace_back(std::move(values), std::move(buff));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_many(
|
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_many(
|
||||||
std::size_t count
|
std::size_t count
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
error_code code;
|
error_code code;
|
||||||
error_info info;
|
error_info info;
|
||||||
auto res = fetch_many(count, code, info);
|
auto res = fetch_many(count, code, info);
|
||||||
detail::check_error_code(code, info);
|
detail::check_error_code(code, info);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_all(
|
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_all(
|
||||||
error_code& err,
|
error_code& err,
|
||||||
error_info& info
|
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>
|
template <typename StreamType>
|
||||||
std::vector<boost::mysql::owning_row> boost::mysql::resultset<StreamType>::fetch_all()
|
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 StreamType>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::resultset<StreamType>::fetch_one_signature
|
typename boost::mysql::resultset<StreamType>::fetch_one_signature
|
||||||
)
|
)
|
||||||
boost::mysql::resultset<StreamType>::async_fetch_one(
|
boost::mysql::resultset<StreamType>::async_fetch_one(
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, fetch_one_signature>();
|
detail::check_completion_token<CompletionToken, fetch_one_signature>();
|
||||||
|
|
||||||
using HandlerSignature = fetch_one_signature;
|
using HandlerSignature = fetch_one_signature;
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
||||||
|
|
||||||
struct Op: BaseType, boost::asio::coroutine
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
resultset<StreamType>& resultset_;
|
resultset<StreamType>& resultset_;
|
||||||
error_info* output_info_;
|
error_info* output_info_;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
resultset<StreamType>& obj,
|
resultset<StreamType>& obj,
|
||||||
error_info* output_info
|
error_info* output_info
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), obj.channel_->next_layer().get_executor()),
|
BaseType(std::move(handler), obj.channel_->next_layer().get_executor()),
|
||||||
resultset_(obj),
|
resultset_(obj),
|
||||||
output_info_(output_info)
|
output_info_(output_info)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code err,
|
error_code err,
|
||||||
error_info info,
|
error_info info,
|
||||||
detail::read_row_result result,
|
detail::read_row_result result,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
if (resultset_.complete())
|
if (resultset_.complete())
|
||||||
{
|
{
|
||||||
this->complete(cont, error_code(), nullptr);
|
this->complete(cont, error_code(), nullptr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
yield detail::async_read_row(
|
yield detail::async_read_row(
|
||||||
resultset_.deserializer_,
|
resultset_.deserializer_,
|
||||||
*resultset_.channel_,
|
*resultset_.channel_,
|
||||||
resultset_.meta_.fields(),
|
resultset_.meta_.fields(),
|
||||||
resultset_.buffer_,
|
resultset_.buffer_,
|
||||||
resultset_.current_row_.values(),
|
resultset_.current_row_.values(),
|
||||||
resultset_.ok_packet_,
|
resultset_.ok_packet_,
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
resultset_.eof_received_ = result == detail::read_row_result::eof;
|
resultset_.eof_received_ = result == detail::read_row_result::eof;
|
||||||
detail::conditional_assign(output_info_, std::move(info));
|
detail::conditional_assign(output_info_, std::move(info));
|
||||||
this->complete(
|
this->complete(
|
||||||
cont,
|
cont,
|
||||||
err,
|
err,
|
||||||
result == detail::read_row_result::row ? &resultset_.current_row_ : nullptr
|
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(
|
Op(
|
||||||
std::move(initiator.completion_handler),
|
std::move(initiator.completion_handler),
|
||||||
*this,
|
*this,
|
||||||
info
|
info
|
||||||
)(error_code(), error_info(), detail::read_row_result::error, false);
|
)(error_code(), error_info(), detail::read_row_result::error, false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::resultset<StreamType>::fetch_many_signature
|
typename boost::mysql::resultset<StreamType>::fetch_many_signature
|
||||||
)
|
)
|
||||||
boost::mysql::resultset<StreamType>::async_fetch_many(
|
boost::mysql::resultset<StreamType>::async_fetch_many(
|
||||||
std::size_t count,
|
std::size_t count,
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
detail::conditional_clear(info);
|
detail::conditional_clear(info);
|
||||||
detail::check_completion_token<CompletionToken, fetch_many_signature>();
|
detail::check_completion_token<CompletionToken, fetch_many_signature>();
|
||||||
|
|
||||||
using HandlerSignature = fetch_many_signature;
|
using HandlerSignature = fetch_many_signature;
|
||||||
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
using HandlerType = BOOST_ASIO_HANDLER_TYPE(CompletionToken, HandlerSignature);
|
||||||
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
using BaseType = boost::beast::async_base<HandlerType, typename StreamType::executor_type>;
|
||||||
|
|
||||||
struct OpImpl
|
struct OpImpl
|
||||||
{
|
{
|
||||||
resultset<StreamType>& parent_resultset;
|
resultset<StreamType>& parent_resultset;
|
||||||
std::vector<owning_row> rows;
|
std::vector<owning_row> rows;
|
||||||
detail::bytestring buffer;
|
detail::bytestring buffer;
|
||||||
std::vector<value> values;
|
std::vector<value> values;
|
||||||
std::size_t remaining;
|
std::size_t remaining;
|
||||||
error_info* output_info_;
|
error_info* output_info_;
|
||||||
|
|
||||||
OpImpl(resultset<StreamType>& obj, std::size_t count, error_info* output_info):
|
OpImpl(resultset<StreamType>& obj, std::size_t count, error_info* output_info):
|
||||||
parent_resultset(obj),
|
parent_resultset(obj),
|
||||||
remaining(count),
|
remaining(count),
|
||||||
output_info_(output_info)
|
output_info_(output_info)
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
void row_received()
|
void row_received()
|
||||||
{
|
{
|
||||||
rows.emplace_back(std::move(values), std::move(buffer));
|
rows.emplace_back(std::move(values), std::move(buffer));
|
||||||
values = std::vector<value>();
|
values = std::vector<value>();
|
||||||
buffer = detail::bytestring();
|
buffer = detail::bytestring();
|
||||||
--remaining;
|
--remaining;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Op: BaseType, boost::asio::coroutine
|
struct Op: BaseType, boost::asio::coroutine
|
||||||
{
|
{
|
||||||
std::shared_ptr<OpImpl> impl_;
|
std::shared_ptr<OpImpl> impl_;
|
||||||
|
|
||||||
Op(
|
Op(
|
||||||
HandlerType&& handler,
|
HandlerType&& handler,
|
||||||
std::shared_ptr<OpImpl>&& impl
|
std::shared_ptr<OpImpl>&& impl
|
||||||
):
|
):
|
||||||
BaseType(std::move(handler), impl->parent_resultset.channel_->next_layer().get_executor()),
|
BaseType(std::move(handler), impl->parent_resultset.channel_->next_layer().get_executor()),
|
||||||
impl_(std::move(impl))
|
impl_(std::move(impl))
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
error_code err,
|
error_code err,
|
||||||
error_info info,
|
error_info info,
|
||||||
detail::read_row_result result,
|
detail::read_row_result result,
|
||||||
bool cont=true
|
bool cont=true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
reenter(*this)
|
reenter(*this)
|
||||||
{
|
{
|
||||||
while (!impl_->parent_resultset.complete() && impl_->remaining > 0)
|
while (!impl_->parent_resultset.complete() && impl_->remaining > 0)
|
||||||
{
|
{
|
||||||
yield detail::async_read_row(
|
yield detail::async_read_row(
|
||||||
impl_->parent_resultset.deserializer_,
|
impl_->parent_resultset.deserializer_,
|
||||||
*impl_->parent_resultset.channel_,
|
*impl_->parent_resultset.channel_,
|
||||||
impl_->parent_resultset.meta_.fields(),
|
impl_->parent_resultset.meta_.fields(),
|
||||||
impl_->buffer,
|
impl_->buffer,
|
||||||
impl_->values,
|
impl_->values,
|
||||||
impl_->parent_resultset.ok_packet_,
|
impl_->parent_resultset.ok_packet_,
|
||||||
std::move(*this)
|
std::move(*this)
|
||||||
);
|
);
|
||||||
if (result == detail::read_row_result::error)
|
if (result == detail::read_row_result::error)
|
||||||
{
|
{
|
||||||
detail::conditional_assign(impl_->output_info_, std::move(info));
|
detail::conditional_assign(impl_->output_info_, std::move(info));
|
||||||
this->complete(cont, err, std::move(impl_->rows));
|
this->complete(cont, err, std::move(impl_->rows));
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
else if (result == detail::read_row_result::eof)
|
else if (result == detail::read_row_result::eof)
|
||||||
{
|
{
|
||||||
impl_->parent_resultset.eof_received_ = true;
|
impl_->parent_resultset.eof_received_ = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
impl_->row_received();
|
impl_->row_received();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->complete(cont, err, std::move(impl_->rows));
|
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(
|
Op(
|
||||||
std::move(initiator.completion_handler),
|
std::move(initiator.completion_handler),
|
||||||
std::make_shared<OpImpl>(*this, count, info)
|
std::make_shared<OpImpl>(*this, count, info)
|
||||||
)(error_code(), error_info(), detail::read_row_result::error, false);
|
)(error_code(), error_info(), detail::read_row_result::error, false);
|
||||||
return initiator.result.get();
|
return initiator.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename StreamType>
|
template <typename StreamType>
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_ASIO_INITFN_RESULT_TYPE(
|
BOOST_ASIO_INITFN_RESULT_TYPE(
|
||||||
CompletionToken,
|
CompletionToken,
|
||||||
typename boost::mysql::resultset<StreamType>::fetch_all_signature
|
typename boost::mysql::resultset<StreamType>::fetch_all_signature
|
||||||
)
|
)
|
||||||
boost::mysql::resultset<StreamType>::async_fetch_all(
|
boost::mysql::resultset<StreamType>::async_fetch_all(
|
||||||
CompletionToken&& token,
|
CompletionToken&& token,
|
||||||
error_info* info
|
error_info* info
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return async_fetch_many(
|
return async_fetch_many(
|
||||||
std::numeric_limits<std::size_t>::max(),
|
std::numeric_limits<std::size_t>::max(),
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,35 +32,35 @@ static_assert(time::max() >= max_time);
|
|||||||
|
|
||||||
struct print_visitor
|
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>
|
template <typename T>
|
||||||
void operator()(const T& value) const { os << value; }
|
void operator()(const T& value) const { os << value; }
|
||||||
|
|
||||||
void operator()(const date& value) const { ::date::operator<<(os, value); }
|
void operator()(const date& value) const { ::date::operator<<(os, value); }
|
||||||
void operator()(const time& value) const
|
void operator()(const time& value) const
|
||||||
{
|
{
|
||||||
char buffer [100] {};
|
char buffer [100] {};
|
||||||
const char* sign = value < std::chrono::microseconds(0) ? "-" : "";
|
const char* sign = value < std::chrono::microseconds(0) ? "-" : "";
|
||||||
auto hours = std::abs(std::chrono::duration_cast<std::chrono::hours>(value).count());
|
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 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 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());
|
auto micros = std::abs((value % std::chrono::seconds(1)).count());
|
||||||
|
|
||||||
snprintf(buffer, sizeof(buffer), "%s%02d:%02u:%02u:%06u",
|
snprintf(buffer, sizeof(buffer), "%s%02d:%02u:%02u:%06u",
|
||||||
sign,
|
sign,
|
||||||
static_cast<int>(hours),
|
static_cast<int>(hours),
|
||||||
static_cast<unsigned>(mins),
|
static_cast<unsigned>(mins),
|
||||||
static_cast<unsigned>(secs),
|
static_cast<unsigned>(secs),
|
||||||
static_cast<unsigned>(micros)
|
static_cast<unsigned>(micros)
|
||||||
);
|
);
|
||||||
|
|
||||||
os << buffer;
|
os << buffer;
|
||||||
}
|
}
|
||||||
void operator()(const datetime& value) const { ::date::operator<<(os, value); }
|
void operator()(const datetime& value) const { ::date::operator<<(os, value); }
|
||||||
void operator()(std::nullptr_t) const { os << "<NULL>"; }
|
void operator()(std::nullptr_t) const { os << "<NULL>"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
@ -68,41 +68,41 @@ struct print_visitor
|
|||||||
} // boost
|
} // boost
|
||||||
|
|
||||||
inline bool boost::mysql::operator==(
|
inline bool boost::mysql::operator==(
|
||||||
const value& lhs,
|
const value& lhs,
|
||||||
const value& rhs
|
const value& rhs
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (lhs.index() != rhs.index()) return false;
|
if (lhs.index() != rhs.index()) return false;
|
||||||
return std::visit([&lhs](const auto& rhs_value) {
|
return std::visit([&lhs](const auto& rhs_value) {
|
||||||
using T = std::decay_t<decltype(rhs_value)>;
|
using T = std::decay_t<decltype(rhs_value)>;
|
||||||
return std::get<T>(lhs) == rhs_value;
|
return std::get<T>(lhs) == rhs_value;
|
||||||
}, rhs);
|
}, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool boost::mysql::operator==(
|
inline bool boost::mysql::operator==(
|
||||||
const std::vector<value>& lhs,
|
const std::vector<value>& lhs,
|
||||||
const std::vector<value>& rhs
|
const std::vector<value>& rhs
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
return detail::container_equals(lhs, rhs);
|
return detail::container_equals(lhs, rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::ostream& boost::mysql::operator<<(
|
inline std::ostream& boost::mysql::operator<<(
|
||||||
std::ostream& os,
|
std::ostream& os,
|
||||||
const value& value
|
const value& value
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::visit(detail::print_visitor(os), value);
|
std::visit(detail::print_visitor(os), value);
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
std::array<boost::mysql::value, sizeof...(Types)>
|
std::array<boost::mysql::value, sizeof...(Types)>
|
||||||
boost::mysql::make_values(
|
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))...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,104 +23,104 @@ namespace mysql {
|
|||||||
*/
|
*/
|
||||||
class field_metadata
|
class field_metadata
|
||||||
{
|
{
|
||||||
detail::column_definition_packet msg_;
|
detail::column_definition_packet msg_;
|
||||||
mutable field_type field_type_ { field_type::_not_computed };
|
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:
|
public:
|
||||||
/// Default constructor.
|
/// Default constructor.
|
||||||
field_metadata() = default;
|
field_metadata() = default;
|
||||||
|
|
||||||
// Private, do not use.
|
// Private, do not use.
|
||||||
field_metadata(const detail::column_definition_packet& msg) noexcept: msg_(msg) {};
|
field_metadata(const detail::column_definition_packet& msg) noexcept: msg_(msg) {};
|
||||||
|
|
||||||
/// Returns the name of the database (schema) the field belongs to.
|
/// Returns the name of the database (schema) the field belongs to.
|
||||||
std::string_view database() const noexcept { return msg_.schema.value; }
|
std::string_view database() const noexcept { return msg_.schema.value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns the name of the virtual table the field belongs to.
|
* \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
|
* \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").
|
* (e.g. in "SELECT * FROM employees emp", table() will be "emp").
|
||||||
*/
|
*/
|
||||||
std::string_view table() const noexcept { return msg_.table.value; }
|
std::string_view table() const noexcept { return msg_.table.value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns the name of the physical table the field belongs to.
|
* \brief Returns the name of the physical table the field belongs to.
|
||||||
* \details E.g. in "SELECT * FROM employees emp",
|
* \details E.g. in "SELECT * FROM employees emp",
|
||||||
* original_table() will be "employees".
|
* original_table() will be "employees".
|
||||||
*/
|
*/
|
||||||
std::string_view original_table() const noexcept { return msg_.org_table.value; }
|
std::string_view original_table() const noexcept { return msg_.org_table.value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns the actual name of the field.
|
* \brief Returns the actual name of the field.
|
||||||
* \details If the field was aliased, this will be the name of the alias
|
* \details If the field was aliased, this will be the name of the alias
|
||||||
* (e.g. in "SELECT id AS employee_id FROM employees",
|
* (e.g. in "SELECT id AS employee_id FROM employees",
|
||||||
* field_name() will be "employee_id").
|
* field_name() will be "employee_id").
|
||||||
*/
|
*/
|
||||||
std::string_view field_name() const noexcept { return msg_.name.value; }
|
std::string_view field_name() const noexcept { return msg_.name.value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns the original (physical) name of the field.
|
* \brief Returns the original (physical) name of the field.
|
||||||
* \details E.g. in "SELECT id AS employee_id FROM employees",
|
* \details E.g. in "SELECT id AS employee_id FROM employees",
|
||||||
* original_field_name() will be "id".
|
* original_field_name() will be "id".
|
||||||
*/
|
*/
|
||||||
std::string_view original_field_name() const noexcept { return msg_.org_name.value; }
|
std::string_view original_field_name() const noexcept { return msg_.org_name.value; }
|
||||||
|
|
||||||
/// Returns the maximum length of the field.
|
/// Returns the maximum length of the field.
|
||||||
unsigned column_length() const noexcept { return msg_.column_length.value; }
|
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).
|
/// Returns the type of the field (see field_type for more info).
|
||||||
field_type type() const noexcept;
|
field_type type() const noexcept;
|
||||||
|
|
||||||
/// Returns the number of decimals of the field.
|
/// Returns the number of decimals of the field.
|
||||||
unsigned decimals() const noexcept { return msg_.decimals.value; }
|
unsigned decimals() const noexcept { return msg_.decimals.value; }
|
||||||
|
|
||||||
/// Returns true if the field is not allowed to be NULL, false if it is nullable.
|
/// 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); }
|
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.
|
/// 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); }
|
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).
|
/// 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); }
|
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).
|
/// 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); }
|
bool is_multiple_key() const noexcept { return flag_set(detail::column_flags::multiple_key); }
|
||||||
|
|
||||||
/// Returns true if the field has no sign.
|
/// Returns true if the field has no sign.
|
||||||
bool is_unsigned() const noexcept { return flag_set(detail::column_flags::unsigned_); }
|
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).
|
/// 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); }
|
bool is_zerofill() const noexcept { return flag_set(detail::column_flags::zerofill); }
|
||||||
|
|
||||||
/// Returns true if the field is defined as 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); }
|
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.
|
/// 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); }
|
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.
|
/// 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); }
|
bool is_set_to_now_on_update() const noexcept { return flag_set(detail::column_flags::on_update_now); }
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class resultset_metadata
|
class resultset_metadata
|
||||||
{
|
{
|
||||||
std::vector<bytestring> buffers_;
|
std::vector<bytestring> buffers_;
|
||||||
std::vector<field_metadata> fields_;
|
std::vector<field_metadata> fields_;
|
||||||
public:
|
public:
|
||||||
resultset_metadata() = default;
|
resultset_metadata() = default;
|
||||||
resultset_metadata(std::vector<bytestring>&& buffers, std::vector<field_metadata>&& fields):
|
resultset_metadata(std::vector<bytestring>&& buffers, std::vector<field_metadata>&& fields):
|
||||||
buffers_(std::move(buffers)), fields_(std::move(fields)) {};
|
buffers_(std::move(buffers)), fields_(std::move(fields)) {};
|
||||||
resultset_metadata(const resultset_metadata&) = delete;
|
resultset_metadata(const resultset_metadata&) = delete;
|
||||||
resultset_metadata(resultset_metadata&&) = default;
|
resultset_metadata(resultset_metadata&&) = default;
|
||||||
resultset_metadata& operator=(const resultset_metadata&) = delete;
|
resultset_metadata& operator=(const resultset_metadata&) = delete;
|
||||||
resultset_metadata& operator=(resultset_metadata&&) = default;
|
resultset_metadata& operator=(resultset_metadata&&) = default;
|
||||||
~resultset_metadata() = default;
|
~resultset_metadata() = default;
|
||||||
const auto& fields() const noexcept { return fields_; }
|
const auto& fields() const noexcept { return fields_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@ -64,122 +64,122 @@ constexpr std::array<value, 0> no_statement_params {};
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
class prepared_statement
|
class prepared_statement
|
||||||
{
|
{
|
||||||
detail::channel<Stream>* channel_ {};
|
detail::channel<Stream>* channel_ {};
|
||||||
detail::com_stmt_prepare_ok_packet stmt_msg_;
|
detail::com_stmt_prepare_ok_packet stmt_msg_;
|
||||||
|
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
void check_num_params(ForwardIterator first, ForwardIterator last, error_code& err, error_info& info) const;
|
void check_num_params(ForwardIterator first, ForwardIterator last, error_code& err, error_info& info) const;
|
||||||
public:
|
public:
|
||||||
/// Default constructor.
|
/// Default constructor.
|
||||||
prepared_statement() = default;
|
prepared_statement() = default;
|
||||||
|
|
||||||
// Private. Do not use.
|
// Private. Do not use.
|
||||||
prepared_statement(detail::channel<Stream>& chan, const detail::com_stmt_prepare_ok_packet& msg) noexcept:
|
prepared_statement(detail::channel<Stream>& chan, const detail::com_stmt_prepare_ok_packet& msg) noexcept:
|
||||||
channel_(&chan), stmt_msg_(msg) {}
|
channel_(&chan), stmt_msg_(msg) {}
|
||||||
|
|
||||||
/// Retrieves the stream object associated with the underlying connection.
|
/// Retrieves the stream object associated with the underlying connection.
|
||||||
Stream& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
|
Stream& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
|
||||||
|
|
||||||
/// Retrieves the stream object associated with the underlying connection.
|
/// Retrieves the stream object associated with the underlying connection.
|
||||||
const Stream& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
|
const Stream& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
|
||||||
|
|
||||||
/// Returns true if the statement is not a default-constructed object.
|
/// Returns true if the statement is not a default-constructed object.
|
||||||
bool valid() const noexcept { return channel_ != nullptr; }
|
bool valid() const noexcept { return channel_ != nullptr; }
|
||||||
|
|
||||||
/// Returns a server-side identifier for the statement (unique in a per-connection basis).
|
/// 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; }
|
unsigned id() const noexcept { assert(valid()); return stmt_msg_.statement_id.value; }
|
||||||
|
|
||||||
/// Returns the number of parameters that should be passed to execute().
|
/// Returns the number of parameters that should be passed to execute().
|
||||||
unsigned num_params() const noexcept { assert(valid()); return stmt_msg_.num_params.value; }
|
unsigned num_params() const noexcept { assert(valid()); return stmt_msg_.num_params.value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Executes a statement (collection, sync with error code version).
|
* \brief Executes a statement (collection, sync with error code version).
|
||||||
* \details Collection should be a sequence for which std::begin() and
|
* \details Collection should be a sequence for which std::begin() and
|
||||||
* std::end() return forward iterators to a valid boost::mysql::value range.
|
* std::end() return forward iterators to a valid boost::mysql::value range.
|
||||||
* Use no_statement_params to execute a statement with no params.
|
* Use no_statement_params to execute a statement with no params.
|
||||||
*/
|
*/
|
||||||
template <typename Collection>
|
template <typename Collection>
|
||||||
resultset<Stream> execute(const Collection& params, error_code& err, error_info& info) const
|
resultset<Stream> execute(const Collection& params, error_code& err, error_info& info) const
|
||||||
{
|
{
|
||||||
return execute(std::begin(params), std::end(params), err, info);
|
return execute(std::begin(params), std::end(params), err, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a statement (collection, sync with exceptions code version).
|
/// Executes a statement (collection, sync with exceptions code version).
|
||||||
template <typename Collection>
|
template <typename Collection>
|
||||||
resultset<Stream> execute(const Collection& params) const
|
resultset<Stream> execute(const Collection& params) const
|
||||||
{
|
{
|
||||||
return execute(std::begin(params), std::end(params));
|
return execute(std::begin(params), std::end(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The handler signature for execute.
|
/// The handler signature for execute.
|
||||||
using execute_signature = void(error_code, resultset<Stream>);
|
using execute_signature = void(error_code, resultset<Stream>);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Executes a statement (collection, sync with exceptions code version).
|
* \brief Executes a statement (collection, sync with exceptions code version).
|
||||||
* \details It is **not** necessary to keep the collection of parameters or the
|
* \details It is **not** necessary to keep the collection of parameters or the
|
||||||
* values they may point to alive.
|
* values they may point to alive.
|
||||||
*/
|
*/
|
||||||
template <typename Collection, typename CompletionToken>
|
template <typename Collection, typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
|
||||||
async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr) const
|
async_execute(const Collection& params, CompletionToken&& token, error_info* info=nullptr) const
|
||||||
{
|
{
|
||||||
return async_execute(
|
return async_execute(
|
||||||
std::begin(params),
|
std::begin(params),
|
||||||
std::end(params),
|
std::end(params),
|
||||||
std::forward<CompletionToken>(token),
|
std::forward<CompletionToken>(token),
|
||||||
info
|
info
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Executes a statement (iterator, sync with error code version).
|
* \brief Executes a statement (iterator, sync with error code version).
|
||||||
* \details params_first and params_last should point to a valid range, identifying
|
* \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
|
* 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.
|
* of these iterators should be convertible to boost::mysql::value.
|
||||||
*/
|
*/
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last,
|
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last,
|
||||||
error_code&, error_info&) const;
|
error_code&, error_info&) const;
|
||||||
|
|
||||||
/// Executes a statement (iterator, sync with exceptions version).
|
/// Executes a statement (iterator, sync with exceptions version).
|
||||||
template <typename ForwardIterator>
|
template <typename ForwardIterator>
|
||||||
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last) const;
|
resultset<Stream> execute(ForwardIterator params_first, ForwardIterator params_last) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Executes a statement (iterator, async version).
|
* \brief Executes a statement (iterator, async version).
|
||||||
* \details The sequence [params_first, params_last) and the values that may be pointed
|
* \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.
|
* by the elements of the sequence need **not** be kept alive.
|
||||||
*/
|
*/
|
||||||
template <typename ForwardIterator, typename CompletionToken>
|
template <typename ForwardIterator, typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, execute_signature)
|
||||||
async_execute(ForwardIterator params_first, ForwardIterator params_last,
|
async_execute(ForwardIterator params_first, ForwardIterator params_last,
|
||||||
CompletionToken&& token, error_info* info=nullptr) const;
|
CompletionToken&& token, error_info* info=nullptr) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Closes a prepared statement, deallocating it from the server (sync with error code version).
|
* \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.
|
* \details This function may be used to free resources from the server.
|
||||||
* Closing the parent connection object causes all prepared statements associated
|
* Closing the parent connection object causes all prepared statements associated
|
||||||
* with the connection to also be deallocated from the server. Thus, you should be
|
* 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
|
* 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. 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.
|
* 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
|
* 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.
|
* statement, other than assignment. Failing to do so results in undefined behavior.
|
||||||
*/
|
*/
|
||||||
void close(error_code&, error_info&);
|
void close(error_code&, error_info&);
|
||||||
|
|
||||||
/// Closes a prepared statement, deallocating it from the server (sync with exceptions version).
|
/// Closes a prepared statement, deallocating it from the server (sync with exceptions version).
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
/// The handler signature for close.
|
/// The handler signature for close.
|
||||||
using close_signature = void(error_code);
|
using close_signature = void(error_code);
|
||||||
|
|
||||||
/// Closes a prepared statement, deallocating it from the server (async version).
|
/// Closes a prepared statement, deallocating it from the server (async version).
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, close_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, close_signature)
|
||||||
async_close(CompletionToken&& token, error_info* info=nullptr);
|
async_close(CompletionToken&& token, error_info* info=nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,148 +62,148 @@ namespace mysql {
|
|||||||
* resultset, other than assignment. Resultsets are movable but not copyable.
|
* resultset, other than assignment. Resultsets are movable but not copyable.
|
||||||
*/
|
*/
|
||||||
template <
|
template <
|
||||||
typename StreamType
|
typename StreamType
|
||||||
>
|
>
|
||||||
class resultset
|
class resultset
|
||||||
{
|
{
|
||||||
using channel_type = detail::channel<StreamType>;
|
using channel_type = detail::channel<StreamType>;
|
||||||
|
|
||||||
detail::deserialize_row_fn deserializer_ {};
|
detail::deserialize_row_fn deserializer_ {};
|
||||||
channel_type* channel_;
|
channel_type* channel_;
|
||||||
detail::resultset_metadata meta_;
|
detail::resultset_metadata meta_;
|
||||||
row current_row_;
|
row current_row_;
|
||||||
detail::bytestring buffer_;
|
detail::bytestring buffer_;
|
||||||
detail::ok_packet ok_packet_;
|
detail::ok_packet ok_packet_;
|
||||||
bool eof_received_ {false};
|
bool eof_received_ {false};
|
||||||
public:
|
public:
|
||||||
/// Default constructor.
|
/// Default constructor.
|
||||||
resultset(): channel_(nullptr) {};
|
resultset(): channel_(nullptr) {};
|
||||||
|
|
||||||
// Private, do not use
|
// Private, do not use
|
||||||
resultset(channel_type& channel, detail::resultset_metadata&& meta, detail::deserialize_row_fn deserializer):
|
resultset(channel_type& channel, detail::resultset_metadata&& meta, detail::deserialize_row_fn deserializer):
|
||||||
deserializer_(deserializer), channel_(&channel), meta_(std::move(meta)) {};
|
deserializer_(deserializer), channel_(&channel), meta_(std::move(meta)) {};
|
||||||
resultset(channel_type& channel, detail::bytestring&& buffer, const detail::ok_packet& ok_pack):
|
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) {};
|
channel_(&channel), buffer_(std::move(buffer)), ok_packet_(ok_pack), eof_received_(true) {};
|
||||||
|
|
||||||
/// Retrieves the stream object associated with the underlying connection.
|
/// Retrieves the stream object associated with the underlying connection.
|
||||||
StreamType& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
|
StreamType& next_layer() noexcept { assert(channel_); return channel_->next_layer(); }
|
||||||
|
|
||||||
/// Retrieves the stream object associated with the underlying connection.
|
/// Retrieves the stream object associated with the underlying connection.
|
||||||
const StreamType& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
|
const StreamType& next_layer() const noexcept { assert(channel_); return channel_->next_layer(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Fetches a single row (sync with error code version).
|
* \brief Fetches a single row (sync with error code version).
|
||||||
* \details The returned object will be nullptr if there are no more rows
|
* \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.
|
* to be read. Calling fetch_one on a complete resultset returns nullptr.
|
||||||
*
|
*
|
||||||
* The returned row points into memory owned by the resultset. Destroying
|
* The returned row points into memory owned by the resultset. Destroying
|
||||||
* or moving the resultset object invalidates the returned row. Calling
|
* or moving the resultset object invalidates the returned row. Calling
|
||||||
* any of the fetch methods again does also invalidate the returned row.
|
* any of the fetch methods again does also invalidate the returned row.
|
||||||
* fetch_one is the fetch method that performs the less memory allocations
|
* fetch_one is the fetch method that performs the less memory allocations
|
||||||
* of the three.
|
* of the three.
|
||||||
*/
|
*/
|
||||||
const row* fetch_one(error_code& err, error_info& info);
|
const row* fetch_one(error_code& err, error_info& info);
|
||||||
|
|
||||||
/// Fetches a single row (sync with exceptions version).
|
/// Fetches a single row (sync with exceptions version).
|
||||||
const row* fetch_one();
|
const row* fetch_one();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Fetches at most count rows (sync with error code version).
|
* \brief Fetches at most count rows (sync with error code version).
|
||||||
* \details The returned rows are guaranteed to be valid as long as the
|
* \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
|
* resultset is alive. Contrary to fetch_one, subsequent calls to any of
|
||||||
* the fetch methods do not invalidate the returned rows.
|
* the fetch methods do not invalidate the returned rows.
|
||||||
*
|
*
|
||||||
* Only if count is **greater** than the available number of rows,
|
* Only if count is **greater** than the available number of rows,
|
||||||
* the resultset will be completed.
|
* the resultset will be completed.
|
||||||
*/
|
*/
|
||||||
std::vector<owning_row> fetch_many(std::size_t count, error_code& err, error_info& info);
|
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).
|
/// Fetches at most count rows (sync with exceptions version).
|
||||||
std::vector<owning_row> fetch_many(std::size_t count);
|
std::vector<owning_row> fetch_many(std::size_t count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Fetches all available rows (sync with error code version).
|
* \brief Fetches all available rows (sync with error code version).
|
||||||
* \details The returned rows are guaranteed to be valid as long as the
|
* \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
|
* resultset is alive. Contrary to fetch_one, subsequent calls to any of
|
||||||
* the fetch methods do not invalidate the returned rows.
|
* the fetch methods do not invalidate the returned rows.
|
||||||
*
|
*
|
||||||
* The resultset is guaranteed to be complete() after this call returns.
|
* The resultset is guaranteed to be complete() after this call returns.
|
||||||
*/
|
*/
|
||||||
std::vector<owning_row> fetch_all(error_code& err, error_info& info);
|
std::vector<owning_row> fetch_all(error_code& err, error_info& info);
|
||||||
|
|
||||||
/// Fetches all available rows (sync with exceptions version).
|
/// Fetches all available rows (sync with exceptions version).
|
||||||
std::vector<owning_row> fetch_all();
|
std::vector<owning_row> fetch_all();
|
||||||
|
|
||||||
/// Handler signature for fetch_one.
|
/// Handler signature for fetch_one.
|
||||||
using fetch_one_signature = void(error_code, const row*);
|
using fetch_one_signature = void(error_code, const row*);
|
||||||
|
|
||||||
/// Fetchs a single row (async version).
|
/// Fetchs a single row (async version).
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_one_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_one_signature)
|
||||||
async_fetch_one(CompletionToken&& token, error_info* info=nullptr);
|
async_fetch_one(CompletionToken&& token, error_info* info=nullptr);
|
||||||
|
|
||||||
/// Handler signature for fetch_many.
|
/// Handler signature for fetch_many.
|
||||||
using fetch_many_signature = void(error_code, std::vector<owning_row>);
|
using fetch_many_signature = void(error_code, std::vector<owning_row>);
|
||||||
|
|
||||||
/// Fetches at most count rows (async version).
|
/// Fetches at most count rows (async version).
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_many_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_many_signature)
|
||||||
async_fetch_many(std::size_t count, CompletionToken&& token, error_info* info=nullptr);
|
async_fetch_many(std::size_t count, CompletionToken&& token, error_info* info=nullptr);
|
||||||
|
|
||||||
/// Handler signature for fetch_all.
|
/// Handler signature for fetch_all.
|
||||||
using fetch_all_signature = void(error_code, std::vector<owning_row>);
|
using fetch_all_signature = void(error_code, std::vector<owning_row>);
|
||||||
|
|
||||||
/// Fetches all available rows (async version).
|
/// Fetches all available rows (async version).
|
||||||
template <typename CompletionToken>
|
template <typename CompletionToken>
|
||||||
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_all_signature)
|
BOOST_MYSQL_INITFN_RESULT_TYPE(CompletionToken, fetch_all_signature)
|
||||||
async_fetch_all(CompletionToken&& token, error_info* info=nullptr);
|
async_fetch_all(CompletionToken&& token, error_info* info=nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns whether this object represents a valid resultset.
|
* \brief Returns whether this object represents a valid resultset.
|
||||||
* \details Returns false for default-constructed resultsets. It is
|
* \details Returns false for default-constructed resultsets. It is
|
||||||
* undefined to call any member function on an invalid resultset,
|
* undefined to call any member function on an invalid resultset,
|
||||||
* except assignment.
|
* except assignment.
|
||||||
*/
|
*/
|
||||||
bool valid() const noexcept { return channel_ != nullptr; }
|
bool valid() const noexcept { return channel_ != nullptr; }
|
||||||
|
|
||||||
/// Returns whether the resultset has been completely read or not.
|
/// Returns whether the resultset has been completely read or not.
|
||||||
bool complete() const noexcept { return eof_received_; }
|
bool complete() const noexcept { return eof_received_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Returns metadata about the fields in the query.
|
* \brief Returns metadata about the fields in the query.
|
||||||
* \details There will be as many field_metadata objects as fields
|
* \details There will be as many field_metadata objects as fields
|
||||||
* in the SQL query, and in the same order. For SQL statements
|
* in the SQL query, and in the same order. For SQL statements
|
||||||
* that do not return values (like UPDATEs), it will be empty.
|
* that do not return values (like UPDATEs), it will be empty.
|
||||||
* \see field_metadata for more details.
|
* \see field_metadata for more details.
|
||||||
*/
|
*/
|
||||||
const std::vector<field_metadata>& fields() const noexcept { return meta_.fields(); }
|
const std::vector<field_metadata>& fields() const noexcept { return meta_.fields(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief The number of rows affected by the SQL that generated this resultset.
|
* \brief The number of rows affected by the SQL that generated this resultset.
|
||||||
* \warning The resultset **must be complete** before calling this function.
|
* \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; }
|
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.
|
* \brief The last insert ID produced by the SQL that generated this resultset.
|
||||||
* \warning The resultset **must be complete** before calling this function.
|
* \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; }
|
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.
|
* \brief The number of warnings produced by the SQL that generated this resultset.
|
||||||
* \warning The resultset **must be complete** before calling this function.
|
* \warning The resultset **must be complete** before calling this function.
|
||||||
*/
|
*/
|
||||||
unsigned warning_count() const noexcept { assert(complete()); return ok_packet_.warnings.value; }
|
unsigned warning_count() const noexcept { assert(complete()); return ok_packet_.warnings.value; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Additionat text information about the execution of
|
* \brief Additionat text information about the execution of
|
||||||
* the SQL that generated this resultset.
|
* the SQL that generated this resultset.
|
||||||
* \warning The resultset **must be complete** before calling this function.
|
* \warning The resultset **must be complete** before calling this function.
|
||||||
*/
|
*/
|
||||||
std::string_view info() const noexcept { assert(complete()); return ok_packet_.info.value; }
|
std::string_view info() const noexcept { assert(complete()); return ok_packet_.info.value; }
|
||||||
|
|
||||||
// TODO: status flags accessors
|
// TODO: status flags accessors
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,17 +32,17 @@ namespace mysql {
|
|||||||
*/
|
*/
|
||||||
class row
|
class row
|
||||||
{
|
{
|
||||||
std::vector<value> values_;
|
std::vector<value> values_;
|
||||||
public:
|
public:
|
||||||
/// Default and initializing constructor.
|
/// Default and initializing constructor.
|
||||||
row(std::vector<value>&& values = {}):
|
row(std::vector<value>&& values = {}):
|
||||||
values_(std::move(values)) {};
|
values_(std::move(values)) {};
|
||||||
|
|
||||||
/// Accessor for the sequence of values.
|
/// Accessor for the sequence of values.
|
||||||
const std::vector<value>& values() const noexcept { return values_; }
|
const std::vector<value>& values() const noexcept { return values_; }
|
||||||
|
|
||||||
/// Accessor for the sequence of values.
|
/// Accessor for the sequence of values.
|
||||||
std::vector<value>& values() noexcept { return values_; }
|
std::vector<value>& values() noexcept { return values_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,16 +52,16 @@ public:
|
|||||||
*/
|
*/
|
||||||
class owning_row : public row
|
class owning_row : public row
|
||||||
{
|
{
|
||||||
detail::bytestring buffer_;
|
detail::bytestring buffer_;
|
||||||
public:
|
public:
|
||||||
owning_row() = default;
|
owning_row() = default;
|
||||||
owning_row(std::vector<value>&& values, detail::bytestring&& buffer) :
|
owning_row(std::vector<value>&& values, detail::bytestring&& buffer) :
|
||||||
row(std::move(values)), buffer_(std::move(buffer)) {};
|
row(std::move(values)), buffer_(std::move(buffer)) {};
|
||||||
owning_row(const owning_row&) = delete;
|
owning_row(const owning_row&) = delete;
|
||||||
owning_row(owning_row&&) = default;
|
owning_row(owning_row&&) = default;
|
||||||
owning_row& operator=(const owning_row&) = delete;
|
owning_row& operator=(const owning_row&) = delete;
|
||||||
owning_row& operator=(owning_row&&) = default;
|
owning_row& operator=(owning_row&&) = default;
|
||||||
~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)
|
inline std::ostream& operator<<(std::ostream& os, const row& value)
|
||||||
{
|
{
|
||||||
os << '{';
|
os << '{';
|
||||||
const auto& arr = value.values();
|
const auto& arr = value.values();
|
||||||
if (!arr.empty())
|
if (!arr.empty())
|
||||||
{
|
{
|
||||||
os << arr[0];
|
os << arr[0];
|
||||||
for (auto it = std::next(arr.begin()); it != arr.end(); ++it)
|
for (auto it = std::next(arr.begin()); it != arr.end(); ++it)
|
||||||
{
|
{
|
||||||
os << ", " << *it;
|
os << ", " << *it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return os << '}';
|
return os << '}';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow comparisons between vectors of rows and owning rows
|
// Allow comparisons between vectors of rows and owning rows
|
||||||
template <
|
template <
|
||||||
typename RowTypeLeft,
|
typename RowTypeLeft,
|
||||||
typename RowTypeRight,
|
typename RowTypeRight,
|
||||||
typename=std::enable_if_t<std::is_base_of_v<row, RowTypeLeft> && std::is_base_of_v<row, 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)
|
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 <
|
template <
|
||||||
typename RowTypeLeft,
|
typename RowTypeLeft,
|
||||||
typename RowTypeRight,
|
typename RowTypeRight,
|
||||||
typename=std::enable_if_t<std::is_base_of_v<row, RowTypeLeft> && std::is_base_of_v<row, 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)
|
inline bool operator!=(const std::vector<RowTypeLeft>& lhs, const std::vector<RowTypeRight>& rhs)
|
||||||
{
|
{
|
||||||
return !(lhs == rhs);
|
return !(lhs == rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // mysql
|
} // mysql
|
||||||
|
@ -57,17 +57,17 @@ using time = std::chrono::microseconds;
|
|||||||
* They are represented as binary strings.
|
* They are represented as binary strings.
|
||||||
*/
|
*/
|
||||||
using value = std::variant<
|
using value = std::variant<
|
||||||
std::int32_t, // signed TINYINT, SMALLINT, MEDIUMINT, INT
|
std::int32_t, // signed TINYINT, SMALLINT, MEDIUMINT, INT
|
||||||
std::int64_t, // signed BIGINT
|
std::int64_t, // signed BIGINT
|
||||||
std::uint32_t, // unsigned TINYINT, SMALLINT, MEDIUMINT, INT, YEAR
|
std::uint32_t, // unsigned TINYINT, SMALLINT, MEDIUMINT, INT, YEAR
|
||||||
std::uint64_t, // unsigned BIGINT
|
std::uint64_t, // unsigned BIGINT
|
||||||
std::string_view, // CHAR, VARCHAR, BINARY, VARBINARY, TEXT (all sizes), BLOB (all sizes), ENUM, SET, DECIMAL, BIT, GEOMTRY
|
std::string_view, // CHAR, VARCHAR, BINARY, VARBINARY, TEXT (all sizes), BLOB (all sizes), ENUM, SET, DECIMAL, BIT, GEOMTRY
|
||||||
float, // FLOAT
|
float, // FLOAT
|
||||||
double, // DOUBLE
|
double, // DOUBLE
|
||||||
date, // DATE
|
date, // DATE
|
||||||
datetime, // DATETIME, TIMESTAMP
|
datetime, // DATETIME, TIMESTAMP
|
||||||
time, // TIME
|
time, // TIME
|
||||||
std::nullptr_t // Any of the above when the value is NULL
|
std::nullptr_t // Any of the above when the value is NULL
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,150 +22,150 @@ endif()
|
|||||||
# Unit testing. We do not use gtest_discover_tests because
|
# Unit testing. We do not use gtest_discover_tests because
|
||||||
# of the runtime penalty (specially considerable in integration tests)
|
# of the runtime penalty (specially considerable in integration tests)
|
||||||
add_executable(
|
add_executable(
|
||||||
mysql_unittests
|
mysql_unittests
|
||||||
unit/detail/auth/auth_calculator.cpp
|
unit/detail/auth/auth_calculator.cpp
|
||||||
unit/detail/protocol/serialization_test_common.cpp
|
unit/detail/protocol/serialization_test_common.cpp
|
||||||
unit/detail/protocol/serialization.cpp
|
unit/detail/protocol/serialization.cpp
|
||||||
unit/detail/protocol/common_messages.cpp
|
unit/detail/protocol/common_messages.cpp
|
||||||
unit/detail/protocol/handshake_messages.cpp
|
unit/detail/protocol/handshake_messages.cpp
|
||||||
unit/detail/protocol/query_messages.cpp
|
unit/detail/protocol/query_messages.cpp
|
||||||
unit/detail/protocol/prepared_statement_messages.cpp
|
unit/detail/protocol/prepared_statement_messages.cpp
|
||||||
unit/detail/protocol/capabilities.cpp
|
unit/detail/protocol/capabilities.cpp
|
||||||
unit/detail/protocol/text_deserialization.cpp
|
unit/detail/protocol/text_deserialization.cpp
|
||||||
unit/detail/protocol/binary_deserialization.cpp
|
unit/detail/protocol/binary_deserialization.cpp
|
||||||
unit/detail/protocol/null_bitmap_traits.cpp
|
unit/detail/protocol/null_bitmap_traits.cpp
|
||||||
unit/metadata.cpp
|
unit/metadata.cpp
|
||||||
unit/value.cpp
|
unit/value.cpp
|
||||||
unit/row.cpp
|
unit/row.cpp
|
||||||
unit/error.cpp
|
unit/error.cpp
|
||||||
unit/prepared_statement.cpp
|
unit/prepared_statement.cpp
|
||||||
)
|
)
|
||||||
# A codegen issue in MSVC C++17 makes gmock expectations not work
|
# A codegen issue in MSVC C++17 makes gmock expectations not work
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
target_sources(mysql_unittests PRIVATE unit/detail/protocol/channel.cpp)
|
target_sources(mysql_unittests PRIVATE unit/detail/protocol/channel.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
mysql_unittests
|
mysql_unittests
|
||||||
PRIVATE
|
PRIVATE
|
||||||
common
|
common
|
||||||
)
|
)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
mysql_unittests
|
mysql_unittests
|
||||||
PRIVATE
|
PRIVATE
|
||||||
gtest
|
gtest
|
||||||
gtest_main
|
gtest_main
|
||||||
gmock
|
gmock
|
||||||
mysql_asio
|
mysql_asio
|
||||||
)
|
)
|
||||||
_mysql_common_target_settings(mysql_unittests)
|
_mysql_common_target_settings(mysql_unittests)
|
||||||
|
|
||||||
if (BOOST_MYSQL_VALGRIND_TESTS)
|
if (BOOST_MYSQL_VALGRIND_TESTS)
|
||||||
Mysqlvalgrind_AddMemcheckTest(
|
Mysqlvalgrind_AddMemcheckTest(
|
||||||
NAME mysql_unittests_memcheck
|
NAME mysql_unittests_memcheck
|
||||||
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
|
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
|
||||||
)
|
)
|
||||||
else()
|
else()
|
||||||
add_test(
|
add_test(
|
||||||
NAME mysql_unittests
|
NAME mysql_unittests
|
||||||
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
|
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/mysql_unittests
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Integration testing
|
# Integration testing
|
||||||
find_package(Boost REQUIRED COMPONENTS coroutine)
|
find_package(Boost REQUIRED COMPONENTS coroutine)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
mysql_integrationtests
|
mysql_integrationtests
|
||||||
integration/metadata_validator.cpp
|
integration/metadata_validator.cpp
|
||||||
integration/network_functions.cpp
|
integration/network_functions.cpp
|
||||||
integration/handshake.cpp
|
integration/handshake.cpp
|
||||||
integration/query.cpp
|
integration/query.cpp
|
||||||
integration/prepare_statement.cpp
|
integration/prepare_statement.cpp
|
||||||
integration/execute_statement.cpp
|
integration/execute_statement.cpp
|
||||||
integration/close_statement.cpp
|
integration/close_statement.cpp
|
||||||
integration/resultset.cpp
|
integration/resultset.cpp
|
||||||
integration/prepared_statement_lifecycle.cpp
|
integration/prepared_statement_lifecycle.cpp
|
||||||
integration/database_types.cpp
|
integration/database_types.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
mysql_integrationtests
|
mysql_integrationtests
|
||||||
PRIVATE
|
PRIVATE
|
||||||
gtest
|
gtest
|
||||||
gtest_main
|
gtest_main
|
||||||
gmock
|
gmock
|
||||||
mysql_asio
|
mysql_asio
|
||||||
Boost::coroutine
|
Boost::coroutine
|
||||||
)
|
)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
mysql_integrationtests
|
mysql_integrationtests
|
||||||
PRIVATE
|
PRIVATE
|
||||||
common
|
common
|
||||||
)
|
)
|
||||||
_mysql_common_target_settings(mysql_integrationtests)
|
_mysql_common_target_settings(mysql_integrationtests)
|
||||||
|
|
||||||
# Integration test setup
|
# Integration test setup
|
||||||
_mysql_sql_setup_fixture(
|
_mysql_sql_setup_fixture(
|
||||||
TEST_NAME mysql_integrationtests_setup
|
TEST_NAME mysql_integrationtests_setup
|
||||||
FIXTURE_NAME mysql_integrationtests_fixture
|
FIXTURE_NAME mysql_integrationtests_fixture
|
||||||
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup.sql
|
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup.sql
|
||||||
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
|
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
|
||||||
)
|
)
|
||||||
|
|
||||||
# Regular integration tests
|
# Regular integration tests
|
||||||
add_test(
|
add_test(
|
||||||
NAME mysql_integrationtests
|
NAME mysql_integrationtests
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
|
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
|
||||||
"--gtest_filter=-*RequiresSha256*" # Exclude anything using SHA256
|
"--gtest_filter=-*RequiresSha256*" # Exclude anything using SHA256
|
||||||
)
|
)
|
||||||
set_tests_properties(
|
set_tests_properties(
|
||||||
mysql_integrationtests
|
mysql_integrationtests
|
||||||
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
|
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
|
||||||
)
|
)
|
||||||
|
|
||||||
# If we are using memcheck, then run a subset of the integration tests
|
# If we are using memcheck, then run a subset of the integration tests
|
||||||
# under valgrind. Coroutine tests don't work well under Valgrind, and
|
# under valgrind. Coroutine tests don't work well under Valgrind, and
|
||||||
# SSL tests are too slow
|
# SSL tests are too slow
|
||||||
if (BOOST_MYSQL_VALGRIND_TESTS)
|
if (BOOST_MYSQL_VALGRIND_TESTS)
|
||||||
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
|
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
|
||||||
set(GTEST_FILTER "--gtest_filter=-*sslrequire*:*coroutine*")
|
set(GTEST_FILTER "--gtest_filter=-*sslrequire*:*coroutine*")
|
||||||
else()
|
else()
|
||||||
set(GTEST_FILTER "--gtest_filter=-*RequiresSha256*:*sslrequire*:*coroutine*")
|
set(GTEST_FILTER "--gtest_filter=-*RequiresSha256*:*sslrequire*:*coroutine*")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_test(
|
add_test(
|
||||||
NAME mysql_integrationtests_memcheck
|
NAME mysql_integrationtests_memcheck
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
|
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
|
||||||
${GTEST_FILTER}
|
${GTEST_FILTER}
|
||||||
)
|
)
|
||||||
set_tests_properties(
|
set_tests_properties(
|
||||||
mysql_integrationtests_memcheck
|
mysql_integrationtests_memcheck
|
||||||
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
|
PROPERTIES FIXTURES_REQUIRED mysql_integrationtests_setup
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SHA256 tests
|
# SHA256 tests
|
||||||
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
|
if (DEFINED ENV{BOOST_MYSQL_HAS_SHA256})
|
||||||
# Setup
|
# Setup
|
||||||
_mysql_sql_setup_fixture(
|
_mysql_sql_setup_fixture(
|
||||||
TEST_NAME mysql_integrationtests_sha256_setup
|
TEST_NAME mysql_integrationtests_sha256_setup
|
||||||
FIXTURE_NAME mysql_integrationtests_sha256_fixture
|
FIXTURE_NAME mysql_integrationtests_sha256_fixture
|
||||||
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup_sha256.sql
|
SQL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/integration/db_setup_sha256.sql
|
||||||
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
|
SKIP_VAR BOOST_MYSQL_SKIP_DB_SETUP
|
||||||
)
|
)
|
||||||
|
|
||||||
# Actual tests
|
# Actual tests
|
||||||
add_test(
|
add_test(
|
||||||
NAME mysql_integrationtests_sha256
|
NAME mysql_integrationtests_sha256
|
||||||
COMMAND
|
COMMAND
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
|
${CMAKE_CURRENT_BINARY_DIR}/mysql_integrationtests
|
||||||
"--gtest_filter=*RequiresSha256*" # Run only SHA256 stuff
|
"--gtest_filter=*RequiresSha256*" # Run only SHA256 stuff
|
||||||
)
|
)
|
||||||
set_tests_properties(
|
set_tests_properties(
|
||||||
mysql_integrationtests_sha256
|
mysql_integrationtests_sha256
|
||||||
PROPERTIES FIXTURES_REQUIRED
|
PROPERTIES FIXTURES_REQUIRED
|
||||||
"mysql_integrationtests_fixture;mysql_integrationtests_sha256_fixture"
|
"mysql_integrationtests_fixture;mysql_integrationtests_sha256_fixture"
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
@ -31,146 +31,146 @@ namespace test {
|
|||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
std::vector<value> makevalues(Types&&... args)
|
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>
|
template <typename... Types>
|
||||||
row makerow(Types&&... args)
|
row makerow(Types&&... args)
|
||||||
{
|
{
|
||||||
return row(makevalues(std::forward<Types>(args)...));
|
return row(makevalues(std::forward<Types>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Types>
|
template <typename... Types>
|
||||||
std::vector<row> makerows(std::size_t row_size, Types&&... args)
|
std::vector<row> makerows(std::size_t row_size, Types&&... args)
|
||||||
{
|
{
|
||||||
auto values = makevalues(std::forward<Types>(args)...);
|
auto values = makevalues(std::forward<Types>(args)...);
|
||||||
assert(values.size() % row_size == 0);
|
assert(values.size() % row_size == 0);
|
||||||
std::vector<row> res;
|
std::vector<row> res;
|
||||||
for (std::size_t i = 0; i < values.size(); i += row_size)
|
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);
|
std::vector<value> row_values (values.begin() + i, values.begin() + i + row_size);
|
||||||
res.push_back(row(std::move(row_values)));
|
res.push_back(row(std::move(row_values)));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline date makedate(int years, int months, int days)
|
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)
|
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))) +
|
return mysql::datetime(mysql::date(::date::year(years)/::date::month(months)/::date::day(days))) +
|
||||||
std::chrono::hours(hours) + std::chrono::minutes(mins) +
|
std::chrono::hours(hours) + std::chrono::minutes(mins) +
|
||||||
std::chrono::seconds(secs) + std::chrono::microseconds(micros);
|
std::chrono::seconds(secs) + std::chrono::microseconds(micros);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mysql::time maket(int hours, int mins, int secs, int micros=0)
|
inline mysql::time maket(int hours, int mins, int secs, int micros=0)
|
||||||
{
|
{
|
||||||
return std::chrono::hours(hours) + std::chrono::minutes(mins)
|
return std::chrono::hours(hours) + std::chrono::minutes(mins)
|
||||||
+ std::chrono::seconds(secs) + std::chrono::microseconds(micros);
|
+ std::chrono::seconds(secs) + std::chrono::microseconds(micros);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N>
|
template <std::size_t N>
|
||||||
inline std::string_view makesv(const char (&value) [N])
|
inline std::string_view makesv(const char (&value) [N])
|
||||||
{
|
{
|
||||||
static_assert(N>=1);
|
static_assert(N>=1);
|
||||||
return std::string_view(value, N-1); // discard null terminator
|
return std::string_view(value, N-1); // discard null terminator
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::size_t N>
|
template <std::size_t N>
|
||||||
inline std::string_view makesv(const std::uint8_t (&value) [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)
|
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)
|
inline void validate_string_contains(std::string value, const std::vector<std::string>& to_check)
|
||||||
{
|
{
|
||||||
std::transform(value.begin(), value.end(), value.begin(), &tolower);
|
std::transform(value.begin(), value.end(), value.begin(), &tolower);
|
||||||
for (const auto& elm: to_check)
|
for (const auto& elm: to_check)
|
||||||
{
|
{
|
||||||
EXPECT_THAT(value, testing::HasSubstr(elm));
|
EXPECT_THAT(value, testing::HasSubstr(elm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void validate_error_info(const boost::mysql::error_info& value, const std::vector<std::string>& to_check)
|
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)
|
inline std::string buffer_diff(std::string_view s0, std::string_view s1)
|
||||||
{
|
{
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << std::hex;
|
ss << std::hex;
|
||||||
for (std::size_t i = 0; i < std::min(s0.size(), s1.size()); ++i)
|
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 b0 = reinterpret_cast<const std::uint8_t*>(s0.data())[i];
|
||||||
unsigned b1 = reinterpret_cast<const std::uint8_t*>(s1.data())[i];
|
unsigned b1 = reinterpret_cast<const std::uint8_t*>(s1.data())[i];
|
||||||
if (b0 != b1)
|
if (b0 != b1)
|
||||||
{
|
{
|
||||||
ss << "i=" << i << ": " << b0 << " != " << b1 << "\n";
|
ss << "i=" << i << ": " << b0 << " != " << b1 << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s0.size() != s1.size())
|
if (s0.size() != s1.size())
|
||||||
{
|
{
|
||||||
ss << "sizes: " << s0.size() << " != " << s1.size() << "\n";
|
ss << "sizes: " << s0.size() << " != " << s1.size() << "\n";
|
||||||
}
|
}
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void compare_buffers(std::string_view s0, std::string_view s1, const char* msg = "")
|
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)
|
inline void concat(std::vector<std::uint8_t>& lhs, boost::asio::const_buffer rhs)
|
||||||
{
|
{
|
||||||
auto current_size = lhs.size();
|
auto current_size = lhs.size();
|
||||||
lhs.resize(current_size + rhs.size());
|
lhs.resize(current_size + rhs.size());
|
||||||
memcpy(lhs.data() + current_size, rhs.data(), 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)
|
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(
|
inline std::vector<std::uint8_t> concat_copy(
|
||||||
std::vector<uint8_t>&& lhs,
|
std::vector<uint8_t>&& lhs,
|
||||||
const std::vector<uint8_t>& rhs
|
const std::vector<uint8_t>& rhs
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
concat(lhs, rhs);
|
concat(lhs, rhs);
|
||||||
return std::move(lhs);
|
return std::move(lhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void check_call(const std::string& command)
|
inline void check_call(const std::string& command)
|
||||||
{
|
{
|
||||||
int code = std::system(command.c_str());
|
int code = std::system(command.c_str());
|
||||||
if (code != 0) // we are assuming 0 means success
|
if (code != 0) // we are assuming 0 means success
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
detail::stringize("Command '", command, "' returned status code ", code)
|
detail::stringize("Command '", command, "' returned status code ", code)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char* to_string(ssl_mode m)
|
inline const char* to_string(ssl_mode m)
|
||||||
{
|
{
|
||||||
switch (m)
|
switch (m)
|
||||||
{
|
{
|
||||||
case ssl_mode::disable: return "ssldisable";
|
case ssl_mode::disable: return "ssldisable";
|
||||||
case ssl_mode::enable: return "sslenable";
|
case ssl_mode::enable: return "sslenable";
|
||||||
case ssl_mode::require: return "sslrequire";
|
case ssl_mode::require: return "sslrequire";
|
||||||
default: assert(false); return "";
|
default: assert(false); return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct named_param {};
|
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; }
|
std::ostream& operator<<(std::ostream& os, const T& v) { return os << v.name; }
|
||||||
|
|
||||||
constexpr auto test_name_generator = [](const auto& param_info) {
|
constexpr auto test_name_generator = [](const auto& param_info) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << param_info.param;
|
os << param_info.param;
|
||||||
std::string res = os.str();
|
std::string res = os.str();
|
||||||
std::replace_if(res.begin(), res.end(), [](char c) { return !std::isalnum(c); }, '_');
|
std::replace_if(res.begin(), res.end(), [](char c) { return !std::isalnum(c); }, '_');
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // test
|
} // test
|
||||||
|
@ -17,31 +17,31 @@ template <typename Stream>
|
|||||||
struct CloseStatementTest : NetworkTest<Stream>
|
struct CloseStatementTest : NetworkTest<Stream>
|
||||||
{
|
{
|
||||||
|
|
||||||
void ExistingOrClosedStatement()
|
void ExistingOrClosedStatement()
|
||||||
{
|
{
|
||||||
auto* net = this->GetParam().net;
|
auto* net = this->GetParam().net;
|
||||||
|
|
||||||
// Prepare a statement
|
// Prepare a statement
|
||||||
auto stmt = net->prepare_statement(this->conn, "SELECT * FROM empty_table");
|
auto stmt = net->prepare_statement(this->conn, "SELECT * FROM empty_table");
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
|
|
||||||
// Verify it works fine
|
// Verify it works fine
|
||||||
auto exec_result = net->execute_statement(stmt.value, {});
|
auto exec_result = net->execute_statement(stmt.value, {});
|
||||||
exec_result.validate_no_error();
|
exec_result.validate_no_error();
|
||||||
exec_result.value.fetch_all(); // clean all packets sent for this execution
|
exec_result.value.fetch_all(); // clean all packets sent for this execution
|
||||||
|
|
||||||
// Close the statement
|
// Close the statement
|
||||||
auto close_result = net->close_statement(stmt.value);
|
auto close_result = net->close_statement(stmt.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
|
|
||||||
// Close it again
|
// Close it again
|
||||||
close_result = net->close_statement(stmt.value);
|
close_result = net->close_statement(stmt.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
|
|
||||||
// Verify close took effect
|
// Verify close took effect
|
||||||
exec_result = net->execute_statement(stmt.value, {});
|
exec_result = net->execute_statement(stmt.value, {});
|
||||||
exec_result.validate_error(boost::mysql::errc::unknown_stmt_handler, {"unknown prepared statement"});
|
exec_result.validate_error(boost::mysql::errc::unknown_stmt_handler, {"unknown prepared statement"});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(CloseStatementTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(CloseStatementTest)
|
||||||
|
@ -32,118 +32,118 @@ namespace
|
|||||||
*/
|
*/
|
||||||
struct database_types_testcase
|
struct database_types_testcase
|
||||||
{
|
{
|
||||||
std::string table;
|
std::string table;
|
||||||
std::string field;
|
std::string field;
|
||||||
std::string row_id;
|
std::string row_id;
|
||||||
value expected_value;
|
value expected_value;
|
||||||
meta_validator mvalid;
|
meta_validator mvalid;
|
||||||
|
|
||||||
template <typename ValueType, typename... Args>
|
template <typename ValueType, typename... Args>
|
||||||
database_types_testcase(std::string table, std::string field, std::string row_id,
|
database_types_testcase(std::string table, std::string field, std::string row_id,
|
||||||
ValueType&& expected_value, Args&&... args) :
|
ValueType&& expected_value, Args&&... args) :
|
||||||
table(table),
|
table(table),
|
||||||
field(field),
|
field(field),
|
||||||
row_id(move(row_id)),
|
row_id(move(row_id)),
|
||||||
expected_value(std::forward<ValueType>(expected_value)),
|
expected_value(std::forward<ValueType>(expected_value)),
|
||||||
mvalid(move(table), move(field), std::forward<Args>(args)...)
|
mvalid(move(table), move(field), std::forward<Args>(args)...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, const database_types_testcase& v)
|
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
|
// Note: NetworkTest with do_handshake=true requires GetParam() to have an ssl data member
|
||||||
struct DatabaseTypesTest :
|
struct DatabaseTypesTest :
|
||||||
NetworkTest<boost::asio::ip::tcp::socket, database_types_testcase, false>
|
NetworkTest<boost::asio::ip::tcp::socket, database_types_testcase, false>
|
||||||
{
|
{
|
||||||
DatabaseTypesTest()
|
DatabaseTypesTest()
|
||||||
{
|
{
|
||||||
handshake(boost::mysql::ssl_mode::disable);
|
handshake(boost::mysql::ssl_mode::disable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_P(DatabaseTypesTest, Query_MetadataAndValueCorrect)
|
TEST_P(DatabaseTypesTest, Query_MetadataAndValueCorrect)
|
||||||
{
|
{
|
||||||
const auto& param = GetParam();
|
const auto& param = GetParam();
|
||||||
|
|
||||||
// Compose the query
|
// Compose the query
|
||||||
std::ostringstream queryss;
|
std::ostringstream queryss;
|
||||||
queryss << "SELECT " << param.field
|
queryss << "SELECT " << param.field
|
||||||
<< " FROM " << param.table
|
<< " FROM " << param.table
|
||||||
<< " WHERE id = '" << param.row_id << "'";
|
<< " WHERE id = '" << param.row_id << "'";
|
||||||
auto query = queryss.str();
|
auto query = queryss.str();
|
||||||
|
|
||||||
// Execute it
|
// Execute it
|
||||||
auto result = conn.query(query);
|
auto result = conn.query(query);
|
||||||
auto rows = result.fetch_all();
|
auto rows = result.fetch_all();
|
||||||
|
|
||||||
// Validate the received metadata
|
// Validate the received metadata
|
||||||
validate_meta(result.fields(), {param.mvalid});
|
validate_meta(result.fields(), {param.mvalid});
|
||||||
|
|
||||||
// Validate the returned value
|
// Validate the returned value
|
||||||
row expected_row ({param.expected_value});
|
row expected_row ({param.expected_value});
|
||||||
ASSERT_EQ(rows.size(), 1);
|
ASSERT_EQ(rows.size(), 1);
|
||||||
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
|
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(DatabaseTypesTest, PreparedStatementExecuteResult_MetadataAndValueCorrect)
|
TEST_P(DatabaseTypesTest, PreparedStatementExecuteResult_MetadataAndValueCorrect)
|
||||||
{
|
{
|
||||||
// Parameter
|
// Parameter
|
||||||
const auto& param = GetParam();
|
const auto& param = GetParam();
|
||||||
|
|
||||||
// Prepare the statement
|
// Prepare the statement
|
||||||
std::ostringstream stmtss;
|
std::ostringstream stmtss;
|
||||||
stmtss << "SELECT " << param.field
|
stmtss << "SELECT " << param.field
|
||||||
<< " FROM " << param.table
|
<< " FROM " << param.table
|
||||||
<< " WHERE id = ?";
|
<< " WHERE id = ?";
|
||||||
auto stmt_sql = stmtss.str();
|
auto stmt_sql = stmtss.str();
|
||||||
auto stmt = conn.prepare_statement(stmt_sql);
|
auto stmt = conn.prepare_statement(stmt_sql);
|
||||||
|
|
||||||
// Execute it with the provided parameters
|
// Execute it with the provided parameters
|
||||||
auto result = stmt.execute(makevalues(param.row_id));
|
auto result = stmt.execute(makevalues(param.row_id));
|
||||||
auto rows = result.fetch_all();
|
auto rows = result.fetch_all();
|
||||||
|
|
||||||
// Validate the received metadata
|
// Validate the received metadata
|
||||||
validate_meta(result.fields(), {param.mvalid});
|
validate_meta(result.fields(), {param.mvalid});
|
||||||
|
|
||||||
// Validate the returned value
|
// Validate the returned value
|
||||||
row expected_row ({param.expected_value});
|
row expected_row ({param.expected_value});
|
||||||
ASSERT_EQ(rows.size(), 1);
|
ASSERT_EQ(rows.size(), 1);
|
||||||
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
|
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(DatabaseTypesTest, PreparedStatementExecuteParam_ValueSerializedCorrectly)
|
TEST_P(DatabaseTypesTest, PreparedStatementExecuteParam_ValueSerializedCorrectly)
|
||||||
{
|
{
|
||||||
// This test is not applicable (yet) to nullptr values or bit values.
|
// This test is not applicable (yet) to nullptr values or bit values.
|
||||||
// Doing "field = ?" where ? is nullptr never matches anything.
|
// Doing "field = ?" where ? is nullptr never matches anything.
|
||||||
// Bit values are returned as strings bit need to be sent as integers in
|
// Bit values are returned as strings bit need to be sent as integers in
|
||||||
// prepared statements. TODO: make this better.
|
// prepared statements. TODO: make this better.
|
||||||
const auto& param = GetParam();
|
const auto& param = GetParam();
|
||||||
if (param.expected_value == value(nullptr) ||
|
if (param.expected_value == value(nullptr) ||
|
||||||
param.mvalid.type() == field_type::bit)
|
param.mvalid.type() == field_type::bit)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the statement
|
// Prepare the statement
|
||||||
std::ostringstream stmtss;
|
std::ostringstream stmtss;
|
||||||
stmtss << "SELECT " << param.field
|
stmtss << "SELECT " << param.field
|
||||||
<< " FROM " << param.table
|
<< " FROM " << param.table
|
||||||
<< " WHERE id = ? AND " << param.field << " = ?";
|
<< " WHERE id = ? AND " << param.field << " = ?";
|
||||||
auto stmt_sql = stmtss.str();
|
auto stmt_sql = stmtss.str();
|
||||||
auto stmt = conn.prepare_statement(stmt_sql);
|
auto stmt = conn.prepare_statement(stmt_sql);
|
||||||
|
|
||||||
// Execute it with the provided parameters
|
// Execute it with the provided parameters
|
||||||
auto result = stmt.execute(makevalues(param.row_id, param.expected_value));
|
auto result = stmt.execute(makevalues(param.row_id, param.expected_value));
|
||||||
auto rows = result.fetch_all();
|
auto rows = result.fetch_all();
|
||||||
|
|
||||||
// Validate the returned value
|
// Validate the returned value
|
||||||
row expected_row ({param.expected_value});
|
row expected_row ({param.expected_value});
|
||||||
ASSERT_EQ(rows.size(), 1);
|
ASSERT_EQ(rows.size(), 1);
|
||||||
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
|
EXPECT_EQ(static_cast<const row&>(rows[0]), expected_row); // make gtest pick operator<<
|
||||||
}
|
}
|
||||||
|
|
||||||
using flagsvec = std::vector<meta_validator::flag_getter>;
|
using flagsvec = std::vector<meta_validator::flag_getter>;
|
||||||
@ -154,147 +154,147 @@ const flagsvec flags_zerofill { &field_metadata::is_unsigned, &field_metadata::i
|
|||||||
|
|
||||||
// Integers
|
// Integers
|
||||||
INSTANTIATE_TEST_SUITE_P(TINYINT, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "min", std::uint32_t(0), field_type::tinyint, flags_zerofill)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(SMALLINT, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "min", std::uint32_t(0), field_type::smallint, flags_zerofill)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(MEDIUMINT, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "min", std::uint32_t(0), field_type::mediumint, flags_zerofill)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(INT, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "min", std::uint32_t(0), field_type::int_, flags_zerofill)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(BIGINT, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "min", std::uint64_t(0), field_type::bigint, flags_zerofill)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// Floating point
|
// Floating point
|
||||||
INSTANTIATE_TEST_SUITE_P(FLOAT, DatabaseTypesTest, Values(
|
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", "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_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", "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_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", "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_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_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_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", "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", "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", "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", "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", "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_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", "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", "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", "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", "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", "negative_exp_positive_fractional", 3.14e-20f, field_type::float_, flags_zerofill, 31)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(DOUBLE, DatabaseTypesTest, Values(
|
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", "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_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", "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_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", "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_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_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_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", "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", "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", "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", "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", "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_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", "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", "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", "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", "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", "negative_exp_positive_fractional", 3.14e-200, field_type::double_, flags_zerofill, 31)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// Dates and times
|
// Dates and times
|
||||||
INSTANTIATE_TEST_SUITE_P(DATE, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "max", 9999_y/12/31_d, field_type::date)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// Infrastructure to generate DATETIME and TIMESTAMP test cases
|
// 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
|
// Given a number of microseconds, removes the least significant part according to decimals
|
||||||
int round_micros(int input, int decimals)
|
int round_micros(int input, int decimals)
|
||||||
{
|
{
|
||||||
assert(decimals >= 0 && decimals <= 6);
|
assert(decimals >= 0 && decimals <= 6);
|
||||||
if (decimals == 0) return 0;
|
if (decimals == 0) return 0;
|
||||||
auto modulus = static_cast<int>(std::pow(10, 6 - decimals));
|
auto modulus = static_cast<int>(std::pow(10, 6 - decimals));
|
||||||
return (input / modulus) * modulus;
|
return (input / modulus) * modulus;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::microseconds round_micros(std::chrono::microseconds input, int decimals)
|
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)
|
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
|
// id represents which components (h, m, s, u) should the test case have
|
||||||
constexpr struct
|
constexpr struct
|
||||||
{
|
{
|
||||||
char letter;
|
char letter;
|
||||||
std::chrono::microseconds offset;
|
std::chrono::microseconds offset;
|
||||||
} bit_meaning [] = {
|
} bit_meaning [] = {
|
||||||
{ 'h', std::chrono::hours(23) }, // bit 0
|
{ 'h', std::chrono::hours(23) }, // bit 0
|
||||||
{ 'm', std::chrono::minutes(1) },
|
{ 'm', std::chrono::minutes(1) },
|
||||||
{ 's', std::chrono::seconds(50) },
|
{ 's', std::chrono::seconds(50) },
|
||||||
{ 'u', std::chrono::microseconds(123456) }
|
{ 'u', std::chrono::microseconds(123456) }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
datetime dt = makedt(2010, 5, 2); // components all tests have
|
datetime dt = makedt(2010, 5, 2); // components all tests have
|
||||||
|
|
||||||
for (std::size_t i = 0; i < id.size(); ++i)
|
for (std::size_t i = 0; i < id.size(); ++i)
|
||||||
{
|
{
|
||||||
if (id[i]) // component present
|
if (id[i]) // component present
|
||||||
{
|
{
|
||||||
char letter = bit_meaning[i].letter;
|
char letter = bit_meaning[i].letter;
|
||||||
auto offset = bit_meaning[i].offset;
|
auto offset = bit_meaning[i].offset;
|
||||||
name.push_back(letter); // add to name
|
name.push_back(letter); // add to name
|
||||||
dt += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
|
dt += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (name.empty()) name = "date";
|
if (name.empty()) name = "date";
|
||||||
|
|
||||||
return {name, dt};
|
return {name, dt};
|
||||||
}
|
}
|
||||||
|
|
||||||
database_types_testcase create_datetime_testcase(
|
database_types_testcase create_datetime_testcase(
|
||||||
int decimals,
|
int decimals,
|
||||||
std::string id,
|
std::string id,
|
||||||
value expected,
|
value expected,
|
||||||
field_type type
|
field_type type
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
static std::unordered_map<field_type, const char*> table_map {
|
static std::unordered_map<field_type, const char*> table_map {
|
||||||
{ field_type::datetime, "types_datetime" },
|
{ field_type::datetime, "types_datetime" },
|
||||||
{ field_type::timestamp, "types_timestamp" },
|
{ field_type::timestamp, "types_timestamp" },
|
||||||
{ field_type::time, "types_time" }
|
{ field_type::time, "types_time" }
|
||||||
};
|
};
|
||||||
// Inconsistencies between Maria and MySQL in the unsigned flag
|
// Inconsistencies between Maria and MySQL in the unsigned flag
|
||||||
// we don't really care here about signedness of timestamps
|
// we don't really care here about signedness of timestamps
|
||||||
return database_types_testcase(
|
return database_types_testcase(
|
||||||
table_map.at(type),
|
table_map.at(type),
|
||||||
"field_" + std::to_string(decimals),
|
"field_" + std::to_string(decimals),
|
||||||
std::move(id),
|
std::move(id),
|
||||||
expected,
|
expected,
|
||||||
type,
|
type,
|
||||||
no_flags,
|
no_flags,
|
||||||
decimals,
|
decimals,
|
||||||
flagsvec{ &field_metadata::is_unsigned }
|
flagsvec{ &field_metadata::is_unsigned }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, boost::mysql::time> time_from_id(std::bitset<6> id, int decimals)
|
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
|
// id represents which components (h, m, s, u) should the test case have
|
||||||
constexpr struct
|
constexpr struct
|
||||||
{
|
{
|
||||||
char letter;
|
char letter;
|
||||||
std::chrono::microseconds offset;
|
std::chrono::microseconds offset;
|
||||||
} bit_meaning [] = {
|
} bit_meaning [] = {
|
||||||
{ 'n', std::chrono::hours(0) }, // bit 0: negative bit
|
{ 'n', std::chrono::hours(0) }, // bit 0: negative bit
|
||||||
{ 'd', std::chrono::hours(48) }, // bit 1
|
{ 'd', std::chrono::hours(48) }, // bit 1
|
||||||
{ 'h', std::chrono::hours(23) }, // bit 2
|
{ 'h', std::chrono::hours(23) }, // bit 2
|
||||||
{ 'm', std::chrono::minutes(1) },
|
{ 'm', std::chrono::minutes(1) },
|
||||||
{ 's', std::chrono::seconds(50) },
|
{ 's', std::chrono::seconds(50) },
|
||||||
{ 'u', std::chrono::microseconds(123456) }
|
{ 'u', std::chrono::microseconds(123456) }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
boost::mysql::time t {0};
|
boost::mysql::time t {0};
|
||||||
|
|
||||||
for (std::size_t i = 1; i < id.size(); ++i)
|
for (std::size_t i = 1; i < id.size(); ++i)
|
||||||
{
|
{
|
||||||
if (id[i]) // component present
|
if (id[i]) // component present
|
||||||
{
|
{
|
||||||
char letter = bit_meaning[i].letter;
|
char letter = bit_meaning[i].letter;
|
||||||
auto offset = bit_meaning[i].offset;
|
auto offset = bit_meaning[i].offset;
|
||||||
name.push_back(letter); // add to name
|
name.push_back(letter); // add to name
|
||||||
t += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
|
t += letter == 'u' ? round_micros(offset, decimals) : offset; // add to value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (name.empty()) name = "zero";
|
if (name.empty()) name = "zero";
|
||||||
if (id[0]) // bit sign
|
if (id[0]) // bit sign
|
||||||
{
|
{
|
||||||
name = "negative_" + name;
|
name = "negative_" + name;
|
||||||
t = -t;
|
t = -t;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {name, t};
|
return {name, t};
|
||||||
}
|
}
|
||||||
|
|
||||||
// shared between DATETIME and TIMESTAMP
|
// shared between DATETIME and TIMESTAMP
|
||||||
std::vector<database_types_testcase> generate_common_datetime_cases(
|
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)
|
for (int decimals = 0; decimals <= 6; ++decimals)
|
||||||
{
|
{
|
||||||
// Regular values
|
// Regular values
|
||||||
auto max_int_id = static_cast<std::size_t>(std::pow(2, 4)); // 4 components can be varied
|
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)
|
for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
|
||||||
{
|
{
|
||||||
std::bitset<4> bitset_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
|
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);
|
auto [id, value] = datetime_from_id(int_id, decimals);
|
||||||
res.push_back(create_datetime_testcase(decimals, move(id), value, type));
|
res.push_back(create_datetime_testcase(decimals, move(id), value, type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<database_types_testcase> generate_datetime_cases()
|
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
|
// min and max
|
||||||
for (int decimals = 0; decimals <= 6; ++decimals)
|
for (int decimals = 0; decimals <= 6; ++decimals)
|
||||||
{
|
{
|
||||||
res.push_back(create_datetime_testcase(decimals, "min",
|
res.push_back(create_datetime_testcase(decimals, "min",
|
||||||
makedt(1000, 1, 1), field_type::datetime));
|
makedt(1000, 1, 1), field_type::datetime));
|
||||||
res.push_back(create_datetime_testcase(decimals, "max",
|
res.push_back(create_datetime_testcase(decimals, "max",
|
||||||
makedt(9999, 12, 31, 23, 59, 59, round_micros(999999, decimals)), field_type::datetime));
|
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()
|
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> generate_time_cases()
|
||||||
{
|
{
|
||||||
std::vector<database_types_testcase> res;
|
std::vector<database_types_testcase> res;
|
||||||
|
|
||||||
for (int decimals = 0; decimals <= 6; ++decimals)
|
for (int decimals = 0; decimals <= 6; ++decimals)
|
||||||
{
|
{
|
||||||
// Regular values
|
// Regular values
|
||||||
auto max_int_id = static_cast<std::size_t>(std::pow(2, 6)); // 6 components can be varied
|
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)
|
for (std::size_t int_id = 0; int_id < max_int_id; ++int_id)
|
||||||
{
|
{
|
||||||
std::bitset<6> bitset_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[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
|
if (bitset_id.to_ulong() == 1) continue; // negative zero does not make sense
|
||||||
auto [id, value] = time_from_id(int_id, decimals);
|
auto [id, value] = time_from_id(int_id, decimals);
|
||||||
res.push_back(create_datetime_testcase(decimals, move(id), value, field_type::time));
|
res.push_back(create_datetime_testcase(decimals, move(id), value, field_type::time));
|
||||||
}
|
}
|
||||||
|
|
||||||
// min and max
|
// min and max
|
||||||
auto max_value = decimals == 0 ? maket(838, 59, 59) : maket(838, 59, 58, round_micros(999999, decimals));
|
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, "min", -max_value, field_type::time));
|
||||||
res.push_back(create_datetime_testcase(decimals, "max", 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(TIME, DatabaseTypesTest, ValuesIn(generate_time_cases()), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(YEAR, DatabaseTypesTest, Values(
|
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", "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", "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", "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", "zero", std::uint32_t(0), field_type::year, flags_zerofill)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(STRING, DatabaseTypesTest, Values(
|
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", "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", "utf8", u8"\u00F1", field_type::char_),
|
||||||
database_types_testcase("types_string", "field_char", "empty", "", 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", "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", "utf8", u8"\u00D1", field_type::varchar),
|
||||||
database_types_testcase("types_string", "field_varchar", "empty", "", 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", "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", "utf8", u8"\u00e1", field_type::text),
|
||||||
database_types_testcase("types_string", "field_tinytext", "empty", "", 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", "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", "utf8", u8"\u00e9", field_type::text),
|
||||||
database_types_testcase("types_string", "field_text", "empty", "", 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", "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", "utf8", u8"\u00ed", field_type::text),
|
||||||
database_types_testcase("types_string", "field_mediumtext", "empty", "", 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", "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", "utf8", u8"\u00f3", field_type::text),
|
||||||
database_types_testcase("types_string", "field_longtext", "empty", "", 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", "regular", "red,green", field_type::set),
|
||||||
database_types_testcase("types_string", "field_set", "empty", "", field_type::set)
|
database_types_testcase("types_string", "field_set", "empty", "", field_type::set)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(BINARY, DatabaseTypesTest, Values(
|
INSTANTIATE_TEST_SUITE_P(BINARY, DatabaseTypesTest, Values(
|
||||||
// BINARY values get padded with zeros to the declared length
|
// 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", "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", "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_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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "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", "empty", "", field_type::blob)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// These types do not have a more concrete representation in the library yet.
|
// 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
|
// Check we get them as strings and we get the metadata correctly
|
||||||
std::uint8_t geometry_value [] = {
|
std::uint8_t geometry_value [] = {
|
||||||
0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00,
|
||||||
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x40
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x40
|
||||||
};
|
};
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(NOT_IMPLEMENTED_TYPES, DatabaseTypesTest, Values(
|
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_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_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_geometry", "regular", makesv(geometry_value), field_type::geometry)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// Tests for certain metadata flags and NULL values
|
// Tests for certain metadata flags and NULL values
|
||||||
INSTANTIATE_TEST_SUITE_P(METADATA_FLAGS, DatabaseTypesTest, Values(
|
INSTANTIATE_TEST_SUITE_P(METADATA_FLAGS, DatabaseTypesTest, Values(
|
||||||
database_types_testcase("types_flags", "field_timestamp", "default", nullptr, field_type::timestamp,
|
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}),
|
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_,
|
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,
|
flagsvec{&field_metadata::is_primary_key, &field_metadata::is_not_null,
|
||||||
&field_metadata::is_auto_increment}),
|
&field_metadata::is_auto_increment}),
|
||||||
database_types_testcase("types_flags", "field_not_null", "default", "char", field_type::char_,
|
database_types_testcase("types_flags", "field_not_null", "default", "char", field_type::char_,
|
||||||
flagsvec{&field_metadata::is_not_null}),
|
flagsvec{&field_metadata::is_not_null}),
|
||||||
database_types_testcase("types_flags", "field_unique", "default", std::int32_t(21), field_type::int_,
|
database_types_testcase("types_flags", "field_unique", "default", std::int32_t(21), field_type::int_,
|
||||||
flagsvec{&field_metadata::is_unique_key}),
|
flagsvec{&field_metadata::is_unique_key}),
|
||||||
database_types_testcase("types_flags", "field_indexed", "default", std::int32_t(42), field_type::int_,
|
database_types_testcase("types_flags", "field_indexed", "default", std::int32_t(42), field_type::int_,
|
||||||
flagsvec{&field_metadata::is_multiple_key})
|
flagsvec{&field_metadata::is_multiple_key})
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,25 +30,25 @@ INSERT INTO updates_table (field_varchar, field_int)
|
|||||||
VALUES ('f0', 42), ('f1', 43), ('fnull', NULL);
|
VALUES ('f0', 42), ('f1', 43), ('fnull', NULL);
|
||||||
|
|
||||||
CREATE TABLE empty_table (
|
CREATE TABLE empty_table (
|
||||||
id INT,
|
id INT,
|
||||||
field_varchar VARCHAR(255)
|
field_varchar VARCHAR(255)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE one_row_table (
|
CREATE TABLE one_row_table (
|
||||||
id INT,
|
id INT,
|
||||||
field_varchar VARCHAR(255)
|
field_varchar VARCHAR(255)
|
||||||
);
|
);
|
||||||
INSERT INTO one_row_table VALUES (1, 'f0');
|
INSERT INTO one_row_table VALUES (1, 'f0');
|
||||||
|
|
||||||
CREATE TABLE two_rows_table (
|
CREATE TABLE two_rows_table (
|
||||||
id INT,
|
id INT,
|
||||||
field_varchar VARCHAR(255)
|
field_varchar VARCHAR(255)
|
||||||
);
|
);
|
||||||
INSERT INTO two_rows_table VALUES (1, 'f0'), (2, 'f1');
|
INSERT INTO two_rows_table VALUES (1, 'f0'), (2, 'f1');
|
||||||
|
|
||||||
CREATE TABLE three_rows_table (
|
CREATE TABLE three_rows_table (
|
||||||
id INT,
|
id INT,
|
||||||
field_varchar VARCHAR(255)
|
field_varchar VARCHAR(255)
|
||||||
);
|
);
|
||||||
INSERT INTO three_rows_table VALUES (1, 'f0'), (2, 'f1'), (3, 'f2');
|
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
|
-- Integer types
|
||||||
CREATE TABLE types_tinyint(
|
CREATE TABLE types_tinyint(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed TINYINT,
|
field_signed TINYINT,
|
||||||
field_unsigned TINYINT UNSIGNED,
|
field_unsigned TINYINT UNSIGNED,
|
||||||
field_width TINYINT(4),
|
field_width TINYINT(4),
|
||||||
field_zerofill TINYINT(6) ZEROFILL
|
field_zerofill TINYINT(6) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_tinyint VALUES
|
INSERT INTO types_tinyint VALUES
|
||||||
("regular", 20, 20, 20, 20),
|
("regular", 20, 20, 20, 20),
|
||||||
("negative", -20, NULL, -20, NULL),
|
("negative", -20, NULL, -20, NULL),
|
||||||
("min", -0x80, 0, NULL, 0),
|
("min", -0x80, 0, NULL, 0),
|
||||||
("max", 0x7f, 0xff, NULL, NULL)
|
("max", 0x7f, 0xff, NULL, NULL)
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_smallint(
|
CREATE TABLE types_smallint(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed SMALLINT,
|
field_signed SMALLINT,
|
||||||
field_unsigned SMALLINT UNSIGNED,
|
field_unsigned SMALLINT UNSIGNED,
|
||||||
field_width SMALLINT(8),
|
field_width SMALLINT(8),
|
||||||
field_zerofill SMALLINT(7) ZEROFILL
|
field_zerofill SMALLINT(7) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_smallint VALUES
|
INSERT INTO types_smallint VALUES
|
||||||
("regular", 20, 20, 20, 20),
|
("regular", 20, 20, 20, 20),
|
||||||
("negative", -20, NULL, -20, NULL),
|
("negative", -20, NULL, -20, NULL),
|
||||||
("min", -0x8000, 0, NULL, 0),
|
("min", -0x8000, 0, NULL, 0),
|
||||||
("max", 0x7fff, 0xffff, NULL, NULL)
|
("max", 0x7fff, 0xffff, NULL, NULL)
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_mediumint(
|
CREATE TABLE types_mediumint(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed MEDIUMINT,
|
field_signed MEDIUMINT,
|
||||||
field_unsigned MEDIUMINT UNSIGNED,
|
field_unsigned MEDIUMINT UNSIGNED,
|
||||||
field_width MEDIUMINT(8),
|
field_width MEDIUMINT(8),
|
||||||
field_zerofill MEDIUMINT(7) ZEROFILL
|
field_zerofill MEDIUMINT(7) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_mediumint VALUES
|
INSERT INTO types_mediumint VALUES
|
||||||
("regular", 20, 20, 20, 20),
|
("regular", 20, 20, 20, 20),
|
||||||
("negative", -20, NULL, -20, NULL),
|
("negative", -20, NULL, -20, NULL),
|
||||||
("min", -0x800000, 0, NULL, 0),
|
("min", -0x800000, 0, NULL, 0),
|
||||||
("max", 0x7fffff, 0xffffff, NULL, NULL)
|
("max", 0x7fffff, 0xffffff, NULL, NULL)
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_int(
|
CREATE TABLE types_int(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed INT,
|
field_signed INT,
|
||||||
field_unsigned INT UNSIGNED,
|
field_unsigned INT UNSIGNED,
|
||||||
field_width INT(8),
|
field_width INT(8),
|
||||||
field_zerofill INT(7) ZEROFILL
|
field_zerofill INT(7) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_int VALUES
|
INSERT INTO types_int VALUES
|
||||||
("regular", 20, 20, 20, 20),
|
("regular", 20, 20, 20, 20),
|
||||||
("negative", -20, NULL, -20, NULL),
|
("negative", -20, NULL, -20, NULL),
|
||||||
("min", -0x80000000, 0, NULL, 0),
|
("min", -0x80000000, 0, NULL, 0),
|
||||||
("max", 0x7fffffff, 0xffffffff, NULL, NULL)
|
("max", 0x7fffffff, 0xffffffff, NULL, NULL)
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_bigint(
|
CREATE TABLE types_bigint(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed BIGINT,
|
field_signed BIGINT,
|
||||||
field_unsigned BIGINT UNSIGNED,
|
field_unsigned BIGINT UNSIGNED,
|
||||||
field_width BIGINT(8),
|
field_width BIGINT(8),
|
||||||
field_zerofill BIGINT(7) ZEROFILL
|
field_zerofill BIGINT(7) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_bigint VALUES
|
INSERT INTO types_bigint VALUES
|
||||||
("regular", 20, 20, 20, 20),
|
("regular", 20, 20, 20, 20),
|
||||||
("negative", -20, NULL, -20, NULL),
|
("negative", -20, NULL, -20, NULL),
|
||||||
("min", -0x8000000000000000, 0, NULL, 0),
|
("min", -0x8000000000000000, 0, NULL, 0),
|
||||||
("max", 0x7fffffffffffffff, 0xffffffffffffffff, NULL, NULL)
|
("max", 0x7fffffffffffffff, 0xffffffffffffffff, NULL, NULL)
|
||||||
;
|
;
|
||||||
|
|
||||||
-- Floating point types
|
-- Floating point types
|
||||||
CREATE TABLE types_float(
|
CREATE TABLE types_float(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed FLOAT,
|
field_signed FLOAT,
|
||||||
field_unsigned FLOAT UNSIGNED,
|
field_unsigned FLOAT UNSIGNED,
|
||||||
field_width FLOAT(30, 10),
|
field_width FLOAT(30, 10),
|
||||||
field_zerofill FLOAT(20) ZEROFILL
|
field_zerofill FLOAT(20) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_float VALUES
|
INSERT INTO types_float VALUES
|
||||||
("zero", 0, 0, 0, 0),
|
("zero", 0, 0, 0, 0),
|
||||||
("int_positive", 4, NULL, NULL, NULL),
|
("int_positive", 4, NULL, NULL, NULL),
|
||||||
("int_negative", -4, NULL, NULL, NULL),
|
("int_negative", -4, NULL, NULL, NULL),
|
||||||
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
|
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
|
||||||
("fractional_negative", -4.2, NULL, -4.2, NULL),
|
("fractional_negative", -4.2, NULL, -4.2, NULL),
|
||||||
("positive_exp_positive_int", 3e20, NULL, NULL, NULL),
|
("positive_exp_positive_int", 3e20, NULL, NULL, NULL),
|
||||||
("positive_exp_negative_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_positive_fractional", 3.14e20, NULL, NULL, 3.14e20),
|
||||||
("positive_exp_negative_fractional", -3.14e20, NULL, NULL, NULL),
|
("positive_exp_negative_fractional", -3.14e20, NULL, NULL, NULL),
|
||||||
("negative_exp_positive_fractional", 3.14e-20, NULL, NULL, 3.14e-20)
|
("negative_exp_positive_fractional", 3.14e-20, NULL, NULL, 3.14e-20)
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_double(
|
CREATE TABLE types_double(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_signed DOUBLE,
|
field_signed DOUBLE,
|
||||||
field_unsigned DOUBLE UNSIGNED,
|
field_unsigned DOUBLE UNSIGNED,
|
||||||
field_width DOUBLE(60, 10),
|
field_width DOUBLE(60, 10),
|
||||||
field_zerofill FLOAT(40) ZEROFILL
|
field_zerofill FLOAT(40) ZEROFILL
|
||||||
);
|
);
|
||||||
INSERT INTO types_double VALUES
|
INSERT INTO types_double VALUES
|
||||||
("zero", 0, 0, 0, 0),
|
("zero", 0, 0, 0, 0),
|
||||||
("int_positive", 4, NULL, NULL, NULL),
|
("int_positive", 4, NULL, NULL, NULL),
|
||||||
("int_negative", -4, NULL, NULL, NULL),
|
("int_negative", -4, NULL, NULL, NULL),
|
||||||
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
|
("fractional_positive", 4.2, 4.2, 4.2, 4.2),
|
||||||
("fractional_negative", -4.2, NULL, -4.2, NULL),
|
("fractional_negative", -4.2, NULL, -4.2, NULL),
|
||||||
("positive_exp_positive_int", 3e200, NULL, NULL, NULL),
|
("positive_exp_positive_int", 3e200, NULL, NULL, NULL),
|
||||||
("positive_exp_negative_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_positive_fractional", 3.14e200, NULL, NULL, 3.14e200),
|
||||||
("positive_exp_negative_fractional", -3.14e200, NULL, NULL, NULL),
|
("positive_exp_negative_fractional", -3.14e200, NULL, NULL, NULL),
|
||||||
("negative_exp_positive_fractional", 3.14e-200, NULL, NULL, 3.14e-200)
|
("negative_exp_positive_fractional", 3.14e-200, NULL, NULL, 3.14e-200)
|
||||||
;
|
;
|
||||||
|
|
||||||
-- Dates and times
|
-- Dates and times
|
||||||
CREATE TABLE types_date(
|
CREATE TABLE types_date(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_date DATE
|
field_date DATE
|
||||||
);
|
);
|
||||||
INSERT INTO types_date VALUES
|
INSERT INTO types_date VALUES
|
||||||
("regular", "2010-03-28"),
|
("regular", "2010-03-28"),
|
||||||
("leap", "1788-02-29"),
|
("leap", "1788-02-29"),
|
||||||
("min", "1000-01-01"),
|
("min", "1000-01-01"),
|
||||||
("max", "9999-12-31")
|
("max", "9999-12-31")
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_datetime(
|
CREATE TABLE types_datetime(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_0 DATETIME(0),
|
field_0 DATETIME(0),
|
||||||
field_1 DATETIME(1),
|
field_1 DATETIME(1),
|
||||||
field_2 DATETIME(2),
|
field_2 DATETIME(2),
|
||||||
field_3 DATETIME(3),
|
field_3 DATETIME(3),
|
||||||
field_4 DATETIME(4),
|
field_4 DATETIME(4),
|
||||||
field_5 DATETIME(5),
|
field_5 DATETIME(5),
|
||||||
field_6 DATETIME(6)
|
field_6 DATETIME(6)
|
||||||
);
|
);
|
||||||
INSERT INTO types_datetime VALUES
|
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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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")
|
("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(
|
CREATE TABLE types_timestamp(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_0 TIMESTAMP(0) NULL DEFAULT NULL,
|
field_0 TIMESTAMP(0) NULL DEFAULT NULL,
|
||||||
field_1 TIMESTAMP(1) NULL DEFAULT NULL,
|
field_1 TIMESTAMP(1) NULL DEFAULT NULL,
|
||||||
field_2 TIMESTAMP(2) NULL DEFAULT NULL,
|
field_2 TIMESTAMP(2) NULL DEFAULT NULL,
|
||||||
field_3 TIMESTAMP(3) NULL DEFAULT NULL,
|
field_3 TIMESTAMP(3) NULL DEFAULT NULL,
|
||||||
field_4 TIMESTAMP(4) NULL DEFAULT NULL,
|
field_4 TIMESTAMP(4) NULL DEFAULT NULL,
|
||||||
field_5 TIMESTAMP(5) NULL DEFAULT NULL,
|
field_5 TIMESTAMP(5) NULL DEFAULT NULL,
|
||||||
field_6 TIMESTAMP(6) 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
|
-- TODO: min and max not reproducible due to time zones. Find a programmatic way of determining the values here
|
||||||
INSERT INTO types_timestamp
|
INSERT INTO types_timestamp
|
||||||
@ -227,149 +227,149 @@ SELECT * FROM types_datetime
|
|||||||
WHERE id NOT IN ('min', 'max');
|
WHERE id NOT IN ('min', 'max');
|
||||||
|
|
||||||
CREATE TABLE types_time(
|
CREATE TABLE types_time(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_0 TIME(0),
|
field_0 TIME(0),
|
||||||
field_1 TIME(1),
|
field_1 TIME(1),
|
||||||
field_2 TIME(2),
|
field_2 TIME(2),
|
||||||
field_3 TIME(3),
|
field_3 TIME(3),
|
||||||
field_4 TIME(4),
|
field_4 TIME(4),
|
||||||
field_5 TIME(5),
|
field_5 TIME(5),
|
||||||
field_6 TIME(6)
|
field_6 TIME(6)
|
||||||
);
|
);
|
||||||
INSERT INTO types_time VALUES
|
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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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"),
|
("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")
|
("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(
|
CREATE TABLE types_year(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_default YEAR
|
field_default YEAR
|
||||||
);
|
);
|
||||||
INSERT INTO types_year VALUES
|
INSERT INTO types_year VALUES
|
||||||
("regular", 2019),
|
("regular", 2019),
|
||||||
("min", 1901),
|
("min", 1901),
|
||||||
("max", 2155),
|
("max", 2155),
|
||||||
("zero", 0)
|
("zero", 0)
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_string(
|
CREATE TABLE types_string(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_char CHAR(20),
|
field_char CHAR(20),
|
||||||
field_varchar VARCHAR(30),
|
field_varchar VARCHAR(30),
|
||||||
field_tinytext TINYTEXT,
|
field_tinytext TINYTEXT,
|
||||||
field_text TEXT,
|
field_text TEXT,
|
||||||
field_mediumtext MEDIUMTEXT,
|
field_mediumtext MEDIUMTEXT,
|
||||||
field_longtext LONGTEXT,
|
field_longtext LONGTEXT,
|
||||||
field_enum ENUM("red", "green", "blue"),
|
field_enum ENUM("red", "green", "blue"),
|
||||||
field_set SET("red", "green", "blue")
|
field_set SET("red", "green", "blue")
|
||||||
);
|
);
|
||||||
INSERT INTO types_string VALUES
|
INSERT INTO types_string VALUES
|
||||||
("regular", "test_char", "test_varchar", "test_tinytext", "test_text", "test_mediumtext", "test_longtext", "red", "red,green"),
|
("regular", "test_char", "test_varchar", "test_tinytext", "test_text", "test_mediumtext", "test_longtext", "red", "red,green"),
|
||||||
("utf8", "ñ", "Ñ", "á", "é", "í", "ó", NULL, NULL),
|
("utf8", "ñ", "Ñ", "á", "é", "í", "ó", NULL, NULL),
|
||||||
("empty", "", "", "", "", "", "", NULL, "")
|
("empty", "", "", "", "", "", "", NULL, "")
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_binary(
|
CREATE TABLE types_binary(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_binary BINARY(10),
|
field_binary BINARY(10),
|
||||||
field_varbinary VARBINARY(30),
|
field_varbinary VARBINARY(30),
|
||||||
field_tinyblob TINYBLOB,
|
field_tinyblob TINYBLOB,
|
||||||
field_blob BLOB,
|
field_blob BLOB,
|
||||||
field_mediumblob MEDIUMBLOB,
|
field_mediumblob MEDIUMBLOB,
|
||||||
field_longblob LONGBLOB
|
field_longblob LONGBLOB
|
||||||
);
|
);
|
||||||
INSERT INTO types_binary VALUES
|
INSERT INTO types_binary VALUES
|
||||||
("regular", "\0_binary", "\0_varbinary", "\0_tinyblob", "\0_blob", "\0_mediumblob", "\0_longblob"),
|
("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'),
|
("nonascii", X'00FF', X'01FE', X'02FD', X'03FC', X'04FB', X'05FA'),
|
||||||
("empty", "", "", "", "", "", "")
|
("empty", "", "", "", "", "", "")
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_not_implemented(
|
CREATE TABLE types_not_implemented(
|
||||||
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
id VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
field_bit BIT(8),
|
field_bit BIT(8),
|
||||||
field_decimal DECIMAL,
|
field_decimal DECIMAL,
|
||||||
field_geometry GEOMETRY
|
field_geometry GEOMETRY
|
||||||
);
|
);
|
||||||
INSERT INTO types_not_implemented VALUES
|
INSERT INTO types_not_implemented VALUES
|
||||||
("regular", 0xfe, 300, POINT(1, 2))
|
("regular", 0xfe, 300, POINT(1, 2))
|
||||||
;
|
;
|
||||||
|
|
||||||
CREATE TABLE types_flags(
|
CREATE TABLE types_flags(
|
||||||
id VARCHAR(50) NOT NULL,
|
id VARCHAR(50) NOT NULL,
|
||||||
field_timestamp TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
field_timestamp TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
field_primary_key INT NOT NULL AUTO_INCREMENT,
|
field_primary_key INT NOT NULL AUTO_INCREMENT,
|
||||||
field_not_null CHAR(8) NOT NULL DEFAULT "",
|
field_not_null CHAR(8) NOT NULL DEFAULT "",
|
||||||
field_unique INT,
|
field_unique INT,
|
||||||
field_indexed INT,
|
field_indexed INT,
|
||||||
PRIMARY KEY (field_primary_key, id),
|
PRIMARY KEY (field_primary_key, id),
|
||||||
UNIQUE KEY (field_unique),
|
UNIQUE KEY (field_unique),
|
||||||
KEY (field_indexed)
|
KEY (field_indexed)
|
||||||
);
|
);
|
||||||
INSERT INTO types_flags VALUES
|
INSERT INTO types_flags VALUES
|
||||||
("default", NULL, 50, "char", 21, 42)
|
("default", NULL, 50, "char", 21, 42)
|
||||||
;
|
;
|
||||||
|
|
||||||
-- Users
|
-- Users
|
||||||
|
@ -21,94 +21,94 @@ namespace
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct ExecuteStatementTest : public NetworkTest<Stream>
|
struct ExecuteStatementTest : public NetworkTest<Stream>
|
||||||
{
|
{
|
||||||
prepared_statement<Stream> do_prepare(std::string_view stmt)
|
prepared_statement<Stream> do_prepare(std::string_view stmt)
|
||||||
{
|
{
|
||||||
auto res = this->GetParam().net->prepare_statement(this->conn, stmt);
|
auto res = this->GetParam().net->prepare_statement(this->conn, stmt);
|
||||||
res.validate_no_error();
|
res.validate_no_error();
|
||||||
return std::move(res.value);
|
return std::move(res.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto do_execute(prepared_statement<Stream>& stmt, value_list_it first, value_list_it 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);
|
return this->GetParam().net->execute_statement(stmt, first, last);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto do_execute(prepared_statement<Stream>& stmt, const std::vector<value>& params)
|
auto do_execute(prepared_statement<Stream>& stmt, const std::vector<value>& params)
|
||||||
{
|
{
|
||||||
return this->GetParam().net->execute_statement(stmt, params);
|
return this->GetParam().net->execute_statement(stmt, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterator version
|
// Iterator version
|
||||||
void Iterator_OkNoParams()
|
void Iterator_OkNoParams()
|
||||||
{
|
{
|
||||||
std::forward_list<value> params;
|
std::forward_list<value> params;
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table");
|
auto stmt = do_prepare("SELECT * FROM empty_table");
|
||||||
auto result = do_execute(stmt, params.begin(), params.end()); // execute
|
auto result = do_execute(stmt, params.begin(), params.end()); // execute
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Iterator_OkWithParams()
|
void Iterator_OkWithParams()
|
||||||
{
|
{
|
||||||
std::forward_list<value> params { value("item"), value(42) };
|
std::forward_list<value> params { value("item"), value(42) };
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||||
auto result = do_execute(stmt, params.begin(), params.end());
|
auto result = do_execute(stmt, params.begin(), params.end());
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Iterator_MismatchedNumParams()
|
void Iterator_MismatchedNumParams()
|
||||||
{
|
{
|
||||||
std::forward_list<value> params { value("item") };
|
std::forward_list<value> params { value("item") };
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||||
auto result = do_execute(stmt, params.begin(), params.end());
|
auto result = do_execute(stmt, params.begin(), params.end());
|
||||||
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
|
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
|
||||||
EXPECT_FALSE(result.value.valid());
|
EXPECT_FALSE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Iterator_ServerError()
|
void Iterator_ServerError()
|
||||||
{
|
{
|
||||||
std::forward_list<value> params { value("f0"), value("bad_date") };
|
std::forward_list<value> params { value("f0"), value("bad_date") };
|
||||||
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
|
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
|
||||||
auto result = do_execute(stmt, params.begin(), params.end());
|
auto result = do_execute(stmt, params.begin(), params.end());
|
||||||
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
|
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
|
||||||
EXPECT_FALSE(result.value.valid());
|
EXPECT_FALSE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Container version
|
// Container version
|
||||||
void Container_OkNoParams()
|
void Container_OkNoParams()
|
||||||
{
|
{
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table");
|
auto stmt = do_prepare("SELECT * FROM empty_table");
|
||||||
auto result = do_execute(stmt, std::vector<value>()); // execute
|
auto result = do_execute(stmt, std::vector<value>()); // execute
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container_OkWithParams()
|
void Container_OkWithParams()
|
||||||
{
|
{
|
||||||
std::vector<value> params { value("item"), value(42) };
|
std::vector<value> params { value("item"), value(42) };
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||||
auto result = do_execute(stmt, params);
|
auto result = do_execute(stmt, params);
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container_MismatchedNumParams()
|
void Container_MismatchedNumParams()
|
||||||
{
|
{
|
||||||
std::vector<value> params { value("item") };
|
std::vector<value> params { value("item") };
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||||
auto result = do_execute(stmt, params);
|
auto result = do_execute(stmt, params);
|
||||||
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
|
result.validate_error(errc::wrong_num_params, {"param", "2", "1", "statement", "execute"});
|
||||||
EXPECT_FALSE(result.value.valid());
|
EXPECT_FALSE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Container_ServerError()
|
void Container_ServerError()
|
||||||
{
|
{
|
||||||
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
|
auto stmt = do_prepare("INSERT INTO inserts_table (field_varchar, field_date) VALUES (?, ?)");
|
||||||
auto result = do_execute(stmt, makevalues("f0", "bad_date"));
|
auto result = do_execute(stmt, makevalues("f0", "bad_date"));
|
||||||
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
|
result.validate_error(errc::truncated_wrong_value, {"field_date", "bad_date", "incorrect date value"});
|
||||||
EXPECT_FALSE(result.value.valid());
|
EXPECT_FALSE(result.value.valid());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(ExecuteStatementTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(ExecuteStatementTest)
|
||||||
@ -127,22 +127,22 @@ BOOST_MYSQL_NETWORK_TEST(ExecuteStatementTest, Container_ServerError)
|
|||||||
// Other containers
|
// Other containers
|
||||||
struct ExecuteStatementOtherContainersTest : IntegTest<boost::asio::ip::tcp::socket>
|
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)
|
TEST_F(ExecuteStatementOtherContainersTest, NoParams_CanUseNoStatementParamsVariable)
|
||||||
{
|
{
|
||||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
|
auto stmt = conn.prepare_statement("SELECT * FROM empty_table");
|
||||||
auto result = stmt.execute(boost::mysql::no_statement_params);
|
auto result = stmt.execute(boost::mysql::no_statement_params);
|
||||||
EXPECT_TRUE(result.valid());
|
EXPECT_TRUE(result.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExecuteStatementOtherContainersTest, CArray)
|
TEST_F(ExecuteStatementOtherContainersTest, CArray)
|
||||||
{
|
{
|
||||||
value arr [] = { value("hola"), value(10) };
|
value arr [] = { value("hola"), value(10) };
|
||||||
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
auto stmt = conn.prepare_statement("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||||
auto result = stmt.execute(arr);
|
auto result = stmt.execute(arr);
|
||||||
EXPECT_TRUE(result.valid());
|
EXPECT_TRUE(result.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,67 +32,67 @@ namespace
|
|||||||
// Handshake tests not depending on whether we use SSL or not
|
// Handshake tests not depending on whether we use SSL or not
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct SslIndifferentHandshakeTest :
|
struct SslIndifferentHandshakeTest :
|
||||||
NetworkTest<Stream, network_testcase_with_ssl<Stream>, false>
|
NetworkTest<Stream, network_testcase_with_ssl<Stream>, false>
|
||||||
{
|
{
|
||||||
auto do_handshake()
|
auto do_handshake()
|
||||||
{
|
{
|
||||||
this->connection_params.set_ssl(boost::mysql::ssl_options(this->GetParam().ssl));
|
this->connection_params.set_ssl(boost::mysql::ssl_options(this->GetParam().ssl));
|
||||||
return this->GetParam().net->handshake(this->conn, this->connection_params);
|
return this->GetParam().net->handshake(this->conn, this->connection_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// does handshake and verifies it went OK
|
// does handshake and verifies it went OK
|
||||||
void do_handshake_ok()
|
void do_handshake_ok()
|
||||||
{
|
{
|
||||||
auto result = do_handshake();
|
auto result = do_handshake();
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
this->validate_ssl(this->GetParam().ssl);
|
this->validate_ssl(this->GetParam().ssl);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct SslSensitiveHandshakeTest : NetworkTest<Stream, network_testcase<Stream>, false>
|
struct SslSensitiveHandshakeTest : NetworkTest<Stream, network_testcase<Stream>, false>
|
||||||
{
|
{
|
||||||
void set_ssl(ssl_mode m)
|
void set_ssl(ssl_mode m)
|
||||||
{
|
{
|
||||||
this->connection_params.set_ssl(boost::mysql::ssl_options(m));
|
this->connection_params.set_ssl(boost::mysql::ssl_options(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto do_handshake()
|
auto do_handshake()
|
||||||
{
|
{
|
||||||
return this->GetParam().net->handshake(this->conn, this->connection_params);
|
return this->GetParam().net->handshake(this->conn, this->connection_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_handshake_ok(ssl_mode m)
|
void do_handshake_ok(ssl_mode m)
|
||||||
{
|
{
|
||||||
set_ssl(m);
|
set_ssl(m);
|
||||||
auto result = do_handshake();
|
auto result = do_handshake();
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
this->validate_ssl(m);
|
this->validate_ssl(m);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// mysql_native_password
|
// mysql_native_password
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct MysqlNativePasswordHandshakeTest : SslIndifferentHandshakeTest<Stream>
|
struct MysqlNativePasswordHandshakeTest : SslIndifferentHandshakeTest<Stream>
|
||||||
{
|
{
|
||||||
void RegularUser_SuccessfulLogin()
|
void RegularUser_SuccessfulLogin()
|
||||||
{
|
{
|
||||||
this->set_credentials("mysqlnp_user", "mysqlnp_password");
|
this->set_credentials("mysqlnp_user", "mysqlnp_password");
|
||||||
this->do_handshake_ok();
|
this->do_handshake_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyPassword_SuccessfulLogin()
|
void EmptyPassword_SuccessfulLogin()
|
||||||
{
|
{
|
||||||
this->set_credentials("mysqlnp_empty_password_user", "");
|
this->set_credentials("mysqlnp_empty_password_user", "");
|
||||||
this->do_handshake_ok();
|
this->do_handshake_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BadPassword_FailedLogin()
|
void BadPassword_FailedLogin()
|
||||||
{
|
{
|
||||||
this->set_credentials("mysqlnp_user", "bad_password");
|
this->set_credentials("mysqlnp_user", "bad_password");
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
result.validate_error(errc::access_denied_error, {"access denied", "mysqlnp_user"});
|
result.validate_error(errc::access_denied_error, {"access denied", "mysqlnp_user"});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(MysqlNativePasswordHandshakeTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(MysqlNativePasswordHandshakeTest)
|
||||||
@ -106,26 +106,26 @@ BOOST_MYSQL_NETWORK_TEST(MysqlNativePasswordHandshakeTest, BadPassword_FailedLog
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct MiscSslIndifferentHandshakeTest : SslIndifferentHandshakeTest<Stream>
|
struct MiscSslIndifferentHandshakeTest : SslIndifferentHandshakeTest<Stream>
|
||||||
{
|
{
|
||||||
void NoDatabase_SuccessfulLogin()
|
void NoDatabase_SuccessfulLogin()
|
||||||
{
|
{
|
||||||
this->connection_params.set_database("");
|
this->connection_params.set_database("");
|
||||||
this->do_handshake_ok();
|
this->do_handshake_ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BadDatabase_FailedLogin()
|
void BadDatabase_FailedLogin()
|
||||||
{
|
{
|
||||||
this->connection_params.set_database("bad_database");
|
this->connection_params.set_database("bad_database");
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
result.validate_error(errc::dbaccess_denied_error, {"database", "bad_database"});
|
result.validate_error(errc::dbaccess_denied_error, {"database", "bad_database"});
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnknownAuthPlugin_FailedLogin_RequiresSha256()
|
void UnknownAuthPlugin_FailedLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
// Note: sha256_password is not supported, so it's an unknown plugin to us
|
// Note: sha256_password is not supported, so it's an unknown plugin to us
|
||||||
this->set_credentials("sha2p_user", "sha2p_password");
|
this->set_credentials("sha2p_user", "sha2p_password");
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
result.validate_error(errc::unknown_auth_plugin, {});
|
result.validate_error(errc::unknown_auth_plugin, {});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(MiscSslIndifferentHandshakeTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(MiscSslIndifferentHandshakeTest)
|
||||||
@ -138,104 +138,104 @@ BOOST_MYSQL_NETWORK_TEST(MiscSslIndifferentHandshakeTest, UnknownAuthPlugin_Fail
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct CachingSha2HandshakeTest : SslSensitiveHandshakeTest<Stream>
|
struct CachingSha2HandshakeTest : SslSensitiveHandshakeTest<Stream>
|
||||||
{
|
{
|
||||||
void load_sha256_cache(const std::string& user, const std::string& password)
|
void load_sha256_cache(const std::string& user, const std::string& password)
|
||||||
{
|
{
|
||||||
if (password.empty())
|
if (password.empty())
|
||||||
{
|
{
|
||||||
check_call(stringize("mysql -u ", user, " -e \"\""));
|
check_call(stringize("mysql -u ", user, " -e \"\""));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
check_call(stringize("mysql -u ", user, " -p", password, " -e \"\""));
|
check_call(stringize("mysql -u ", user, " -p", password, " -e \"\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_sha256_cache()
|
void clear_sha256_cache()
|
||||||
{
|
{
|
||||||
check_call("mysql -u root -e \"FLUSH PRIVILEGES\"");
|
check_call("mysql -u root -e \"FLUSH PRIVILEGES\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual tests
|
// Actual tests
|
||||||
void SslOnCacheHit_SuccessfulLogin_RequiresSha256()
|
void SslOnCacheHit_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
this->set_credentials("csha2p_user", "csha2p_password");
|
this->set_credentials("csha2p_user", "csha2p_password");
|
||||||
load_sha256_cache("csha2p_user", "csha2p_password");
|
load_sha256_cache("csha2p_user", "csha2p_password");
|
||||||
this->do_handshake_ok(ssl_mode::require);
|
this->do_handshake_ok(ssl_mode::require);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SslOffCacheHit_SuccessfulLogin_RequiresSha256()
|
void SslOffCacheHit_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
// As we are sending password hashed, it is OK to not have SSL for this
|
// As we are sending password hashed, it is OK to not have SSL for this
|
||||||
this->set_credentials("csha2p_user", "csha2p_password");
|
this->set_credentials("csha2p_user", "csha2p_password");
|
||||||
load_sha256_cache("csha2p_user", "csha2p_password");
|
load_sha256_cache("csha2p_user", "csha2p_password");
|
||||||
this->do_handshake_ok(ssl_mode::disable);
|
this->do_handshake_ok(ssl_mode::disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SslOnCacheMiss_SuccessfulLogin_RequiresSha256()
|
void SslOnCacheMiss_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
this->set_credentials("csha2p_user", "csha2p_password");
|
this->set_credentials("csha2p_user", "csha2p_password");
|
||||||
clear_sha256_cache();
|
clear_sha256_cache();
|
||||||
this->do_handshake_ok(ssl_mode::require);
|
this->do_handshake_ok(ssl_mode::require);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SslOffCacheMiss_FailedLogin_RequiresSha256()
|
void SslOffCacheMiss_FailedLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
// A cache miss would force us send a plaintext password over
|
// A cache miss would force us send a plaintext password over
|
||||||
// a non-TLS connection, so we fail
|
// a non-TLS connection, so we fail
|
||||||
this->set_ssl(ssl_mode::disable);
|
this->set_ssl(ssl_mode::disable);
|
||||||
this->set_credentials("csha2p_user", "csha2p_password");
|
this->set_credentials("csha2p_user", "csha2p_password");
|
||||||
clear_sha256_cache();
|
clear_sha256_cache();
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
result.validate_error(errc::auth_plugin_requires_ssl, {});
|
result.validate_error(errc::auth_plugin_requires_ssl, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyPasswordSslOnCacheHit_SuccessfulLogin_RequiresSha256()
|
void EmptyPasswordSslOnCacheHit_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
this->set_credentials("csha2p_empty_password_user", "");
|
this->set_credentials("csha2p_empty_password_user", "");
|
||||||
load_sha256_cache("csha2p_empty_password_user", "");
|
load_sha256_cache("csha2p_empty_password_user", "");
|
||||||
this->do_handshake_ok(ssl_mode::require);
|
this->do_handshake_ok(ssl_mode::require);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyPasswordSslOffCacheHit_SuccessfulLogin_RequiresSha256()
|
void EmptyPasswordSslOffCacheHit_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
// Empty passwords are allowed over non-TLS connections
|
// Empty passwords are allowed over non-TLS connections
|
||||||
this->set_credentials("csha2p_empty_password_user", "");
|
this->set_credentials("csha2p_empty_password_user", "");
|
||||||
load_sha256_cache("csha2p_empty_password_user", "");
|
load_sha256_cache("csha2p_empty_password_user", "");
|
||||||
this->do_handshake_ok(ssl_mode::disable);
|
this->do_handshake_ok(ssl_mode::disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyPasswordSslOnCacheMiss_SuccessfulLogin_RequiresSha256()
|
void EmptyPasswordSslOnCacheMiss_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
this->set_credentials("csha2p_empty_password_user", "");
|
this->set_credentials("csha2p_empty_password_user", "");
|
||||||
clear_sha256_cache();
|
clear_sha256_cache();
|
||||||
this->do_handshake_ok(ssl_mode::require);
|
this->do_handshake_ok(ssl_mode::require);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmptyPasswordSslOffCacheMiss_SuccessfulLogin_RequiresSha256()
|
void EmptyPasswordSslOffCacheMiss_SuccessfulLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
// Empty passwords are allowed over non-TLS connections
|
// Empty passwords are allowed over non-TLS connections
|
||||||
this->set_credentials("csha2p_empty_password_user", "");
|
this->set_credentials("csha2p_empty_password_user", "");
|
||||||
clear_sha256_cache();
|
clear_sha256_cache();
|
||||||
this->do_handshake_ok(ssl_mode::disable);
|
this->do_handshake_ok(ssl_mode::disable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BadPasswordSslOnCacheMiss_FailedLogin_RequiresSha256()
|
void BadPasswordSslOnCacheMiss_FailedLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
|
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
|
||||||
clear_sha256_cache();
|
clear_sha256_cache();
|
||||||
this->set_credentials("csha2p_user", "bad_password");
|
this->set_credentials("csha2p_user", "bad_password");
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
|
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BadPasswordSslOnCacheHit_FailedLogin_RequiresSha256()
|
void BadPasswordSslOnCacheHit_FailedLogin_RequiresSha256()
|
||||||
{
|
{
|
||||||
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
|
this->set_ssl(ssl_mode::require); // Note: test over non-TLS would return "ssl required"
|
||||||
this->set_credentials("csha2p_user", "bad_password");
|
this->set_credentials("csha2p_user", "bad_password");
|
||||||
load_sha256_cache("csha2p_user", "csha2p_password");
|
load_sha256_cache("csha2p_user", "csha2p_password");
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
|
result.validate_error(errc::access_denied_error, {"access denied", "csha2p_user"});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(CachingSha2HandshakeTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(CachingSha2HandshakeTest)
|
||||||
@ -255,23 +255,23 @@ BOOST_MYSQL_NETWORK_TEST(CachingSha2HandshakeTest, BadPasswordSslOnCacheHit_Fail
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct MiscSslSensitiveHandshakeTest : SslSensitiveHandshakeTest<Stream>
|
struct MiscSslSensitiveHandshakeTest : SslSensitiveHandshakeTest<Stream>
|
||||||
{
|
{
|
||||||
void BadUser_FailedLogin()
|
void BadUser_FailedLogin()
|
||||||
{
|
{
|
||||||
// unreliable without SSL. If the default plugin requires SSL
|
// unreliable without SSL. If the default plugin requires SSL
|
||||||
// (like SHA256), this would fail with 'ssl required'
|
// (like SHA256), this would fail with 'ssl required'
|
||||||
this->set_ssl(ssl_mode::require);
|
this->set_ssl(ssl_mode::require);
|
||||||
this->set_credentials("non_existing_user", "bad_password");
|
this->set_credentials("non_existing_user", "bad_password");
|
||||||
auto result = this->do_handshake();
|
auto result = this->do_handshake();
|
||||||
EXPECT_NE(result.err, error_code()); // may be access denied or unknown auth plugin
|
EXPECT_NE(result.err, error_code()); // may be access denied or unknown auth plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
void SslEnable_SuccessfulLogin()
|
void SslEnable_SuccessfulLogin()
|
||||||
{
|
{
|
||||||
// In all our CI systems, our servers support SSL, so
|
// In all our CI systems, our servers support SSL, so
|
||||||
// ssl_mode::enable will do the same as ssl_mode::require.
|
// ssl_mode::enable will do the same as ssl_mode::require.
|
||||||
// We test for this fact.
|
// We test for this fact.
|
||||||
this->do_handshake_ok(ssl_mode::enable);
|
this->do_handshake_ok(ssl_mode::enable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(MiscSslSensitiveHandshakeTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(MiscSslSensitiveHandshakeTest)
|
||||||
|
@ -26,15 +26,15 @@ namespace test {
|
|||||||
|
|
||||||
inline void physical_connect(tcp_connection& conn)
|
inline void physical_connect(tcp_connection& conn)
|
||||||
{
|
{
|
||||||
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::loopback(), 3306);
|
boost::asio::ip::tcp::endpoint endpoint (boost::asio::ip::address_v4::loopback(), 3306);
|
||||||
conn.next_layer().connect(endpoint);
|
conn.next_layer().connect(endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||||
inline void physical_connect(unix_connection& conn)
|
inline void physical_connect(unix_connection& conn)
|
||||||
{
|
{
|
||||||
boost::asio::local::stream_protocol::endpoint endpoint ("/var/run/mysqld/mysqld.sock");
|
boost::asio::local::stream_protocol::endpoint endpoint ("/var/run/mysqld/mysqld.sock");
|
||||||
conn.next_layer().connect(endpoint);
|
conn.next_layer().connect(endpoint);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -50,102 +50,102 @@ inline void physical_connect(unix_connection& conn)
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct IntegTest : testing::Test
|
struct IntegTest : testing::Test
|
||||||
{
|
{
|
||||||
using stream_type = Stream;
|
using stream_type = Stream;
|
||||||
|
|
||||||
mysql::connection_params connection_params {"integ_user", "integ_password", "awesome"};
|
mysql::connection_params connection_params {"integ_user", "integ_password", "awesome"};
|
||||||
boost::asio::io_context ctx;
|
boost::asio::io_context ctx;
|
||||||
mysql::connection<Stream> conn {ctx};
|
mysql::connection<Stream> conn {ctx};
|
||||||
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard { ctx.get_executor() };
|
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard { ctx.get_executor() };
|
||||||
std::thread runner {[this]{ ctx.run(); } };
|
std::thread runner {[this]{ ctx.run(); } };
|
||||||
|
|
||||||
IntegTest()
|
IntegTest()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
physical_connect(conn);
|
physical_connect(conn);
|
||||||
}
|
}
|
||||||
catch (...) // prevent terminate without an active exception on connect error
|
catch (...) // prevent terminate without an active exception on connect error
|
||||||
{
|
{
|
||||||
guard.reset();
|
guard.reset();
|
||||||
runner.join();
|
runner.join();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~IntegTest()
|
~IntegTest()
|
||||||
{
|
{
|
||||||
error_code code;
|
error_code code;
|
||||||
conn.next_layer().close(code);
|
conn.next_layer().close(code);
|
||||||
guard.reset();
|
guard.reset();
|
||||||
runner.join();
|
runner.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_credentials(std::string_view user, std::string_view password)
|
void set_credentials(std::string_view user, std::string_view password)
|
||||||
{
|
{
|
||||||
connection_params.set_username(user);
|
connection_params.set_username(user);
|
||||||
connection_params.set_password(password);
|
connection_params.set_password(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handshake(ssl_mode m = ssl_mode::require)
|
void handshake(ssl_mode m = ssl_mode::require)
|
||||||
{
|
{
|
||||||
connection_params.set_ssl(ssl_options(m));
|
connection_params.set_ssl(ssl_options(m));
|
||||||
conn.handshake(connection_params);
|
conn.handshake(connection_params);
|
||||||
validate_ssl(m);
|
validate_ssl(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool should_use_ssl(ssl_mode m)
|
static bool should_use_ssl(ssl_mode m)
|
||||||
{
|
{
|
||||||
return m == ssl_mode::enable || m == ssl_mode::require;
|
return m == ssl_mode::enable || m == ssl_mode::require;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifies that we are or are not using SSL, depending on what mode was requested.
|
// Verifies that we are or are not using SSL, depending on what mode was requested.
|
||||||
void validate_ssl(ssl_mode m)
|
void validate_ssl(ssl_mode m)
|
||||||
{
|
{
|
||||||
if (should_use_ssl(m))
|
if (should_use_ssl(m))
|
||||||
{
|
{
|
||||||
// All our test systems MUST support SSL to run these tests
|
// All our test systems MUST support SSL to run these tests
|
||||||
EXPECT_TRUE(conn.uses_ssl());
|
EXPECT_TRUE(conn.uses_ssl());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EXPECT_FALSE(conn.uses_ssl());
|
EXPECT_FALSE(conn.uses_ssl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_eof(
|
void validate_eof(
|
||||||
const resultset<Stream>& result,
|
const resultset<Stream>& result,
|
||||||
int affected_rows=0,
|
int affected_rows=0,
|
||||||
int warnings=0,
|
int warnings=0,
|
||||||
int last_insert=0,
|
int last_insert=0,
|
||||||
std::string_view info=""
|
std::string_view info=""
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
EXPECT_TRUE(result.valid());
|
EXPECT_TRUE(result.valid());
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
EXPECT_EQ(result.affected_rows(), affected_rows);
|
EXPECT_EQ(result.affected_rows(), affected_rows);
|
||||||
EXPECT_EQ(result.warning_count(), warnings);
|
EXPECT_EQ(result.warning_count(), warnings);
|
||||||
EXPECT_EQ(result.last_insert_id(), last_insert);
|
EXPECT_EQ(result.last_insert_id(), last_insert);
|
||||||
EXPECT_EQ(result.info(), info);
|
EXPECT_EQ(result.info(), info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_2fields_meta(
|
void validate_2fields_meta(
|
||||||
const std::vector<field_metadata>& fields,
|
const std::vector<field_metadata>& fields,
|
||||||
const std::string& table
|
const std::string& table
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
validate_meta(fields, {
|
validate_meta(fields, {
|
||||||
meta_validator(table, "id", field_type::int_),
|
meta_validator(table, "id", field_type::int_),
|
||||||
meta_validator(table, "field_varchar", field_type::varchar)
|
meta_validator(table, "field_varchar", field_type::varchar)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_2fields_meta(
|
void validate_2fields_meta(
|
||||||
const resultset<Stream>& result,
|
const resultset<Stream>& result,
|
||||||
const std::string& table
|
const std::string& table
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
validate_2fields_meta(result.fields(), table);
|
validate_2fields_meta(result.fields(), table);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -154,19 +154,19 @@ struct IntegTest : testing::Test
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct network_testcase
|
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()
|
static std::vector<network_testcase<Stream>> make_all()
|
||||||
{
|
{
|
||||||
std::vector<network_testcase<Stream>> res;
|
std::vector<network_testcase<Stream>> res;
|
||||||
for (auto* net: make_all_network_functions<Stream>())
|
for (auto* net: make_all_network_functions<Stream>())
|
||||||
{
|
{
|
||||||
res.push_back(network_testcase<Stream>{net});
|
res.push_back(network_testcase<Stream>{net});
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// To be used as test parameter, when a test should be run over
|
// To be used as test parameter, when a test should be run over
|
||||||
@ -174,26 +174,26 @@ struct network_testcase
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct network_testcase_with_ssl
|
struct network_testcase_with_ssl
|
||||||
{
|
{
|
||||||
network_functions<Stream>* net;
|
network_functions<Stream>* net;
|
||||||
ssl_mode ssl;
|
ssl_mode ssl;
|
||||||
|
|
||||||
std::string name() const
|
std::string name() const
|
||||||
{
|
{
|
||||||
return detail::stringize(this->net->name(), '_', to_string(ssl));
|
return detail::stringize(this->net->name(), '_', to_string(ssl));
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<network_testcase_with_ssl<Stream>> make_all()
|
static std::vector<network_testcase_with_ssl<Stream>> make_all()
|
||||||
{
|
{
|
||||||
std::vector<network_testcase_with_ssl<Stream>> res;
|
std::vector<network_testcase_with_ssl<Stream>> res;
|
||||||
for (auto* net: make_all_network_functions<Stream>())
|
for (auto* net: make_all_network_functions<Stream>())
|
||||||
{
|
{
|
||||||
for (auto ssl: {ssl_mode::require, ssl_mode::disable})
|
for (auto ssl: {ssl_mode::require, ssl_mode::disable})
|
||||||
{
|
{
|
||||||
res.push_back(network_testcase_with_ssl<Stream>{net, ssl});
|
res.push_back(network_testcase_with_ssl<Stream>{net, ssl});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -217,20 +217,20 @@ struct network_testcase_with_ssl
|
|||||||
* calling the method you defined in the fixture.
|
* calling the method you defined in the fixture.
|
||||||
*/
|
*/
|
||||||
template <
|
template <
|
||||||
typename Stream,
|
typename Stream,
|
||||||
typename Param=network_testcase_with_ssl<Stream>,
|
typename Param=network_testcase_with_ssl<Stream>,
|
||||||
bool do_handshake=true
|
bool do_handshake=true
|
||||||
>
|
>
|
||||||
struct NetworkTest : public IntegTest<Stream>,
|
struct NetworkTest : public IntegTest<Stream>,
|
||||||
public testing::WithParamInterface<Param>
|
public testing::WithParamInterface<Param>
|
||||||
{
|
{
|
||||||
NetworkTest()
|
NetworkTest()
|
||||||
{
|
{
|
||||||
if constexpr (do_handshake)
|
if constexpr (do_handshake)
|
||||||
{
|
{
|
||||||
this->handshake(this->GetParam().ssl);
|
this->handshake(this->GetParam().ssl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // test
|
} // test
|
||||||
@ -239,66 +239,66 @@ struct NetworkTest : public IntegTest<Stream>,
|
|||||||
|
|
||||||
// Typedefs
|
// Typedefs
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_HELPER(TestSuiteName, Suffix, Stream) \
|
#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) \
|
#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
|
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName) \
|
#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
|
#else
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
|
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
|
#define BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_TCP(TestSuiteName) \
|
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_TCP(TestSuiteName) \
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
|
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS_UNIX(TestSuiteName)
|
||||||
|
|
||||||
// Test definition
|
// Test definition
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_HELPER(TestSuiteName, TestName, Suffix) \
|
#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) \
|
#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
|
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName) \
|
#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
|
#else
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
|
#define BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BOOST_MYSQL_NETWORK_TEST(TestSuiteName, TestName) \
|
#define BOOST_MYSQL_NETWORK_TEST(TestSuiteName, TestName) \
|
||||||
BOOST_MYSQL_NETWORK_TEST_TCP(TestSuiteName, TestName) \
|
BOOST_MYSQL_NETWORK_TEST_TCP(TestSuiteName, TestName) \
|
||||||
BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
|
BOOST_MYSQL_NETWORK_TEST_UNIX(TestSuiteName, TestName)
|
||||||
|
|
||||||
// Test suite instantiation
|
// Test suite instantiation
|
||||||
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, Suffix) \
|
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_HELPER(TestSuiteName, Suffix) \
|
||||||
INSTANTIATE_TEST_SUITE_P(Default, TestSuiteName##Suffix, testing::ValuesIn( \
|
INSTANTIATE_TEST_SUITE_P(Default, TestSuiteName##Suffix, testing::ValuesIn( \
|
||||||
TestSuiteName##Suffix::ParamType::make_all() \
|
TestSuiteName##Suffix::ParamType::make_all() \
|
||||||
), [](const auto& param_info) { \
|
), [](const auto& param_info) { \
|
||||||
return param_info.param.name(); \
|
return param_info.param.name(); \
|
||||||
});
|
});
|
||||||
|
|
||||||
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_TCP(TestSuiteName) \
|
#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
|
#ifdef BOOST_ASIO_HAS_LOCAL_SOCKETS
|
||||||
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName) \
|
#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
|
#else
|
||||||
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
|
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName) \
|
#define BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName) \
|
||||||
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_TCP(TestSuiteName) \
|
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_TCP(TestSuiteName) \
|
||||||
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
|
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE_UNIX(TestSuiteName)
|
||||||
|
|
||||||
// Typedefs + Instantiation
|
// Typedefs + Instantiation
|
||||||
#define BOOST_MYSQL_NETWORK_TEST_SUITE(TestSuiteName) \
|
#define BOOST_MYSQL_NETWORK_TEST_SUITE(TestSuiteName) \
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
|
BOOST_MYSQL_NETWORK_TEST_SUITE_TYPEDEFS(TestSuiteName) \
|
||||||
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName)
|
BOOST_MYSQL_INSTANTIATE_NETWORK_TEST_SUITE(TestSuiteName)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,80 +11,80 @@
|
|||||||
using namespace boost::mysql::test;
|
using namespace boost::mysql::test;
|
||||||
|
|
||||||
#define MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(getter) \
|
#define MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(getter) \
|
||||||
{ #getter, &boost::mysql::field_metadata::getter }
|
{ #getter, &boost::mysql::field_metadata::getter }
|
||||||
|
|
||||||
static struct flag_entry
|
static struct flag_entry
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
meta_validator::flag_getter getter;
|
meta_validator::flag_getter getter;
|
||||||
} flag_names [] = {
|
} flag_names [] = {
|
||||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_not_null),
|
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_primary_key),
|
||||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unique_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_multiple_key),
|
||||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unsigned),
|
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_unsigned),
|
||||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_zerofill),
|
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_zerofill),
|
||||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_auto_increment),
|
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(has_no_default_value),
|
||||||
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_set_to_now_on_update)
|
MYSQL_TEST_FLAG_GETTER_NAME_ENTRY(is_set_to_now_on_update)
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool contains(
|
static bool contains(
|
||||||
meta_validator::flag_getter flag,
|
meta_validator::flag_getter flag,
|
||||||
const std::vector<meta_validator::flag_getter>& flagvec
|
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(
|
void meta_validator::validate(
|
||||||
const mysql::field_metadata& value
|
const mysql::field_metadata& value
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
// Fixed fields
|
// Fixed fields
|
||||||
EXPECT_EQ(value.database(), "awesome");
|
EXPECT_EQ(value.database(), "awesome");
|
||||||
EXPECT_EQ(value.table(), table_);
|
EXPECT_EQ(value.table(), table_);
|
||||||
EXPECT_EQ(value.original_table(), org_table_);
|
EXPECT_EQ(value.original_table(), org_table_);
|
||||||
EXPECT_EQ(value.field_name(), field_);
|
EXPECT_EQ(value.field_name(), field_);
|
||||||
EXPECT_EQ(value.original_field_name(), org_field_);
|
EXPECT_EQ(value.original_field_name(), org_field_);
|
||||||
EXPECT_GT(value.column_length(), 0u);
|
EXPECT_GT(value.column_length(), 0u);
|
||||||
EXPECT_EQ(value.type(), type_);
|
EXPECT_EQ(value.type(), type_);
|
||||||
EXPECT_EQ(value.decimals(), decimals_);
|
EXPECT_EQ(value.decimals(), decimals_);
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
std::vector<flag_entry> all_flags (std::begin(flag_names), std::end(flag_names));
|
std::vector<flag_entry> all_flags (std::begin(flag_names), std::end(flag_names));
|
||||||
|
|
||||||
for (flag_getter true_flag: flags_)
|
for (flag_getter true_flag: flags_)
|
||||||
{
|
{
|
||||||
auto it = std::find_if(
|
auto it = std::find_if(
|
||||||
all_flags.begin(),
|
all_flags.begin(),
|
||||||
all_flags.end(),
|
all_flags.end(),
|
||||||
[true_flag](const flag_entry& entry) { return entry.getter == true_flag; }
|
[true_flag](const flag_entry& entry) { return entry.getter == true_flag; }
|
||||||
);
|
);
|
||||||
ASSERT_NE(it, all_flags.end()); // no repeated flag
|
ASSERT_NE(it, all_flags.end()); // no repeated flag
|
||||||
ASSERT_FALSE(contains(true_flag, ignore_flags_)); // ignore flags cannot be set to true
|
ASSERT_FALSE(contains(true_flag, ignore_flags_)); // ignore flags cannot be set to true
|
||||||
EXPECT_TRUE((value.*true_flag)()) << it->name;
|
EXPECT_TRUE((value.*true_flag)()) << it->name;
|
||||||
all_flags.erase(it);
|
all_flags.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& entry: all_flags)
|
for (const auto& entry: all_flags)
|
||||||
{
|
{
|
||||||
if (!contains(entry.getter, ignore_flags_))
|
if (!contains(entry.getter, ignore_flags_))
|
||||||
{
|
{
|
||||||
EXPECT_FALSE((value.*entry.getter)()) << entry.name;
|
EXPECT_FALSE((value.*entry.getter)()) << entry.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void boost::mysql::test::validate_meta(
|
void boost::mysql::test::validate_meta(
|
||||||
const std::vector<field_metadata>& actual,
|
const std::vector<field_metadata>& actual,
|
||||||
const std::vector<meta_validator>& expected
|
const std::vector<meta_validator>& expected
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ASSERT_EQ(actual.size(), expected.size());
|
ASSERT_EQ(actual.size(), expected.size());
|
||||||
for (std::size_t i = 0; i < actual.size(); ++i)
|
for (std::size_t i = 0; i < actual.size(); ++i)
|
||||||
{
|
{
|
||||||
expected[i].validate(actual[i]);
|
expected[i].validate(actual[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,36 +18,36 @@ namespace test {
|
|||||||
class meta_validator
|
class meta_validator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using flag_getter = bool (field_metadata::*)() const noexcept;
|
using flag_getter = bool (field_metadata::*)() const noexcept;
|
||||||
meta_validator(std::string table, std::string field, field_type type,
|
meta_validator(std::string table, std::string field, field_type type,
|
||||||
std::vector<flag_getter> flags={}, unsigned decimals=0,
|
std::vector<flag_getter> flags={}, unsigned decimals=0,
|
||||||
std::vector<flag_getter> ignore_flags={}):
|
std::vector<flag_getter> ignore_flags={}):
|
||||||
table_(std::move(table)), org_table_(table_), field_(std::move(field)), org_field_(field_),
|
table_(std::move(table)), org_table_(table_), field_(std::move(field)), org_field_(field_),
|
||||||
decimals_(decimals), type_(type), flags_(std::move(flags)),
|
decimals_(decimals), type_(type), flags_(std::move(flags)),
|
||||||
ignore_flags_(std::move(ignore_flags))
|
ignore_flags_(std::move(ignore_flags))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
meta_validator(std::string table, std::string org_table,
|
meta_validator(std::string table, std::string org_table,
|
||||||
std::string field, std::string org_field, field_type type,
|
std::string field, std::string org_field, field_type type,
|
||||||
std::vector<flag_getter> flags={}, unsigned decimals=0,
|
std::vector<flag_getter> flags={}, unsigned decimals=0,
|
||||||
std::vector<flag_getter> ignore_flags={}):
|
std::vector<flag_getter> ignore_flags={}):
|
||||||
table_(std::move(table)), org_table_(std::move(org_table)),
|
table_(std::move(table)), org_table_(std::move(org_table)),
|
||||||
field_(std::move(field)), org_field_(std::move(org_field)),
|
field_(std::move(field)), org_field_(std::move(org_field)),
|
||||||
decimals_(decimals), type_(type), flags_(std::move(flags)),
|
decimals_(decimals), type_(type), flags_(std::move(flags)),
|
||||||
ignore_flags_(std::move(ignore_flags))
|
ignore_flags_(std::move(ignore_flags))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void validate(const field_metadata& value) const;
|
void validate(const field_metadata& value) const;
|
||||||
field_type type() const noexcept { return type_; }
|
field_type type() const noexcept { return type_; }
|
||||||
private:
|
private:
|
||||||
std::string table_;
|
std::string table_;
|
||||||
std::string org_table_;
|
std::string org_table_;
|
||||||
std::string field_;
|
std::string field_;
|
||||||
std::string org_field_;
|
std::string org_field_;
|
||||||
unsigned decimals_;
|
unsigned decimals_;
|
||||||
field_type type_;
|
field_type type_;
|
||||||
std::vector<flag_getter> flags_;
|
std::vector<flag_getter> flags_;
|
||||||
std::vector<flag_getter> ignore_flags_;
|
std::vector<flag_getter> ignore_flags_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void validate_meta(const std::vector<field_metadata>& actual, const std::vector<meta_validator>& expected);
|
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
@ -50,46 +50,46 @@ struct no_result {};
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct network_result
|
struct network_result
|
||||||
{
|
{
|
||||||
error_code err;
|
error_code err;
|
||||||
std::optional<error_info> info; // some async initiators (futures) don't support this
|
std::optional<error_info> info; // some async initiators (futures) don't support this
|
||||||
T value;
|
T value;
|
||||||
|
|
||||||
network_result() = default;
|
network_result() = default;
|
||||||
|
|
||||||
network_result(error_code ec, error_info info, T&& value = {}):
|
network_result(error_code ec, error_info info, T&& value = {}):
|
||||||
err(ec), info(std::move(info)), value(std::move(value)) {}
|
err(ec), info(std::move(info)), value(std::move(value)) {}
|
||||||
|
|
||||||
network_result(error_code ec, T&& value = {}):
|
network_result(error_code ec, T&& value = {}):
|
||||||
err(ec), value(std::move(value)) {}
|
err(ec), value(std::move(value)) {}
|
||||||
|
|
||||||
void validate_no_error() const
|
void validate_no_error() const
|
||||||
{
|
{
|
||||||
ASSERT_EQ(err, error_code());
|
ASSERT_EQ(err, error_code());
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(*info, error_info());
|
EXPECT_EQ(*info, error_info());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_error(
|
void validate_error(
|
||||||
error_code expected_errc,
|
error_code expected_errc,
|
||||||
const std::vector<std::string>& expected_msg
|
const std::vector<std::string>& expected_msg
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
EXPECT_EQ(err, expected_errc);
|
EXPECT_EQ(err, expected_errc);
|
||||||
if (info)
|
if (info)
|
||||||
{
|
{
|
||||||
validate_string_contains(info->message(), expected_msg);
|
validate_string_contains(info->message(), expected_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void validate_error(
|
void validate_error(
|
||||||
errc expected_errc,
|
errc expected_errc,
|
||||||
const std::vector<std::string>& expected_msg
|
const std::vector<std::string>& expected_msg
|
||||||
) const
|
) const
|
||||||
{
|
{
|
||||||
validate_error(detail::make_error_code(expected_errc), expected_msg);
|
validate_error(detail::make_error_code(expected_errc), expected_msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using value_list_it = std::forward_list<value>::const_iterator;
|
using value_list_it = std::forward_list<value>::const_iterator;
|
||||||
@ -98,24 +98,24 @@ template <typename Stream>
|
|||||||
class network_functions
|
class network_functions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using connection_type = connection<Stream>;
|
using connection_type = connection<Stream>;
|
||||||
using prepared_statement_type = prepared_statement<Stream>;
|
using prepared_statement_type = prepared_statement<Stream>;
|
||||||
using resultset_type = resultset<Stream>;
|
using resultset_type = resultset<Stream>;
|
||||||
|
|
||||||
virtual ~network_functions() = default;
|
virtual ~network_functions() = default;
|
||||||
virtual const char* name() const = 0;
|
virtual const char* name() const = 0;
|
||||||
virtual network_result<no_result> handshake(connection_type&, const connection_params&) = 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<resultset_type> query(connection_type&, std::string_view query) = 0;
|
||||||
virtual network_result<prepared_statement_type> prepare_statement(
|
virtual network_result<prepared_statement_type> prepare_statement(
|
||||||
connection_type&, std::string_view statement) = 0;
|
connection_type&, std::string_view statement) = 0;
|
||||||
virtual network_result<resultset_type> execute_statement(
|
virtual network_result<resultset_type> execute_statement(
|
||||||
prepared_statement_type&, value_list_it params_first, value_list_it params_last) = 0;
|
prepared_statement_type&, value_list_it params_first, value_list_it params_last) = 0;
|
||||||
virtual network_result<resultset_type> execute_statement(
|
virtual network_result<resultset_type> execute_statement(
|
||||||
prepared_statement_type&, const std::vector<value>&) = 0;
|
prepared_statement_type&, const std::vector<value>&) = 0;
|
||||||
virtual network_result<no_result> close_statement(prepared_statement_type&) = 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<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_many(resultset_type&, std::size_t count) = 0;
|
||||||
virtual network_result<std::vector<owning_row>> fetch_all(resultset_type&) = 0;
|
virtual network_result<std::vector<owning_row>> fetch_all(resultset_type&) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
|
@ -20,35 +20,35 @@ namespace
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct PrepareStatementTest : public NetworkTest<Stream>
|
struct PrepareStatementTest : public NetworkTest<Stream>
|
||||||
{
|
{
|
||||||
auto do_prepare(std::string_view stmt)
|
auto do_prepare(std::string_view stmt)
|
||||||
{
|
{
|
||||||
return this->GetParam().net->prepare_statement(this->conn, stmt);
|
return this->GetParam().net->prepare_statement(this->conn, stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OkNoParams()
|
void OkNoParams()
|
||||||
{
|
{
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table");
|
auto stmt = do_prepare("SELECT * FROM empty_table");
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
ASSERT_TRUE(stmt.value.valid());
|
ASSERT_TRUE(stmt.value.valid());
|
||||||
EXPECT_GT(stmt.value.id(), 0u);
|
EXPECT_GT(stmt.value.id(), 0u);
|
||||||
EXPECT_EQ(stmt.value.num_params(), 0);
|
EXPECT_EQ(stmt.value.num_params(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OkWithParams()
|
void OkWithParams()
|
||||||
{
|
{
|
||||||
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
auto stmt = do_prepare("SELECT * FROM empty_table WHERE id IN (?, ?)");
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
ASSERT_TRUE(stmt.value.valid());
|
ASSERT_TRUE(stmt.value.valid());
|
||||||
EXPECT_GT(stmt.value.id(), 0u);
|
EXPECT_GT(stmt.value.id(), 0u);
|
||||||
EXPECT_EQ(stmt.value.num_params(), 2);
|
EXPECT_EQ(stmt.value.num_params(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidStatement()
|
void InvalidStatement()
|
||||||
{
|
{
|
||||||
auto stmt = do_prepare("SELECT * FROM bad_table WHERE id IN (?, ?)");
|
auto stmt = do_prepare("SELECT * FROM bad_table WHERE id IN (?, ?)");
|
||||||
stmt.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
|
stmt.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
|
||||||
EXPECT_FALSE(stmt.value.valid());
|
EXPECT_FALSE(stmt.value.valid());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(PrepareStatementTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(PrepareStatementTest)
|
||||||
|
@ -17,219 +17,219 @@ namespace
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct PreparedStatementLifecycleTest : NetworkTest<Stream>
|
struct PreparedStatementLifecycleTest : NetworkTest<Stream>
|
||||||
{
|
{
|
||||||
std::int64_t get_table_size(const std::string& table)
|
std::int64_t get_table_size(const std::string& table)
|
||||||
{
|
{
|
||||||
return std::get<std::int64_t>(
|
return std::get<std::int64_t>(
|
||||||
this->conn.query("SELECT COUNT(*) FROM " + table).fetch_all().at(0).values().at(0));
|
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")
|
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 + "'")
|
return this->conn.query("SELECT field_int FROM updates_table WHERE field_varchar = '" + field_varchar + "'")
|
||||||
.fetch_all().at(0).values().at(0);
|
.fetch_all().at(0).values().at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectWithParametersMultipleExecutions()
|
void SelectWithParametersMultipleExecutions()
|
||||||
{
|
{
|
||||||
auto* net = this->GetParam().net;
|
auto* net = this->GetParam().net;
|
||||||
|
|
||||||
// Prepare a statement
|
// Prepare a statement
|
||||||
auto stmt = net->prepare_statement(
|
auto stmt = net->prepare_statement(
|
||||||
this->conn,
|
this->conn,
|
||||||
"SELECT * FROM two_rows_table WHERE id = ? OR field_varchar = ?"
|
"SELECT * FROM two_rows_table WHERE id = ? OR field_varchar = ?"
|
||||||
);
|
);
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
|
|
||||||
// Execute it. Only one row will be returned (because of the id)
|
// Execute it. Only one row will be returned (because of the id)
|
||||||
auto result = net->execute_statement(stmt.value, makevalues(1, "non_existent"));
|
auto result = net->execute_statement(stmt.value, makevalues(1, "non_existent"));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_FALSE(result.value.complete());
|
EXPECT_FALSE(result.value.complete());
|
||||||
this->validate_2fields_meta(result.value, "two_rows_table");
|
this->validate_2fields_meta(result.value, "two_rows_table");
|
||||||
|
|
||||||
auto rows = net->fetch_all(result.value);
|
auto rows = net->fetch_all(result.value);
|
||||||
rows.validate_no_error();
|
rows.validate_no_error();
|
||||||
ASSERT_EQ(rows.value.size(), 1);
|
ASSERT_EQ(rows.value.size(), 1);
|
||||||
EXPECT_EQ(static_cast<const row&>(rows.value[0]), (makerow(1, "f0")));
|
EXPECT_EQ(static_cast<const row&>(rows.value[0]), (makerow(1, "f0")));
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
|
|
||||||
// Execute it again, but with different values. This time, two rows are returned
|
// Execute it again, but with different values. This time, two rows are returned
|
||||||
result = net->execute_statement(stmt.value, makevalues(1, "f1"));
|
result = net->execute_statement(stmt.value, makevalues(1, "f1"));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_FALSE(result.value.complete());
|
EXPECT_FALSE(result.value.complete());
|
||||||
this->validate_2fields_meta(result.value, "two_rows_table");
|
this->validate_2fields_meta(result.value, "two_rows_table");
|
||||||
|
|
||||||
rows = net->fetch_all(result.value);
|
rows = net->fetch_all(result.value);
|
||||||
rows.validate_no_error();
|
rows.validate_no_error();
|
||||||
ASSERT_EQ(rows.value.size(), 2);
|
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[0]), (makerow(1, "f0")));
|
||||||
EXPECT_EQ(static_cast<const row&>(rows.value[1]), (makerow(2, "f1")));
|
EXPECT_EQ(static_cast<const row&>(rows.value[1]), (makerow(2, "f1")));
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
|
|
||||||
// Close it
|
// Close it
|
||||||
auto close_result = net->close_statement(stmt.value);
|
auto close_result = net->close_statement(stmt.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertWithParametersMultipleExecutions()
|
void InsertWithParametersMultipleExecutions()
|
||||||
{
|
{
|
||||||
auto* net = this->GetParam().net;
|
auto* net = this->GetParam().net;
|
||||||
|
|
||||||
// Get the number of rows before insertion
|
// Get the number of rows before insertion
|
||||||
auto rows_before = get_table_size("inserts_table");
|
auto rows_before = get_table_size("inserts_table");
|
||||||
|
|
||||||
// Prepare a statement
|
// Prepare a statement
|
||||||
auto stmt = net->prepare_statement(
|
auto stmt = net->prepare_statement(
|
||||||
this->conn,
|
this->conn,
|
||||||
"INSERT INTO inserts_table (field_varchar) VALUES (?)"
|
"INSERT INTO inserts_table (field_varchar) VALUES (?)"
|
||||||
);
|
);
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
|
|
||||||
// Insert one value
|
// Insert one value
|
||||||
auto result = net->execute_statement(stmt.value, makevalues("value0"));
|
auto result = net->execute_statement(stmt.value, makevalues("value0"));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
EXPECT_TRUE(result.value.fields().empty());
|
EXPECT_TRUE(result.value.fields().empty());
|
||||||
|
|
||||||
// Insert another one
|
// Insert another one
|
||||||
result = net->execute_statement(stmt.value, makevalues("value1"));
|
result = net->execute_statement(stmt.value, makevalues("value1"));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
EXPECT_TRUE(result.value.fields().empty());
|
EXPECT_TRUE(result.value.fields().empty());
|
||||||
|
|
||||||
// Validate we did something
|
// Validate we did something
|
||||||
auto rows_after = get_table_size("inserts_table");
|
auto rows_after = get_table_size("inserts_table");
|
||||||
EXPECT_EQ(rows_after, rows_before + 2);
|
EXPECT_EQ(rows_after, rows_before + 2);
|
||||||
|
|
||||||
// Close it
|
// Close it
|
||||||
auto close_result = net->close_statement(stmt.value);
|
auto close_result = net->close_statement(stmt.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateWithParametersMultipleExecutions()
|
void UpdateWithParametersMultipleExecutions()
|
||||||
{
|
{
|
||||||
auto* net = this->GetParam().net;
|
auto* net = this->GetParam().net;
|
||||||
|
|
||||||
// Prepare a statement
|
// Prepare a statement
|
||||||
auto stmt = net->prepare_statement(
|
auto stmt = net->prepare_statement(
|
||||||
this->conn,
|
this->conn,
|
||||||
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
|
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
|
||||||
);
|
);
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
|
|
||||||
// Set field_int to something
|
// Set field_int to something
|
||||||
auto result = net->execute_statement(stmt.value, makevalues(200, "f0"));
|
auto result = net->execute_statement(stmt.value, makevalues(200, "f0"));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
EXPECT_TRUE(result.value.fields().empty());
|
EXPECT_TRUE(result.value.fields().empty());
|
||||||
|
|
||||||
// Verify that took effect
|
// Verify that took effect
|
||||||
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(200)));
|
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(200)));
|
||||||
|
|
||||||
// Set field_int to something different
|
// Set field_int to something different
|
||||||
result = net->execute_statement(stmt.value, makevalues(250, "f0"));
|
result = net->execute_statement(stmt.value, makevalues(250, "f0"));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
EXPECT_TRUE(result.value.fields().empty());
|
EXPECT_TRUE(result.value.fields().empty());
|
||||||
|
|
||||||
// Verify that took effect
|
// Verify that took effect
|
||||||
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(250)));
|
EXPECT_EQ(get_updates_table_value(), value(std::int32_t(250)));
|
||||||
|
|
||||||
// Close the statement
|
// Close the statement
|
||||||
auto close_result = net->close_statement(stmt.value);
|
auto close_result = net->close_statement(stmt.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultipleStatements()
|
void MultipleStatements()
|
||||||
{
|
{
|
||||||
auto* net = this->GetParam().net;
|
auto* net = this->GetParam().net;
|
||||||
|
|
||||||
// Prepare an update
|
// Prepare an update
|
||||||
auto stmt_update = net->prepare_statement(
|
auto stmt_update = net->prepare_statement(
|
||||||
this->conn,
|
this->conn,
|
||||||
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
|
"UPDATE updates_table SET field_int = ? WHERE field_varchar = ?"
|
||||||
);
|
);
|
||||||
stmt_update.validate_no_error();
|
stmt_update.validate_no_error();
|
||||||
|
|
||||||
// Prepare a select
|
// Prepare a select
|
||||||
auto stmt_select = net->prepare_statement(
|
auto stmt_select = net->prepare_statement(
|
||||||
this->conn,
|
this->conn,
|
||||||
"SELECT field_int FROM updates_table WHERE field_varchar = ?"
|
"SELECT field_int FROM updates_table WHERE field_varchar = ?"
|
||||||
);
|
);
|
||||||
stmt_select.validate_no_error();
|
stmt_select.validate_no_error();
|
||||||
|
|
||||||
// They have different IDs
|
// They have different IDs
|
||||||
EXPECT_NE(stmt_update.value.id(), stmt_select.value.id());
|
EXPECT_NE(stmt_update.value.id(), stmt_select.value.id());
|
||||||
|
|
||||||
// Execute update
|
// Execute update
|
||||||
auto update_result = net->execute_statement(stmt_update.value, makevalues(210, "f0"));
|
auto update_result = net->execute_statement(stmt_update.value, makevalues(210, "f0"));
|
||||||
update_result.validate_no_error();
|
update_result.validate_no_error();
|
||||||
EXPECT_TRUE(update_result.value.complete());
|
EXPECT_TRUE(update_result.value.complete());
|
||||||
|
|
||||||
// Execute select
|
// Execute select
|
||||||
auto select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
|
auto select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
|
||||||
select_result.validate_no_error();
|
select_result.validate_no_error();
|
||||||
auto rows = net->fetch_all(select_result.value);
|
auto rows = net->fetch_all(select_result.value);
|
||||||
rows.validate_no_error();
|
rows.validate_no_error();
|
||||||
EXPECT_EQ(rows.value.size(), 1);
|
EXPECT_EQ(rows.value.size(), 1);
|
||||||
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(210));
|
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(210));
|
||||||
|
|
||||||
// Execute update again
|
// Execute update again
|
||||||
update_result = net->execute_statement(stmt_update.value, makevalues(220, "f0"));
|
update_result = net->execute_statement(stmt_update.value, makevalues(220, "f0"));
|
||||||
update_result.validate_no_error();
|
update_result.validate_no_error();
|
||||||
EXPECT_TRUE(update_result.value.complete());
|
EXPECT_TRUE(update_result.value.complete());
|
||||||
|
|
||||||
// Update no longer needed, close it
|
// Update no longer needed, close it
|
||||||
auto close_result = net->close_statement(stmt_update.value);
|
auto close_result = net->close_statement(stmt_update.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
|
|
||||||
// Execute select again
|
// Execute select again
|
||||||
select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
|
select_result = net->execute_statement(stmt_select.value, makevalues("f0"));
|
||||||
select_result.validate_no_error();
|
select_result.validate_no_error();
|
||||||
rows = net->fetch_all(select_result.value);
|
rows = net->fetch_all(select_result.value);
|
||||||
rows.validate_no_error();
|
rows.validate_no_error();
|
||||||
EXPECT_EQ(rows.value.size(), 1);
|
EXPECT_EQ(rows.value.size(), 1);
|
||||||
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(220));
|
EXPECT_EQ(static_cast<row&>(rows.value[0]), makerow(220));
|
||||||
|
|
||||||
// Close select
|
// Close select
|
||||||
close_result = net->close_statement(stmt_select.value);
|
close_result = net->close_statement(stmt_select.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertWithNullValues()
|
void InsertWithNullValues()
|
||||||
{
|
{
|
||||||
auto* net = this->GetParam().net;
|
auto* net = this->GetParam().net;
|
||||||
|
|
||||||
// Statement to perform the updates
|
// Statement to perform the updates
|
||||||
auto stmt = net->prepare_statement(
|
auto stmt = net->prepare_statement(
|
||||||
this->conn,
|
this->conn,
|
||||||
"UPDATE updates_table SET field_int = ? WHERE field_varchar = 'fnull'"
|
"UPDATE updates_table SET field_int = ? WHERE field_varchar = 'fnull'"
|
||||||
);
|
);
|
||||||
stmt.validate_no_error();
|
stmt.validate_no_error();
|
||||||
|
|
||||||
// Set the value we will be updating to something non-NULL
|
// Set the value we will be updating to something non-NULL
|
||||||
auto result = net->execute_statement(stmt.value, makevalues(42));
|
auto result = net->execute_statement(stmt.value, makevalues(42));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
|
|
||||||
// Verify it took effect
|
// Verify it took effect
|
||||||
ASSERT_EQ(get_updates_table_value("fnull"), value(std::int32_t(42)));
|
ASSERT_EQ(get_updates_table_value("fnull"), value(std::int32_t(42)));
|
||||||
|
|
||||||
// Update the value to NULL
|
// Update the value to NULL
|
||||||
result = net->execute_statement(stmt.value, makevalues(nullptr));
|
result = net->execute_statement(stmt.value, makevalues(nullptr));
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
|
|
||||||
// Verify it took effect
|
// Verify it took effect
|
||||||
ASSERT_EQ(get_updates_table_value("fnull"), value(nullptr));
|
ASSERT_EQ(get_updates_table_value("fnull"), value(nullptr));
|
||||||
|
|
||||||
// Close statement
|
// Close statement
|
||||||
auto close_result = net->close_statement(stmt.value);
|
auto close_result = net->close_statement(stmt.value);
|
||||||
close_result.validate_no_error();
|
close_result.validate_no_error();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(PreparedStatementLifecycleTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(PreparedStatementLifecycleTest)
|
||||||
|
@ -26,72 +26,72 @@ namespace
|
|||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct QueryTest : public NetworkTest<Stream>
|
struct QueryTest : public NetworkTest<Stream>
|
||||||
{
|
{
|
||||||
auto do_query(std::string_view sql)
|
auto do_query(std::string_view sql)
|
||||||
{
|
{
|
||||||
return this->GetParam().net->query(this->conn, sql);
|
return this->GetParam().net->query(this->conn, sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertQueryOk()
|
void InsertQueryOk()
|
||||||
{
|
{
|
||||||
const char* sql = "INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
|
const char* sql = "INSERT INTO inserts_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
|
||||||
auto result = do_query(sql);
|
auto result = do_query(sql);
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.fields().empty());
|
EXPECT_TRUE(result.value.fields().empty());
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
EXPECT_EQ(result.value.affected_rows(), 1);
|
EXPECT_EQ(result.value.affected_rows(), 1);
|
||||||
EXPECT_EQ(result.value.warning_count(), 0);
|
EXPECT_EQ(result.value.warning_count(), 0);
|
||||||
EXPECT_GT(result.value.last_insert_id(), 0);
|
EXPECT_GT(result.value.last_insert_id(), 0);
|
||||||
EXPECT_EQ(result.value.info(), "");
|
EXPECT_EQ(result.value.info(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void InsertQueryFailed()
|
void InsertQueryFailed()
|
||||||
{
|
{
|
||||||
const char* sql = "INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
|
const char* sql = "INSERT INTO bad_table (field_varchar, field_date) VALUES ('v0', '2010-10-11')";
|
||||||
auto result = do_query(sql);
|
auto result = do_query(sql);
|
||||||
result.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
|
result.validate_error(errc::no_such_table, {"table", "doesn't exist", "bad_table"});
|
||||||
EXPECT_FALSE(result.value.valid());
|
EXPECT_FALSE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateQueryOk()
|
void UpdateQueryOk()
|
||||||
{
|
{
|
||||||
const char* sql = "UPDATE updates_table SET field_int = field_int+1";
|
const char* sql = "UPDATE updates_table SET field_int = field_int+1";
|
||||||
auto result = do_query(sql);
|
auto result = do_query(sql);
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.fields().empty());
|
EXPECT_TRUE(result.value.fields().empty());
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_TRUE(result.value.complete());
|
EXPECT_TRUE(result.value.complete());
|
||||||
EXPECT_EQ(result.value.affected_rows(), 2);
|
EXPECT_EQ(result.value.affected_rows(), 2);
|
||||||
EXPECT_EQ(result.value.warning_count(), 0);
|
EXPECT_EQ(result.value.warning_count(), 0);
|
||||||
EXPECT_EQ(result.value.last_insert_id(), 0);
|
EXPECT_EQ(result.value.last_insert_id(), 0);
|
||||||
EXPECT_THAT(std::string(result.value.info()), HasSubstr("Rows matched"));
|
EXPECT_THAT(std::string(result.value.info()), HasSubstr("Rows matched"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectOk()
|
void SelectOk()
|
||||||
{
|
{
|
||||||
auto result = do_query("SELECT * FROM empty_table");
|
auto result = do_query("SELECT * FROM empty_table");
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
EXPECT_TRUE(result.value.valid());
|
EXPECT_TRUE(result.value.valid());
|
||||||
EXPECT_FALSE(result.value.complete());
|
EXPECT_FALSE(result.value.complete());
|
||||||
this->validate_2fields_meta(result.value, "empty_table");
|
this->validate_2fields_meta(result.value, "empty_table");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectQueryFailed()
|
void SelectQueryFailed()
|
||||||
{
|
{
|
||||||
auto result = do_query("SELECT field_varchar, field_bad FROM one_row_table");
|
auto result = do_query("SELECT field_varchar, field_bad FROM one_row_table");
|
||||||
result.validate_error(errc::bad_field_error, {"unknown column", "field_bad"});
|
result.validate_error(errc::bad_field_error, {"unknown column", "field_bad"});
|
||||||
EXPECT_FALSE(result.value.valid());
|
EXPECT_FALSE(result.value.valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some system-level query tests (TODO: this does not feel right here)
|
// Some system-level query tests (TODO: this does not feel right here)
|
||||||
void QueryAndFetch_AliasedTableAndField_MetadataCorrect()
|
void QueryAndFetch_AliasedTableAndField_MetadataCorrect()
|
||||||
{
|
{
|
||||||
auto result = do_query("SELECT field_varchar AS field_alias FROM empty_table table_alias");
|
auto result = do_query("SELECT field_varchar AS field_alias FROM empty_table table_alias");
|
||||||
meta_validator validator ("table_alias", "empty_table", "field_alias",
|
meta_validator validator ("table_alias", "empty_table", "field_alias",
|
||||||
"field_varchar", field_type::varchar);
|
"field_varchar", field_type::varchar);
|
||||||
result.validate_no_error();
|
result.validate_no_error();
|
||||||
validate_meta(result.value.fields(), {validator});
|
validate_meta(result.value.fields(), {validator});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(QueryTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(QueryTest)
|
||||||
|
@ -28,31 +28,31 @@ template <typename Stream>
|
|||||||
class resultset_generator
|
class resultset_generator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~resultset_generator() {}
|
virtual ~resultset_generator() {}
|
||||||
virtual const char* name() const = 0;
|
virtual const char* name() const = 0;
|
||||||
virtual resultset<Stream> generate(connection<Stream>&, std::string_view) = 0;
|
virtual resultset<Stream> generate(connection<Stream>&, std::string_view) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
class text_resultset_generator : public resultset_generator<Stream>
|
class text_resultset_generator : public resultset_generator<Stream>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const char* name() const override { return "text"; }
|
const char* name() const override { return "text"; }
|
||||||
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
|
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
|
||||||
{
|
{
|
||||||
return conn.query(query);
|
return conn.query(query);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
class binary_resultset_generator : public resultset_generator<Stream>
|
class binary_resultset_generator : public resultset_generator<Stream>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const char* name() const override { return "binary"; }
|
const char* name() const override { return "binary"; }
|
||||||
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
|
resultset<Stream> generate(connection<Stream>& conn, std::string_view query) override
|
||||||
{
|
{
|
||||||
return conn.prepare_statement(query).execute(boost::mysql::no_statement_params);
|
return conn.prepare_statement(query).execute(boost::mysql::no_statement_params);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Stream> text_resultset_generator<Stream> text_obj;
|
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>
|
template <typename Stream>
|
||||||
struct resultset_testcase : network_testcase_with_ssl<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):
|
resultset_testcase(network_testcase_with_ssl<Stream> base, resultset_generator<Stream>* gen):
|
||||||
network_testcase_with_ssl<Stream>(base), gen(gen) {}
|
network_testcase_with_ssl<Stream>(base), gen(gen) {}
|
||||||
|
|
||||||
std::string name() const
|
std::string name() const
|
||||||
{
|
{
|
||||||
return network_testcase_with_ssl<Stream>::name() + '_' + gen->name();
|
return network_testcase_with_ssl<Stream>::name() + '_' + gen->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<resultset_testcase<Stream>> make_all()
|
static std::vector<resultset_testcase<Stream>> make_all()
|
||||||
{
|
{
|
||||||
resultset_generator<Stream>* all_resultset_generators [] = {
|
resultset_generator<Stream>* all_resultset_generators [] = {
|
||||||
&text_obj<Stream>,
|
&text_obj<Stream>,
|
||||||
&binary_obj<Stream>
|
&binary_obj<Stream>
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<resultset_testcase<Stream>> res;
|
std::vector<resultset_testcase<Stream>> res;
|
||||||
for (auto* gen: all_resultset_generators)
|
for (auto* gen: all_resultset_generators)
|
||||||
{
|
{
|
||||||
for (auto base: network_testcase_with_ssl<Stream>::make_all())
|
for (auto base: network_testcase_with_ssl<Stream>::make_all())
|
||||||
{
|
{
|
||||||
res.push_back(resultset_testcase<Stream>(base, gen));
|
res.push_back(resultset_testcase<Stream>(base, gen));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Stream>
|
template <typename Stream>
|
||||||
struct ResultsetTest : public NetworkTest<Stream, resultset_testcase<Stream>, true>
|
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_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_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_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_fetch_all(resultset<Stream>& r) { return this->GetParam().net->fetch_all(r); }
|
||||||
|
|
||||||
// FetchOne
|
// FetchOne
|
||||||
void FetchOne_NoResults()
|
void FetchOne_NoResults()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM empty_table");
|
auto result = do_generate("SELECT * FROM empty_table");
|
||||||
EXPECT_TRUE(result.valid());
|
EXPECT_TRUE(result.valid());
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
EXPECT_EQ(result.fields().size(), 2);
|
EXPECT_EQ(result.fields().size(), 2);
|
||||||
|
|
||||||
// Already in the end of the resultset, we receive the EOF
|
// Already in the end of the resultset, we receive the EOF
|
||||||
auto row_result = do_fetch_one(result);
|
auto row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
EXPECT_EQ(row_result.value, nullptr);
|
EXPECT_EQ(row_result.value, nullptr);
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
|
|
||||||
// Fetching again just returns null
|
// Fetching again just returns null
|
||||||
row_result = do_fetch_one(result);
|
row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
EXPECT_EQ(row_result.value, nullptr);
|
EXPECT_EQ(row_result.value, nullptr);
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchOne_OneRow()
|
void FetchOne_OneRow()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM one_row_table");
|
auto result = do_generate("SELECT * FROM one_row_table");
|
||||||
EXPECT_TRUE(result.valid());
|
EXPECT_TRUE(result.valid());
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
EXPECT_EQ(result.fields().size(), 2);
|
EXPECT_EQ(result.fields().size(), 2);
|
||||||
|
|
||||||
// Fetch only row
|
// Fetch only row
|
||||||
auto row_result = do_fetch_one(result);
|
auto row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
ASSERT_NE(row_result.value, nullptr);
|
ASSERT_NE(row_result.value, nullptr);
|
||||||
this->validate_2fields_meta(result, "one_row_table");
|
this->validate_2fields_meta(result, "one_row_table");
|
||||||
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
|
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
|
|
||||||
// Fetch next: end of resultset
|
// Fetch next: end of resultset
|
||||||
row_result = do_fetch_one(result);
|
row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
ASSERT_EQ(row_result.value, nullptr);
|
ASSERT_EQ(row_result.value, nullptr);
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchOne_TwoRows()
|
void FetchOne_TwoRows()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM two_rows_table");
|
auto result = do_generate("SELECT * FROM two_rows_table");
|
||||||
EXPECT_TRUE(result.valid());
|
EXPECT_TRUE(result.valid());
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
EXPECT_EQ(result.fields().size(), 2);
|
EXPECT_EQ(result.fields().size(), 2);
|
||||||
|
|
||||||
// Fetch first row
|
// Fetch first row
|
||||||
auto row_result = do_fetch_one(result);
|
auto row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
ASSERT_NE(row_result.value, nullptr);
|
ASSERT_NE(row_result.value, nullptr);
|
||||||
this->validate_2fields_meta(result, "two_rows_table");
|
this->validate_2fields_meta(result, "two_rows_table");
|
||||||
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
|
EXPECT_EQ(row_result.value->values(), makevalues(1, "f0"));
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
|
|
||||||
// Fetch next row
|
// Fetch next row
|
||||||
row_result = do_fetch_one(result);
|
row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
ASSERT_NE(row_result.value, nullptr);
|
ASSERT_NE(row_result.value, nullptr);
|
||||||
this->validate_2fields_meta(result, "two_rows_table");
|
this->validate_2fields_meta(result, "two_rows_table");
|
||||||
EXPECT_EQ(row_result.value->values(), makevalues(2, "f1"));
|
EXPECT_EQ(row_result.value->values(), makevalues(2, "f1"));
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
|
|
||||||
// Fetch next: end of resultset
|
// Fetch next: end of resultset
|
||||||
row_result = do_fetch_one(result);
|
row_result = do_fetch_one(result);
|
||||||
row_result.validate_no_error();
|
row_result.validate_no_error();
|
||||||
ASSERT_EQ(row_result.value, nullptr);
|
ASSERT_EQ(row_result.value, nullptr);
|
||||||
this->validate_eof(result);
|
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
|
// FetchMany
|
||||||
void FetchMany_NoResults()
|
void FetchMany_NoResults()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM empty_table");
|
auto result = do_generate("SELECT * FROM empty_table");
|
||||||
|
|
||||||
// Fetch many, but there are no results
|
// Fetch many, but there are no results
|
||||||
auto rows_result = do_fetch_many(result, 10);
|
auto rows_result = do_fetch_many(result, 10);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_TRUE(rows_result.value.empty());
|
EXPECT_TRUE(rows_result.value.empty());
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
|
|
||||||
// Fetch again, should return OK and empty
|
// Fetch again, should return OK and empty
|
||||||
rows_result = do_fetch_many(result, 10);
|
rows_result = do_fetch_many(result, 10);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_TRUE(rows_result.value.empty());
|
EXPECT_TRUE(rows_result.value.empty());
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchMany_MoreRowsThanCount()
|
void FetchMany_MoreRowsThanCount()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM three_rows_table");
|
auto result = do_generate("SELECT * FROM three_rows_table");
|
||||||
|
|
||||||
// Fetch 2, one remaining
|
// Fetch 2, one remaining
|
||||||
auto rows_result = do_fetch_many(result, 2);
|
auto rows_result = do_fetch_many(result, 2);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
||||||
|
|
||||||
// Fetch another two (completes the resultset)
|
// Fetch another two (completes the resultset)
|
||||||
rows_result = do_fetch_many(result, 2);
|
rows_result = do_fetch_many(result, 2);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 3, "f2")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 3, "f2")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchMany_LessRowsThanCount()
|
void FetchMany_LessRowsThanCount()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM two_rows_table");
|
auto result = do_generate("SELECT * FROM two_rows_table");
|
||||||
|
|
||||||
// Fetch 3, resultset exhausted
|
// Fetch 3, resultset exhausted
|
||||||
auto rows_result = do_fetch_many(result, 3);
|
auto rows_result = do_fetch_many(result, 3);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchMany_SameRowsAsCount()
|
void FetchMany_SameRowsAsCount()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM two_rows_table");
|
auto result = do_generate("SELECT * FROM two_rows_table");
|
||||||
|
|
||||||
// Fetch 2, 0 remaining but resultset not exhausted
|
// Fetch 2, 0 remaining but resultset not exhausted
|
||||||
auto rows_result = do_fetch_many(result, 2);
|
auto rows_result = do_fetch_many(result, 2);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
||||||
|
|
||||||
// Fetch again, exhausts the resultset
|
// Fetch again, exhausts the resultset
|
||||||
rows_result = do_fetch_many(result, 2);
|
rows_result = do_fetch_many(result, 2);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_EQ(rows_result.value.size(), 0);
|
EXPECT_EQ(rows_result.value.size(), 0);
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchMany_CountEqualsOne()
|
void FetchMany_CountEqualsOne()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM one_row_table");
|
auto result = do_generate("SELECT * FROM one_row_table");
|
||||||
|
|
||||||
// Fetch 1, 1 remaining
|
// Fetch 1, 1 remaining
|
||||||
auto rows_result = do_fetch_many(result, 1);
|
auto rows_result = do_fetch_many(result, 1);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_FALSE(result.complete());
|
EXPECT_FALSE(result.complete());
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchAll
|
// FetchAll
|
||||||
void FetchAll_NoResults()
|
void FetchAll_NoResults()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM empty_table");
|
auto result = do_generate("SELECT * FROM empty_table");
|
||||||
|
|
||||||
// Fetch many, but there are no results
|
// Fetch many, but there are no results
|
||||||
auto rows_result = do_fetch_all(result);
|
auto rows_result = do_fetch_all(result);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_TRUE(rows_result.value.empty());
|
EXPECT_TRUE(rows_result.value.empty());
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
|
|
||||||
// Fetch again, should return OK and empty
|
// Fetch again, should return OK and empty
|
||||||
rows_result = do_fetch_all(result);
|
rows_result = do_fetch_all(result);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_TRUE(rows_result.value.empty());
|
EXPECT_TRUE(rows_result.value.empty());
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchAll_OneRow()
|
void FetchAll_OneRow()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM one_row_table");
|
auto result = do_generate("SELECT * FROM one_row_table");
|
||||||
|
|
||||||
auto rows_result = do_fetch_all(result);
|
auto rows_result = do_fetch_all(result);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FetchAll_SeveralRows()
|
void FetchAll_SeveralRows()
|
||||||
{
|
{
|
||||||
auto result = do_generate("SELECT * FROM two_rows_table");
|
auto result = do_generate("SELECT * FROM two_rows_table");
|
||||||
|
|
||||||
auto rows_result = do_fetch_all(result);
|
auto rows_result = do_fetch_all(result);
|
||||||
rows_result.validate_no_error();
|
rows_result.validate_no_error();
|
||||||
this->validate_eof(result);
|
this->validate_eof(result);
|
||||||
EXPECT_TRUE(result.complete());
|
EXPECT_TRUE(result.complete());
|
||||||
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
EXPECT_EQ(rows_result.value, (makerows(2, 1, "f0", 2, "f1")));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BOOST_MYSQL_NETWORK_TEST_SUITE(ResultsetTest)
|
BOOST_MYSQL_NETWORK_TEST_SUITE(ResultsetTest)
|
||||||
|
@ -21,159 +21,159 @@ namespace
|
|||||||
// mysql_native_password
|
// mysql_native_password
|
||||||
struct MysqlNativePasswordTest : public Test
|
struct MysqlNativePasswordTest : public Test
|
||||||
{
|
{
|
||||||
auth_calculator calc;
|
auth_calculator calc;
|
||||||
std::uint8_t challenge_buffer [20] {
|
std::uint8_t challenge_buffer [20] {
|
||||||
0x79, 0x64, 0x3d, 0x12, 0x1d, 0x71, 0x74, 0x47,
|
0x79, 0x64, 0x3d, 0x12, 0x1d, 0x71, 0x74, 0x47,
|
||||||
0x5f, 0x48, 0x3e, 0x3e, 0x0b, 0x62, 0x0a, 0x03,
|
0x5f, 0x48, 0x3e, 0x3e, 0x0b, 0x62, 0x0a, 0x03,
|
||||||
0x3d, 0x27, 0x3a, 0x4c
|
0x3d, 0x27, 0x3a, 0x4c
|
||||||
}; // Values snooped using Wireshark
|
}; // Values snooped using Wireshark
|
||||||
std::uint8_t expected_buffer [20] {
|
std::uint8_t expected_buffer [20] {
|
||||||
0xf1, 0xb2, 0xfb, 0x1c, 0x8d, 0xe7, 0x5d, 0xb8,
|
0xf1, 0xb2, 0xfb, 0x1c, 0x8d, 0xe7, 0x5d, 0xb8,
|
||||||
0xeb, 0xa8, 0x12, 0x6a, 0xd1, 0x0f, 0xe9, 0xb1,
|
0xeb, 0xa8, 0x12, 0x6a, 0xd1, 0x0f, 0xe9, 0xb1,
|
||||||
0x10, 0x50, 0xd4, 0x28
|
0x10, 0x50, 0xd4, 0x28
|
||||||
};
|
};
|
||||||
std::string_view challenge = makesv(challenge_buffer);
|
std::string_view challenge = makesv(challenge_buffer);
|
||||||
std::string_view expected = makesv(expected_buffer);
|
std::string_view expected = makesv(expected_buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslFalse_ReturnsExpectedHash)
|
TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslFalse_ReturnsExpectedHash)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("mysql_native_password", "root", challenge, false);
|
auto err = calc.calculate("mysql_native_password", "root", challenge, false);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), expected);
|
EXPECT_EQ(calc.response(), expected);
|
||||||
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslTrue_ReturnsExpectedHash)
|
TEST_F(MysqlNativePasswordTest, NonEmptyPasswordSslTrue_ReturnsExpectedHash)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("mysql_native_password", "root", challenge, true);
|
auto err = calc.calculate("mysql_native_password", "root", challenge, true);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), expected);
|
EXPECT_EQ(calc.response(), expected);
|
||||||
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlNativePasswordTest, EmptyPasswordSslFalse_ReturnsEmpty)
|
TEST_F(MysqlNativePasswordTest, EmptyPasswordSslFalse_ReturnsEmpty)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("mysql_native_password", "", challenge, false);
|
auto err = calc.calculate("mysql_native_password", "", challenge, false);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), "");
|
EXPECT_EQ(calc.response(), "");
|
||||||
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlNativePasswordTest, EmptyPasswordSslTrue_ReturnsEmpty)
|
TEST_F(MysqlNativePasswordTest, EmptyPasswordSslTrue_ReturnsEmpty)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("mysql_native_password", "", challenge, false);
|
auto err = calc.calculate("mysql_native_password", "", challenge, false);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), "");
|
EXPECT_EQ(calc.response(), "");
|
||||||
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
EXPECT_EQ(calc.plugin_name(), "mysql_native_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlNativePasswordTest, BadChallengeLength_Fail)
|
TEST_F(MysqlNativePasswordTest, BadChallengeLength_Fail)
|
||||||
{
|
{
|
||||||
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "", true)),
|
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "", true)),
|
||||||
make_error_code(errc::protocol_value_error));
|
make_error_code(errc::protocol_value_error));
|
||||||
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "bad_challenge", true)),
|
EXPECT_EQ((calc.calculate("mysql_native_password", "password", "bad_challenge", true)),
|
||||||
make_error_code(errc::protocol_value_error));
|
make_error_code(errc::protocol_value_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
// caching_sha2_password
|
// caching_sha2_password
|
||||||
struct CachingSha2PasswordTest : public Test
|
struct CachingSha2PasswordTest : public Test
|
||||||
{
|
{
|
||||||
auth_calculator calc;
|
auth_calculator calc;
|
||||||
std::uint8_t challenge_buffer [20] {
|
std::uint8_t challenge_buffer [20] {
|
||||||
0x3e, 0x3b, 0x4, 0x55, 0x4, 0x70, 0x16, 0x3a,
|
0x3e, 0x3b, 0x4, 0x55, 0x4, 0x70, 0x16, 0x3a,
|
||||||
0x4c, 0x15, 0x35, 0x3, 0x15, 0x76, 0x73, 0x22,
|
0x4c, 0x15, 0x35, 0x3, 0x15, 0x76, 0x73, 0x22,
|
||||||
0x46, 0x8, 0x18, 0x1
|
0x46, 0x8, 0x18, 0x1
|
||||||
}; // Values snooped using the MySQL Python connector
|
}; // Values snooped using the MySQL Python connector
|
||||||
std::uint8_t expected_buffer [32] {
|
std::uint8_t expected_buffer [32] {
|
||||||
0xa1, 0xc1, 0xe1, 0xe9, 0x1b, 0xb6, 0x54, 0x4b,
|
0xa1, 0xc1, 0xe1, 0xe9, 0x1b, 0xb6, 0x54, 0x4b,
|
||||||
0xa7, 0x37, 0x4b, 0x9c, 0x56, 0x6d, 0x69, 0x3e,
|
0xa7, 0x37, 0x4b, 0x9c, 0x56, 0x6d, 0x69, 0x3e,
|
||||||
0x6, 0xca, 0x7, 0x2, 0x98, 0xac, 0xd1, 0x6,
|
0x6, 0xca, 0x7, 0x2, 0x98, 0xac, 0xd1, 0x6,
|
||||||
0x18, 0xc6, 0x90, 0x38, 0x9d, 0x88, 0xe1, 0x20
|
0x18, 0xc6, 0x90, 0x38, 0x9d, 0x88, 0xe1, 0x20
|
||||||
};
|
};
|
||||||
std::string_view challenge = makesv(challenge_buffer);
|
std::string_view challenge = makesv(challenge_buffer);
|
||||||
std::string_view expected = makesv(expected_buffer);
|
std::string_view expected = makesv(expected_buffer);
|
||||||
std::string_view cleartext_challenge { "\4" };
|
std::string_view cleartext_challenge { "\4" };
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslFalse_ReturnsExpectedHash)
|
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslFalse_ReturnsExpectedHash)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "hola", challenge, false);
|
auto err = calc.calculate("caching_sha2_password", "hola", challenge, false);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), expected);
|
EXPECT_EQ(calc.response(), expected);
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslTrue_ReturnsExpectedHash)
|
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordChallengeAuthSslTrue_ReturnsExpectedHash)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "hola", challenge, true);
|
auto err = calc.calculate("caching_sha2_password", "hola", challenge, true);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), expected);
|
EXPECT_EQ(calc.response(), expected);
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslFalse_Fail)
|
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslFalse_Fail)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, false);
|
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, false);
|
||||||
EXPECT_EQ(err, make_error_code(errc::auth_plugin_requires_ssl));
|
EXPECT_EQ(err, make_error_code(errc::auth_plugin_requires_ssl));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslTrue_ReturnsPassword)
|
TEST_F(CachingSha2PasswordTest, NonEmptyPasswordCleartextAuthSslTrue_ReturnsPassword)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, true);
|
auto err = calc.calculate("caching_sha2_password", "hola", cleartext_challenge, true);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), std::string("hola") + '\0');
|
EXPECT_EQ(calc.response(), std::string("hola") + '\0');
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslFalse_ReturnsEmpty)
|
TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslFalse_ReturnsEmpty)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "", challenge, false);
|
auto err = calc.calculate("caching_sha2_password", "", challenge, false);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), "");
|
EXPECT_EQ(calc.response(), "");
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslTrue_ReturnsEmpty)
|
TEST_F(CachingSha2PasswordTest, EmptyPasswordChallengeAuthSslTrue_ReturnsEmpty)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "", challenge, true);
|
auto err = calc.calculate("caching_sha2_password", "", challenge, true);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), "");
|
EXPECT_EQ(calc.response(), "");
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslFalse_ReturnsEmpty)
|
TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslFalse_ReturnsEmpty)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, false);
|
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, false);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), "");
|
EXPECT_EQ(calc.response(), "");
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslTrue_ReturnsEmpty)
|
TEST_F(CachingSha2PasswordTest, EmptyPasswordCleartextAuthSslTrue_ReturnsEmpty)
|
||||||
{
|
{
|
||||||
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, true);
|
auto err = calc.calculate("caching_sha2_password", "", cleartext_challenge, true);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(calc.response(), "");
|
EXPECT_EQ(calc.response(), "");
|
||||||
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
EXPECT_EQ(calc.plugin_name(), "caching_sha2_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CachingSha2PasswordTest, BadChallengeLength_Fail)
|
TEST_F(CachingSha2PasswordTest, BadChallengeLength_Fail)
|
||||||
{
|
{
|
||||||
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "", true)),
|
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "", true)),
|
||||||
make_error_code(errc::protocol_value_error));
|
make_error_code(errc::protocol_value_error));
|
||||||
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "bad_challenge", true)),
|
EXPECT_EQ((calc.calculate("caching_sha2_password", "password", "bad_challenge", true)),
|
||||||
make_error_code(errc::protocol_value_error));
|
make_error_code(errc::protocol_value_error));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bad authentication plugin
|
// Bad authentication plugin
|
||||||
TEST(AuthCalculator, UnknownAuthPlugin_Fail)
|
TEST(AuthCalculator, UnknownAuthPlugin_Fail)
|
||||||
{
|
{
|
||||||
auth_calculator calc;
|
auth_calculator calc;
|
||||||
EXPECT_EQ((calc.calculate("bad_plugin", "password", "challenge", true)),
|
EXPECT_EQ((calc.calculate("bad_plugin", "password", "challenge", true)),
|
||||||
make_error_code(errc::unknown_auth_plugin));
|
make_error_code(errc::unknown_auth_plugin));
|
||||||
EXPECT_EQ((calc.calculate("", "password", "challenge", true)),
|
EXPECT_EQ((calc.calculate("", "password", "challenge", true)),
|
||||||
make_error_code(errc::unknown_auth_plugin));
|
make_error_code(errc::unknown_auth_plugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,241 +24,241 @@ namespace
|
|||||||
using boost::mysql::operator<<;
|
using boost::mysql::operator<<;
|
||||||
|
|
||||||
std::vector<boost::mysql::field_metadata> make_meta(
|
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;
|
std::vector<boost::mysql::field_metadata> res;
|
||||||
for (const auto type: types)
|
for (const auto type: types)
|
||||||
{
|
{
|
||||||
column_definition_packet coldef;
|
column_definition_packet coldef;
|
||||||
coldef.type = type;
|
coldef.type = type;
|
||||||
res.emplace_back(coldef);
|
res.emplace_back(coldef);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// for deserialize_binary_value
|
// for deserialize_binary_value
|
||||||
struct BinaryValueParam : named_param
|
struct BinaryValueParam : named_param
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<std::uint8_t> from;
|
std::vector<std::uint8_t> from;
|
||||||
value expected;
|
value expected;
|
||||||
protocol_field_type type;
|
protocol_field_type type;
|
||||||
std::uint16_t flags;
|
std::uint16_t flags;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
BinaryValueParam(
|
BinaryValueParam(
|
||||||
std::string name,
|
std::string name,
|
||||||
std::vector<std::uint8_t> from,
|
std::vector<std::uint8_t> from,
|
||||||
T&& expected_value,
|
T&& expected_value,
|
||||||
protocol_field_type type,
|
protocol_field_type type,
|
||||||
std::uint16_t flags=0
|
std::uint16_t flags=0
|
||||||
):
|
):
|
||||||
name(std::move(name)),
|
name(std::move(name)),
|
||||||
from(std::move(from)),
|
from(std::move(from)),
|
||||||
expected(std::forward<T>(expected_value)),
|
expected(std::forward<T>(expected_value)),
|
||||||
type(type),
|
type(type),
|
||||||
flags(flags)
|
flags(flags)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DeserializeBinaryValueTest : public TestWithParam<BinaryValueParam> {};
|
struct DeserializeBinaryValueTest : public TestWithParam<BinaryValueParam> {};
|
||||||
|
|
||||||
TEST_P(DeserializeBinaryValueTest, CorrectFormat_SetsOutputValueReturnsTrue)
|
TEST_P(DeserializeBinaryValueTest, CorrectFormat_SetsOutputValueReturnsTrue)
|
||||||
{
|
{
|
||||||
column_definition_packet coldef;
|
column_definition_packet coldef;
|
||||||
coldef.type = GetParam().type;
|
coldef.type = GetParam().type;
|
||||||
coldef.flags.value = GetParam().flags;
|
coldef.flags.value = GetParam().flags;
|
||||||
boost::mysql::field_metadata meta (coldef);
|
boost::mysql::field_metadata meta (coldef);
|
||||||
value actual_value;
|
value actual_value;
|
||||||
const auto& buffer = GetParam().from;
|
const auto& buffer = GetParam().from;
|
||||||
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
|
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
|
||||||
auto err = deserialize_binary_value(ctx, meta, actual_value);
|
auto err = deserialize_binary_value(ctx, meta, actual_value);
|
||||||
EXPECT_EQ(err, errc::ok);
|
EXPECT_EQ(err, errc::ok);
|
||||||
EXPECT_EQ(actual_value, GetParam().expected);
|
EXPECT_EQ(actual_value, GetParam().expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(StringTypes, DeserializeBinaryValueTest, Values(
|
INSTANTIATE_TEST_SUITE_P(StringTypes, DeserializeBinaryValueTest, Values(
|
||||||
BinaryValueParam("varchar", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::var_string),
|
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("char", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::string),
|
||||||
BinaryValueParam("varbinary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
BinaryValueParam("varbinary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
||||||
protocol_field_type::var_string, column_flags::binary),
|
protocol_field_type::var_string, column_flags::binary),
|
||||||
BinaryValueParam("binary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
BinaryValueParam("binary", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
||||||
protocol_field_type::string, column_flags::binary),
|
protocol_field_type::string, column_flags::binary),
|
||||||
BinaryValueParam("text_blob", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
BinaryValueParam("text_blob", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
||||||
protocol_field_type::blob, column_flags::blob),
|
protocol_field_type::blob, column_flags::blob),
|
||||||
BinaryValueParam("enum", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
BinaryValueParam("enum", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
||||||
protocol_field_type::string, column_flags::enum_),
|
protocol_field_type::string, column_flags::enum_),
|
||||||
BinaryValueParam("set", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
BinaryValueParam("set", {0x04, 0x74, 0x65, 0x73, 0x74}, "test",
|
||||||
protocol_field_type::string, column_flags::set),
|
protocol_field_type::string, column_flags::set),
|
||||||
|
|
||||||
BinaryValueParam("bit", {0x02, 0x02, 0x01}, "\2\1", protocol_field_type::bit),
|
BinaryValueParam("bit", {0x02, 0x02, 0x01}, "\2\1", protocol_field_type::bit),
|
||||||
BinaryValueParam("decimal", {0x02, 0x31, 0x30}, "10", protocol_field_type::newdecimal),
|
BinaryValueParam("decimal", {0x02, 0x31, 0x30}, "10", protocol_field_type::newdecimal),
|
||||||
BinaryValueParam("geomtry", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::geometry)
|
BinaryValueParam("geomtry", {0x04, 0x74, 0x65, 0x73, 0x74}, "test", protocol_field_type::geometry)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(IntTypes, DeserializeBinaryValueTest, Values(
|
INSTANTIATE_TEST_SUITE_P(IntTypes, DeserializeBinaryValueTest, Values(
|
||||||
BinaryValueParam("tinyint_unsigned", {0x14}, std::uint32_t(20),
|
BinaryValueParam("tinyint_unsigned", {0x14}, std::uint32_t(20),
|
||||||
protocol_field_type::tiny, column_flags::unsigned_),
|
protocol_field_type::tiny, column_flags::unsigned_),
|
||||||
BinaryValueParam("tinyint_signed", {0xec}, std::int32_t(-20), protocol_field_type::tiny),
|
BinaryValueParam("tinyint_signed", {0xec}, std::int32_t(-20), protocol_field_type::tiny),
|
||||||
|
|
||||||
BinaryValueParam("smallint_unsigned", {0x14, 0x00}, std::uint32_t(20),
|
BinaryValueParam("smallint_unsigned", {0x14, 0x00}, std::uint32_t(20),
|
||||||
protocol_field_type::short_, column_flags::unsigned_),
|
protocol_field_type::short_, column_flags::unsigned_),
|
||||||
BinaryValueParam("smallint_signed", {0xec, 0xff}, std::int32_t(-20), protocol_field_type::short_),
|
BinaryValueParam("smallint_signed", {0xec, 0xff}, std::int32_t(-20), protocol_field_type::short_),
|
||||||
|
|
||||||
BinaryValueParam("mediumint_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
|
BinaryValueParam("mediumint_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
|
||||||
protocol_field_type::int24, column_flags::unsigned_),
|
protocol_field_type::int24, column_flags::unsigned_),
|
||||||
BinaryValueParam("mediumint_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::int24),
|
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),
|
BinaryValueParam("int_unsigned", {0x14, 0x00, 0x00, 0x00}, std::uint32_t(20),
|
||||||
protocol_field_type::long_, column_flags::unsigned_),
|
protocol_field_type::long_, column_flags::unsigned_),
|
||||||
BinaryValueParam("int_signed", {0xec, 0xff, 0xff, 0xff}, std::int32_t(-20), protocol_field_type::long_),
|
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),
|
BinaryValueParam("bigint_unsigned", {0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, std::uint64_t(20),
|
||||||
protocol_field_type::longlong, column_flags::unsigned_),
|
protocol_field_type::longlong, column_flags::unsigned_),
|
||||||
BinaryValueParam("bigint_signed", {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, std::int64_t(-20),
|
BinaryValueParam("bigint_signed", {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, std::int64_t(-20),
|
||||||
protocol_field_type::longlong)
|
protocol_field_type::longlong)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(FloatingPointTypes, DeserializeBinaryValueTest, Values(
|
INSTANTIATE_TEST_SUITE_P(FloatingPointTypes, DeserializeBinaryValueTest, Values(
|
||||||
BinaryValueParam("float", {0x66, 0x66, 0x86, 0xc0}, -4.2f, protocol_field_type::float_),
|
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("double", {0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x10, 0xc0}, -4.2, protocol_field_type::double_)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(TimeTypes, DeserializeBinaryValueTest, Values(
|
INSTANTIATE_TEST_SUITE_P(TimeTypes, DeserializeBinaryValueTest, Values(
|
||||||
BinaryValueParam("date", {0x04, 0xda, 0x07, 0x03, 0x1c}, makedate(2010, 3, 28), protocol_field_type::date),
|
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},
|
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),
|
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},
|
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),
|
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},
|
BinaryValueParam("time", { 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xa0, 0x86, 0x01, 0x00},
|
||||||
maket(120, 2, 3, 100000), protocol_field_type::time),
|
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("year", {0xe3, 0x07}, std::uint32_t(2019), protocol_field_type::year, column_flags::unsigned_)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// for deserialize_binary_row
|
// for deserialize_binary_row
|
||||||
struct BinaryRowParam : named_param
|
struct BinaryRowParam : named_param
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<std::uint8_t> from;
|
std::vector<std::uint8_t> from;
|
||||||
std::vector<value> expected;
|
std::vector<value> expected;
|
||||||
std::vector<protocol_field_type> types;
|
std::vector<protocol_field_type> types;
|
||||||
|
|
||||||
BinaryRowParam(
|
BinaryRowParam(
|
||||||
std::string name,
|
std::string name,
|
||||||
std::vector<std::uint8_t> from,
|
std::vector<std::uint8_t> from,
|
||||||
std::vector<value> expected,
|
std::vector<value> expected,
|
||||||
std::vector<protocol_field_type> types
|
std::vector<protocol_field_type> types
|
||||||
):
|
):
|
||||||
name(std::move(name)),
|
name(std::move(name)),
|
||||||
from(std::move(from)),
|
from(std::move(from)),
|
||||||
expected(std::move(expected)),
|
expected(std::move(expected)),
|
||||||
types(std::move(types))
|
types(std::move(types))
|
||||||
{
|
{
|
||||||
assert(expected.size() == types.size());
|
assert(expected.size() == types.size());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DeserializeBinaryRowTest : public TestWithParam<BinaryRowParam> {};
|
struct DeserializeBinaryRowTest : public TestWithParam<BinaryRowParam> {};
|
||||||
|
|
||||||
TEST_P(DeserializeBinaryRowTest, CorrectFormat_SetsOutputValueReturnsTrue)
|
TEST_P(DeserializeBinaryRowTest, CorrectFormat_SetsOutputValueReturnsTrue)
|
||||||
{
|
{
|
||||||
auto meta = make_meta(GetParam().types);
|
auto meta = make_meta(GetParam().types);
|
||||||
const auto& buffer = GetParam().from;
|
const auto& buffer = GetParam().from;
|
||||||
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
|
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
|
||||||
|
|
||||||
std::vector<value> actual;
|
std::vector<value> actual;
|
||||||
auto err = deserialize_binary_row(ctx, meta, actual);
|
auto err = deserialize_binary_row(ctx, meta, actual);
|
||||||
EXPECT_EQ(err, error_code());
|
EXPECT_EQ(err, error_code());
|
||||||
EXPECT_EQ(actual, GetParam().expected);
|
EXPECT_EQ(actual, GetParam().expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(Default, DeserializeBinaryRowTest, testing::Values(
|
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_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("one_null", {0x00, 0x04}, makevalues(nullptr), {protocol_field_type::tiny}),
|
||||||
BinaryRowParam("two_values", {0x00, 0x00, 0x03, 0x6d, 0x69, 0x6e, 0x6d, 0x07},
|
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_}),
|
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},
|
BinaryRowParam("one_value_one_null", {0x00, 0x08, 0x03, 0x6d, 0x61, 0x78},
|
||||||
makevalues("max", nullptr), {protocol_field_type::var_string, protocol_field_type::tiny}),
|
makevalues("max", nullptr), {protocol_field_type::var_string, protocol_field_type::tiny}),
|
||||||
BinaryRowParam("two_nulls", {0x00, 0x0c},
|
BinaryRowParam("two_nulls", {0x00, 0x0c},
|
||||||
makevalues(nullptr, nullptr), {protocol_field_type::tiny, protocol_field_type::tiny}),
|
makevalues(nullptr, nullptr), {protocol_field_type::tiny, protocol_field_type::tiny}),
|
||||||
BinaryRowParam("six_nulls", {0x00, 0xfc}, std::vector<value>(6, value(nullptr)),
|
BinaryRowParam("six_nulls", {0x00, 0xfc}, std::vector<value>(6, value(nullptr)),
|
||||||
std::vector<protocol_field_type>(6, protocol_field_type::tiny)),
|
std::vector<protocol_field_type>(6, protocol_field_type::tiny)),
|
||||||
BinaryRowParam("seven_nulls", {0x00, 0xfc, 0x01}, std::vector<value>(7, value(nullptr)),
|
BinaryRowParam("seven_nulls", {0x00, 0xfc, 0x01}, std::vector<value>(7, value(nullptr)),
|
||||||
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
|
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
|
||||||
BinaryRowParam("several_values", {
|
BinaryRowParam("several_values", {
|
||||||
0x00, 0x90, 0x00, 0xfd, 0x14, 0x00, 0xc3, 0xf5, 0x48,
|
0x00, 0x90, 0x00, 0xfd, 0x14, 0x00, 0xc3, 0xf5, 0x48,
|
||||||
0x40, 0x02, 0x61, 0x62, 0x04, 0xe2, 0x07, 0x0a,
|
0x40, 0x02, 0x61, 0x62, 0x04, 0xe2, 0x07, 0x0a,
|
||||||
0x05, 0x71, 0x99, 0x6d, 0xe2, 0x93, 0x4d, 0xf5,
|
0x05, 0x71, 0x99, 0x6d, 0xe2, 0x93, 0x4d, 0xf5,
|
||||||
0x3d
|
0x3d
|
||||||
}, makevalues(
|
}, makevalues(
|
||||||
std::int32_t(-3),
|
std::int32_t(-3),
|
||||||
std::int32_t(20),
|
std::int32_t(20),
|
||||||
nullptr,
|
nullptr,
|
||||||
3.14f,
|
3.14f,
|
||||||
"ab",
|
"ab",
|
||||||
nullptr,
|
nullptr,
|
||||||
makedate(2018, 10, 5),
|
makedate(2018, 10, 5),
|
||||||
3.10e-10
|
3.10e-10
|
||||||
), {
|
), {
|
||||||
protocol_field_type::tiny,
|
protocol_field_type::tiny,
|
||||||
protocol_field_type::short_,
|
protocol_field_type::short_,
|
||||||
protocol_field_type::long_,
|
protocol_field_type::long_,
|
||||||
protocol_field_type::float_,
|
protocol_field_type::float_,
|
||||||
protocol_field_type::string,
|
protocol_field_type::string,
|
||||||
protocol_field_type::long_,
|
protocol_field_type::long_,
|
||||||
protocol_field_type::date,
|
protocol_field_type::date,
|
||||||
protocol_field_type::double_
|
protocol_field_type::double_
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// errc cases for deserialize_binary_row
|
// errc cases for deserialize_binary_row
|
||||||
struct BinaryRowErrorParam : named_param
|
struct BinaryRowErrorParam : named_param
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::vector<std::uint8_t> from;
|
std::vector<std::uint8_t> from;
|
||||||
errc expected;
|
errc expected;
|
||||||
std::vector<protocol_field_type> types;
|
std::vector<protocol_field_type> types;
|
||||||
|
|
||||||
BinaryRowErrorParam(
|
BinaryRowErrorParam(
|
||||||
std::string name,
|
std::string name,
|
||||||
std::vector<std::uint8_t> from,
|
std::vector<std::uint8_t> from,
|
||||||
errc expected,
|
errc expected,
|
||||||
std::vector<protocol_field_type> types
|
std::vector<protocol_field_type> types
|
||||||
):
|
):
|
||||||
name(std::move(name)),
|
name(std::move(name)),
|
||||||
from(std::move(from)),
|
from(std::move(from)),
|
||||||
expected(expected),
|
expected(expected),
|
||||||
types(std::move(types))
|
types(std::move(types))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DeserializeBinaryRowErrorTest : public TestWithParam<BinaryRowErrorParam> {};
|
struct DeserializeBinaryRowErrorTest : public TestWithParam<BinaryRowErrorParam> {};
|
||||||
|
|
||||||
TEST_P(DeserializeBinaryRowErrorTest, ErrorCondition_ReturnsErrorCode)
|
TEST_P(DeserializeBinaryRowErrorTest, ErrorCondition_ReturnsErrorCode)
|
||||||
{
|
{
|
||||||
auto meta = make_meta(GetParam().types);
|
auto meta = make_meta(GetParam().types);
|
||||||
const auto& buffer = GetParam().from;
|
const auto& buffer = GetParam().from;
|
||||||
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
|
deserialization_context ctx (buffer.data(), buffer.data() + buffer.size(), capabilities());
|
||||||
|
|
||||||
std::vector<value> actual;
|
std::vector<value> actual;
|
||||||
auto err = deserialize_binary_row(ctx, meta, actual);
|
auto err = deserialize_binary_row(ctx, meta, actual);
|
||||||
EXPECT_EQ(err, make_error_code(GetParam().expected));
|
EXPECT_EQ(err, make_error_code(GetParam().expected));
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(Default, DeserializeBinaryRowErrorTest, testing::Values(
|
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_1", {0x00}, errc::incomplete_message, {protocol_field_type::tiny}),
|
||||||
BinaryRowErrorParam("no_space_null_bitmap_2", {0x00, 0xfc}, errc::incomplete_message,
|
BinaryRowErrorParam("no_space_null_bitmap_2", {0x00, 0xfc}, errc::incomplete_message,
|
||||||
std::vector<protocol_field_type>(7, protocol_field_type::tiny)),
|
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_single", {0x00, 0x00}, errc::incomplete_message, {protocol_field_type::tiny}),
|
||||||
BinaryRowErrorParam("no_space_value_last", {0x00, 0x00, 0x01}, errc::incomplete_message,
|
BinaryRowErrorParam("no_space_value_last", {0x00, 0x00, 0x01}, errc::incomplete_message,
|
||||||
std::vector<protocol_field_type>(2, protocol_field_type::tiny)),
|
std::vector<protocol_field_type>(2, protocol_field_type::tiny)),
|
||||||
BinaryRowErrorParam("no_space_value_middle", {0x00, 0x00, 0x01}, errc::incomplete_message,
|
BinaryRowErrorParam("no_space_value_middle", {0x00, 0x00, 0x01}, errc::incomplete_message,
|
||||||
std::vector<protocol_field_type>(3, protocol_field_type::tiny)),
|
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("extra_bytes", {0x00, 0x00, 0x01, 0x02}, errc::extra_bytes, {protocol_field_type::tiny})
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,64 +13,64 @@ using namespace testing;
|
|||||||
|
|
||||||
TEST(Capabilities, Has_BitSet_ReturnsTrue)
|
TEST(Capabilities, Has_BitSet_ReturnsTrue)
|
||||||
{
|
{
|
||||||
capabilities caps (CLIENT_COMPRESS);
|
capabilities caps (CLIENT_COMPRESS);
|
||||||
EXPECT_TRUE(caps.has(CLIENT_COMPRESS));
|
EXPECT_TRUE(caps.has(CLIENT_COMPRESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Capabilities, Has_BitNotSet_ReturnsFalse)
|
TEST(Capabilities, Has_BitNotSet_ReturnsFalse)
|
||||||
{
|
{
|
||||||
capabilities caps (CLIENT_COMPRESS);
|
capabilities caps (CLIENT_COMPRESS);
|
||||||
EXPECT_FALSE(caps.has(CLIENT_SSL));
|
EXPECT_FALSE(caps.has(CLIENT_SSL));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Capabilities, Has_MultipleBitsSet_ReturnsTrueForSetBits)
|
TEST(Capabilities, Has_MultipleBitsSet_ReturnsTrueForSetBits)
|
||||||
{
|
{
|
||||||
capabilities caps (
|
capabilities caps (
|
||||||
CLIENT_CONNECT_WITH_DB |
|
CLIENT_CONNECT_WITH_DB |
|
||||||
CLIENT_SSL |
|
CLIENT_SSL |
|
||||||
CLIENT_COMPRESS);
|
CLIENT_COMPRESS);
|
||||||
for (int i = 0; i < 32; ++i)
|
for (int i = 0; i < 32; ++i)
|
||||||
{
|
{
|
||||||
std::uint32_t cap_bit = 1 << i;
|
std::uint32_t cap_bit = 1 << i;
|
||||||
bool is_set =
|
bool is_set =
|
||||||
cap_bit == CLIENT_CONNECT_WITH_DB ||
|
cap_bit == CLIENT_CONNECT_WITH_DB ||
|
||||||
cap_bit == CLIENT_SSL ||
|
cap_bit == CLIENT_SSL ||
|
||||||
cap_bit == CLIENT_COMPRESS;
|
cap_bit == CLIENT_COMPRESS;
|
||||||
EXPECT_EQ(caps.has(cap_bit), is_set);
|
EXPECT_EQ(caps.has(cap_bit), is_set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CapabilitiesHasAllTest : public Test
|
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)
|
TEST_F(CapabilitiesHasAllTest, HasAll_HasNone_ReturnsFalse)
|
||||||
{
|
{
|
||||||
capabilities lhs (0);
|
capabilities lhs (0);
|
||||||
EXPECT_FALSE(lhs.has_all(rhs));
|
EXPECT_FALSE(lhs.has_all(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CapabilitiesHasAllTest, HasAll_HasSomeButNotAll_ReturnsFalse)
|
TEST_F(CapabilitiesHasAllTest, HasAll_HasSomeButNotAll_ReturnsFalse)
|
||||||
{
|
{
|
||||||
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS);
|
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS);
|
||||||
EXPECT_FALSE(lhs.has_all(rhs));
|
EXPECT_FALSE(lhs.has_all(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CapabilitiesHasAllTest, HasAll_HasSomeButNotAllPlusUnrelated_ReturnsFalse)
|
TEST_F(CapabilitiesHasAllTest, HasAll_HasSomeButNotAllPlusUnrelated_ReturnsFalse)
|
||||||
{
|
{
|
||||||
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS | CLIENT_TRANSACTIONS);
|
capabilities lhs (CLIENT_CONNECT_WITH_DB | CLIENT_COMPRESS | CLIENT_TRANSACTIONS);
|
||||||
EXPECT_FALSE(lhs.has_all(rhs));
|
EXPECT_FALSE(lhs.has_all(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CapabilitiesHasAllTest, HasAll_HasOnlyTheRequestedOnes_ReturnsTrue)
|
TEST_F(CapabilitiesHasAllTest, HasAll_HasOnlyTheRequestedOnes_ReturnsTrue)
|
||||||
{
|
{
|
||||||
capabilities lhs (rhs);
|
capabilities lhs (rhs);
|
||||||
EXPECT_TRUE(lhs.has_all(rhs));
|
EXPECT_TRUE(lhs.has_all(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CapabilitiesHasAllTest, HasAll_HasTheRequestedOnesAndOthers_ReturnsTrue)
|
TEST_F(CapabilitiesHasAllTest, HasAll_HasTheRequestedOnesAndOthers_ReturnsTrue)
|
||||||
{
|
{
|
||||||
capabilities lhs = rhs | capabilities(CLIENT_TRANSACTIONS);
|
capabilities lhs = rhs | capabilities(CLIENT_TRANSACTIONS);
|
||||||
EXPECT_TRUE(lhs.has_all(rhs));
|
EXPECT_TRUE(lhs.has_all(rhs));
|
||||||
}
|
}
|
||||||
|
@ -26,369 +26,369 @@ namespace
|
|||||||
class MockStream
|
class MockStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD2(read_buffer, std::size_t(boost::asio::mutable_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&));
|
MOCK_METHOD2(write_buffer, std::size_t(boost::asio::const_buffer, error_code&));
|
||||||
|
|
||||||
void set_default_behavior()
|
void set_default_behavior()
|
||||||
{
|
{
|
||||||
ON_CALL(*this, read_buffer).WillByDefault(DoAll(
|
ON_CALL(*this, read_buffer).WillByDefault(DoAll(
|
||||||
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
|
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
|
||||||
Return(0)
|
Return(0)
|
||||||
));
|
));
|
||||||
ON_CALL(*this, write_buffer).WillByDefault(DoAll(
|
ON_CALL(*this, write_buffer).WillByDefault(DoAll(
|
||||||
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
|
SetArgReferee<1>(make_error_code(boost::system::errc::timed_out)),
|
||||||
Return(0)
|
Return(0)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename MutableBufferSequence>
|
template <typename MutableBufferSequence>
|
||||||
std::size_t read_some(MutableBufferSequence mb, error_code& ec)
|
std::size_t read_some(MutableBufferSequence mb, error_code& ec)
|
||||||
{
|
{
|
||||||
if (buffer_size(mb) == 0)
|
if (buffer_size(mb) == 0)
|
||||||
{
|
{
|
||||||
ec.clear();
|
ec.clear();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size_t res = 0;
|
size_t res = 0;
|
||||||
for (auto it = buffer_sequence_begin(mb); it != buffer_sequence_end(mb); ++it)
|
for (auto it = buffer_sequence_begin(mb); it != buffer_sequence_end(mb); ++it)
|
||||||
res += read_buffer(*it, ec);
|
res += read_buffer(*it, ec);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename MutableBufferSequence>
|
template <typename MutableBufferSequence>
|
||||||
std::size_t read_some(MutableBufferSequence mb)
|
std::size_t read_some(MutableBufferSequence mb)
|
||||||
{
|
{
|
||||||
error_code ec;
|
error_code ec;
|
||||||
auto res = read_some(mb, ec);
|
auto res = read_some(mb, ec);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
throw boost::system::system_error(ec);
|
throw boost::system::system_error(ec);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ConstBufferSequence>
|
template <typename ConstBufferSequence>
|
||||||
std::size_t write_some(ConstBufferSequence cb, error_code& ec)
|
std::size_t write_some(ConstBufferSequence cb, error_code& ec)
|
||||||
{
|
{
|
||||||
if (buffer_size(cb) == 0)
|
if (buffer_size(cb) == 0)
|
||||||
{
|
{
|
||||||
ec.clear();
|
ec.clear();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
size_t res = 0;
|
size_t res = 0;
|
||||||
for (auto it = buffer_sequence_begin(cb); it != buffer_sequence_end(cb); ++it)
|
for (auto it = buffer_sequence_begin(cb); it != buffer_sequence_end(cb); ++it)
|
||||||
{
|
{
|
||||||
auto written = write_buffer(*it, ec);
|
auto written = write_buffer(*it, ec);
|
||||||
res += written;
|
res += written;
|
||||||
if (written < it->size()) break;
|
if (written < it->size()) break;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ConstBufferSequence>
|
template <typename ConstBufferSequence>
|
||||||
std::size_t write_some(ConstBufferSequence mb)
|
std::size_t write_some(ConstBufferSequence mb)
|
||||||
{
|
{
|
||||||
error_code ec;
|
error_code ec;
|
||||||
auto res = write_some(mb, ec);
|
auto res = write_some(mb, ec);
|
||||||
if (res)
|
if (res)
|
||||||
{
|
{
|
||||||
throw boost::system::system_error(ec);
|
throw boost::system::system_error(ec);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
using lowest_layer_type = MockStream;
|
using lowest_layer_type = MockStream;
|
||||||
using executor_type = boost::asio::system_executor;
|
using executor_type = boost::asio::system_executor;
|
||||||
|
|
||||||
MockStream& lowest_layer() { return *this; }
|
MockStream& lowest_layer() { return *this; }
|
||||||
executor_type get_executor() { return boost::asio::system_executor(); }
|
executor_type get_executor() { return boost::asio::system_executor(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MysqlChannelFixture : public Test
|
struct MysqlChannelFixture : public Test
|
||||||
{
|
{
|
||||||
using MockChannel = channel<NiceMock<MockStream>>;
|
using MockChannel = channel<NiceMock<MockStream>>;
|
||||||
NiceMock<MockStream> stream;
|
NiceMock<MockStream> stream;
|
||||||
MockChannel chan {stream};
|
MockChannel chan {stream};
|
||||||
error_code code;
|
error_code code;
|
||||||
InSequence seq;
|
InSequence seq;
|
||||||
|
|
||||||
MysqlChannelFixture()
|
MysqlChannelFixture()
|
||||||
{
|
{
|
||||||
stream.set_default_behavior();
|
stream.set_default_behavior();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct MysqlChannelReadTest : public MysqlChannelFixture
|
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> buffer { 0xab, 0xac, 0xad, 0xae }; // simulate buffer was not empty, to verify we clear it
|
||||||
std::vector<uint8_t> bytes_to_read;
|
std::vector<uint8_t> bytes_to_read;
|
||||||
std::size_t index {0};
|
std::size_t index {0};
|
||||||
|
|
||||||
void verify_buffer(const std::vector<uint8_t>& expected)
|
void verify_buffer(const std::vector<uint8_t>& expected)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(buffer, expected);
|
EXPECT_EQ(buffer, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto buffer_copier(const std::vector<uint8_t>& buffer)
|
static auto buffer_copier(const std::vector<uint8_t>& buffer)
|
||||||
{
|
{
|
||||||
return [buffer](boost::asio::mutable_buffer b, error_code& ec) {
|
return [buffer](boost::asio::mutable_buffer b, error_code& ec) {
|
||||||
assert(b.size() >= buffer.size());
|
assert(b.size() >= buffer.size());
|
||||||
memcpy(b.data(), buffer.data(), buffer.size());
|
memcpy(b.data(), buffer.data(), buffer.size());
|
||||||
ec.clear();
|
ec.clear();
|
||||||
return buffer.size();
|
return buffer.size();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto make_read_handler()
|
auto make_read_handler()
|
||||||
{
|
{
|
||||||
return [this](boost::asio::mutable_buffer b, error_code& ec) {
|
return [this](boost::asio::mutable_buffer b, error_code& ec) {
|
||||||
std::size_t to_copy = std::min(b.size(), bytes_to_read.size() - index);
|
std::size_t to_copy = std::min(b.size(), bytes_to_read.size() - index);
|
||||||
memcpy(b.data(), bytes_to_read.data() + index, to_copy);
|
memcpy(b.data(), bytes_to_read.data() + index, to_copy);
|
||||||
index += to_copy;
|
index += to_copy;
|
||||||
ec.clear();
|
ec.clear();
|
||||||
return to_copy;
|
return to_copy;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto read_failer(error_code error)
|
static auto read_failer(error_code error)
|
||||||
{
|
{
|
||||||
return [error](boost::asio::mutable_buffer, error_code& ec) {
|
return [error](boost::asio::mutable_buffer, error_code& ec) {
|
||||||
ec = error;
|
ec = error;
|
||||||
return size_t(0);
|
return size_t(0);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_AllReadsSuccessful_ReadHeaderPopulatesBuffer)
|
TEST_F(MysqlChannelReadTest, SyncRead_AllReadsSuccessful_ReadHeaderPopulatesBuffer)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, read_buffer)
|
ON_CALL(stream, read_buffer)
|
||||||
.WillByDefault(Invoke(make_read_handler()));
|
.WillByDefault(Invoke(make_read_handler()));
|
||||||
bytes_to_read = {
|
bytes_to_read = {
|
||||||
0x03, 0x00, 0x00, 0x00,
|
0x03, 0x00, 0x00, 0x00,
|
||||||
0xfe, 0x03, 0x02
|
0xfe, 0x03, 0x02
|
||||||
};
|
};
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
verify_buffer({0xfe, 0x03, 0x02});
|
verify_buffer({0xfe, 0x03, 0x02});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_MoreThan16M_JoinsPackets)
|
TEST_F(MysqlChannelReadTest, SyncRead_MoreThan16M_JoinsPackets)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, read_buffer)
|
ON_CALL(stream, read_buffer)
|
||||||
.WillByDefault(Invoke(make_read_handler()));
|
.WillByDefault(Invoke(make_read_handler()));
|
||||||
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x00});
|
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x00});
|
||||||
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
|
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
|
||||||
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x01});
|
concat(bytes_to_read, {0xff, 0xff, 0xff, 0x01});
|
||||||
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
|
concat(bytes_to_read, std::vector<uint8_t>(0xffffff, 0x20));
|
||||||
concat(bytes_to_read, {0x04, 0x00, 0x00, 0x02});
|
concat(bytes_to_read, {0x04, 0x00, 0x00, 0x02});
|
||||||
concat(bytes_to_read, {0x20, 0x20, 0x20, 0x20});
|
concat(bytes_to_read, {0x20, 0x20, 0x20, 0x20});
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
verify_buffer(std::vector<uint8_t>(0xffffff * 2 + 4, 0x20));
|
verify_buffer(std::vector<uint8_t>(0xffffff * 2 + 4, 0x20));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_EmptyPacket_LeavesBufferEmpty)
|
TEST_F(MysqlChannelReadTest, SyncRead_EmptyPacket_LeavesBufferEmpty)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, read_buffer)
|
ON_CALL(stream, read_buffer)
|
||||||
.WillByDefault(Invoke(make_read_handler()));
|
.WillByDefault(Invoke(make_read_handler()));
|
||||||
concat(bytes_to_read, {0x00, 0x00, 0x00, 0x00});
|
concat(bytes_to_read, {0x00, 0x00, 0x00, 0x00});
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
verify_buffer(std::vector<uint8_t>{});
|
verify_buffer(std::vector<uint8_t>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_ShortReads_InvokesReadAgain)
|
TEST_F(MysqlChannelReadTest, SyncRead_ShortReads_InvokesReadAgain)
|
||||||
{
|
{
|
||||||
EXPECT_CALL(stream, read_buffer)
|
EXPECT_CALL(stream, read_buffer)
|
||||||
.WillOnce(Invoke(buffer_copier({0x04})))
|
.WillOnce(Invoke(buffer_copier({0x04})))
|
||||||
.WillOnce(Invoke(buffer_copier({ 0x00, 0x00, 0x00})))
|
.WillOnce(Invoke(buffer_copier({ 0x00, 0x00, 0x00})))
|
||||||
.WillOnce(Invoke(buffer_copier({0x01, 0x02})))
|
.WillOnce(Invoke(buffer_copier({0x01, 0x02})))
|
||||||
.WillOnce(Invoke(buffer_copier({0x03, 0x04})));
|
.WillOnce(Invoke(buffer_copier({0x03, 0x04})));
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
verify_buffer({0x01, 0x02, 0x03, 0x04});
|
verify_buffer({0x01, 0x02, 0x03, 0x04});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_ReadErrorInHeader_ReturnsFailureErrorCode)
|
TEST_F(MysqlChannelReadTest, SyncRead_ReadErrorInHeader_ReturnsFailureErrorCode)
|
||||||
{
|
{
|
||||||
auto expected_error = make_error_code(boost::system::errc::not_supported);
|
auto expected_error = make_error_code(boost::system::errc::not_supported);
|
||||||
EXPECT_CALL(stream, read_buffer)
|
EXPECT_CALL(stream, read_buffer)
|
||||||
.WillOnce(Invoke(read_failer(expected_error)));
|
.WillOnce(Invoke(read_failer(expected_error)));
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, expected_error);
|
EXPECT_EQ(code, expected_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_ReadErrorInPacket_ReturnsFailureErrorCode)
|
TEST_F(MysqlChannelReadTest, SyncRead_ReadErrorInPacket_ReturnsFailureErrorCode)
|
||||||
{
|
{
|
||||||
auto expected_error = make_error_code(boost::system::errc::not_supported);
|
auto expected_error = make_error_code(boost::system::errc::not_supported);
|
||||||
EXPECT_CALL(stream, read_buffer)
|
EXPECT_CALL(stream, read_buffer)
|
||||||
.WillOnce(Invoke(buffer_copier({0xff, 0xff, 0xff, 0x00})))
|
.WillOnce(Invoke(buffer_copier({0xff, 0xff, 0xff, 0x00})))
|
||||||
.WillOnce(Invoke(read_failer(expected_error)));
|
.WillOnce(Invoke(read_failer(expected_error)));
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, expected_error);
|
EXPECT_EQ(code, expected_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberMismatch_ReturnsAppropriateErrorCode)
|
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberMismatch_ReturnsAppropriateErrorCode)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, read_buffer)
|
ON_CALL(stream, read_buffer)
|
||||||
.WillByDefault(Invoke(make_read_handler()));
|
.WillByDefault(Invoke(make_read_handler()));
|
||||||
bytes_to_read = {0xff, 0xff, 0xff, 0x05};
|
bytes_to_read = {0xff, 0xff, 0xff, 0x05};
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, make_error_code(errc::sequence_number_mismatch));
|
EXPECT_EQ(code, make_error_code(errc::sequence_number_mismatch));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberNotZero_RespectsCurrentSequenceNumber)
|
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberNotZero_RespectsCurrentSequenceNumber)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, read_buffer)
|
ON_CALL(stream, read_buffer)
|
||||||
.WillByDefault(Invoke(make_read_handler()));
|
.WillByDefault(Invoke(make_read_handler()));
|
||||||
bytes_to_read = {
|
bytes_to_read = {
|
||||||
0x03, 0x00, 0x00, 0x21,
|
0x03, 0x00, 0x00, 0x21,
|
||||||
0xfe, 0x03, 0x02
|
0xfe, 0x03, 0x02
|
||||||
};
|
};
|
||||||
chan.reset_sequence_number(0x21);
|
chan.reset_sequence_number(0x21);
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
verify_buffer({0xfe, 0x03, 0x02});
|
verify_buffer({0xfe, 0x03, 0x02});
|
||||||
EXPECT_EQ(chan.sequence_number(), 0x22);
|
EXPECT_EQ(chan.sequence_number(), 0x22);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberFF_SequenceNumberWraps)
|
TEST_F(MysqlChannelReadTest, SyncRead_SequenceNumberFF_SequenceNumberWraps)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, read_buffer)
|
ON_CALL(stream, read_buffer)
|
||||||
.WillByDefault(Invoke(make_read_handler()));
|
.WillByDefault(Invoke(make_read_handler()));
|
||||||
bytes_to_read = {
|
bytes_to_read = {
|
||||||
0x03, 0x00, 0x00, 0xff,
|
0x03, 0x00, 0x00, 0xff,
|
||||||
0xfe, 0x03, 0x02
|
0xfe, 0x03, 0x02
|
||||||
};
|
};
|
||||||
chan.reset_sequence_number(0xff);
|
chan.reset_sequence_number(0xff);
|
||||||
chan.read(buffer, code);
|
chan.read(buffer, code);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
verify_buffer({0xfe, 0x03, 0x02});
|
verify_buffer({0xfe, 0x03, 0x02});
|
||||||
EXPECT_EQ(chan.sequence_number(), 0);
|
EXPECT_EQ(chan.sequence_number(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct MysqlChannelWriteTest : public MysqlChannelFixture
|
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)
|
void verify_buffer(const std::vector<uint8_t>& expected)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(bytes_written, expected);
|
EXPECT_EQ(bytes_written, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto make_write_handler(std::size_t max_bytes_written = 0xffffffff)
|
auto make_write_handler(std::size_t max_bytes_written = 0xffffffff)
|
||||||
{
|
{
|
||||||
return [this, max_bytes_written](boost::asio::const_buffer buff, error_code& ec) {
|
return [this, max_bytes_written](boost::asio::const_buffer buff, error_code& ec) {
|
||||||
auto actual_size = std::min(buff.size(), max_bytes_written);
|
auto actual_size = std::min(buff.size(), max_bytes_written);
|
||||||
concat(bytes_written, boost::asio::buffer(buff.data(), actual_size));
|
concat(bytes_written, boost::asio::buffer(buff.data(), actual_size));
|
||||||
ec.clear();
|
ec.clear();
|
||||||
return actual_size;
|
return actual_size;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto write_failer(boost::system::errc::errc_t error)
|
static auto write_failer(boost::system::errc::errc_t error)
|
||||||
{
|
{
|
||||||
return [error](boost::asio::const_buffer, error_code& ec) {
|
return [error](boost::asio::const_buffer, error_code& ec) {
|
||||||
ec = make_error_code(error);
|
ec = make_error_code(error);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_AllWritesSuccessful_WritesHeaderAndBuffer)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_AllWritesSuccessful_WritesHeaderAndBuffer)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(make_write_handler()));
|
.WillByDefault(Invoke(make_write_handler()));
|
||||||
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
||||||
verify_buffer({
|
verify_buffer({
|
||||||
0x03, 0x00, 0x00, 0x00, // header
|
0x03, 0x00, 0x00, 0x00, // header
|
||||||
0xaa, 0xab, 0xac // body
|
0xaa, 0xab, 0xac // body
|
||||||
});
|
});
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_MoreThan16M_SplitsInPackets)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_MoreThan16M_SplitsInPackets)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(make_write_handler()));
|
.WillByDefault(Invoke(make_write_handler()));
|
||||||
chan.write(buffer(std::vector<uint8_t>(2*0xffffff + 4, 0xab)), code);
|
chan.write(buffer(std::vector<uint8_t>(2*0xffffff + 4, 0xab)), code);
|
||||||
std::vector<uint8_t> expected_buffer {0xff, 0xff, 0xff, 0x00};
|
std::vector<uint8_t> expected_buffer {0xff, 0xff, 0xff, 0x00};
|
||||||
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
|
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
|
||||||
concat(expected_buffer, {0xff, 0xff, 0xff, 0x01});
|
concat(expected_buffer, {0xff, 0xff, 0xff, 0x01});
|
||||||
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
|
concat(expected_buffer, std::vector<std::uint8_t>(0xffffff, 0xab));
|
||||||
concat(expected_buffer, {0x04, 0x00, 0x00, 0x02});
|
concat(expected_buffer, {0x04, 0x00, 0x00, 0x02});
|
||||||
concat(expected_buffer, std::vector<std::uint8_t>(4, 0xab));
|
concat(expected_buffer, std::vector<std::uint8_t>(4, 0xab));
|
||||||
verify_buffer(expected_buffer);
|
verify_buffer(expected_buffer);
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_EmptyPacket_WritesHeader)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_EmptyPacket_WritesHeader)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(make_write_handler()));
|
.WillByDefault(Invoke(make_write_handler()));
|
||||||
chan.reset_sequence_number(2);
|
chan.reset_sequence_number(2);
|
||||||
chan.write(buffer(std::vector<uint8_t>{}), code);
|
chan.write(buffer(std::vector<uint8_t>{}), code);
|
||||||
verify_buffer({0x00, 0x00, 0x00, 0x02});
|
verify_buffer({0x00, 0x00, 0x00, 0x02});
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_ShortWrites_WritesHeaderAndBuffer)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_ShortWrites_WritesHeaderAndBuffer)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(make_write_handler(2)));
|
.WillByDefault(Invoke(make_write_handler(2)));
|
||||||
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
||||||
verify_buffer({
|
verify_buffer({
|
||||||
0x03, 0x00, 0x00, 0x00, // header
|
0x03, 0x00, 0x00, 0x00, // header
|
||||||
0xaa, 0xab, 0xac // body
|
0xaa, 0xab, 0xac // body
|
||||||
});
|
});
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_WriteErrorInHeader_ReturnsErrorCode)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_WriteErrorInHeader_ReturnsErrorCode)
|
||||||
{
|
{
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(write_failer(boost::system::errc::broken_pipe)));
|
.WillByDefault(Invoke(write_failer(boost::system::errc::broken_pipe)));
|
||||||
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
|
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
|
||||||
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
|
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_WriteErrorInPacket_ReturnsErrorCode)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_WriteErrorInPacket_ReturnsErrorCode)
|
||||||
{
|
{
|
||||||
EXPECT_CALL(stream, write_buffer)
|
EXPECT_CALL(stream, write_buffer)
|
||||||
.WillOnce(Return(4))
|
.WillOnce(Return(4))
|
||||||
.WillOnce(Invoke(write_failer(boost::system::errc::broken_pipe)));
|
.WillOnce(Invoke(write_failer(boost::system::errc::broken_pipe)));
|
||||||
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
|
chan.write(buffer(std::vector<uint8_t>(10, 0x01)), code);
|
||||||
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
|
EXPECT_EQ(code, make_error_code(boost::system::errc::broken_pipe));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_SequenceNumberNotZero_RespectsSequenceNumber)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_SequenceNumberNotZero_RespectsSequenceNumber)
|
||||||
{
|
{
|
||||||
chan.reset_sequence_number(0xab);
|
chan.reset_sequence_number(0xab);
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(make_write_handler()));
|
.WillByDefault(Invoke(make_write_handler()));
|
||||||
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
||||||
verify_buffer({
|
verify_buffer({
|
||||||
0x03, 0x00, 0x00, 0xab, // header
|
0x03, 0x00, 0x00, 0xab, // header
|
||||||
0xaa, 0xab, 0xac // body
|
0xaa, 0xab, 0xac // body
|
||||||
});
|
});
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
EXPECT_EQ(chan.sequence_number(), 0xac);
|
EXPECT_EQ(chan.sequence_number(), 0xac);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(MysqlChannelWriteTest, SyncWrite_SequenceIsFF_WrapsSequenceNumber)
|
TEST_F(MysqlChannelWriteTest, SyncWrite_SequenceIsFF_WrapsSequenceNumber)
|
||||||
{
|
{
|
||||||
chan.reset_sequence_number(0xff);
|
chan.reset_sequence_number(0xff);
|
||||||
ON_CALL(stream, write_buffer)
|
ON_CALL(stream, write_buffer)
|
||||||
.WillByDefault(Invoke(make_write_handler()));
|
.WillByDefault(Invoke(make_write_handler()));
|
||||||
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
chan.write(buffer(std::vector<uint8_t>{0xaa, 0xab, 0xac}), code);
|
||||||
verify_buffer({
|
verify_buffer({
|
||||||
0x03, 0x00, 0x00, 0xff, // header
|
0x03, 0x00, 0x00, 0xff, // header
|
||||||
0xaa, 0xab, 0xac // body
|
0xaa, 0xab, 0xac // body
|
||||||
});
|
});
|
||||||
EXPECT_EQ(code, error_code());
|
EXPECT_EQ(code, error_code());
|
||||||
EXPECT_EQ(chan.sequence_number(), 0);
|
EXPECT_EQ(chan.sequence_number(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // anon namespace
|
} // anon namespace
|
||||||
|
@ -16,166 +16,166 @@ namespace
|
|||||||
{
|
{
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(PacketHeader, FullSerializationTest, ::testing::Values(
|
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(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(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(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(0xffffff), int1(0xff)}, {0xff, 0xff, 0xff, 0xff}, "max_packet_max_seqnum")
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(OkPacket, DeserializeTest, ::testing::Values(
|
INSTANTIATE_TEST_SUITE_P(OkPacket, DeserializeTest, ::testing::Values(
|
||||||
serialization_testcase(ok_packet{
|
serialization_testcase(ok_packet{
|
||||||
int_lenenc(4), // affected rows
|
int_lenenc(4), // affected rows
|
||||||
int_lenenc(0), // last insert ID
|
int_lenenc(0), // last insert ID
|
||||||
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT | SERVER_QUERY_NO_INDEX_USED)), // server status
|
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT | SERVER_QUERY_NO_INDEX_USED)), // server status
|
||||||
int2(0), // warnings
|
int2(0), // warnings
|
||||||
string_lenenc("Rows matched: 5 Changed: 4 Warnings: 0")
|
string_lenenc("Rows matched: 5 Changed: 4 Warnings: 0")
|
||||||
}, {
|
}, {
|
||||||
0x04, 0x00, 0x22, 0x00, 0x00, 0x00, 0x28, 0x52, 0x6f, 0x77, 0x73,
|
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,
|
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,
|
0x6e, 0x67, 0x65, 0x64, 0x3a, 0x20, 0x34, 0x20, 0x20, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67,
|
||||||
0x73, 0x3a, 0x20, 0x30
|
0x73, 0x3a, 0x20, 0x30
|
||||||
}, "successful_update"),
|
}, "successful_update"),
|
||||||
|
|
||||||
serialization_testcase(ok_packet{
|
serialization_testcase(ok_packet{
|
||||||
int_lenenc(1), // affected rows
|
int_lenenc(1), // affected rows
|
||||||
int_lenenc(6), // last insert ID
|
int_lenenc(6), // last insert ID
|
||||||
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
|
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
|
||||||
int2(0), // warnings
|
int2(0), // warnings
|
||||||
string_lenenc("") // no message
|
string_lenenc("") // no message
|
||||||
},{
|
},{
|
||||||
0x01, 0x06, 0x02, 0x00, 0x00, 0x00
|
0x01, 0x06, 0x02, 0x00, 0x00, 0x00
|
||||||
}, "successful_insert"),
|
}, "successful_insert"),
|
||||||
|
|
||||||
serialization_testcase(ok_packet{
|
serialization_testcase(ok_packet{
|
||||||
int_lenenc(0), // affected rows
|
int_lenenc(0), // affected rows
|
||||||
int_lenenc(0), // last insert ID
|
int_lenenc(0), // last insert ID
|
||||||
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
|
int2(static_cast<std::uint16_t>(SERVER_STATUS_AUTOCOMMIT)), // server status
|
||||||
int2(0), // warnings
|
int2(0), // warnings
|
||||||
string_lenenc("") // no message
|
string_lenenc("") // no message
|
||||||
}, {
|
}, {
|
||||||
0x00, 0x00, 0x02, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x02, 0x00, 0x00, 0x00
|
||||||
}, "successful_login")
|
}, "successful_login")
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(ErrPacket, DeserializeTest, ::testing::Values(
|
INSTANTIATE_TEST_SUITE_P(ErrPacket, DeserializeTest, ::testing::Values(
|
||||||
serialization_testcase(err_packet{
|
serialization_testcase(err_packet{
|
||||||
int2(1049), // eror code
|
int2(1049), // eror code
|
||||||
string_fixed<1>{0x23}, // sql state marker
|
string_fixed<1>{0x23}, // sql state marker
|
||||||
string_fixed<5>{'4', '2', '0', '0', '0'}, // sql state
|
string_fixed<5>{'4', '2', '0', '0', '0'}, // sql state
|
||||||
string_eof("Unknown database 'a'") // err msg
|
string_eof("Unknown database 'a'") // err msg
|
||||||
}, {
|
}, {
|
||||||
0x19, 0x04, 0x23, 0x34, 0x32, 0x30, 0x30, 0x30, 0x55, 0x6e, 0x6b,
|
0x19, 0x04, 0x23, 0x34, 0x32, 0x30, 0x30, 0x30, 0x55, 0x6e, 0x6b,
|
||||||
0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x64, 0x61, 0x74,
|
0x6e, 0x6f, 0x77, 0x6e, 0x20, 0x64, 0x61, 0x74,
|
||||||
0x61, 0x62, 0x61, 0x73, 0x65, 0x20, 0x27, 0x61, 0x27
|
0x61, 0x62, 0x61, 0x73, 0x65, 0x20, 0x27, 0x61, 0x27
|
||||||
}, "wrong_use_database"),
|
}, "wrong_use_database"),
|
||||||
|
|
||||||
serialization_testcase(err_packet{
|
serialization_testcase(err_packet{
|
||||||
int2(1146), // eror code
|
int2(1146), // eror code
|
||||||
string_fixed<1>{0x23}, // sql state marker
|
string_fixed<1>{0x23}, // sql state marker
|
||||||
string_fixed<5>{'4', '2', 'S', '0', '2'}, // sql state
|
string_fixed<5>{'4', '2', 'S', '0', '2'}, // sql state
|
||||||
string_eof("Table 'awesome.unknown' doesn't exist") // err msg
|
string_eof("Table 'awesome.unknown' doesn't exist") // err msg
|
||||||
}, {
|
}, {
|
||||||
0x7a, 0x04, 0x23, 0x34, 0x32, 0x53, 0x30, 0x32,
|
0x7a, 0x04, 0x23, 0x34, 0x32, 0x53, 0x30, 0x32,
|
||||||
0x54, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x27, 0x61,
|
0x54, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x27, 0x61,
|
||||||
0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x2e, 0x75,
|
0x77, 0x65, 0x73, 0x6f, 0x6d, 0x65, 0x2e, 0x75,
|
||||||
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x27, 0x20,
|
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x27, 0x20,
|
||||||
0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20,
|
0x64, 0x6f, 0x65, 0x73, 0x6e, 0x27, 0x74, 0x20,
|
||||||
0x65, 0x78, 0x69, 0x73, 0x74
|
0x65, 0x78, 0x69, 0x73, 0x74
|
||||||
}, "unknown_table"),
|
}, "unknown_table"),
|
||||||
|
|
||||||
serialization_testcase(err_packet{
|
serialization_testcase(err_packet{
|
||||||
int2(1045), // error code
|
int2(1045), // error code
|
||||||
string_fixed<1>{0x23}, // SQL state marker
|
string_fixed<1>{0x23}, // SQL state marker
|
||||||
string_fixed<5>{'2', '8', '0', '0', '0'}, // SQL state
|
string_fixed<5>{'2', '8', '0', '0', '0'}, // SQL state
|
||||||
string_eof("Access denied for user 'root'@'localhost' (using password: YES)")
|
string_eof("Access denied for user 'root'@'localhost' (using password: YES)")
|
||||||
}, {
|
}, {
|
||||||
0x15, 0x04, 0x23, 0x32, 0x38, 0x30, 0x30, 0x30,
|
0x15, 0x04, 0x23, 0x32, 0x38, 0x30, 0x30, 0x30,
|
||||||
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x64,
|
0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x64,
|
||||||
0x65, 0x6e, 0x69, 0x65, 0x64, 0x20, 0x66, 0x6f,
|
0x65, 0x6e, 0x69, 0x65, 0x64, 0x20, 0x66, 0x6f,
|
||||||
0x72, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x27,
|
0x72, 0x20, 0x75, 0x73, 0x65, 0x72, 0x20, 0x27,
|
||||||
0x72, 0x6f, 0x6f, 0x74, 0x27, 0x40, 0x27, 0x6c,
|
0x72, 0x6f, 0x6f, 0x74, 0x27, 0x40, 0x27, 0x6c,
|
||||||
0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
|
0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
|
||||||
0x27, 0x20, 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67,
|
0x27, 0x20, 0x28, 0x75, 0x73, 0x69, 0x6e, 0x67,
|
||||||
0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
0x20, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
|
||||||
0x64, 0x3a, 0x20, 0x59, 0x45, 0x53, 0x29
|
0x64, 0x3a, 0x20, 0x59, 0x45, 0x53, 0x29
|
||||||
}, "failed_login")
|
}, "failed_login")
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
// Column definition
|
// Column definition
|
||||||
INSTANTIATE_TEST_SUITE_P(ColumnDefinition, DeserializeSpaceTest, testing::Values(
|
INSTANTIATE_TEST_SUITE_P(ColumnDefinition, DeserializeSpaceTest, testing::Values(
|
||||||
serialization_testcase(column_definition_packet{
|
serialization_testcase(column_definition_packet{
|
||||||
string_lenenc("def"), //catalog
|
string_lenenc("def"), //catalog
|
||||||
string_lenenc("awesome"), // schema (database)
|
string_lenenc("awesome"), // schema (database)
|
||||||
string_lenenc("test_table"), // table
|
string_lenenc("test_table"), // table
|
||||||
string_lenenc("test_table"), // physical table
|
string_lenenc("test_table"), // physical table
|
||||||
string_lenenc("id"), // field name
|
string_lenenc("id"), // field name
|
||||||
string_lenenc("id"), // physical field name
|
string_lenenc("id"), // physical field name
|
||||||
collation::binary,
|
collation::binary,
|
||||||
int4(11), // length
|
int4(11), // length
|
||||||
protocol_field_type::long_,
|
protocol_field_type::long_,
|
||||||
int2(
|
int2(
|
||||||
column_flags::not_null |
|
column_flags::not_null |
|
||||||
column_flags::pri_key |
|
column_flags::pri_key |
|
||||||
column_flags::auto_increment |
|
column_flags::auto_increment |
|
||||||
column_flags::part_key
|
column_flags::part_key
|
||||||
),
|
),
|
||||||
int1(0) // decimals
|
int1(0) // decimals
|
||||||
}, {
|
}, {
|
||||||
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
|
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
|
||||||
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
|
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
|
||||||
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
|
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
|
||||||
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
|
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
|
||||||
0x6c, 0x65, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64,
|
0x6c, 0x65, 0x02, 0x69, 0x64, 0x02, 0x69, 0x64,
|
||||||
0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03,
|
0x0c, 0x3f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x03,
|
||||||
0x03, 0x42, 0x00, 0x00, 0x00
|
0x03, 0x42, 0x00, 0x00, 0x00
|
||||||
}, "numeric_auto_increment_primary_key"),
|
}, "numeric_auto_increment_primary_key"),
|
||||||
serialization_testcase(column_definition_packet{
|
serialization_testcase(column_definition_packet{
|
||||||
string_lenenc("def"), //catalog
|
string_lenenc("def"), //catalog
|
||||||
string_lenenc("awesome"), // schema (database)
|
string_lenenc("awesome"), // schema (database)
|
||||||
string_lenenc("child"), // table
|
string_lenenc("child"), // table
|
||||||
string_lenenc("child_table"), // physical table
|
string_lenenc("child_table"), // physical table
|
||||||
string_lenenc("field_alias"), // field name
|
string_lenenc("field_alias"), // field name
|
||||||
string_lenenc("field_varchar"), // physical field name
|
string_lenenc("field_varchar"), // physical field name
|
||||||
collation::utf8_general_ci,
|
collation::utf8_general_ci,
|
||||||
int4(765), // length
|
int4(765), // length
|
||||||
protocol_field_type::var_string,
|
protocol_field_type::var_string,
|
||||||
int2(), // no column flags
|
int2(), // no column flags
|
||||||
int1(0) // decimals
|
int1(0) // decimals
|
||||||
}, {
|
}, {
|
||||||
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
|
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
|
||||||
0x73, 0x6f, 0x6d, 0x65, 0x05, 0x63, 0x68, 0x69,
|
0x73, 0x6f, 0x6d, 0x65, 0x05, 0x63, 0x68, 0x69,
|
||||||
0x6c, 0x64, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64,
|
0x6c, 0x64, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64,
|
||||||
0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0b, 0x66,
|
0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0b, 0x66,
|
||||||
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x61, 0x6c, 0x69,
|
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x61, 0x6c, 0x69,
|
||||||
0x61, 0x73, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64,
|
0x61, 0x73, 0x0d, 0x66, 0x69, 0x65, 0x6c, 0x64,
|
||||||
0x5f, 0x76, 0x61, 0x72, 0x63, 0x68, 0x61, 0x72,
|
0x5f, 0x76, 0x61, 0x72, 0x63, 0x68, 0x61, 0x72,
|
||||||
0x0c, 0x21, 0x00, 0xfd, 0x02, 0x00, 0x00, 0xfd,
|
0x0c, 0x21, 0x00, 0xfd, 0x02, 0x00, 0x00, 0xfd,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
}, "varchar_field_aliased_field_and_table_names_join"),
|
}, "varchar_field_aliased_field_and_table_names_join"),
|
||||||
|
|
||||||
serialization_testcase(column_definition_packet{
|
serialization_testcase(column_definition_packet{
|
||||||
string_lenenc("def"), //catalog
|
string_lenenc("def"), //catalog
|
||||||
string_lenenc("awesome"), // schema (database)
|
string_lenenc("awesome"), // schema (database)
|
||||||
string_lenenc("test_table"), // table
|
string_lenenc("test_table"), // table
|
||||||
string_lenenc("test_table"), // physical table
|
string_lenenc("test_table"), // physical table
|
||||||
string_lenenc("field_float"), // field name
|
string_lenenc("field_float"), // field name
|
||||||
string_lenenc("field_float"), // physical field name
|
string_lenenc("field_float"), // physical field name
|
||||||
collation::binary, // binary
|
collation::binary, // binary
|
||||||
int4(12), // length
|
int4(12), // length
|
||||||
protocol_field_type::float_,
|
protocol_field_type::float_,
|
||||||
int2(), // no column flags
|
int2(), // no column flags
|
||||||
int1(31) // decimals
|
int1(31) // decimals
|
||||||
}, {
|
}, {
|
||||||
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
|
0x03, 0x64, 0x65, 0x66, 0x07, 0x61, 0x77, 0x65,
|
||||||
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
|
0x73, 0x6f, 0x6d, 0x65, 0x0a, 0x74, 0x65, 0x73,
|
||||||
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
|
0x74, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x0a,
|
||||||
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
|
0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x61, 0x62,
|
||||||
0x6c, 0x65, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64,
|
0x6c, 0x65, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64,
|
||||||
0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x0b, 0x66,
|
0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x0b, 0x66,
|
||||||
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x6f,
|
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x6c, 0x6f,
|
||||||
0x61, 0x74, 0x0c, 0x3f, 0x00, 0x0c, 0x00, 0x00,
|
0x61, 0x74, 0x0c, 0x3f, 0x00, 0x0c, 0x00, 0x00,
|
||||||
0x00, 0x04, 0x00, 0x00, 0x1f, 0x00, 0x00
|
0x00, 0x04, 0x00, 0x00, 0x1f, 0x00, 0x00
|
||||||
}, "float_field")
|
}, "float_field")
|
||||||
), test_name_generator);
|
), test_name_generator);
|
||||||
|
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user