diff --git a/libs/libmysqlxx/include/mysqlxx/Connection.h b/libs/libmysqlxx/include/mysqlxx/Connection.h index bbee58c58a1..c932f5e8274 100644 --- a/libs/libmysqlxx/include/mysqlxx/Connection.h +++ b/libs/libmysqlxx/include/mysqlxx/Connection.h @@ -21,6 +21,8 @@ public: const char* password = 0, unsigned int port = 0); + Connection(const std::string & config_name); + virtual ~Connection(); virtual void connect(const char* db, diff --git a/libs/libmysqlxx/include/mysqlxx/Exception.h b/libs/libmysqlxx/include/mysqlxx/Exception.h index c4751a4e158..83788474576 100644 --- a/libs/libmysqlxx/include/mysqlxx/Exception.h +++ b/libs/libmysqlxx/include/mysqlxx/Exception.h @@ -9,7 +9,21 @@ namespace mysqlxx { -POCO_DECLARE_EXCEPTION(Foundation_API, Exception, Poco::Exception); +struct Exception : public Poco::Exception +{ + Exception(const std::string & msg, int code = 0) : Poco::Exception(msg, code) {} + int errnum() { return code(); } +}; + +struct ConnectionFailed : public Exception +{ + ConnectionFailed(const std::string & msg, int code = 0) : Exception(msg, code) {} +}; + +struct BadQuery : public Exception +{ + BadQuery(const std::string & msg, int code = 0) : Exception(msg, code) {} +}; inline void checkError(MYSQL & driver) diff --git a/libs/libmysqlxx/include/mysqlxx/Manip.h b/libs/libmysqlxx/include/mysqlxx/Manip.h new file mode 100644 index 00000000000..4e2b21f6c11 --- /dev/null +++ b/libs/libmysqlxx/include/mysqlxx/Manip.h @@ -0,0 +1,218 @@ +#ifndef MYSQLXX_MANIP_H +#define MYSQLXX_MANIP_H + +#include +#include +#include + + +namespace mysqlxx +{ + +/** @brief Манипулятор ostream, который escape-ит строки для записи в tab delimited файл. */ +enum escape_enum +{ + escape +}; + + +/** @brief Манипулятор ostream, который quote-ит строки для записи в MySQL запрос. + * Внимание! Не использует функции MySQL API, а использует свой метод quote-инга, + * который может быть некорректным при использовании некоторых японских кодировок + * (multi-byte attack), а также может оказаться некорректным при изменении libmysqlclient. + * Это сделано для увеличения производительности и это имеет значение. + */ +enum quote_enum +{ + quote +}; + + +struct EscapeManipResult +{ + std::ostream & ostr; + + EscapeManipResult(std::ostream & ostr_) : ostr(ostr_) {} + + std::ostream & operator<< (bool value) { return ostr << value; } + std::ostream & operator<< (char value) { return ostr << value; } + std::ostream & operator<< (unsigned char value) { return ostr << value; } + std::ostream & operator<< (signed char value) { return ostr << value; } + std::ostream & operator<< (short value) { return ostr << value; } + std::ostream & operator<< (unsigned short value) { return ostr << value; } + std::ostream & operator<< (int value) { return ostr << value; } + std::ostream & operator<< (unsigned int value) { return ostr << value; } + std::ostream & operator<< (long value) { return ostr << value; } + std::ostream & operator<< (unsigned long value) { return ostr << value; } + std::ostream & operator<< (float value) { return ostr << value; } + std::ostream & operator<< (double value) { return ostr << value; } + std::ostream & operator<< (long long value) { return ostr << value; } + std::ostream & operator<< (unsigned long long value) { return ostr << value; } + + std::ostream & operator<< (const std::string & value) + { + writeEscapedData(value.data(), value.length()); + return ostr; + } + + + std::ostream & operator<< (const char * value) + { + while (const char * it = std::strpbrk(value, "\t\n\\")) + { + ostr.write(value, it - value); + switch (*it) + { + case '\t': + ostr.write("\\t", 2); + break; + case '\n': + ostr.write("\\n", 2); + break; + case '\\': + ostr.write("\\\\", 2); + break; + default: + ; + } + value = it + 1; + } + return ostr << value; + } + +private: + + void writeEscapedData(const char * data, size_t length) + { + size_t i = 0; + + while (true) + { + size_t remaining_length = std::strlen(data); + (*this) << data; + if (i + remaining_length == length) + break; + + ostr.write("\\0", 2); + i += remaining_length + 1; + data += remaining_length + 1; + } + } +}; + +inline EscapeManipResult operator<< (std::ostream & ostr, escape_enum manip) +{ + return EscapeManipResult(ostr); +} + + +struct QuoteManipResult +{ +public: + std::ostream & ostr; + + QuoteManipResult(std::ostream & ostr_) : ostr(ostr_) {} + + std::ostream & operator<< (bool value) { return ostr << value; } + std::ostream & operator<< (char value) { return ostr << value; } + std::ostream & operator<< (unsigned char value) { return ostr << value; } + std::ostream & operator<< (signed char value) { return ostr << value; } + std::ostream & operator<< (short value) { return ostr << value; } + std::ostream & operator<< (unsigned short value) { return ostr << value; } + std::ostream & operator<< (int value) { return ostr << value; } + std::ostream & operator<< (unsigned int value) { return ostr << value; } + std::ostream & operator<< (long value) { return ostr << value; } + std::ostream & operator<< (unsigned long value) { return ostr << value; } + std::ostream & operator<< (float value) { return ostr << value; } + std::ostream & operator<< (double value) { return ostr << value; } + std::ostream & operator<< (long long value) { return ostr << value; } + std::ostream & operator<< (unsigned long long value) { return ostr << value; } + + std::ostream & operator<< (const std::string & value) + { + ostr.put('\''); + writeEscapedData(value.data(), value.length()); + ostr.put('\''); + + return ostr; + } + + + std::ostream & operator<< (const char * value) + { + ostr.put('\''); + writeEscapedCString(value); + ostr.put('\''); + return ostr; + } + + +/* template + std::ostream & operator<< (const std::pair & pair) + { + return ostr << quote << pair.first + << ',' << quote << pair.second; + } + + + template + std::ostream & operator<< (const Poco::Tuple & tuple) + { + return ostr << quote << tuple.template get<0>() + << ',' << quote << tuple.template get<1>() + << ',' << quote << tuple.template get<2>(); + }*/ + +private: + + void writeEscapedCString(const char * value) + { + while (const char * it = std::strpbrk(value, "'\\\"")) + { + ostr.write(value, it - value); + switch (*it) + { + case '"': + ostr.write("\\\"", 2); + break; + case '\'': + ostr.write("\\'", 2); + break; + case '\\': + ostr.write("\\\\", 2); + break; + default: + ; + } + value = it + 1; + } + ostr << value; + } + + + void writeEscapedData(const char * data, size_t length) + { + size_t i = 0; + + while (true) + { + size_t remaining_length = std::strlen(data); + writeEscapedCString(data); + if (i + remaining_length == length) + break; + + ostr.write("\\0", 2); + i += remaining_length + 1; + data += remaining_length + 1; + } + } +}; + +inline QuoteManipResult operator<< (std::ostream & ostr, quote_enum manip) +{ + return QuoteManipResult(ostr); +} + +} + +#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Pool.h b/libs/libmysqlxx/include/mysqlxx/Pool.h new file mode 100644 index 00000000000..82018ece1be --- /dev/null +++ b/libs/libmysqlxx/include/mysqlxx/Pool.h @@ -0,0 +1,345 @@ +#ifndef MYSQLXX_POOL_H +#define MYSQLXX_POOL_H + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + + +namespace mysqlxx +{ + +/** @brief Пул соединений с MySQL. */ +class Pool +{ +protected: + /** @brief Информация о соединении. */ + struct Connection + { + /** @brief Конструктор. */ + Connection() + : RefCount(0) + { + } + + mysqlxx::Connection Conn; + int RefCount; + }; + +public: + /** @brief Количество соединений с MySQL, создаваемых при запуске. */ + static const int Std_DefaultConnections; + /** @brief Максимально возможное количество соедиений. */ + static const int Std_MaxConnections; + /** @brief Время, в секундах, сколько "спать", если не можем подключиться. */ + static const int SleepOnConnectFail; + + /** @brief Соединение с базой данных. */ + class Entry + { + public: + /** @brief Конструктор по-умолчанию. */ + Entry() + : Data(NULL), pool(NULL) + { + } + + /** @brief Конструктор копирования. */ + Entry(const Entry & src) + : Data(src.Data), pool(src.pool) + { + if (Data) + Data->RefCount++; + } + + /** @brief Деструктор. */ + virtual ~Entry() + { + if (Data) + Data->RefCount--; + } + + /** @brief Оператор присваивания. */ + Entry & operator= (const Entry & src) + { + pool = src.pool; + if (Data) + Data->RefCount--; + Data = src.Data; + if (Data) + Data->RefCount++; + return * this; + } + + /** @brief Оператор доступа к вложенному объекту. */ + operator mysqlxx::Connection & () + { + if (Data == NULL) + throw Poco::RuntimeException("Tried to access NULL database connection."); + ForceConnected(); + return Data->Conn; + } + + /** @brief Оператор доступа к вложенному объекту. */ + operator const mysqlxx::Connection & () const + { + if (Data == NULL) + throw Poco::RuntimeException("Tried to access NULL database connection."); + ForceConnected(); + return Data->Conn; + } + + /** @brief Оператор доступа к вложенному объекту. */ + const mysqlxx::Connection * operator->() const + { + if (Data == NULL) + throw Poco::RuntimeException("Tried to access NULL database connection."); + ForceConnected(); + return &Data->Conn; + } + + /** @brief Оператор доступа к вложенному объекту. */ + mysqlxx::Connection * operator->() + { + if (Data == NULL) + throw Poco::RuntimeException("Tried to access NULL database connection."); + ForceConnected(); + return &Data->Conn; + } + + /** @brief Конструктор */ + Entry(Pool::Connection * Conn, Pool * p) + : Data(Conn), pool(p) + { + if (Data) + Data->RefCount++; + } + + friend class Pool; + + private: + /** @brief Указатель на соединение. */ + Connection * Data; + /** @brief Указатель на пул, которому мы принадлежим. */ + Pool * pool; + + /** @brief Переподключается к базе данных в случае необходимости. */ + void ForceConnected() const + { + Poco::Util::Application & app = Poco::Util::Application::instance(); + + if (Data->Conn.ping()) + return; + + bool first = true; + do + { + if (first) + first = false; + else + ::sleep(5); + + app.logger().information("MYSQL: Reconnecting to " + pool->DBName + "@" + + pool->DBHost + ":" + Poco::NumberFormatter::format(pool->DBPort) + " as user " + pool->DBUser); + Data->Conn.connect(pool->DBName.c_str(), pool->DBHost.c_str(), pool->DBUser.c_str(), + pool->DBPass.c_str(), pool->DBPort); + } + while (!Data->Conn.ping()); + + pool->afterConnect(Data->Conn); + } + }; + + /** + * @brief Конструктор. + * @param ConfigName Имя параметра в конфигурационном файле. + * @param DefConn Количество подключений по-умолчанию + * @param MaxConn Максимальное количество подключений + * @param AllowMultiQueries Не используется. + */ + Pool(const std::string & ConfigName, int DefConn = Std_DefaultConnections, int MaxConn = Std_MaxConnections, + bool AllowMultiQueries = false, const std::string & InitConnect_ = "") + : DefaultConnections(DefConn), MaxConnections(MaxConn), InitConnect(InitConnect_), + Initialized(false), CfgName(ConfigName) + { + } + + /** @brief Деструктор. */ + ~Pool() + { + Poco::ScopedLock Locker(Lock); + + for (ConnList::iterator it = Connections.begin(); it != Connections.end(); it++) + delete static_cast(*it); + } + + /** @brief Выделяет соединение для работы. */ + Entry Get() + { + Poco::ScopedLock Locker(Lock); + + Initialize(); + for (;;) + { + for (ConnList::iterator it = Connections.begin(); it != Connections.end(); it++) + { + if ((*it)->RefCount == 0) + return Entry(*it, this); + } + + if (Connections.size() < (size_t)MaxConnections) + { + Connection * Conn = AllocConnection(); + if (Conn) + { + return Entry(Conn, this); + } + } + + Lock.unlock(); + sched_yield(); + ::sleep(SleepOnConnectFail); + Lock.lock(); + } + } + + /** @brief Возвращает имя базы данных. */ + const std::string & DatabaseName() + { + Poco::ScopedLock Locker(Lock); + + Initialize(); + return DBName; + } + +protected: + /** @brief Количество соединений с MySQL, создаваемых при запуске. */ + int DefaultConnections; + /** @brief Максимально возможное количество соедиений. */ + int MaxConnections; + /** @brief Признак возможности использования множественных запросов. */ + bool AllowMulti; + /** @brief Запрос, выполняющийся сразу после соединения с БД. Пример: "SET NAMES cp1251". */ + std::string InitConnect; + +private: + /** @brief Признак того, что мы инициализированы. */ + bool Initialized; + /** @brief Список соединений. */ + typedef std::list ConnList; + /** @brief Список соединений. */ + ConnList Connections; + /** @brief Замок для доступа к списку соединений. */ + Poco::FastMutex Lock; + /** @brief Имя раздела в конфигурационном файле. */ + std::string CfgName; + /** @brief Имя сервера базы данных. */ + std::string DBHost; + /** @brief Порт сервера базы данных. */ + int DBPort; + /** @brief Имя пользователя базы данных. */ + std::string DBUser; + /** @brief Пароль пользователя базы данных. */ + std::string DBPass; + /** @brief Имя базы данных. */ + std::string DBName; + + /** @brief Выполняет инициализацию класса, если мы еще не инициализированы. */ + inline void Initialize() + { + if (!Initialized) + { + Poco::Util::Application & app = Poco::Util::Application::instance(); + Poco::Util::LayeredConfiguration & cfg = app.config(); + + DBHost = cfg.getString(CfgName + ".host"); + DBPort = cfg.getInt(CfgName + ".port"); + DBUser = cfg.getString(CfgName + ".user"); + DBPass = cfg.getString(CfgName + ".password"); + DBName = cfg.getString(CfgName + ".db", ""); + + for (int i = 0; i < DefaultConnections; i++) + AllocConnection(); + + Initialized = true; + } + } + + /** @brief Создает новое соединение. */ + Connection * AllocConnection() + { + Poco::Util::Application & app = Poco::Util::Application::instance(); + Connection * Conn; + + Conn = new Connection(); + try + { + app.logger().information("MYSQL: Connecting to " + DBName + "@" + + DBHost + ":" + Poco::NumberFormatter::format(DBPort) + " as user " + DBUser); + Conn->Conn.connect(DBName.c_str(), DBHost.c_str(), DBUser.c_str(), DBPass.c_str(), DBPort); + } + catch (mysqlxx::ConnectionFailed & e) + { + if (e.errnum() == ER_ACCESS_DENIED_ERROR + || e.errnum() == ER_DBACCESS_DENIED_ERROR + || e.errnum() == ER_BAD_DB_ERROR) + { + app.logger().error(e.what()); + throw; + } + else + { + app.logger().error(e.what()); + delete Conn; + + if (Daemon::instance().isCancelled()) + throw Poco::Exception("Daemon is cancelled while trying to connect to MySQL server."); + + return NULL; + } + } + + afterConnect(Conn->Conn); + Connections.push_back(Conn); + return Conn; + } + + + /** @brief Действия, выполняемые после соединения. */ + void afterConnect(mysqlxx::Connection & Conn) + { + Poco::Util::Application & app = Poco::Util::Application::instance(); + + /// Всегда устанавливаем кодировку по умолчанию - UTF8! + mysqlxx::Query Q = Conn.query(); + Q << "SET NAMES utf8"; + Q.execute(); + + /// Инициализирующий запрос (например, установка другой кодировки) + if (!InitConnect.empty()) + { + mysqlxx::Query Q = Conn.query(); + Q << InitConnect; + app.logger().trace(Q.str()); + Q.execute(); + } + } +}; + +} + +#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Query.h b/libs/libmysqlxx/include/mysqlxx/Query.h index dd917cd29ff..079e7a5b5cc 100644 --- a/libs/libmysqlxx/include/mysqlxx/Query.h +++ b/libs/libmysqlxx/include/mysqlxx/Query.h @@ -21,8 +21,13 @@ public: UseQueryResult use(); StoreQueryResult store(); + std::string str() + { + return query_stream.str(); + } + template - Query & operator<< (T & x) + Query & operator<< (const T & x) { query_stream << x; return *this; diff --git a/libs/libmysqlxx/include/mysqlxx/Row.h b/libs/libmysqlxx/include/mysqlxx/Row.h index 571e300a390..96509030550 100644 --- a/libs/libmysqlxx/include/mysqlxx/Row.h +++ b/libs/libmysqlxx/include/mysqlxx/Row.h @@ -25,13 +25,13 @@ public: lengths = mysql_fetch_lengths(&res->getRes()); } - String operator[] (int n) + String operator[] (int n) const { std::cerr << lengths[0] << std::endl; return String(row[n], lengths[n]); } - String operator[] (const char * name) + String operator[] (const char * name) const { std::cerr << "???" << std::endl; unsigned n = res->getNumFields(); @@ -44,13 +44,13 @@ public: throw Exception(std::string("Unknown column ") + name); } - String at(size_t n) + String at(size_t n) const { return operator[](n); } - operator bool() { return row; } - bool operator !() { return !row; } + operator bool() const { return row; } + bool operator !() const { return !row; } private: MYSQL_ROW row; diff --git a/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h b/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h index bf994053b06..83df6d2d1c7 100644 --- a/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h +++ b/libs/libmysqlxx/include/mysqlxx/StoreQueryResult.h @@ -16,6 +16,8 @@ class StoreQueryResult : public std::vector, public ResultBase { public: StoreQueryResult(MYSQL_RES & res_, Connection & conn_); + + size_t num_rows() const { return size(); } }; } diff --git a/libs/libmysqlxx/include/mysqlxx/String.h b/libs/libmysqlxx/include/mysqlxx/String.h index 2f95eea276d..00dab3a9acd 100644 --- a/libs/libmysqlxx/include/mysqlxx/String.h +++ b/libs/libmysqlxx/include/mysqlxx/String.h @@ -160,28 +160,33 @@ public: { } - UInt64 getUInt() + bool getBool() const + { + return m_length > 0 && m_data[0] != '0'; + } + + UInt64 getUInt() const { UInt64 res; readIntText(res, m_data, m_length); return res; } - Int64 getInt() + Int64 getInt() const { Int64 res; readIntText(res, m_data, m_length); return res; } - double getDouble() + double getDouble() const { double res; readFloatText(res, m_data, m_length); return res; } - time_t getDateTime() + time_t getDateTime() const { Yandex::DateLUTSingleton & date_lut = Yandex::DateLUTSingleton::instance(); @@ -206,47 +211,45 @@ public: throw Exception("Cannot parse DateTime: " + getString()); } - std::string getString() + std::string getString() const { return std::string(m_data, m_length); } - operator UInt64() - { - return getUInt(); - } + template T get(); -/* operator Int64() - { - return checkDateTime() ? getDateTime() : getInt(); - } - - operator double() - { - return getDouble(); - } - - operator std::string() - { - return getString(); - }*/ - - const char * data() { return m_data; } - size_t length() { return m_length; } - size_t size() { return m_length; } + const char * data() const { return m_data; } + size_t length() const { return m_length; } + size_t size() const { return m_length; } private: const char * m_data; size_t m_length; - bool checkDateTime() + bool checkDateTime() const { return m_length >= 10 && m_data[4] == '-' && m_data[7] == '-'; } }; -inline std::ostream & operator<< (std::ostream & ostr, String & x) +template <> bool String::get() { return getBool(); } +template <> char String::get() { return getInt(); } +template <> signed char String::get() { return getInt(); } +template <> unsigned char String::get() { return getUInt(); } +template <> short String::get() { return getInt(); } +template <> unsigned short String::get() { return getUInt(); } +template <> int String::get() { return getInt(); } +template <> unsigned int String::get() { return getUInt(); } +template <> long String::get() { return getInt(); } +template <> unsigned long String::get() { return getUInt(); } +template <> long long String::get() { return getInt(); } +template <> unsigned long long String::get() { return getUInt(); } +template <> float String::get() { return getDouble(); } +template <> double String::get() { return getDouble(); } + + +inline std::ostream & operator<< (std::ostream & ostr, const String & x) { return ostr.write(x.data(), x.size()); } diff --git a/libs/libmysqlxx/include/mysqlxx/Transaction.h b/libs/libmysqlxx/include/mysqlxx/Transaction.h new file mode 100644 index 00000000000..77b51427509 --- /dev/null +++ b/libs/libmysqlxx/include/mysqlxx/Transaction.h @@ -0,0 +1,47 @@ +#ifndef MYSQLXX_TRANSACTION_H +#define MYSQLXX_TRANSACTION_H + +#include + +#include + + +namespace mysqlxx +{ + +class Transaction : private boost::noncopyable +{ +public: + Transaction(Connection & conn_) + : conn(conn_), finished(false) + { + conn.query("START TRANSACTION").execute(); + } + + virtual ~Transaction() + { + if (!finished) + rollback(); + } + + void commit() + { + conn.query("COMMIT").execute(); + finished = true; + } + + void rollback() + { + conn.query("ROLLBACK").execute(); + finished = true; + } + +private: + Connection & conn; + bool finished; +}; + + +} + +#endif diff --git a/libs/libmysqlxx/include/mysqlxx/Types.h b/libs/libmysqlxx/include/mysqlxx/Types.h index 057e87fdcf9..a8fa7996ff0 100644 --- a/libs/libmysqlxx/include/mysqlxx/Types.h +++ b/libs/libmysqlxx/include/mysqlxx/Types.h @@ -15,6 +15,11 @@ typedef int Int32; typedef unsigned long * MYSQL_LENGTHS; typedef MYSQL_FIELD * MYSQL_FIELDS; +/// Для совместимости с mysql++ +typedef time_t sql_datetime; +typedef time_t sql_date; +typedef char sql_char; + } #endif diff --git a/libs/libmysqlxx/include/mysqlxx/mysqlxx.h b/libs/libmysqlxx/include/mysqlxx/mysqlxx.h index 538cb0f48b3..72c0d903e12 100644 --- a/libs/libmysqlxx/include/mysqlxx/mysqlxx.h +++ b/libs/libmysqlxx/include/mysqlxx/mysqlxx.h @@ -2,6 +2,9 @@ #define MYSQLXX_H #include +#include +#include +#include /** mysqlxx - чрезвычайно простая библиотека для замены библиотеки mysql++. * diff --git a/libs/libmysqlxx/src/Connection.cpp b/libs/libmysqlxx/src/Connection.cpp index 76d63303485..d7529139820 100644 --- a/libs/libmysqlxx/src/Connection.cpp +++ b/libs/libmysqlxx/src/Connection.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -21,6 +23,20 @@ Connection::Connection( connect(db, server, user, password, port); } +Connection::Connection(const std::string & config_name) +{ + is_connected = false; + Poco::Util::LayeredConfiguration & cfg = Poco::Util::Application::instance().config(); + + std::string db = cfg.getString(config_name + ".db"); + std::string server = cfg.getString(config_name + ".host"); + std::string user = cfg.getString(config_name + ".user"); + std::string password = cfg.getString(config_name + ".password"); + unsigned port = cfg.getInt(config_name + ".port"); + + connect(db.c_str(), server.c_str(), user.c_str(), password.c_str(), port); +} + Connection::~Connection() { disconnect(); @@ -36,10 +52,10 @@ void Connection::connect(const char* db, disconnect(); if (!mysql_init(&driver)) - onError(driver); + throw ConnectionFailed(mysql_error(&driver), mysql_errno(&driver)); if (!mysql_real_connect(&driver, server, user, password, db, port, NULL, driver.client_flag)) - onError(driver); + throw ConnectionFailed(mysql_error(&driver), mysql_errno(&driver)); is_connected = true; } diff --git a/libs/libmysqlxx/src/Exception.cpp b/libs/libmysqlxx/src/Exception.cpp index e4e75c033db..6a396e082fd 100644 --- a/libs/libmysqlxx/src/Exception.cpp +++ b/libs/libmysqlxx/src/Exception.cpp @@ -6,6 +6,4 @@ namespace mysqlxx { -POCO_IMPLEMENT_EXCEPTION(Exception, Poco::Exception, "Exception"); - } diff --git a/libs/libmysqlxx/src/Query.cpp b/libs/libmysqlxx/src/Query.cpp index 9dca43bcd5a..eeba97b382e 100644 --- a/libs/libmysqlxx/src/Query.cpp +++ b/libs/libmysqlxx/src/Query.cpp @@ -24,7 +24,7 @@ void Query::execute() { std::string query_string = query_stream.str(); if (mysql_real_query(&conn.getDriver(), query_string.data(), query_string.size())) - onError(conn.getDriver()); + throw BadQuery(mysql_error(&conn.getDriver()), mysql_errno(&conn.getDriver())); } UseQueryResult Query::use()