mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-23 16:12:01 +00:00
Merge pull request #337 from yandex/mysqlxx-dont-expose-mysql-headers
Mysqlxx don't expose mysql headers
This commit is contained in:
commit
1436361c51
@ -1,10 +1,13 @@
|
|||||||
add_library (mysqlxx
|
add_library (mysqlxx
|
||||||
src/Connection.cpp
|
src/Connection.cpp
|
||||||
|
src/Exception.cpp
|
||||||
src/Query.cpp
|
src/Query.cpp
|
||||||
src/ResultBase.cpp
|
src/ResultBase.cpp
|
||||||
src/StoreQueryResult.cpp
|
src/StoreQueryResult.cpp
|
||||||
src/UseQueryResult.cpp
|
src/UseQueryResult.cpp
|
||||||
|
src/Row.cpp
|
||||||
src/Value.cpp
|
src/Value.cpp
|
||||||
|
src/Pool.cpp
|
||||||
src/PoolWithFailover.cpp
|
src/PoolWithFailover.cpp
|
||||||
|
|
||||||
include/mysqlxx/Connection.h
|
include/mysqlxx/Connection.h
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include <Poco/Util/Application.h>
|
#include <Poco/Util/Application.h>
|
||||||
@ -26,16 +27,8 @@ class LibrarySingleton : public Singleton<LibrarySingleton>
|
|||||||
{
|
{
|
||||||
friend class Singleton<LibrarySingleton>;
|
friend class Singleton<LibrarySingleton>;
|
||||||
private:
|
private:
|
||||||
LibrarySingleton()
|
LibrarySingleton();
|
||||||
{
|
~LibrarySingleton();
|
||||||
if (mysql_library_init(0, nullptr, nullptr))
|
|
||||||
throw Exception("Cannot initialize MySQL library.");
|
|
||||||
}
|
|
||||||
|
|
||||||
~LibrarySingleton()
|
|
||||||
{
|
|
||||||
mysql_library_end();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -70,11 +63,7 @@ public:
|
|||||||
/** Конструктор-помошник. Создать соединение, считав все параметры из секции config_name конфигурации.
|
/** Конструктор-помошник. Создать соединение, считав все параметры из секции config_name конфигурации.
|
||||||
* Можно использовать, если вы используете Poco::Util::Application из библиотеки Poco.
|
* Можно использовать, если вы используете Poco::Util::Application из библиотеки Poco.
|
||||||
*/
|
*/
|
||||||
Connection(const std::string & config_name)
|
Connection(const std::string & config_name);
|
||||||
{
|
|
||||||
is_connected = false;
|
|
||||||
connect(config_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Connection();
|
virtual ~Connection();
|
||||||
|
|
||||||
@ -126,7 +115,7 @@ public:
|
|||||||
MYSQL * getDriver();
|
MYSQL * getDriver();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MYSQL driver;
|
std::unique_ptr<MYSQL> driver;
|
||||||
bool is_connected;
|
bool is_connected;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <mysql.h>
|
#include <mysqlxx/Types.h>
|
||||||
|
|
||||||
#include <Poco/Exception.h>
|
#include <Poco/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
@ -48,28 +47,10 @@ struct CannotParseValue : public Exception
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
inline std::string errorMessage(MYSQL * driver)
|
std::string errorMessage(MYSQL * driver);
|
||||||
{
|
|
||||||
std::stringstream res;
|
|
||||||
res << mysql_error(driver) << " (" << driver->host << ":" << driver->port << ")";
|
|
||||||
return res.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// For internal need of library.
|
||||||
/// Для внутренних нужд библиотеки.
|
void checkError(MYSQL * driver);
|
||||||
inline void checkError(MYSQL * driver)
|
void onError(MYSQL * driver);
|
||||||
{
|
|
||||||
unsigned num = mysql_errno(driver);
|
|
||||||
|
|
||||||
if (num)
|
|
||||||
throw Exception(errorMessage(driver), num);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Для внутренних нужд библиотеки.
|
|
||||||
inline void onError(MYSQL * driver)
|
|
||||||
{
|
|
||||||
throw Exception(errorMessage(driver), mysql_errno(driver));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,9 @@
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <mysqld_error.h>
|
|
||||||
|
|
||||||
#include <Poco/Util/Application.h>
|
|
||||||
#include <Poco/Util/LayeredConfiguration.h>
|
|
||||||
#include <Poco/NumberFormatter.h>
|
|
||||||
#include <Poco/Mutex.h>
|
|
||||||
#include <Poco/Exception.h>
|
#include <Poco/Exception.h>
|
||||||
|
|
||||||
#include <common/logger_useful.h>
|
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
|
|
||||||
|
|
||||||
@ -42,10 +35,8 @@ protected:
|
|||||||
/** Информация о соединении. */
|
/** Информация о соединении. */
|
||||||
struct Connection
|
struct Connection
|
||||||
{
|
{
|
||||||
Connection() : ref_count(0) {}
|
|
||||||
|
|
||||||
mysqlxx::Connection conn;
|
mysqlxx::Connection conn;
|
||||||
int ref_count;
|
int ref_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -84,32 +75,24 @@ public:
|
|||||||
|
|
||||||
operator mysqlxx::Connection & ()
|
operator mysqlxx::Connection & ()
|
||||||
{
|
{
|
||||||
if (data == nullptr)
|
|
||||||
throw Poco::RuntimeException("Tried to access NULL database connection.");
|
|
||||||
forceConnected();
|
forceConnected();
|
||||||
return data->conn;
|
return data->conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator const mysqlxx::Connection & () const
|
operator const mysqlxx::Connection & () const
|
||||||
{
|
{
|
||||||
if (data == nullptr)
|
|
||||||
throw Poco::RuntimeException("Tried to access NULL database connection.");
|
|
||||||
forceConnected();
|
forceConnected();
|
||||||
return data->conn;
|
return data->conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mysqlxx::Connection * operator->() const
|
const mysqlxx::Connection * operator->() const
|
||||||
{
|
{
|
||||||
if (data == nullptr)
|
|
||||||
throw Poco::RuntimeException("Tried to access NULL database connection.");
|
|
||||||
forceConnected();
|
forceConnected();
|
||||||
return &data->conn;
|
return &data->conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
mysqlxx::Connection * operator->()
|
mysqlxx::Connection * operator->()
|
||||||
{
|
{
|
||||||
if (data == nullptr)
|
|
||||||
throw Poco::RuntimeException("Tried to access NULL database connection.");
|
|
||||||
forceConnected();
|
forceConnected();
|
||||||
return &data->conn;
|
return &data->conn;
|
||||||
}
|
}
|
||||||
@ -127,6 +110,7 @@ public:
|
|||||||
else
|
else
|
||||||
return "pool is null";
|
return "pool is null";
|
||||||
}
|
}
|
||||||
|
|
||||||
friend class Pool;
|
friend class Pool;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -136,33 +120,7 @@ public:
|
|||||||
Pool * pool = nullptr;
|
Pool * pool = nullptr;
|
||||||
|
|
||||||
/** Переподключается к базе данных в случае необходимости. Если не удалось - подождать и попробовать снова. */
|
/** Переподключается к базе данных в случае необходимости. Если не удалось - подождать и попробовать снова. */
|
||||||
void forceConnected() const
|
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(MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL);
|
|
||||||
|
|
||||||
app.logger().information("MYSQL: Reconnecting to " + pool->description);
|
|
||||||
data->conn.connect(
|
|
||||||
pool->db.c_str(),
|
|
||||||
pool->server.c_str(),
|
|
||||||
pool->user.c_str(),
|
|
||||||
pool->password.c_str(),
|
|
||||||
pool->port,
|
|
||||||
pool->connect_timeout,
|
|
||||||
pool->rw_timeout);
|
|
||||||
}
|
|
||||||
while (!data->conn.ping());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Переподключается к базе данных в случае необходимости. Если не удалось - вернуть false. */
|
/** Переподключается к базе данных в случае необходимости. Если не удалось - вернуть false. */
|
||||||
bool tryForceConnected() const
|
bool tryForceConnected() const
|
||||||
@ -170,22 +128,8 @@ public:
|
|||||||
return data->conn.ping();
|
return data->conn.ping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void incrementRefCount();
|
||||||
void incrementRefCount()
|
void decrementRefCount();
|
||||||
{
|
|
||||||
if (!data)
|
|
||||||
return;
|
|
||||||
++data->ref_count;
|
|
||||||
mysql_thread_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void decrementRefCount()
|
|
||||||
{
|
|
||||||
if (!data)
|
|
||||||
return;
|
|
||||||
--data->ref_count;
|
|
||||||
mysql_thread_end();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -193,10 +137,8 @@ public:
|
|||||||
unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
|
unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
|
||||||
unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
|
unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
|
||||||
const char * parent_config_name_ = nullptr)
|
const char * parent_config_name_ = nullptr)
|
||||||
: Pool{
|
: Pool{Poco::Util::Application::instance().config(), config_name,
|
||||||
Poco::Util::Application::instance().config(), config_name,
|
default_connections_, max_connections_, parent_config_name_}
|
||||||
default_connections_, max_connections_, parent_config_name_
|
|
||||||
}
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,39 +149,7 @@ public:
|
|||||||
Pool(const Poco::Util::AbstractConfiguration & cfg, const std::string & config_name,
|
Pool(const Poco::Util::AbstractConfiguration & cfg, const std::string & config_name,
|
||||||
unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
|
unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
|
||||||
unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
|
unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
|
||||||
const char * parent_config_name_ = nullptr)
|
const char * parent_config_name_ = nullptr);
|
||||||
: default_connections(default_connections_), max_connections(max_connections_)
|
|
||||||
{
|
|
||||||
server = cfg.getString(config_name + ".host");
|
|
||||||
|
|
||||||
if (parent_config_name_)
|
|
||||||
{
|
|
||||||
const std::string parent_config_name(parent_config_name_);
|
|
||||||
db = cfg.getString(config_name + ".db", cfg.getString(parent_config_name + ".db", ""));
|
|
||||||
user = cfg.has(config_name + ".user") ?
|
|
||||||
cfg.getString(config_name + ".user") : cfg.getString(parent_config_name + ".user");
|
|
||||||
password = cfg.has(config_name + ".password") ?
|
|
||||||
cfg.getString(config_name + ".password") : cfg.getString(parent_config_name + ".password");
|
|
||||||
port = cfg.has(config_name + ".port") ? cfg.getInt(config_name + ".port") :
|
|
||||||
cfg.getInt(parent_config_name + ".port");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
db = cfg.getString(config_name + ".db", "");
|
|
||||||
user = cfg.getString(config_name + ".user");
|
|
||||||
password = cfg.getString(config_name + ".password");
|
|
||||||
port = cfg.getInt(config_name + ".port");
|
|
||||||
}
|
|
||||||
|
|
||||||
connect_timeout = cfg.getInt(config_name + ".connect_timeout",
|
|
||||||
cfg.getInt("mysql_connect_timeout",
|
|
||||||
MYSQLXX_DEFAULT_TIMEOUT));
|
|
||||||
|
|
||||||
rw_timeout =
|
|
||||||
cfg.getInt(config_name + ".rw_timeout",
|
|
||||||
cfg.getInt("mysql_rw_timeout",
|
|
||||||
MYSQLXX_DEFAULT_RW_TIMEOUT));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param db_ Имя БД
|
* @param db_ Имя БД
|
||||||
@ -274,73 +184,16 @@ public:
|
|||||||
|
|
||||||
Pool & operator=(const Pool &) = delete;
|
Pool & operator=(const Pool &) = delete;
|
||||||
|
|
||||||
~Pool()
|
~Pool();
|
||||||
{
|
|
||||||
Poco::ScopedLock<Poco::FastMutex> locker(lock);
|
|
||||||
|
|
||||||
for (Connections::iterator it = connections.begin(); it != connections.end(); it++)
|
|
||||||
delete static_cast<Connection *>(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Выделяет соединение для работы. */
|
/** Выделяет соединение для работы. */
|
||||||
Entry Get()
|
Entry Get();
|
||||||
{
|
|
||||||
Poco::ScopedLock<Poco::FastMutex> locker(lock);
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
for (Connections::iterator it = connections.begin(); it != connections.end(); it++)
|
|
||||||
{
|
|
||||||
if ((*it)->ref_count == 0)
|
|
||||||
return Entry(*it, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connections.size() < static_cast<size_t>(max_connections))
|
|
||||||
{
|
|
||||||
Connection * conn = allocConnection();
|
|
||||||
if (conn)
|
|
||||||
return Entry(conn, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock.unlock();
|
|
||||||
::sleep(MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL);
|
|
||||||
lock.lock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Выделяет соединение для работы.
|
/** Выделяет соединение для работы.
|
||||||
* Если база недоступна - возвращает пустой объект Entry.
|
* Если база недоступна - возвращает пустой объект Entry.
|
||||||
* Если пул переполнен - кидает исключение.
|
* Если пул переполнен - кидает исключение.
|
||||||
*/
|
*/
|
||||||
Entry tryGet()
|
Entry tryGet();
|
||||||
{
|
|
||||||
Poco::ScopedLock<Poco::FastMutex> locker(lock);
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
/// Поиск уже установленного, но не использующегося сейчас соединения.
|
|
||||||
for (Connections::iterator it = connections.begin(); it != connections.end(); ++it)
|
|
||||||
{
|
|
||||||
if ((*it)->ref_count == 0)
|
|
||||||
{
|
|
||||||
Entry res(*it, this);
|
|
||||||
return res.tryForceConnected() ? res : Entry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Если пул переполнен.
|
|
||||||
if (connections.size() >= max_connections)
|
|
||||||
throw Poco::Exception("mysqlxx::Pool is full");
|
|
||||||
|
|
||||||
/// Выделение нового соединения.
|
|
||||||
Connection * conn = allocConnection(true);
|
|
||||||
if (conn)
|
|
||||||
return Entry(conn, this);
|
|
||||||
|
|
||||||
return Entry();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Получить описание БД
|
/// Получить описание БД
|
||||||
std::string getDescription() const
|
std::string getDescription() const
|
||||||
@ -362,7 +215,7 @@ private:
|
|||||||
/** Список соединений. */
|
/** Список соединений. */
|
||||||
Connections connections;
|
Connections connections;
|
||||||
/** Замок для доступа к списку соединений. */
|
/** Замок для доступа к списку соединений. */
|
||||||
Poco::FastMutex lock;
|
std::mutex mutex;
|
||||||
/** Описание соединения. */
|
/** Описание соединения. */
|
||||||
std::string description;
|
std::string description;
|
||||||
|
|
||||||
@ -379,61 +232,10 @@ private:
|
|||||||
bool was_successful{false};
|
bool was_successful{false};
|
||||||
|
|
||||||
/** Выполняет инициализацию класса, если мы еще не инициализированы. */
|
/** Выполняет инициализацию класса, если мы еще не инициализированы. */
|
||||||
void initialize()
|
void initialize();
|
||||||
{
|
|
||||||
if (!initialized)
|
|
||||||
{
|
|
||||||
description = db + "@" + server + ":" + Poco::NumberFormatter::format(port) + " as user " + user;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < default_connections; i++)
|
/** Create new connection. */
|
||||||
allocConnection();
|
Connection * allocConnection(bool dont_throw_if_failed_first_time = false);
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Создает новое соединение. */
|
|
||||||
Connection * allocConnection(bool dont_throw_if_failed_first_time = false)
|
|
||||||
{
|
|
||||||
Poco::Util::Application & app = Poco::Util::Application::instance();
|
|
||||||
|
|
||||||
std::unique_ptr<Connection> conn(new Connection);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
app.logger().information("MYSQL: Connecting to " + description);
|
|
||||||
|
|
||||||
conn->conn.connect(
|
|
||||||
db.c_str(),
|
|
||||||
server.c_str(),
|
|
||||||
user.c_str(),
|
|
||||||
password.c_str(),
|
|
||||||
port,
|
|
||||||
connect_timeout,
|
|
||||||
rw_timeout);
|
|
||||||
}
|
|
||||||
catch (mysqlxx::ConnectionFailed & e)
|
|
||||||
{
|
|
||||||
if ((!was_successful && !dont_throw_if_failed_first_time)
|
|
||||||
|| 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());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
was_successful = true;
|
|
||||||
auto * connection = conn.release();
|
|
||||||
connections.push_back(connection);
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include <mysqlxx/Types.h>
|
#include <mysqlxx/Types.h>
|
||||||
|
|
||||||
|
|
||||||
@ -29,10 +28,7 @@ public:
|
|||||||
MYSQL_RES * getRes() { return res; }
|
MYSQL_RES * getRes() { return res; }
|
||||||
const Query * getQuery() const { return query; }
|
const Query * getQuery() const { return query; }
|
||||||
|
|
||||||
virtual ~ResultBase()
|
virtual ~ResultBase();
|
||||||
{
|
|
||||||
mysql_free_result(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MYSQL_RES * res;
|
MYSQL_RES * res;
|
||||||
|
@ -51,18 +51,8 @@ public:
|
|||||||
return Value(row[n], lengths[n], res);
|
return Value(row[n], lengths[n], res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Получить значение по имени. Слегка менее эффективно. */
|
/** Get value by column name. Less efficient. */
|
||||||
Value operator[] (const char * name) const
|
Value operator[] (const char * name) const;
|
||||||
{
|
|
||||||
unsigned n = res->getNumFields();
|
|
||||||
MYSQL_FIELDS fields = res->getFields();
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < n; ++i)
|
|
||||||
if (!strcmp(name, fields[i].name))
|
|
||||||
return operator[](i);
|
|
||||||
|
|
||||||
throw Exception(std::string("Unknown column ") + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Value operator[] (const std::string & name) const
|
Value operator[] (const std::string & name) const
|
||||||
{
|
{
|
||||||
|
@ -1,13 +1,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <mysql.h>
|
|
||||||
#include <Poco/Types.h>
|
#include <Poco/Types.h>
|
||||||
|
|
||||||
#include <common/LocalDate.h>
|
#include <common/LocalDate.h>
|
||||||
#include <common/LocalDateTime.h>
|
#include <common/LocalDateTime.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct st_mysql;
|
||||||
|
using MYSQL = st_mysql;
|
||||||
|
|
||||||
|
struct st_mysql_res;
|
||||||
|
using MYSQL_RES = st_mysql_res;
|
||||||
|
|
||||||
|
using MYSQL_ROW = char**;
|
||||||
|
|
||||||
|
struct st_mysql_field;
|
||||||
|
using MYSQL_FIELD = st_mysql_field;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace mysqlxx
|
namespace mysqlxx
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -224,154 +224,13 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
/// Прочитать беззнаковое целое в простом формате из не-0-terminated строки.
|
/// Прочитать беззнаковое целое в простом формате из не-0-terminated строки.
|
||||||
UInt64 readUIntText(const char * buf, size_t length) const
|
UInt64 readUIntText(const char * buf, size_t length) const;
|
||||||
{
|
|
||||||
UInt64 x = 0;
|
|
||||||
const char * end = buf + length;
|
|
||||||
|
|
||||||
while (buf != end)
|
|
||||||
{
|
|
||||||
switch (*buf)
|
|
||||||
{
|
|
||||||
case '+':
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
x *= 10;
|
|
||||||
x += *buf - '0';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throwException("Cannot parse unsigned integer");
|
|
||||||
}
|
|
||||||
++buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Прочитать знаковое целое в простом формате из не-0-terminated строки.
|
/// Прочитать знаковое целое в простом формате из не-0-terminated строки.
|
||||||
Int64 readIntText(const char * buf, size_t length) const
|
Int64 readIntText(const char * buf, size_t length) const;
|
||||||
{
|
|
||||||
bool negative = false;
|
|
||||||
Int64 x = 0;
|
|
||||||
const char * end = buf + length;
|
|
||||||
|
|
||||||
while (buf != end)
|
|
||||||
{
|
|
||||||
switch (*buf)
|
|
||||||
{
|
|
||||||
case '+':
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
negative = true;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
x *= 10;
|
|
||||||
x += *buf - '0';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throwException("Cannot parse signed integer");
|
|
||||||
}
|
|
||||||
++buf;
|
|
||||||
}
|
|
||||||
if (negative)
|
|
||||||
x = -x;
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки.
|
/// Прочитать число с плавающей запятой в простом формате, с грубым округлением, из не-0-terminated строки.
|
||||||
double readFloatText(const char * buf, size_t length) const
|
double readFloatText(const char * buf, size_t length) const;
|
||||||
{
|
|
||||||
bool negative = false;
|
|
||||||
double x = 0;
|
|
||||||
bool after_point = false;
|
|
||||||
double power_of_ten = 1;
|
|
||||||
const char * end = buf + length;
|
|
||||||
|
|
||||||
while (buf != end)
|
|
||||||
{
|
|
||||||
switch (*buf)
|
|
||||||
{
|
|
||||||
case '+':
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
negative = true;
|
|
||||||
break;
|
|
||||||
case '.':
|
|
||||||
after_point = true;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
if (after_point)
|
|
||||||
{
|
|
||||||
power_of_ten /= 10;
|
|
||||||
x += (*buf - '0') * power_of_ten;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x *= 10;
|
|
||||||
x += *buf - '0';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
{
|
|
||||||
++buf;
|
|
||||||
Int32 exponent = readIntText(buf, end - buf);
|
|
||||||
x *= exp10(exponent);
|
|
||||||
if (negative)
|
|
||||||
x = -x;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
case 'i':
|
|
||||||
case 'I':
|
|
||||||
x = std::numeric_limits<double>::infinity();
|
|
||||||
if (negative)
|
|
||||||
x = -x;
|
|
||||||
return x;
|
|
||||||
case 'n':
|
|
||||||
case 'N':
|
|
||||||
x = std::numeric_limits<double>::quiet_NaN();
|
|
||||||
return x;
|
|
||||||
default:
|
|
||||||
throwException("Cannot parse floating point number");
|
|
||||||
}
|
|
||||||
++buf;
|
|
||||||
}
|
|
||||||
if (negative)
|
|
||||||
x = -x;
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Выкинуть исключение с подробной информацией
|
/// Выкинуть исключение с подробной информацией
|
||||||
void throwException(const char * text) const;
|
void throwException(const char * text) const;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <mysql.h>
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
#include <mysqlxx/Exception.h>
|
#include <mysqlxx/Exception.h>
|
||||||
|
|
||||||
@ -5,8 +6,20 @@
|
|||||||
namespace mysqlxx
|
namespace mysqlxx
|
||||||
{
|
{
|
||||||
|
|
||||||
|
LibrarySingleton::LibrarySingleton()
|
||||||
|
{
|
||||||
|
if (mysql_library_init(0, nullptr, nullptr))
|
||||||
|
throw Exception("Cannot initialize MySQL library.");
|
||||||
|
}
|
||||||
|
|
||||||
|
LibrarySingleton::~LibrarySingleton()
|
||||||
|
{
|
||||||
|
mysql_library_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Connection::Connection()
|
Connection::Connection()
|
||||||
|
: driver(std::make_unique<MYSQL>())
|
||||||
{
|
{
|
||||||
is_connected = false;
|
is_connected = false;
|
||||||
|
|
||||||
@ -22,11 +35,19 @@ Connection::Connection(
|
|||||||
unsigned port,
|
unsigned port,
|
||||||
unsigned timeout,
|
unsigned timeout,
|
||||||
unsigned rw_timeout)
|
unsigned rw_timeout)
|
||||||
|
: driver(std::make_unique<MYSQL>())
|
||||||
{
|
{
|
||||||
is_connected = false;
|
is_connected = false;
|
||||||
connect(db, server, user, password, port, timeout, rw_timeout);
|
connect(db, server, user, password, port, timeout, rw_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connection::Connection(const std::string & config_name)
|
||||||
|
: driver(std::make_unique<MYSQL>())
|
||||||
|
{
|
||||||
|
is_connected = false;
|
||||||
|
connect(config_name);
|
||||||
|
}
|
||||||
|
|
||||||
Connection::~Connection()
|
Connection::~Connection()
|
||||||
{
|
{
|
||||||
disconnect();
|
disconnect();
|
||||||
@ -47,36 +68,36 @@ void Connection::connect(const char* db,
|
|||||||
/// Инициализация библиотеки.
|
/// Инициализация библиотеки.
|
||||||
LibrarySingleton::instance();
|
LibrarySingleton::instance();
|
||||||
|
|
||||||
if (!mysql_init(&driver))
|
if (!mysql_init(driver.get()))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
/// Установим таймауты
|
/// Установим таймауты
|
||||||
if (mysql_options(&driver, MYSQL_OPT_CONNECT_TIMEOUT, reinterpret_cast<const char *>(&timeout)))
|
if (mysql_options(driver.get(), MYSQL_OPT_CONNECT_TIMEOUT, reinterpret_cast<const char *>(&timeout)))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
if (mysql_options(&driver, MYSQL_OPT_READ_TIMEOUT, reinterpret_cast<const char *>(&rw_timeout)))
|
if (mysql_options(driver.get(), MYSQL_OPT_READ_TIMEOUT, reinterpret_cast<const char *>(&rw_timeout)))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
if (mysql_options(&driver, MYSQL_OPT_WRITE_TIMEOUT, reinterpret_cast<const char *>(&rw_timeout)))
|
if (mysql_options(driver.get(), MYSQL_OPT_WRITE_TIMEOUT, reinterpret_cast<const char *>(&rw_timeout)))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
/** Включаем возможность использовать запрос LOAD DATA LOCAL INFILE с серверами,
|
/** Включаем возможность использовать запрос LOAD DATA LOCAL INFILE с серверами,
|
||||||
* которые были скомпилированы без опции --enable-local-infile.
|
* которые были скомпилированы без опции --enable-local-infile.
|
||||||
*/
|
*/
|
||||||
if (mysql_options(&driver, MYSQL_OPT_LOCAL_INFILE, nullptr))
|
if (mysql_options(driver.get(), MYSQL_OPT_LOCAL_INFILE, nullptr))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
if (!mysql_real_connect(&driver, server, user, password, db, port, nullptr, driver.client_flag))
|
if (!mysql_real_connect(driver.get(), server, user, password, db, port, nullptr, driver->client_flag))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
/// Установим кодировки по умолчанию - UTF-8.
|
/// Установим кодировки по умолчанию - UTF-8.
|
||||||
if (mysql_set_character_set(&driver, "UTF8"))
|
if (mysql_set_character_set(driver.get(), "UTF8"))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
/// Установим автоматический реконнект
|
/// Установим автоматический реконнект
|
||||||
my_bool reconnect = true;
|
my_bool reconnect = true;
|
||||||
if (mysql_options(&driver, MYSQL_OPT_RECONNECT, reinterpret_cast<const char *>(&reconnect)))
|
if (mysql_options(driver.get(), MYSQL_OPT_RECONNECT, reinterpret_cast<const char *>(&reconnect)))
|
||||||
throw ConnectionFailed(errorMessage(&driver), mysql_errno(&driver));
|
throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get()));
|
||||||
|
|
||||||
is_connected = true;
|
is_connected = true;
|
||||||
}
|
}
|
||||||
@ -91,14 +112,14 @@ void Connection::disconnect()
|
|||||||
if (!is_connected)
|
if (!is_connected)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mysql_close(&driver);
|
mysql_close(driver.get());
|
||||||
memset(&driver, 0, sizeof(driver));
|
memset(driver.get(), 0, sizeof(*driver));
|
||||||
is_connected = false;
|
is_connected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Connection::ping()
|
bool Connection::ping()
|
||||||
{
|
{
|
||||||
return is_connected && !mysql_ping(&driver);
|
return is_connected && !mysql_ping(driver.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Query Connection::query(const std::string & str)
|
Query Connection::query(const std::string & str)
|
||||||
@ -108,7 +129,7 @@ Query Connection::query(const std::string & str)
|
|||||||
|
|
||||||
MYSQL * Connection::getDriver()
|
MYSQL * Connection::getDriver()
|
||||||
{
|
{
|
||||||
return &driver;
|
return driver.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
32
libs/libmysqlxx/src/Exception.cpp
Normal file
32
libs/libmysqlxx/src/Exception.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <mysql.h>
|
||||||
|
#include <mysqlxx/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace mysqlxx
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string errorMessage(MYSQL * driver)
|
||||||
|
{
|
||||||
|
std::stringstream res;
|
||||||
|
res << mysql_error(driver) << " (" << driver->host << ":" << driver->port << ")";
|
||||||
|
return res.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Для внутренних нужд библиотеки.
|
||||||
|
void checkError(MYSQL * driver)
|
||||||
|
{
|
||||||
|
unsigned num = mysql_errno(driver);
|
||||||
|
|
||||||
|
if (num)
|
||||||
|
throw Exception(errorMessage(driver), num);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Для внутренних нужд библиотеки.
|
||||||
|
void onError(MYSQL * driver)
|
||||||
|
{
|
||||||
|
throw Exception(errorMessage(driver), mysql_errno(driver));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
223
libs/libmysqlxx/src/Pool.cpp
Normal file
223
libs/libmysqlxx/src/Pool.cpp
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#include <mysql.h>
|
||||||
|
#include <mysqld_error.h>
|
||||||
|
#include <mysqlxx/Pool.h>
|
||||||
|
|
||||||
|
#include <Poco/Util/Application.h>
|
||||||
|
#include <Poco/Util/LayeredConfiguration.h>
|
||||||
|
#include <Poco/NumberFormatter.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace mysqlxx
|
||||||
|
{
|
||||||
|
|
||||||
|
void Pool::Entry::incrementRefCount()
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
++data->ref_count;
|
||||||
|
mysql_thread_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pool::Entry::decrementRefCount()
|
||||||
|
{
|
||||||
|
if (!data)
|
||||||
|
return;
|
||||||
|
--data->ref_count;
|
||||||
|
mysql_thread_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pool::Pool(const Poco::Util::AbstractConfiguration & cfg, const std::string & config_name,
|
||||||
|
unsigned default_connections_, unsigned max_connections_,
|
||||||
|
const char * parent_config_name_)
|
||||||
|
: default_connections(default_connections_), max_connections(max_connections_)
|
||||||
|
{
|
||||||
|
server = cfg.getString(config_name + ".host");
|
||||||
|
|
||||||
|
if (parent_config_name_)
|
||||||
|
{
|
||||||
|
const std::string parent_config_name(parent_config_name_);
|
||||||
|
db = cfg.getString(config_name + ".db", cfg.getString(parent_config_name + ".db", ""));
|
||||||
|
user = cfg.has(config_name + ".user")
|
||||||
|
? cfg.getString(config_name + ".user")
|
||||||
|
: cfg.getString(parent_config_name + ".user");
|
||||||
|
password = cfg.has(config_name + ".password")
|
||||||
|
? cfg.getString(config_name + ".password")
|
||||||
|
: cfg.getString(parent_config_name + ".password");
|
||||||
|
port = cfg.has(config_name + ".port")
|
||||||
|
? cfg.getInt(config_name + ".port")
|
||||||
|
: cfg.getInt(parent_config_name + ".port");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
db = cfg.getString(config_name + ".db", "");
|
||||||
|
user = cfg.getString(config_name + ".user");
|
||||||
|
password = cfg.getString(config_name + ".password");
|
||||||
|
port = cfg.getInt(config_name + ".port");
|
||||||
|
}
|
||||||
|
|
||||||
|
connect_timeout = cfg.getInt(config_name + ".connect_timeout",
|
||||||
|
cfg.getInt("mysql_connect_timeout",
|
||||||
|
MYSQLXX_DEFAULT_TIMEOUT));
|
||||||
|
|
||||||
|
rw_timeout =
|
||||||
|
cfg.getInt(config_name + ".rw_timeout",
|
||||||
|
cfg.getInt("mysql_rw_timeout",
|
||||||
|
MYSQLXX_DEFAULT_RW_TIMEOUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pool::~Pool()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
|
||||||
|
for (Connections::iterator it = connections.begin(); it != connections.end(); it++)
|
||||||
|
delete static_cast<Connection *>(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pool::Entry Pool::Get()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
for (Connections::iterator it = connections.begin(); it != connections.end(); it++)
|
||||||
|
{
|
||||||
|
if ((*it)->ref_count == 0)
|
||||||
|
return Entry(*it, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connections.size() < static_cast<size_t>(max_connections))
|
||||||
|
{
|
||||||
|
Connection * conn = allocConnection();
|
||||||
|
if (conn)
|
||||||
|
return Entry(conn, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
::sleep(MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL);
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pool::Entry Pool::tryGet()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
/// Поиск уже установленного, но не использующегося сейчас соединения.
|
||||||
|
for (Connections::iterator it = connections.begin(); it != connections.end(); ++it)
|
||||||
|
{
|
||||||
|
if ((*it)->ref_count == 0)
|
||||||
|
{
|
||||||
|
Entry res(*it, this);
|
||||||
|
return res.tryForceConnected() ? res : Entry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Если пул переполнен.
|
||||||
|
if (connections.size() >= max_connections)
|
||||||
|
throw Poco::Exception("mysqlxx::Pool is full");
|
||||||
|
|
||||||
|
/// Выделение нового соединения.
|
||||||
|
Connection * conn = allocConnection(true);
|
||||||
|
if (conn)
|
||||||
|
return Entry(conn, this);
|
||||||
|
|
||||||
|
return Entry();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Pool::Entry::forceConnected() const
|
||||||
|
{
|
||||||
|
if (data == nullptr)
|
||||||
|
throw Poco::RuntimeException("Tried to access NULL database connection.");
|
||||||
|
|
||||||
|
Poco::Util::Application & app = Poco::Util::Application::instance();
|
||||||
|
|
||||||
|
if (data->conn.ping())
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool first = true;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
::sleep(MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL);
|
||||||
|
|
||||||
|
app.logger().information("MYSQL: Reconnecting to " + pool->description);
|
||||||
|
data->conn.connect(
|
||||||
|
pool->db.c_str(),
|
||||||
|
pool->server.c_str(),
|
||||||
|
pool->user.c_str(),
|
||||||
|
pool->password.c_str(),
|
||||||
|
pool->port,
|
||||||
|
pool->connect_timeout,
|
||||||
|
pool->rw_timeout);
|
||||||
|
}
|
||||||
|
while (!data->conn.ping());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Pool::initialize()
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
description = db + "@" + server + ":" + Poco::NumberFormatter::format(port) + " as user " + user;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < default_connections; i++)
|
||||||
|
allocConnection();
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pool::Connection * Pool::allocConnection(bool dont_throw_if_failed_first_time)
|
||||||
|
{
|
||||||
|
Poco::Util::Application & app = Poco::Util::Application::instance();
|
||||||
|
|
||||||
|
std::unique_ptr<Connection> conn(new Connection);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
app.logger().information("MYSQL: Connecting to " + description);
|
||||||
|
|
||||||
|
conn->conn.connect(
|
||||||
|
db.c_str(),
|
||||||
|
server.c_str(),
|
||||||
|
user.c_str(),
|
||||||
|
password.c_str(),
|
||||||
|
port,
|
||||||
|
connect_timeout,
|
||||||
|
rw_timeout);
|
||||||
|
}
|
||||||
|
catch (mysqlxx::ConnectionFailed & e)
|
||||||
|
{
|
||||||
|
if ((!was_successful && !dont_throw_if_failed_first_time)
|
||||||
|
|| 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());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
was_successful = true;
|
||||||
|
auto * connection = conn.release();
|
||||||
|
connections.push_back(connection);
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
#include <Poco/NumberFormatter.h>
|
||||||
#include <mysqlxx/PoolWithFailover.h>
|
#include <mysqlxx/PoolWithFailover.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace mysqlxx;
|
using namespace mysqlxx;
|
||||||
|
|
||||||
PoolWithFailover::PoolWithFailover(const Poco::Util::AbstractConfiguration & cfg,
|
PoolWithFailover::PoolWithFailover(const Poco::Util::AbstractConfiguration & cfg,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <mysql.h>
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
#include <mysqlxx/Query.h>
|
#include <mysqlxx/Query.h>
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <mysql.h>
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
#include <mysqlxx/ResultBase.h>
|
#include <mysqlxx/ResultBase.h>
|
||||||
|
|
||||||
@ -11,4 +12,9 @@ ResultBase::ResultBase(MYSQL_RES * res_, Connection * conn_, const Query * query
|
|||||||
num_fields = mysql_num_fields(res);
|
num_fields = mysql_num_fields(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResultBase::~ResultBase()
|
||||||
|
{
|
||||||
|
mysql_free_result(res);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
20
libs/libmysqlxx/src/Row.cpp
Normal file
20
libs/libmysqlxx/src/Row.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include <mysql.h>
|
||||||
|
#include <mysqlxx/Row.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace mysqlxx
|
||||||
|
{
|
||||||
|
|
||||||
|
Value Row::operator[] (const char * name) const
|
||||||
|
{
|
||||||
|
unsigned n = res->getNumFields();
|
||||||
|
MYSQL_FIELDS fields = res->getFields();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < n; ++i)
|
||||||
|
if (!strcmp(name, fields[i].name))
|
||||||
|
return operator[](i);
|
||||||
|
|
||||||
|
throw Exception(std::string("Unknown column ") + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <mysql.h>
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
#include <mysqlxx/StoreQueryResult.h>
|
#include <mysqlxx/StoreQueryResult.h>
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include <mysql.h>
|
||||||
#include <mysqlxx/Connection.h>
|
#include <mysqlxx/Connection.h>
|
||||||
#include <mysqlxx/UseQueryResult.h>
|
#include <mysqlxx/UseQueryResult.h>
|
||||||
|
|
||||||
|
@ -4,7 +4,157 @@
|
|||||||
#include <mysqlxx/Exception.h>
|
#include <mysqlxx/Exception.h>
|
||||||
|
|
||||||
|
|
||||||
void mysqlxx::Value::throwException(const char * text) const
|
namespace mysqlxx
|
||||||
|
{
|
||||||
|
|
||||||
|
UInt64 Value::readUIntText(const char * buf, size_t length) const
|
||||||
|
{
|
||||||
|
UInt64 x = 0;
|
||||||
|
const char * end = buf + length;
|
||||||
|
|
||||||
|
while (buf != end)
|
||||||
|
{
|
||||||
|
switch (*buf)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
x *= 10;
|
||||||
|
x += *buf - '0';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throwException("Cannot parse unsigned integer");
|
||||||
|
}
|
||||||
|
++buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Int64 Value::readIntText(const char * buf, size_t length) const
|
||||||
|
{
|
||||||
|
bool negative = false;
|
||||||
|
Int64 x = 0;
|
||||||
|
const char * end = buf + length;
|
||||||
|
|
||||||
|
while (buf != end)
|
||||||
|
{
|
||||||
|
switch (*buf)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
negative = true;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
x *= 10;
|
||||||
|
x += *buf - '0';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throwException("Cannot parse signed integer");
|
||||||
|
}
|
||||||
|
++buf;
|
||||||
|
}
|
||||||
|
if (negative)
|
||||||
|
x = -x;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double Value::readFloatText(const char * buf, size_t length) const
|
||||||
|
{
|
||||||
|
bool negative = false;
|
||||||
|
double x = 0;
|
||||||
|
bool after_point = false;
|
||||||
|
double power_of_ten = 1;
|
||||||
|
const char * end = buf + length;
|
||||||
|
|
||||||
|
while (buf != end)
|
||||||
|
{
|
||||||
|
switch (*buf)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
negative = true;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
after_point = true;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
if (after_point)
|
||||||
|
{
|
||||||
|
power_of_ten /= 10;
|
||||||
|
x += (*buf - '0') * power_of_ten;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x *= 10;
|
||||||
|
x += *buf - '0';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
{
|
||||||
|
++buf;
|
||||||
|
Int32 exponent = readIntText(buf, end - buf);
|
||||||
|
x *= exp10(exponent);
|
||||||
|
if (negative)
|
||||||
|
x = -x;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
case 'i':
|
||||||
|
case 'I':
|
||||||
|
x = std::numeric_limits<double>::infinity();
|
||||||
|
if (negative)
|
||||||
|
x = -x;
|
||||||
|
return x;
|
||||||
|
case 'n':
|
||||||
|
case 'N':
|
||||||
|
x = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
return x;
|
||||||
|
default:
|
||||||
|
throwException("Cannot parse floating point number");
|
||||||
|
}
|
||||||
|
++buf;
|
||||||
|
}
|
||||||
|
if (negative)
|
||||||
|
x = -x;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Value::throwException(const char * text) const
|
||||||
{
|
{
|
||||||
std::stringstream info;
|
std::stringstream info;
|
||||||
info << text;
|
info << text;
|
||||||
@ -20,3 +170,5 @@ void mysqlxx::Value::throwException(const char * text) const
|
|||||||
|
|
||||||
throw CannotParseValue(info.str());
|
throw CannotParseValue(info.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <Poco/ConsoleChannel.h>
|
#include <Poco/ConsoleChannel.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
class App : public Poco::Util::Application
|
class App : public Poco::Util::Application
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -17,8 +18,8 @@ int main()
|
|||||||
app.loadConfiguration("failover.xml");
|
app.loadConfiguration("failover.xml");
|
||||||
|
|
||||||
Poco::AutoPtr<Poco::ConsoleChannel> channel = new Poco::ConsoleChannel(std::cerr);
|
Poco::AutoPtr<Poco::ConsoleChannel> channel = new Poco::ConsoleChannel(std::cerr);
|
||||||
Logger::root().setChannel(channel);
|
Poco::Logger::root().setChannel(channel);
|
||||||
Logger::root().setLevel("trace");
|
Poco::Logger::root().setLevel("trace");
|
||||||
|
|
||||||
mysqlxx::PoolWithFailover pool("mysql_goals");
|
mysqlxx::PoolWithFailover pool("mysql_goals");
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user