diff --git a/CHANGELOG b/CHANGELOG index 091d3377c..23d2a96c3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -80,6 +80,9 @@ Release 1.5.2 (2013-06-xx) - limited allowed types for JSON::Query to Object, Array, Object::Ptr, Array::Ptr and empty - fixed GH #175: HTMLForm does not read URL parameters on POST or PUT +- added GH #187: MySQL: allow access to the underlying connection handle +- added GH #186: MySQL: support for MYSQL_SECURE_AUTH +- fixed GH #174: MySQL: 4GB allocated when reading any largetext or largeblob field Release 1.5.1 (2013-01-11) diff --git a/Data/MySQL/Makefile b/Data/MySQL/Makefile index 2263c3a43..0b18ca555 100644 --- a/Data/MySQL/Makefile +++ b/Data/MySQL/Makefile @@ -14,7 +14,7 @@ SYSFLAGS += -DTHREADSAFE -DNO_TCL objects = Binder Extractor SessionImpl Connector \ MySQLStatementImpl ResultMetadata MySQLException \ - SessionHandle StatementExecutor + SessionHandle StatementExecutor Utility target = PocoDataMySQL target_version = $(LIBVERSION) diff --git a/Data/MySQL/MySQL_VS80.vcproj b/Data/MySQL/MySQL_VS80.vcproj index 4add00b16..9c9b857e2 100644 --- a/Data/MySQL/MySQL_VS80.vcproj +++ b/Data/MySQL/MySQL_VS80.vcproj @@ -403,6 +403,8 @@ RelativePath=".\src\SessionImpl.cpp"/> + + + + + @@ -288,6 +289,7 @@ + diff --git a/Data/MySQL/MySQL_vs100.vcxproj.filters b/Data/MySQL/MySQL_vs100.vcxproj.filters index 98b4c6317..b69fa92ac 100644 --- a/Data/MySQL/MySQL_vs100.vcxproj.filters +++ b/Data/MySQL/MySQL_vs100.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + @@ -74,5 +77,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Data/MySQL/MySQL_vs110.vcxproj b/Data/MySQL/MySQL_vs110.vcxproj index 83928a96f..9888f3287 100644 --- a/Data/MySQL/MySQL_vs110.vcxproj +++ b/Data/MySQL/MySQL_vs110.vcxproj @@ -286,6 +286,7 @@ + @@ -298,6 +299,7 @@ + diff --git a/Data/MySQL/MySQL_vs110.vcxproj.filters b/Data/MySQL/MySQL_vs110.vcxproj.filters index 98b4c6317..b69fa92ac 100644 --- a/Data/MySQL/MySQL_vs110.vcxproj.filters +++ b/Data/MySQL/MySQL_vs110.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + @@ -74,5 +77,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Data/MySQL/MySQL_vs71.vcproj b/Data/MySQL/MySQL_vs71.vcproj index a0a303bd5..d131538ba 100644 --- a/Data/MySQL/MySQL_vs71.vcproj +++ b/Data/MySQL/MySQL_vs71.vcproj @@ -382,6 +382,8 @@ RelativePath=".\src\SessionImpl.cpp"/> + + + @@ -286,6 +287,7 @@ + diff --git a/Data/MySQL/MySQL_x64_vs100.vcxproj.filters b/Data/MySQL/MySQL_x64_vs100.vcxproj.filters index 98b4c6317..b69fa92ac 100644 --- a/Data/MySQL/MySQL_x64_vs100.vcxproj.filters +++ b/Data/MySQL/MySQL_x64_vs100.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + @@ -74,5 +77,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Data/MySQL/MySQL_x64_vs110.vcxproj b/Data/MySQL/MySQL_x64_vs110.vcxproj index 128c8dd0f..fd9ec07f9 100644 --- a/Data/MySQL/MySQL_x64_vs110.vcxproj +++ b/Data/MySQL/MySQL_x64_vs110.vcxproj @@ -280,6 +280,7 @@ + @@ -292,6 +293,7 @@ + diff --git a/Data/MySQL/MySQL_x64_vs110.vcxproj.filters b/Data/MySQL/MySQL_x64_vs110.vcxproj.filters index 98b4c6317..b69fa92ac 100644 --- a/Data/MySQL/MySQL_x64_vs110.vcxproj.filters +++ b/Data/MySQL/MySQL_x64_vs110.vcxproj.filters @@ -42,6 +42,9 @@ Source Files + + Source Files + @@ -74,5 +77,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h b/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h index 4383f95df..4a638eb79 100644 --- a/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h +++ b/Data/MySQL/include/Poco/Data/MySQL/StatementExecutor.h @@ -97,9 +97,9 @@ private: StatementExecutor& operator=(const StatementExecutor&); private: - MYSQL* _pSessionHandle; + MYSQL* _pSessionHandle; MYSQL_STMT* _pHandle; - int _state; + int _state; std::size_t _affectedRowCount; std::string _query; }; diff --git a/Data/MySQL/include/Poco/Data/MySQL/Utility.h b/Data/MySQL/include/Poco/Data/MySQL/Utility.h new file mode 100644 index 000000000..fb82506bc --- /dev/null +++ b/Data/MySQL/include/Poco/Data/MySQL/Utility.h @@ -0,0 +1,101 @@ +// +// Utility.h +// +// $Id: //poco/Main/Data/MySQL/include/Poco/Data/MySQL/Utility.h#2 $ +// +// Library: MySQL +// Package: MySQL +// Module: Utility +// +// Definition of Utility. +// +// Copyright (c) 2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#ifndef MySQL_Utility_INCLUDED +#define MySQL_Utility_INCLUDED + + +#include "Poco/Data/MySQL/MySQL.h" +#include "Poco/Data/Session.h" + + +struct st_mysql; +typedef struct st_mysql MYSQL; + + +namespace Poco { +namespace Data { +namespace MySQL { + + +class MySQL_API Utility + /// Various utility functions for MySQL. +{ +public: + + static std::string serverInfo(MYSQL* pHandle); + /// Returns server info. + + static std::string serverInfo(Poco::Data::Session& session); + /// Returns server info. + + static unsigned long serverVersion(MYSQL* pHandle); + /// Returns server version. + + static unsigned long serverVersion(Poco::Data::Session& session); + /// Returns server version. + + static std::string hostInfo(MYSQL* pHandle); + /// Returns host info. + + static std::string hostInfo(Poco::Data::Session& session); + /// Returns host info. + + static bool hasMicrosecond(); + /// Rturns true if microseconds are suported. + + static MYSQL* handle(Poco::Data::Session& session); + /// Returns native MySQL handle for the session. +}; + + +// +// inlines +// + + +inline MYSQL* Utility::handle(Session& session) +{ + return Poco::AnyCast(session.getProperty("handle")); +} + + +} } } // namespace Poco::Data::MySQL + + +#endif // MySQL_Utility_INCLUDED diff --git a/Data/MySQL/src/ResultMetadata.cpp b/Data/MySQL/src/ResultMetadata.cpp index ab4a7cb8e..4e3fbb8ed 100644 --- a/Data/MySQL/src/ResultMetadata.cpp +++ b/Data/MySQL/src/ResultMetadata.cpp @@ -189,11 +189,15 @@ void ResultMetadata::init(MYSQL_STMT* stmt) {for (std::size_t i = 0; i < count; i++) { + unsigned long size = fieldSize(fields[i]); + unsigned long zero = 0; + if (size == ~zero) size = 0; + _columns.push_back(MetaColumn( i, // position fields[i].name, // name fieldType(fields[i]), // type - fieldSize(fields[i]), // length + size, // length 0, // TODO: precision !IS_NOT_NULL(fields[i].flags) // nullable )); @@ -208,19 +212,19 @@ void ResultMetadata::init(MYSQL_STMT* stmt) std::size_t offset = 0; - {for (std::size_t i = 0; i < count; i++) + for (std::size_t i = 0; i < count; i++) { std::memset(&_row[i], 0, sizeof(MYSQL_BIND)); - + unsigned int len = static_cast(_columns[i].length()); _row[i].buffer_type = fields[i].type; - _row[i].buffer_length = static_cast(_columns[i].length()); - _row[i].buffer = &_buffer[0] + offset; + _row[i].buffer_length = len; + _row[i].buffer = (len > 0) ? (&_buffer[0] + offset) : 0; _row[i].length = &_lengths[i]; _row[i].is_null = &_isNull[i]; _row[i].is_unsigned = (fields[i].flags & UNSIGNED_FLAG) > 0; offset += _row[i].buffer_length; - }} + } } std::size_t ResultMetadata::columnsReturned() const diff --git a/Data/MySQL/src/SessionHandle.cpp b/Data/MySQL/src/SessionHandle.cpp index b4d700d38..900c6b93c 100644 --- a/Data/MySQL/src/SessionHandle.cpp +++ b/Data/MySQL/src/SessionHandle.cpp @@ -95,8 +95,13 @@ void SessionHandle::options(mysql_option opt, unsigned int i) void SessionHandle::connect(const char* host, const char* user, const char* password, const char* db, unsigned int port) { +#ifdef HAVE_MYSQL_REAL_CONNECT if (!mysql_real_connect(_pHandle, host, user, password, db, port, 0, 0)) throw ConnectionFailedException(mysql_error(_pHandle)); +#else + if (!mysql_connect(_pHandle, host, user, password)) + throw ConnectionFailedException(mysql_error(_pHandle)) +#endif } diff --git a/Data/MySQL/src/SessionImpl.cpp b/Data/MySQL/src/SessionImpl.cpp index 22f26bdb3..66514d1e4 100644 --- a/Data/MySQL/src/SessionImpl.cpp +++ b/Data/MySQL/src/SessionImpl.cpp @@ -72,10 +72,8 @@ SessionImpl::SessionImpl(const std::string& connectionString, std::size_t loginT _connected(false), _inTransaction(false) { - addProperty("insertId", - &SessionImpl::setInsertId, - &SessionImpl::getInsertId); - + addProperty("insertId", &SessionImpl::setInsertId, &SessionImpl::getInsertId); + setProperty("handle", static_cast(_handle)); open(); setConnectionTimeout(CONNECTION_TIMEOUT_DEFAULT); } @@ -109,6 +107,7 @@ void SessionImpl::open(const std::string& connect) options["db"] = ""; options["compress"] = ""; options["auto-reconnect"] = ""; + options["secure-auth"] = ""; const std::string& connString = connectionString(); for (std::string::const_iterator start = connString.begin();;) @@ -126,7 +125,7 @@ void SessionImpl::open(const std::string& connect) start = finish + 1; } - if (options["user"] == "") + if (options["user"].empty()) throw MySQLException("create session: specify user name"); const char * db = NULL; @@ -141,16 +140,23 @@ void SessionImpl::open(const std::string& connect) _handle.options(MYSQL_OPT_COMPRESS); else if (options["compress"] == "false") ; - else if (options["compress"] != "") + else if (!options["compress"].empty()) throw MySQLException("create session: specify correct compress option (true or false) or skip it"); if (options["auto-reconnect"] == "true") _handle.options(MYSQL_OPT_RECONNECT, true); else if (options["auto-reconnect"] == "false") _handle.options(MYSQL_OPT_RECONNECT, false); - else if (options["auto-reconnect"] != "") + else if (!options["auto-reconnect"].empty()) throw MySQLException("create session: specify correct auto-reconnect option (true or false) or skip it"); + if (options["secure-auth"] == "true") + _handle.options(MYSQL_SECURE_AUTH, true); + else if (options["secure-auth"] == "false") + _handle.options(MYSQL_SECURE_AUTH, false); + else if (!options["secure-auth"].empty()) + throw MySQLException("create session: specify correct secure-auth option (true or false) or skip it"); + // Real connect _handle.connect(options["host"].c_str(), options["user"].c_str(), diff --git a/Data/MySQL/src/StatementExecutor.cpp b/Data/MySQL/src/StatementExecutor.cpp index 944cca4da..681245705 100644 --- a/Data/MySQL/src/StatementExecutor.cpp +++ b/Data/MySQL/src/StatementExecutor.cpp @@ -36,7 +36,7 @@ #include #include "Poco/Data/MySQL/StatementExecutor.h" -#include +#include "Poco/Format.h" namespace Poco { @@ -120,7 +120,7 @@ void StatementExecutor::execute() my_ulonglong affectedRows = mysql_affected_rows(_pSessionHandle); if (affectedRows != ((my_ulonglong) - 1)) - _affectedRowCount = affectedRows; //Was really a DELETE, UPDATE or INSERT statement + _affectedRowCount = static_cast(affectedRows); //Was really a DELETE, UPDATE or INSERT statement } @@ -131,10 +131,11 @@ bool StatementExecutor::fetch() int res = mysql_stmt_fetch(_pHandle); - if ((res != 0) && (res != MYSQL_NO_DATA)) + // we have specified zero buffers for BLOBs, so DATA_TRUNCATED is normal in this case + if ((res != 0) && (res != MYSQL_NO_DATA) && (res != MYSQL_DATA_TRUNCATED)) throw StatementException("mysql_stmt_fetch error", _pHandle, _query); - return (res == 0); + return (res == 0) || (res == MYSQL_DATA_TRUNCATED); } @@ -146,11 +147,7 @@ bool StatementExecutor::fetchColumn(std::size_t n, MYSQL_BIND *bind) int res = mysql_stmt_fetch_column(_pHandle, bind, static_cast(n), 0); if ((res != 0) && (res != MYSQL_NO_DATA)) - { - std::ostringstream msg; - msg << "mysql_stmt_fetch_column(" << n << ") error"; - throw StatementException(msg.str(), _pHandle, _query); - } + throw StatementException(Poco::format("mysql_stmt_fetch_column(%z) error", n), _pHandle, _query); return (res == 0); } diff --git a/Data/MySQL/src/Utility.cpp b/Data/MySQL/src/Utility.cpp new file mode 100644 index 000000000..9c9059309 --- /dev/null +++ b/Data/MySQL/src/Utility.cpp @@ -0,0 +1,88 @@ +// +// Utility.cpp +// +// $Id: //poco/Main/Data/MySQL/src/Utility.cpp#5 $ +// +// Library: MySQL +// Package: MySQL +// Module: Utility +// +// Implementation of Utility +// +// Copyright (c) 2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// + + +#include "Poco/Data/MySQL/Utility.h" +#include + + +namespace Poco { +namespace Data { +namespace MySQL { + + +std::string Utility::serverInfo(MYSQL* pHandle) +{ + std::string info(mysql_get_server_info(pHandle)); + return info; +} + + +std::string Utility::serverInfo(Session& session) +{ + std::string info(mysql_get_server_info(handle(session))); + return info; +} + + +unsigned long Utility::serverVersion(MYSQL* pHandle) +{ + return mysql_get_server_version(pHandle); +} + + +unsigned long Utility::serverVersion(Session& session) +{ + return mysql_get_server_version(handle(session)); +} + + +std::string Utility::hostInfo(MYSQL* pHandle) +{ + std::string info(mysql_get_host_info(pHandle)); + return info; +} + + +std::string Utility::hostInfo(Session& session) +{ + std::string info(mysql_get_host_info(handle(session))); + return info; +} + + +} } } // namespace Poco::Data::MySQL diff --git a/Data/MySQL/testsuite/src/MySQLTest.cpp b/Data/MySQL/testsuite/src/MySQLTest.cpp index cb653302c..3b05388d2 100644 --- a/Data/MySQL/testsuite/src/MySQLTest.cpp +++ b/Data/MySQL/testsuite/src/MySQLTest.cpp @@ -41,6 +41,7 @@ #include "Poco/Data/LOB.h" #include "Poco/Data/StatementImpl.h" #include "Poco/Data/MySQL/Connector.h" +#include "Poco/Data/MySQL/Utility.h" #include "Poco/Data/MySQL/MySQLException.h" #include "Poco/Nullable.h" #include "Poco/Data/DataException.h" @@ -49,6 +50,7 @@ using namespace Poco::Data; using namespace Poco::Data::Keywords; using Poco::Data::MySQL::ConnectionException; +using Poco::Data::MySQL::Utility; using Poco::Data::MySQL::StatementException; using Poco::format; using Poco::NotFoundException; @@ -64,7 +66,7 @@ Poco::SharedPtr MySQLTest::_pExecutor = 0; // Parameters for barebone-test #define MYSQL_USER "root" #define MYSQL_PWD "poco" -#define MYSQL_HOST "localhost" +#define MYSQL_HOST "192.168.1.25"//"localhost" #define MYSQL_PORT 3306 #define MYSQL_DB "test" @@ -74,7 +76,9 @@ std::string MySQLTest::_dbConnString = "host=" MYSQL_HOST ";user=" MYSQL_USER ";password=" MYSQL_PWD ";db=" MYSQL_DB - ";compress=true;auto-reconnect=true"; + ";compress=true" + ";auto-reconnect=true" + ";secure-auth=true"; MySQLTest::MySQLTest(const std::string& name): @@ -90,6 +94,15 @@ MySQLTest::~MySQLTest() } +void MySQLTest::dbInfo(Session& session) +{ + + std::cout << "Server Info: " << Utility::serverInfo(Utility::handle(session)) << std::endl; + std::cout << "Server Version: " << Utility::serverVersion(Utility::handle(session)) << std::endl; + std::cout << "Host Info: " << Utility::hostInfo(Utility::handle(session)) << std::endl; +} + + void MySQLTest::testConnectNoDB() { std::string dbConnString = "host=" MYSQL_HOST @@ -100,7 +113,9 @@ void MySQLTest::testConnectNoDB() try { Session session(MySQL::Connector::KEY, dbConnString); - std::cout << "Connected to [" << "MySQL" << "] without database. Disconnecting ..." << std::endl; + std::cout << "Connected to [" << "MySQL" << "] without database." << std::endl; + dbInfo(session); + std::cout << "Disconnecting ..." << std::endl; session.close(); std::cout << "Disconnected." << std::endl; } @@ -434,6 +449,9 @@ void MySQLTest::testBLOB() { if (!_pSession) fail ("Test not available."); + recreatePersonBLOBTable(); + _pExecutor->blob(); + const std::size_t maxFldSize = 65534; _pSession->setProperty("maxFieldSize", Poco::Any(maxFldSize-1)); recreatePersonBLOBTable(); @@ -852,6 +870,7 @@ CppUnit::Test* MySQLTest::suite() } std::cout << "*** Connected to [" << "MySQL" << "] test database." << std::endl; + dbInfo(*_pSession); _pExecutor = new SQLExecutor("MySQL SQL Executor", _pSession); diff --git a/Data/MySQL/testsuite/src/MySQLTest.h b/Data/MySQL/testsuite/src/MySQLTest.h index 038fd7b56..f6619a909 100644 --- a/Data/MySQL/testsuite/src/MySQLTest.h +++ b/Data/MySQL/testsuite/src/MySQLTest.h @@ -142,6 +142,8 @@ private: void recreateNullableIntTable(); void recreateNullableStringTable(); + static void dbInfo(Poco::Data::Session& session); + static std::string _dbConnString; static Poco::SharedPtr _pSession; static Poco::SharedPtr _pExecutor; diff --git a/Data/MySQL/testsuite/src/SQLExecutor.cpp b/Data/MySQL/testsuite/src/SQLExecutor.cpp index e8d4be775..cc839e1f7 100644 --- a/Data/MySQL/testsuite/src/SQLExecutor.cpp +++ b/Data/MySQL/testsuite/src/SQLExecutor.cpp @@ -53,6 +53,7 @@ #include #include +#include using namespace Poco::Data; @@ -500,8 +501,8 @@ void SQLExecutor::insertSingleBulk() void SQLExecutor::unsignedInts() { std::string funct = "unsignedInts()"; - unsigned int data = UINT32_MAX; - unsigned int ret = 0; + Poco::UInt32 data = std::numeric_limits::max(); + Poco::UInt32 ret = 0; try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; } catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } @@ -1362,7 +1363,7 @@ void SQLExecutor::time() } -void SQLExecutor::blob(int bigSize) +void SQLExecutor::blob(unsigned int bigSize) { std::string funct = "blob()"; std::string lastName("lastname"); diff --git a/Data/MySQL/testsuite/src/SQLExecutor.h b/Data/MySQL/testsuite/src/SQLExecutor.h index eb2e23e56..8840e249c 100644 --- a/Data/MySQL/testsuite/src/SQLExecutor.h +++ b/Data/MySQL/testsuite/src/SQLExecutor.h @@ -100,7 +100,7 @@ public: void singleSelect(); void emptyDB(); - void blob(int bigSize = 1024); + void blob(unsigned int bigSize = ~0); void blobStmt(); void dateTime(); void date();