Qore Programming Language 1.19.1
Loading...
Searching...
No Matches
DatasourcePool.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 DatasourcePool.h
4
5 Qore Programming Language
6
7 Copyright (C) 2003 - 2023 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORUS_DATASOURCE_POOL_H
33
34#define _QORUS_DATASOURCE_POOL_H
35
36#include <qore/Datasource.h>
37#include <qore/QoreThreadLock.h>
38#include <qore/QoreCondition.h>
39#include <qore/QoreString.h>
40#include <qore/AbstractThreadResource.h>
41
42#include "qore/intern/DatasourceStatementHelper.h"
43#include "qore/intern/QoreSQLStatement.h"
44
45#include <map>
46#include <deque>
47#include <string>
48
49typedef std::map<int, int> thread_use_t; // for marking a datasource in use
50typedef std::deque<int> free_list_t; // for the free list
51
52// class holding datasource configuration params
53class DatasourceConfig {
54protected:
55 DBIDriver* driver;
56
57 std::string user,
58 pass,
59 db,
60 encoding,
61 host;
62
63 int port;
64
65 // options
66 QoreHashNode* opts;
67 // event queue
68 Queue* q;
69 // Queue argument
70 QoreValue arg;
71
72public:
73 DLLLOCAL DatasourceConfig(DBIDriver* n_driver, const char* n_user, const char* n_pass, const char* n_db,
74 const char* n_encoding, const char* n_host, int n_port,
75 const QoreHashNode* n_opts, Queue* n_q, QoreValue n_arg) :
76 driver(n_driver), user(n_user ? n_user : ""), pass(n_pass ? n_pass : ""), db(n_db ? n_db : ""),
77 encoding(n_encoding ? n_encoding : ""), host(n_host ? n_host : ""), port(n_port),
78 opts(n_opts ? n_opts->hashRefSelf() : 0), q(n_q), arg(n_arg) {
79 }
80
81 DLLLOCAL DatasourceConfig(const DatasourceConfig& old) :
82 driver(old.driver), user(old.user), pass(old.pass), db(old.db), encoding(old.encoding), host(old.host),
83 port(old.port), opts(old.opts ? old.opts->hashRefSelf() : nullptr),
84 q(old.q ? old.q->queueRefSelf() : nullptr), arg(old.arg.refSelf()) {
85 }
86
87 DLLLOCAL ~DatasourceConfig() {
88 assert(!q);
89 assert(!arg);
90 assert(!opts);
91 }
92
93 DLLLOCAL void del(ExceptionSink* xsink) {
94 if (q) {
95 q->deref(xsink);
96#ifdef DEBUG
97 q = nullptr;
98#endif
99 }
100 arg.discard(xsink);
101#ifdef DEBUG
102 arg = QoreValue();
103#endif
104 if (opts) {
105 opts->deref(xsink);
106#ifdef DEBUG
107 opts = nullptr;
108#endif
109 }
110 }
111
112 // the first connection (opened in the DatasourcePool constructor) is passed with an xsink obj
113 // because invalid options can cause an exception to be thrown
114 DLLLOCAL Datasource* get(DatasourceStatementHelper* dsh, ExceptionSink* xsink) const;
115
116 DLLLOCAL void setQueue(Queue* n_q, QoreValue n_arg, ExceptionSink* xsink) {
117 if (q)
118 q->deref(xsink);
119 q = n_q;
120 arg.discard(xsink);
121 arg = n_arg;
122 }
123};
124
125class DatasourcePool : public AbstractThreadResource, public QoreCondition, public QoreThreadLock, public DatasourceStatementHelper {
126 friend class DatasourcePoolActionHelper;
127protected:
128 Datasource** pool;
129 int* tid_list; // list of thread IDs per pool index
130 thread_use_t tmap; // map from tids to pool index
131 free_list_t free_list;
132
133 unsigned min,
134 max,
135 cmax, // current max
136 wait_count,
137 wait_max,
138 tl_warning_ms;
139
140 int64 tl_timeout_ms,
141 stats_reqs,
142 stats_hits
143 ;
144
145 ResolvedCallReferenceNode* warning_callback;
146 QoreValue callback_arg;
147
148 DatasourceConfig config;
149
150 bool valid;
151
152#ifdef DEBUG
153 QoreThreadLocalStorage<QoreString> thread_local_storage;
154 void addSQL(const char* cmd, const QoreString* sql);
155 void resetSQL();
156#endif
157
158 DLLLOCAL Datasource* getAllocatedDS();
159 DLLLOCAL Datasource* getDSIntern(bool& new_ds, int64& wait_total, ExceptionSink* xsink);
160 DLLLOCAL Datasource* getDS(bool& new_ds, ExceptionSink* xsink);
161 DLLLOCAL void freeDS(ExceptionSink* xsink);
162 // share the code for exec() and execRaw()
163 DLLLOCAL QoreValue exec_internal(bool doBind, const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink);
164 DLLLOCAL int checkWait(int64 warn_total, ExceptionSink* xsink);
165 DLLLOCAL void init(ExceptionSink* xsink);
166
167public:
168#ifdef DEBUG
169 QoreString* getAndResetSQL();
170#endif
171
172 // min must be 1 or more, max must be greater than min
173 DLLLOCAL DatasourcePool(ExceptionSink* xsink, DBIDriver* ndsl, const char* user, const char* pass, const char* db, const char* charset, const char* hostname, unsigned mn, unsigned mx, int port = 0, const QoreHashNode* opts = 0, Queue* q = nullptr, QoreValue a = QoreValue());
174 DLLLOCAL DatasourcePool(const DatasourcePool& old, ExceptionSink* xsink);
175
176 DLLLOCAL virtual ~DatasourcePool();
177
179 DLLLOCAL virtual void deref(ExceptionSink* xsink) {
180 // if the object is obliterated (due to a constructor error in a child class or a serialization error), make sure
181 // it's destroyed properly
182 if (ROdereference()) {
183 config.del(xsink);
184 delete this;
185 }
186 }
187
188 DLLLOCAL void destructor(ExceptionSink* xsink);
189 DLLLOCAL virtual void cleanup(ExceptionSink* xsink);
190 DLLLOCAL QoreValue select(const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink);
191 DLLLOCAL QoreHashNode* selectRow(const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink);
192 DLLLOCAL QoreValue selectRows(const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink);
193 DLLLOCAL int beginTransaction(ExceptionSink* xsink);
194 DLLLOCAL QoreValue exec(const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink);
195 DLLLOCAL QoreValue execRaw(const QoreString* sql, ExceptionSink* xsink);
196 DLLLOCAL QoreHashNode* describe(const QoreString* query_str, const QoreListNode* args, ExceptionSink* xsink);
197 DLLLOCAL int commit(ExceptionSink* xsink);
198 DLLLOCAL int rollback(ExceptionSink* xsink);
199 DLLLOCAL QoreStringNode* toString();
200 DLLLOCAL unsigned getMin() const;
201 DLLLOCAL unsigned getMax() const;
202 DLLLOCAL QoreStringNode* getPendingUsername() const;
203 DLLLOCAL QoreStringNode* getPendingPassword() const;
204 DLLLOCAL QoreStringNode* getPendingDBName() const;
205 DLLLOCAL QoreStringNode* getPendingDBEncoding() const;
206 DLLLOCAL QoreStringNode* getPendingHostName() const;
207 DLLLOCAL int getPendingPort() const;
208 DLLLOCAL const QoreEncoding* getQoreEncoding() const;
209 DLLLOCAL const DBIDriver* getDriver() const {
210 return pool[0]->getDriver();
211 }
212 DLLLOCAL const char* getDriverName() const {
213 return pool[0]->getDriverName();
214 }
215
216 DLLLOCAL QoreListNode* getCapabilityList() const;
217 DLLLOCAL int getCapabilities() const;
218
219 DLLLOCAL QoreValue getServerVersion(ExceptionSink* xsink);
220 DLLLOCAL QoreValue getClientVersion(ExceptionSink* xsink) {
221 return pool[0]->getClientVersion(xsink);
222 }
223
224 DLLLOCAL QoreStringNode* getDriverRealName(ExceptionSink* xsink);
225
226 DLLLOCAL bool inTransaction();
227
228 DLLLOCAL QoreHashNode* getOptionHash() const {
229 return pool[0]->getOptionHash();
230 }
231
232 DLLLOCAL QoreValue getOption(const char* opt, ExceptionSink* xsink) {
233 return pool[0]->getOption(opt, xsink);
234 }
235
236 // functions supporting DatasourceStatementHelper
237 DLLLOCAL DatasourceStatementHelper* helperRefSelfImpl() {
238 ref();
239 return this;
240 }
241
242 // implementing DatasourceStatementHelper virtual functions
243 DLLLOCAL virtual void helperDestructorImpl(QoreSQLStatement* s, ExceptionSink* xsink) {
244 deref(xsink);
245 }
246
247 DLLLOCAL virtual Datasource* helperStartActionImpl(ExceptionSink* xsink, bool& new_transaction) {
248 return getDS(new_transaction, xsink);
249 }
250
251 DLLLOCAL virtual Datasource* helperEndActionImpl(char cmd, bool new_transaction, ExceptionSink* xsink) {
252 //printd(5, "DatasourcePool::helperEndAction() cmd: %d '%s', nt: %d\n", cmd, DAH_TEXT(cmd), new_transaction);
253 if (cmd == DAH_RELEASE) {
254 freeDS(xsink);
255 return 0;
256 }
257
258 return getAllocatedDS();
259 }
260
261 DLLLOCAL bool currentThreadInTransaction() const {
262 SafeLocker sl((QoreThreadLock*)this);
263 return tmap.find(q_gettid()) != tmap.end();
264 }
265
266 DLLLOCAL QoreHashNode* getConfigHash(ExceptionSink* xsink);
267 DLLLOCAL QoreStringNode* getConfigString(ExceptionSink* xsink);
268
269 DLLLOCAL void clearWarningCallback(ExceptionSink* xsink);
270 DLLLOCAL void setWarningCallback(int64 warning_ms, ResolvedCallReferenceNode* cb, QoreValue arg, ExceptionSink* xsink);
271 DLLLOCAL QoreHashNode* getUsageInfo() const;
272
273 DLLLOCAL void setErrorTimeout(unsigned t_ms) {
274 tl_timeout_ms = t_ms;
275 }
276
277 DLLLOCAL unsigned getErrorTimeout() const {
278 return tl_timeout_ms;
279 }
280
281 DLLLOCAL void setEventQueue(Queue* q, QoreValue arg, ExceptionSink* xsink);
282};
283
284class DatasourcePoolActionHelper {
285protected:
286 DatasourcePool& dsp;
287 ExceptionSink* xsink;
288 Datasource* ds;
289 bool new_ds;
290 char cmd;
291
292public:
293 DLLLOCAL DatasourcePoolActionHelper(DatasourcePool& n_dsp, ExceptionSink* n_xsink, char n_cmd = DAH_NOCHANGE) : dsp(n_dsp), xsink(n_xsink), new_ds(false), cmd(n_cmd) {
294 ds = dsp.getDS(new_ds, xsink);
295 }
296
297 /* release the connection if:
298 1) the connection was aborted (exception already raised)
299 2) the connection was acquired for this call, and
300 the command was NOCHANGE, meaning, leave the connection in the same state it was before the call
301 */
302 DLLLOCAL ~DatasourcePoolActionHelper();
303
304 // issue #3509: allow connections to be released if a beginTransaction() cmd fails on a new datasource
305 DLLLOCAL void releaseNew() {
306 assert(cmd == DAH_ACQUIRE);
307 if (new_ds) {
308 cmd = DAH_RELEASE;
309 }
310 }
311
312#if 0
313 DLLLOCAL void addSQL(const QoreString* sql) {
314 if (ds && !((cmd == DAH_RELEASE) || (new_ds && (cmd == DAH_NOCHANGE)) || ds->wasConnectionAborted()))
315 dsp.addSQL(cmd == DAH_NOCHANGE ? "select" : "exec", sql);
316 }
317#endif
318
319 DLLLOCAL operator bool() const { return ds; }
320
321 DLLLOCAL Datasource* operator*() const { return ds; }
322 DLLLOCAL Datasource* operator->() const { return ds; }
323};
324
325#endif
DLLLOCAL void ref() const
increments the reference count of the object
Definition: AbstractPrivateData.h:51
virtual DLLLOCAL void deref()
decrements the reference count of the object without the possibility of throwing a Qore-language exce...
Definition: AbstractPrivateData.h:65
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false,...
base class for saving data using Qore's thread resource management system
Definition: AbstractThreadResource.h:51
this class provides the internal link to the database driver for Qore's DBI layer
Definition: DBI.h:367
the base class for accessing databases in Qore through a Qore DBI driver
Definition: Datasource.h:55
DLLEXPORT QoreHashNode * getOptionHash() const
returns the valid options for this driver with descriptions and current values for the current dataso...
DLLEXPORT QoreValue getOption(const char *opt, ExceptionSink *xsink)
Returns the current value for the given option.
DLLEXPORT const DBIDriver * getDriver() const
returns the DBIDriver pointer used for this object
DLLEXPORT QoreValue getClientVersion(ExceptionSink *xsink) const
executes the "get_client_version" function of the driver, if any, and returns the result
DLLEXPORT bool wasConnectionAborted() const
returns the connection aborted status
DLLEXPORT const char * getDriverName() const
returns the name of the current Qore DBI driver
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:50
a thread condition class implementing a wrapper for pthread_cond_t
Definition: QoreCondition.h:45
defines string encoding functions in Qore
Definition: QoreEncoding.h:83
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
DLLEXPORT bool ROdereference() const
atomically decrements the reference count
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
Qore's string value type, reference counted, dynamically-allocated only.
Definition: QoreStringNode.h:50
provides access to thread-local storage
Definition: QoreThreadLocalStorage.h:44
provides a mutually-exclusive thread lock
Definition: QoreThreadLock.h:49
base class for resolved call references
Definition: CallReferenceNode.h:109
provides an exception-safe way to manage locks in Qore, only to be used on the stack,...
Definition: QoreThreadLock.h:228
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260
DLLEXPORT int q_gettid() noexcept
returns the current TID number
The main value class in Qore, designed to be passed by value.
Definition: QoreValue.h:276
DLLEXPORT void discard(ExceptionSink *xsink)
dereferences any contained AbstractQoreNode pointer and sets to 0; does not modify other values