forked from willamowius/gnugk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gksql.h
445 lines (389 loc) · 12.9 KB
/
gksql.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
/*
* gksql.h
*
* Generic interface to access SQL databases
*
* Copyright (c) 2004, Michal Zygmuntowicz
* Copyright (c) 2006-2011, Jan Willamowius
*
* This work is published under the GNU Public License version 2 (GPLv2)
* see file COPYING for details.
* We also explicitly grant the right to link this code
* with the OpenH323/H323Plus and OpenSSL library.
*
*/
#ifndef GKSQL_H
#define GKSQL_H "@(#) $Id$"
#include <list>
#include <map>
#include <vector>
#include "h323util.h"
#include "snmp.h"
#include "name.h"
#include "factory.h"
#include "config.h"
/** Abstract base class that encapsulates SQL query result.
Backend specific operations are performed by derived classes.
*/
class GkSQLResult
{
protected:
GkSQLResult(
/// true if the query failed and no result is available
bool queryError = false
) : m_numRows(0), m_numFields(0),
m_queryError(queryError) {}
public:
/// the first element of the pair is a field value and the second
/// is a field name
typedef std::vector< std::pair<PString, PString> > ResultRow;
virtual ~GkSQLResult();
/** @return
True if the query succeeded and the result is available.
Otherwise only GetErrorMessage and GetErrorCode member functions
are meaningful.
*/
bool IsValid() const { return !m_queryError; }
/** @return
Number of rows in the result set (to be fetched) for SELECT-like
query, or number of rows affected for INSERT, UPDATE or DELETE query.
*/
long GetNumRows() const { return m_numRows; }
/** @return
Number of columns in the result set rows for SELECT-like query.
*/
long GetNumFields() const { return m_numFields; }
/** @return
Backend specific error message, if the query failed.
*/
virtual PString GetErrorMessage() = 0;
/** @return
Backend specific error code, if the query failed.
*/
virtual long GetErrorCode() = 0;
/** Fetch a single row from the result set. After each row is fetched,
cursor position is moved to a next row.
@return
True if the row has been fetched, false if no more rows are available.
*/
virtual bool FetchRow(
/// array to be filled with string representations of the row fields
PStringArray& result
) = 0;
virtual bool FetchRow(
/// array to be filled with string representations of the row fields
ResultRow& result
) = 0;
private:
GkSQLResult(const GkSQLResult&);
GkSQLResult& operator=(const GkSQLResult&);
protected:
/// number of rows in the result set or rows affected by the query
long m_numRows;
/// number of columns in each row in the result set
long m_numFields;
/// true if query execution failed
bool m_queryError;
};
/** Abstract class that provides generic access to SQL backend.
Can provide a single SQL connection or maintain a pool of SQL connections.
Thread safe.
NOTE: Currently it implements only fixed SQL connections pool size,
so only minPoolSize parameter is examined.
*/
class GkSQLConnection : public NamedObject
{
public:
struct Info {
bool m_connected;
unsigned m_idleConnections;
unsigned m_busyConnections;
unsigned m_waitingRequests;
int m_minPoolSize;
int m_maxPoolSize;
};
GkSQLConnection(
/// name to use in the log
const char* name = "SQL"
);
virtual ~GkSQLConnection();
/** Create an instance of a GkSQLConnection derived class,
that implements access to a specific SQL backend. The class to be created
is found by searching a global factory list for #driverName# entry.
*/
static GkSQLConnection* Create(
/// driver name for the connection object
const char* driverName,
/// name for the connection to use in the log file
const char* connectionName
);
/** Read SQL settings from the config and connect to the database.
Derived classes do not have to override this member function.
@return
True if settings have been read and connections have been established.
*/
virtual bool Initialize(
/// config to be read
PConfig* cfg,
/// name of the config section with SQL settings
const char* cfgSectionName
);
/** Execute the query and return the result set. It uses first idle SQL
connection or waits for an idle SQL connection, if all connections
are busy with query execution. Pool size defines how many concurrent
queries can be executed by this object.
The query can be parametrized and the parameters are replaced with
strings from the #queryParams# list. Usage:
SELECT name, surname FROM people WHERE name = '%1' and age > %2
Use double %% to embed % and %{1} notation to allow strings like %{1}123.
@return
Query execution result (no matters the query failed or succeeded)
or NULL if timed out waiting for an idle SQL connection.
*/
GkSQLResult* ExecuteQuery(
/// query to be executed
const char* queryStr,
/// query parameters (%1, %2, ... notation), NULL if the query
/// does not take any parameters
const PStringArray* queryParams = NULL,
/// time (ms) to wait for an idle connection, -1 means infinite
long timeout = -1
);
/** Execute the query and return the result set. It uses first idle SQL
connection or waits for an idle SQL connection, if all connections
are busy with query execution. Pool size defines how many concurrent
queries can be executed by this object.
The query can be parametrized and the parameters are replaced with
strings from the #queryParams# list. Usage:
SELECT name, surname FROM people WHERE name = '%{Name}' and age > %a
The parameter names can be a one letter (%a, for example) or whole
strings (%{Name}, for example).
@return
Query execution result (no matters the query failed or succeeded)
or NULL if timed out waiting for an idle SQL connection.
*/
GkSQLResult* ExecuteQuery(
/// query to be executed
const char* queryStr,
/// query parameters (name => value associations)
const std::map<PString, PString>& queryParams,
/// time (ms) to wait for an idle connection, -1 means infinite
long timeout = -1
);
/// Get information about SQL connection state
void GetInfo(
Info &info /// filled with SQL connection state information upon return
);
protected:
/** Generic SQL database connection object - should be extended
by derived classes to include backed specific connection data.
*/
class SQLConnWrapper
{
public:
SQLConnWrapper(
/// unique identifier for this connection
int id,
/// host:port this connection is made to
const PString& host
) : m_id(id), m_host(host) {}
virtual ~SQLConnWrapper();
private:
SQLConnWrapper();
SQLConnWrapper(const SQLConnWrapper&);
SQLConnWrapper& operator=(const SQLConnWrapper&);
public:
/// unique identifier for this connection
int m_id;
/// host:port this connection is made to
PString m_host;
};
typedef SQLConnWrapper* SQLConnPtr;
protected:
/** Create a new SQL connection using parameters stored in this object.
When the connection is to be closed, the object is simply deleted
using delete operator.
@return
NULL if database connection could not be established
or an object derived from SQLConnWrapper class.
*/
virtual SQLConnPtr CreateNewConnection(
/// unique identifier for this connection
int id
) = 0;
/** Get the first idle connection from the pool and set connptr variable
to point to this connection. IMPORTANT: After connection is successfully
acquired, ReleaseSQLConnection has to be called to return back
the connection to the pool.
@return
True if the connection has been acquired, false if it is not available
(timeout, network connection lost, ...).
*/
bool AcquireSQLConnection(
SQLConnPtr& connptr, /// variable to hold connection pointer (handle)
long timeout /// timeout (ms) to wait for an idle connection
);
/** Return previously acquired connection back to the pool. It is important
that the reference references the same variable as when calling
AcquireSQLConnection.
*/
void ReleaseSQLConnection(
SQLConnPtr& connptr, /// connection to release (mark as idle)
bool deleteFromPool = false /// true to delete the connection (a broken connection)
);
/** Execute the query using specified SQL connection.
@return
Query execution result.
*/
virtual GkSQLResult* ExecuteQuery(
/// SQL connection to use for query execution
SQLConnPtr conn,
/// query string
const char* queryStr,
/// maximum time (ms) for the query execution, -1 means infinite
long timeout = -1
) = 0;
/** Replace query parameters placeholders (%1, %2, ...) with actual values
and escape parameter strings.
Derived classes do not need to override this function, unless want to
perform some custom parameter processing.
@return
New query string with all parameters replaced.
*/
virtual PString ReplaceQueryParams(
/// SQL connection to get escape parameters from
SQLConnPtr conn,
/// parametrized query string
const char* queryStr,
/// parameter values
const PStringArray& queryParams
);
/** Replace query parameters placeholders (%a, %{Name}, ...) with
actual values and escape parameter strings. Derived classes do not need
to override this function, unless want to perform some custom parameter
processing.
@return
New query string with all parameters replaced.
*/
virtual PString ReplaceQueryParams(
/// SQL connection to get escape parameters from
SQLConnPtr conn,
/// parametrized query string
const char* queryStr,
/// parameter name => value associations
const std::map<PString, PString>& queryParams
);
/** Escape any special characters in the string, so it can be used in a SQL query.
@return
Escaped string.
*/
virtual PString EscapeString(
/// SQL connection to get escaping parameters from
SQLConnPtr conn,
/// string to be escaped
const char* str
) = 0;
/// Retrieve hostname (IP or DNS) and optional port number (separated by ':') from the string
void GetHostAndPort(
/// string to be examined
const PString& str,
/// set to the host name
PString& host,
/// set to the port number or 0, if no port is given
WORD& port
) const;
private:
GkSQLConnection(const GkSQLConnection&);
GkSQLConnection& operator=(const GkSQLConnection&);
/** Creates m_minPoolSize initial database connections.
Called from Initialize.
@return
True if at least one database connection has been established.
*/
bool Connect();
protected:
/** Disconnect connection pool from DB on connection error
*/
void Disconnect();
/// filled with the actual host from m_hosts the database connection is made to
PString m_host;
/// database port to connect to
WORD m_port;
/// database name
PString m_database;
/// database username to connect as
PString m_username;
/// password associated with the username (if any)
PString m_password;
/// name of shared library
PString m_library;
/// connect timeout
unsigned m_connectTimeout;
/// read timeout
unsigned m_readTimeout;
private:
/// iterator typedefs for convenience
typedef std::list<SQLConnPtr>::iterator iterator;
typedef std::list<SQLConnPtr>::const_iterator const_iterator;
typedef std::list<SQLConnPtr*>::iterator witerator;
typedef std::list<SQLConnPtr*>::const_iterator const_witerator;
/// minimum number of SQL connections active
int m_minPoolSize;
/// maximum number of SQL connections active
int m_maxPoolSize;
/// list of idle SQL connections
std::list<SQLConnPtr> m_idleConnections;
/// list of connections busy with query execution
std::list<SQLConnPtr> m_busyConnections;
/// FIFO queue of queries waiting to be executed when there is no idle connections
std::list<SQLConnPtr*> m_waitingRequests;
/// mutual access to the lists
PTimedMutex m_connectionsMutex;
/// signalled when a connections moves from the busy to the idle list
PSyncPoint m_connectionAvailable;
/// set to true when destructor is being invoked
bool m_destroying;
/// remain false while connection to the database not yet established
/// reset to false on disconnect or error during operation -> reconnect
bool m_connected;
};
typedef Factory<GkSQLConnection>::Creator1<const char*> SQLCreator1;
template<class SQLDriver>
struct GkSQLCreator : public SQLCreator1
{
GkSQLCreator(
const char* name
) : SQLCreator1(name) {}
virtual GkSQLConnection* operator()(
const char* connectionName
) const { return new SQLDriver(connectionName); }
};
inline void GkSQLConnection::GetHostAndPort(const PString & str, PString & host, WORD & port) const
{
if (IsIPv4Address(str) || (str.Left(1) != "[")) {
// IPv4 or IPv6 without bracket and port or DNS name
const PINDEX i = str.Find(':');
if (i == P_MAX_INDEX) {
host = str;
port = 0;
} else {
host = str.Left(i);
port = (WORD)(str.Mid(i+1).AsUnsigned());
}
} else {
const PINDEX j = str.Find("]:");
if (j != P_MAX_INDEX) {
// IPv6 address with port
host = str.Left(j+1);
port = (WORD)(str.Mid(j+2).AsUnsigned());
} else {
host = str;
port = 0;
}
if (host.Left(1) == "[")
host = host.Mid(1);
if (host.Right(1) == "]")
host = host.Left(host.GetLength() - 1);
}
}
#endif /* GKSQL_H */