From: Marcin Siodelski Date: Wed, 11 Jan 2023 21:28:12 +0000 (+0100) Subject: [#2688] Added read-timeout and write-timeout X-Git-Tag: Kea-2.2.1~39 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e5bf6295e5d2f7ca97a9ea2f4f2e9ad91fb0282;p=thirdparty%2Fkea.git [#2688] Added read-timeout and write-timeout New parameters have been added for MySQL connection. --- diff --git a/src/lib/database/dbaccess_parser.cc b/src/lib/database/dbaccess_parser.cc index caee236282..e3d14b485f 100644 --- a/src/lib/database/dbaccess_parser.cc +++ b/src/lib/database/dbaccess_parser.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -48,7 +48,9 @@ DbAccessParser::parse(std::string& access_string, DatabaseConnection::ParameterMap values_copy = values_; int64_t lfc_interval = 0; - int64_t timeout = 0; + int64_t connect_timeout = 0; + int64_t read_timeout = 0; + int64_t write_timeout = 0; int64_t port = 0; int64_t max_reconnect_tries = 0; int64_t reconnect_wait_time = 0; @@ -68,9 +70,19 @@ DbAccessParser::parse(std::string& access_string, boost::lexical_cast(lfc_interval); } else if (param.first == "connect-timeout") { - timeout = param.second->intValue(); + connect_timeout = param.second->intValue(); values_copy[param.first] = - boost::lexical_cast(timeout); + boost::lexical_cast(connect_timeout); + + } else if (param.first == "read-timeout") { + read_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast(read_timeout); + + } else if (param.first == "write-timeout") { + write_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast(write_timeout); } else if (param.first == "max-reconnect-tries") { max_reconnect_tries = param.second->intValue(); @@ -148,11 +160,27 @@ DbAccessParser::parse(std::string& access_string, << " (" << value->getPosition() << ")"); } - // d. Check that the timeout is within a reasonable range. - if ((timeout < 0) || - (timeout > std::numeric_limits::max())) { + // d. Check that the timeouts are within a reasonable range. + if ((connect_timeout < 0) || + (connect_timeout > std::numeric_limits::max())) { ConstElementPtr value = database_config->get("connect-timeout"); - isc_throw(DbConfigError, "connect-timeout value: " << timeout + isc_throw(DbConfigError, "connect-timeout value: " << connect_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits::max() + << " (" << value->getPosition() << ")"); + } + if ((read_timeout < 0) || + (read_timeout > std::numeric_limits::max())) { + ConstElementPtr value = database_config->get("read-timeout"); + isc_throw(DbConfigError, "read-timeout value: " << read_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits::max() + << " (" << value->getPosition() << ")"); + } + if ((write_timeout < 0) || + (write_timeout > std::numeric_limits::max())) { + ConstElementPtr value = database_config->get("write-timeout"); + isc_throw(DbConfigError, "write-timeout value: " << write_timeout << " is out of range, expected value: 0.." << std::numeric_limits::max() << " (" << value->getPosition() << ")"); diff --git a/src/lib/database/tests/dbaccess_parser_unittest.cc b/src/lib/database/tests/dbaccess_parser_unittest.cc index dca15890f5..b51b6f4b80 100644 --- a/src/lib/database/tests/dbaccess_parser_unittest.cc +++ b/src/lib/database/tests/dbaccess_parser_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -167,6 +167,8 @@ private: bool quoteValue(const std::string& parameter) const { return ((parameter != "persist") && (parameter != "lfc-interval") && (parameter != "connect-timeout") && + (parameter != "read-timeout") && + (parameter != "write-timeout") && (parameter != "port") && (parameter != "max-row-errors") && (parameter != "readonly")); @@ -354,8 +356,8 @@ TEST_F(DbAccessParserTest, largeLFCInterval) { } // This test checks that the parser accepts the valid value of the -// timeout parameter. -TEST_F(DbAccessParserTest, validTimeout) { +// connect-timeout parameter. +TEST_F(DbAccessParserTest, validConnectTimeout) { const char* config[] = {"type", "memfile", "name", "/opt/var/lib/kea/kea-leases6.csv", "connect-timeout", "3600", @@ -372,8 +374,8 @@ TEST_F(DbAccessParserTest, validTimeout) { } // This test checks that the parser rejects the negative value of the -// timeout parameter. -TEST_F(DbAccessParserTest, negativeTimeout) { +// connect-timeout parameter. +TEST_F(DbAccessParserTest, negativeConnectTimeout) { const char* config[] = {"type", "memfile", "name", "/opt/var/lib/kea/kea-leases6.csv", "connect-timeout", "-1", @@ -388,8 +390,8 @@ TEST_F(DbAccessParserTest, negativeTimeout) { } // This test checks that the parser rejects a too large (greater than -// the max uint32_t) value of the timeout parameter. -TEST_F(DbAccessParserTest, largeTimeout) { +// the max uint32_t) value of the connecttimeout parameter. +TEST_F(DbAccessParserTest, largeConnectTimeout) { const char* config[] = {"type", "memfile", "name", "/opt/var/lib/kea/kea-leases6.csv", "connect-timeout", "4294967296", @@ -403,6 +405,106 @@ TEST_F(DbAccessParserTest, largeTimeout) { EXPECT_THROW(parser.parse(json_elements), DbConfigError); } +// This test checks that the parser accepts the valid value of the +// read-timeout parameter. +TEST_F(DbAccessParserTest, validReadTimeout) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "read-timeout", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid read timeout", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// read-timeout parameter. +TEST_F(DbAccessParserTest, negativeReadTimeout) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "read-timeout", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the read-timeout parameter. +TEST_F(DbAccessParserTest, largeReadTimeout) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "read-timeout", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts the valid value of the +// write-timeout parameter. +TEST_F(DbAccessParserTest, validWriteTimeout) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "write-timeout", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid write timeout", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// write-timeout parameter. +TEST_F(DbAccessParserTest, negativeWriteTimeout) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "write-timeout", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the write-timeout parameter. +TEST_F(DbAccessParserTest, largeWriteTimeout) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "write-timeout", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + // This test checks that the parser accepts the valid value of the // port parameter. TEST_F(DbAccessParserTest, validPort) { diff --git a/src/lib/database/testutils/schema.cc b/src/lib/database/testutils/schema.cc index d6aef69a3e..a794de6e6f 100644 --- a/src/lib/database/testutils/schema.cc +++ b/src/lib/database/testutils/schema.cc @@ -34,6 +34,13 @@ const char* INVALID_PASSWORD = "password=invalid"; const char* VALID_TIMEOUT = "connect-timeout=10"; const char* INVALID_TIMEOUT_1 = "connect-timeout=foo"; const char* INVALID_TIMEOUT_2 = "connect-timeout=-17"; +const char* INVALID_TIMEOUT_3 = "connect-timeout=0"; +const char* VALID_READ_TIMEOUT = "read-timeout=11"; +const char* VALID_READ_TIMEOUT_ZERO = "read-timeout=0"; +const char* INVALID_READ_TIMEOUT_1 = "read-timeout=bar"; +const char* VALID_WRITE_TIMEOUT = "write-timeout=12"; +const char* VALID_WRITE_TIMEOUT_ZERO = "write-timeout=0"; +const char* INVALID_WRITE_TIMEOUT_1 = "write-timeout=baz"; const char* VALID_READONLY_DB = "readonly=true"; const char* INVALID_READONLY_DB = "readonly=5"; const char* VALID_CERT = "cert-file=" TEST_CA_DIR "/kea-client.crt"; diff --git a/src/lib/database/testutils/schema.h b/src/lib/database/testutils/schema.h index 9e10148f37..f278baa521 100644 --- a/src/lib/database/testutils/schema.h +++ b/src/lib/database/testutils/schema.h @@ -30,6 +30,13 @@ extern const char* INVALID_PASSWORD; extern const char* VALID_TIMEOUT; extern const char* INVALID_TIMEOUT_1; extern const char* INVALID_TIMEOUT_2; +extern const char* INVALID_TIMEOUT_3; +extern const char* VALID_READ_TIMEOUT; +extern const char* VALID_READ_TIMEOUT_ZERO; +extern const char* INVALID_READ_TIMEOUT_1; +extern const char* VALID_WRITE_TIMEOUT; +extern const char* VALID_WRITE_TIMEOUT_ZERO; +extern const char* INVALID_WRITE_TIMEOUT_1; extern const char* VALID_READONLY_DB; extern const char* INVALID_READONLY_DB; extern const char* VALID_CERT; diff --git a/src/lib/mysql/mysql_connection.cc b/src/lib/mysql/mysql_connection.cc index fc65cce45b..aadb89bab7 100644 --- a/src/lib/mysql/mysql_connection.cc +++ b/src/lib/mysql/mysql_connection.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2012-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -65,31 +65,12 @@ MySqlConnection::openDatabase() { } unsigned int port = 0; - string sport; - try { - sport = getParameter("port"); - } catch (...) { - // No port parameter, we are going to use the default port. - sport = ""; - } - - if (sport.size() > 0) { - // Port was given, so try to convert it to an integer. - - try { - port = boost::lexical_cast(sport); - } catch (...) { - // Port given but could not be converted to an unsigned int. - // Just fall back to the default value. - port = 0; - } - - // The port is only valid when it is in the 0..65535 range. - // Again fall back to the default when the given value is invalid. - if (port > numeric_limits::max()) { - port = 0; - } - } + setIntParameterValue("port", 0, numeric_limits::max(), port); + // The port is only valid when it is in the 0..65535 range. + // Again fall back to the default when the given value is invalid. + if (port > numeric_limits::max()) { + port = 0; + } const char* user = NULL; string suser; @@ -120,40 +101,18 @@ MySqlConnection::openDatabase() { } unsigned int connect_timeout = MYSQL_DEFAULT_CONNECTION_TIMEOUT; - string stimeout; + unsigned int read_timeout = 0; + unsigned int write_timeout = 0; try { - stimeout = getParameter("connect-timeout"); - } catch (...) { - // No timeout parameter, we are going to use the default timeout. - stimeout = ""; - } - - if (stimeout.size() > 0) { - // Timeout was given, so try to convert it to an integer. - - try { - connect_timeout = boost::lexical_cast(stimeout); - } catch (...) { - // Timeout given but could not be converted to an unsigned int. Set - // the connection timeout to an invalid value to trigger throwing - // of an exception. - connect_timeout = 0; - } - // The timeout is only valid if greater than zero, as depending on the // database, a zero timeout might signify something like "wait // indefinitely". - // - // The check below also rejects a value greater than the maximum - // integer value. The lexical_cast operation used to obtain a numeric - // value from a string can get confused if trying to convert a negative - // integer to an unsigned int: instead of throwing an exception, it may - // produce a large positive value. - if ((connect_timeout == 0) || - (connect_timeout > numeric_limits::max())) { - isc_throw(DbInvalidTimeout, "database connection timeout (" << - stimeout << ") must be an integer greater than 0"); - } + setIntParameterValue("connect-timeout", 1, numeric_limits::max(), connect_timeout); + setIntParameterValue("read-timeout", 0, numeric_limits::max(), read_timeout); + setIntParameterValue("write-timeout", 0, numeric_limits::max(), write_timeout); + + } catch (const std::exception& ex) { + isc_throw(DbInvalidTimeout, ex.what()); } const char* ca_file(0); @@ -241,6 +200,26 @@ MySqlConnection::openDatabase() { mysql_error(mysql_)); } + // Set the read timeout if it has been specified. Otherwise, the timeout is + // not used. + if (read_timeout > 0) { + result = mysql_options(mysql_, MYSQL_OPT_READ_TIMEOUT, &read_timeout); + if (result != 0) { + isc_throw(DbOpenError, "unable to set database read timeout: " << + mysql_error(mysql_)); + } + } + + // Set the write timeout if it has been specified. Otherwise, the timeout + // is not used. + if (write_timeout > 0) { + result = mysql_options(mysql_, MYSQL_OPT_WRITE_TIMEOUT, &write_timeout); + if (result != 0) { + isc_throw(DbOpenError, "unable to set database write timeout: " << + mysql_error(mysql_)); + } + } + // If TLS is enabled set it. If something should go wrong it will happen // later at the mysql_real_connect call. if (tls_) { @@ -517,5 +496,37 @@ MySqlConnection::rollback() { } } +template +void +MySqlConnection::setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value) { + string svalue; + try { + svalue = getParameter(name); + } catch (...) { + // Do nothing if the parameter is not present. + } + if (svalue.empty()) { + return; + } + try { + // Try to convert the value. + auto parsed_value = boost::lexical_cast(svalue); + // Check if the value is within the specified range. + if ((parsed_value < min) || (parsed_value > max)) { + isc_throw(BadValue, "bad " << svalue << " value"); + } + // Everything is fine. Return the parsed value. + value = parsed_value; + + } catch (...) { + // We may end up here when lexical_cast fails or when the + // parsed value is not within the desired range. In both + // cases let's throw the same general error. + isc_throw(BadValue, name << " parameter (" << + svalue << ") must be an integer between " + << min << " and " << max); + } +} + } // namespace db } // namespace isc diff --git a/src/lib/mysql/mysql_connection.h b/src/lib/mysql/mysql_connection.h index 50ebc28d08..f68a42c5de 100644 --- a/src/lib/mysql/mysql_connection.h +++ b/src/lib/mysql/mysql_connection.h @@ -712,6 +712,27 @@ public: return (cipher ? std::string(cipher) : ""); } +private: + + /// @brief Convenience function parsing and setting an integer parameter, + /// if it exists. + /// + /// If the parameter is not present, this function doesn't change the @c value. + /// Otherwise, it tries to convert the parameter to the type @c T. Finally, + /// it checks if the converted number is within the specified range. + /// + /// @param name Parameter name. + /// @param min Expected minimal value. + /// @param max Expected maximal value. + /// @param [out] value Reference to a value returning the parsed parameter. + /// @tparam T Parameter type. + /// @throw BadValue if the parameter is not a valid number or if it is out + /// of range. + template + void setIntParameterValue(const std::string& name, int64_t min, int64_t max, T& value); + +public: + /// @brief Prepared statements /// /// This field is public, because it is used heavily from MySqlConnection diff --git a/src/lib/mysql/tests/mysql_connection_unittest.cc b/src/lib/mysql/tests/mysql_connection_unittest.cc index 19d1a6ae72..46a8cb0abc 100644 --- a/src/lib/mysql/tests/mysql_connection_unittest.cc +++ b/src/lib/mysql/tests/mysql_connection_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -609,6 +609,126 @@ TEST_F(MySqlConnectionTest, transactions) { EXPECT_THROW(conn_.rollback(), isc::Unexpected); } +// Tests that valid connection timeout is accepted. +TEST_F(MySqlConnectionTest, connectionTimeout) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, VALID_TIMEOUT); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); + + auto mysql = static_cast(conn.mysql_); + ASSERT_TRUE(mysql); + unsigned int timeout = 123; + EXPECT_EQ(0, mysql_get_option(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout)); + EXPECT_EQ(10, timeout); +} + +// Tests that invalid timeout value type causes an error. +TEST_F(MySqlConnectionTest, connectionTimeoutInvalid) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, INVALID_TIMEOUT_1); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbInvalidTimeout); +} + +// Tests that a negative connection timeout value causes an error. +TEST_F(MySqlConnectionTest, connectionTimeoutInvalid2) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, INVALID_TIMEOUT_2); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbInvalidTimeout); +} + +// Tests that a zero connection timeout value causes an error. +TEST_F(MySqlConnectionTest, connectionTimeoutInvalid3) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, INVALID_TIMEOUT_3); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbInvalidTimeout); +} + +// Tests that a valid read timeout is accepted. +TEST_F(MySqlConnectionTest, readTimeout) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, VALID_READ_TIMEOUT); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); + + auto mysql = static_cast(conn.mysql_); + ASSERT_TRUE(mysql); + unsigned int timeout = 123; + EXPECT_EQ(0, mysql_get_option(mysql, MYSQL_OPT_READ_TIMEOUT, &timeout)); + EXPECT_EQ(11, timeout); +} + +// Tests that a zero read timeout is accepted. +TEST_F(MySqlConnectionTest, readTimeoutZero) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, VALID_READ_TIMEOUT_ZERO); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); + + auto mysql = static_cast(conn.mysql_); + ASSERT_TRUE(mysql); + unsigned int timeout = 123; + EXPECT_EQ(0, mysql_get_option(mysql, MYSQL_OPT_READ_TIMEOUT, &timeout)); + EXPECT_EQ(0, timeout); +} + +// Tests that an invalid read timeout causes an error. +TEST_F(MySqlConnectionTest, readTimeoutInvalid) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, INVALID_READ_TIMEOUT_1); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbInvalidTimeout); +} + +// Tests that a valid write timeout is accepted. +TEST_F(MySqlConnectionTest, writeTimeout) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, VALID_WRITE_TIMEOUT); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); + + auto mysql = static_cast(conn.mysql_); + ASSERT_TRUE(mysql); + unsigned int timeout = 123; + EXPECT_EQ(0, mysql_get_option(mysql, MYSQL_OPT_WRITE_TIMEOUT, &timeout)); + EXPECT_EQ(12, timeout); +} + +// Tests that a zero write timeout is accepted. +TEST_F(MySqlConnectionTest, writeTimeoutZero) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, VALID_WRITE_TIMEOUT_ZERO); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + ASSERT_NO_THROW_LOG(conn.openDatabase()); + + auto mysql = static_cast(conn.mysql_); + ASSERT_TRUE(mysql); + unsigned int timeout = 123; + EXPECT_EQ(0, mysql_get_option(mysql, MYSQL_OPT_WRITE_TIMEOUT, &timeout)); + EXPECT_EQ(0, timeout); +} + +// Tests that an invalid write timeout causes an error. +TEST_F(MySqlConnectionTest, writeTimeoutInvalid) { + std::string conn_str = connectionString(MYSQL_VALID_TYPE, VALID_NAME, + VALID_HOST_TCP, VALID_USER, + VALID_PASSWORD, INVALID_WRITE_TIMEOUT_1); + MySqlConnection conn(DatabaseConnection::parse(conn_str)); + EXPECT_THROW(conn.openDatabase(), DbInvalidTimeout); +} + TEST_F(MySqlConnectionWithPrimaryKeyTest, select) { select(); }