1#pragma once
2
3#include <list>
4#include <memory>
5#include <mutex>
6
7#include <Poco/Exception.h>
8#include <mysqlxx/Connection.h>
9
10
11#define MYSQLXX_POOL_DEFAULT_START_CONNECTIONS 1
12#define MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS 16
13#define MYSQLXX_POOL_SLEEP_ON_CONNECT_FAIL 1
14
15
16namespace mysqlxx
17{
18
19/** MySQL connections pool.
20 * This class is poorly connected with mysqlxx and is made in different style (was taken from old code).
21 * Usage:
22 * mysqlxx::Pool pool("mysql_params");
23 *
24 * void thread()
25 * {
26 * mysqlxx::Pool::Entry connection = pool.Get();
27 * std::string s = connection->query("SELECT 'Hello, world!' AS world").use().fetch()["world"].getString();
28 * }
29 * TODO: simplify with PoolBase.
30 */
31class Pool final
32{
33protected:
34 /// Information about connection.
35 struct Connection
36 {
37 mysqlxx::Connection conn;
38 int ref_count = 0;
39 };
40
41public:
42 /// Connection with database.
43 class Entry
44 {
45 public:
46 Entry() {}
47
48 Entry(const Entry & src)
49 : data(src.data), pool(src.pool)
50 {
51 incrementRefCount();
52 }
53
54 ~Entry()
55 {
56 decrementRefCount();
57 }
58
59 Entry & operator= (const Entry & src)
60 {
61 pool = src.pool;
62 if (data)
63 decrementRefCount();
64 data = src.data;
65 if (data)
66 incrementRefCount();
67 return * this;
68 }
69
70 bool isNull() const
71 {
72 return data == nullptr;
73 }
74
75 operator mysqlxx::Connection & () &
76 {
77 forceConnected();
78 return data->conn;
79 }
80
81 operator const mysqlxx::Connection & () const &
82 {
83 forceConnected();
84 return data->conn;
85 }
86
87 const mysqlxx::Connection * operator->() const &
88 {
89 forceConnected();
90 return &data->conn;
91 }
92
93 mysqlxx::Connection * operator->() &
94 {
95 forceConnected();
96 return &data->conn;
97 }
98
99 Entry(Pool::Connection * conn, Pool * p)
100 : data(conn), pool(p)
101 {
102 incrementRefCount();
103 }
104
105 std::string getDescription() const
106 {
107 if (pool)
108 return pool->getDescription();
109 else
110 return "pool is null";
111 }
112
113 void disconnect();
114
115 friend class Pool;
116
117 private:
118 /// Pointer to mysqlxx connection.
119 Connection * data = nullptr;
120 /// Pointer to pool we are belonging to.
121 Pool * pool = nullptr;
122
123 /// Connects to database. If connection is failed then waits and repeats again.
124 void forceConnected() const;
125
126 /// Connects to database. If connection is failed then returns false.
127 bool tryForceConnected() const
128 {
129 return data->conn.ping();
130 }
131
132 void incrementRefCount();
133 void decrementRefCount();
134 };
135
136
137 Pool(const std::string & config_name,
138 unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
139 unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
140 const char * parent_config_name_ = nullptr)
141 : Pool{Poco::Util::Application::instance().config(), config_name,
142 default_connections_, max_connections_, parent_config_name_}
143 {}
144
145 /**
146 * @param config_name Setting name in configuration file
147 * @param default_connections_ Number of default connections
148 * @param max_connections_ Maximum number of connections
149 */
150 Pool(const Poco::Util::AbstractConfiguration & cfg, const std::string & config_name,
151 unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
152 unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
153 const char * parent_config_name_ = nullptr);
154
155 /** Like with mysqlxx::Connection, either port either socket should be specified.
156 * If server is localhost and socket is not empty, than socket is used. Otherwise, server and port is used.
157 */
158 Pool(const std::string & db_,
159 const std::string & server_,
160 const std::string & user_ = "",
161 const std::string & password_ = "",
162 unsigned port_ = 0,
163 const std::string & socket_ = "",
164 unsigned connect_timeout_ = MYSQLXX_DEFAULT_TIMEOUT,
165 unsigned rw_timeout_ = MYSQLXX_DEFAULT_RW_TIMEOUT,
166 unsigned default_connections_ = MYSQLXX_POOL_DEFAULT_START_CONNECTIONS,
167 unsigned max_connections_ = MYSQLXX_POOL_DEFAULT_MAX_CONNECTIONS,
168 unsigned enable_local_infile_ = MYSQLXX_DEFAULT_ENABLE_LOCAL_INFILE)
169 : default_connections(default_connections_), max_connections(max_connections_),
170 db(db_), server(server_), user(user_), password(password_), port(port_), socket(socket_),
171 connect_timeout(connect_timeout_), rw_timeout(rw_timeout_), enable_local_infile(enable_local_infile_) {}
172
173 Pool(const Pool & other)
174 : default_connections{other.default_connections},
175 max_connections{other.max_connections},
176 db{other.db}, server{other.server},
177 user{other.user}, password{other.password},
178 port{other.port}, socket{other.socket},
179 connect_timeout{other.connect_timeout}, rw_timeout{other.rw_timeout},
180 enable_local_infile{other.enable_local_infile}
181 {}
182
183 Pool & operator=(const Pool &) = delete;
184
185 ~Pool();
186
187 /// Allocates connection.
188 Entry Get();
189
190 /// Allocates connection.
191 /// If database is not accessible, returns empty Entry object.
192 /// If pool is overflowed, throws exception.
193 Entry tryGet();
194
195 /// Get description of database.
196 std::string getDescription() const
197 {
198 return description;
199 }
200
201protected:
202 /// Number of MySQL connections which are created at launch.
203 unsigned default_connections;
204 /// Maximum possible number of connections
205 unsigned max_connections;
206
207private:
208 /// Initialization flag.
209 bool initialized{false};
210 /// List of connections.
211 using Connections = std::list<Connection *>;
212 /// List of connections.
213 Connections connections;
214 /// Lock for connections list access
215 std::mutex mutex;
216 /// Description of connection.
217 std::string description;
218
219 /// Connection settings.
220 std::string db;
221 std::string server;
222 std::string user;
223 std::string password;
224 unsigned port;
225 std::string socket;
226 unsigned connect_timeout;
227 unsigned rw_timeout;
228 std::string ssl_ca;
229 std::string ssl_cert;
230 std::string ssl_key;
231 bool enable_local_infile;
232
233 /// True if connection was established at least once.
234 bool was_successful{false};
235
236 /// Initialises class if it wasn't.
237 void initialize();
238
239 /** Create new connection. */
240 Connection * allocConnection(bool dont_throw_if_failed_first_time = false);
241};
242
243}
244