Qore Programming Language  1.12.0
DatasourcePool.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  DatasourcePool.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 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 
49 typedef std::map<int, int> thread_use_t; // for marking a datasource in use
50 typedef std::deque<int> free_list_t; // for the free list
51 
52 // class holding datasource configuration params
53 class DatasourceConfig {
54 protected:
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 
72 public:
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 
125 class DatasourcePool : public AbstractThreadResource, public QoreCondition, public QoreThreadLock, public DatasourceStatementHelper {
126  friend class DatasourcePoolActionHelper;
127 protected:
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 
167 public:
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 bool inTransaction();
225 
226  DLLLOCAL QoreHashNode* getOptionHash() const {
227  return pool[0]->getOptionHash();
228  }
229 
230  DLLLOCAL QoreValue getOption(const char* opt, ExceptionSink* xsink) {
231  return pool[0]->getOption(opt, xsink);
232  }
233 
234  // functions supporting DatasourceStatementHelper
235  DLLLOCAL DatasourceStatementHelper* helperRefSelfImpl() {
236  ref();
237  return this;
238  }
239 
240  // implementing DatasourceStatementHelper virtual functions
241  DLLLOCAL virtual void helperDestructorImpl(QoreSQLStatement* s, ExceptionSink* xsink) {
242  deref(xsink);
243  }
244 
245  DLLLOCAL virtual Datasource* helperStartActionImpl(ExceptionSink* xsink, bool& new_transaction) {
246  return getDS(new_transaction, xsink);
247  }
248 
249  DLLLOCAL virtual Datasource* helperEndActionImpl(char cmd, bool new_transaction, ExceptionSink* xsink) {
250  //printd(5, "DatasourcePool::helperEndAction() cmd: %d '%s', nt: %d\n", cmd, DAH_TEXT(cmd), new_transaction);
251  if (cmd == DAH_RELEASE) {
252  freeDS(xsink);
253  return 0;
254  }
255 
256  return getAllocatedDS();
257  }
258 
259  DLLLOCAL bool currentThreadInTransaction() const {
260  SafeLocker sl((QoreThreadLock*)this);
261  return tmap.find(q_gettid()) != tmap.end();
262  }
263 
264  DLLLOCAL QoreHashNode* getConfigHash(ExceptionSink* xsink);
265  DLLLOCAL QoreStringNode* getConfigString(ExceptionSink* xsink);
266 
267  DLLLOCAL void clearWarningCallback(ExceptionSink* xsink);
268  DLLLOCAL void setWarningCallback(int64 warning_ms, ResolvedCallReferenceNode* cb, QoreValue arg, ExceptionSink* xsink);
269  DLLLOCAL QoreHashNode* getUsageInfo() const;
270 
271  DLLLOCAL void setErrorTimeout(unsigned t_ms) {
272  tl_timeout_ms = t_ms;
273  }
274 
275  DLLLOCAL unsigned getErrorTimeout() const {
276  return tl_timeout_ms;
277  }
278 
279  DLLLOCAL void setEventQueue(Queue* q, QoreValue arg, ExceptionSink* xsink);
280 };
281 
282 class DatasourcePoolActionHelper {
283 protected:
284  DatasourcePool& dsp;
285  ExceptionSink* xsink;
286  Datasource* ds;
287  bool new_ds;
288  char cmd;
289 
290 public:
291  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) {
292  ds = dsp.getDS(new_ds, xsink);
293  }
294 
295  /* release the connection if:
296  1) the connection was aborted (exception already raised)
297  2) the connection was acquired for this call, and
298  the command was NOCHANGE, meaning, leave the connection in the same state it was before the call
299  */
300  DLLLOCAL ~DatasourcePoolActionHelper();
301 
302  // issue #3509: allow connections to be released if a beginTransaction() cmd fails on a new datasource
303  DLLLOCAL void releaseNew() {
304  assert(cmd == DAH_ACQUIRE);
305  if (new_ds) {
306  cmd = DAH_RELEASE;
307  }
308  }
309 
310 #if 0
311  DLLLOCAL void addSQL(const QoreString* sql) {
312  if (ds && !((cmd == DAH_RELEASE) || (new_ds && (cmd == DAH_NOCHANGE)) || ds->wasConnectionAborted()))
313  dsp.addSQL(cmd == DAH_NOCHANGE ? "select" : "exec", sql);
314  }
315 #endif
316 
317  DLLLOCAL operator bool() const { return ds; }
318 
319  DLLLOCAL Datasource* operator*() const { return ds; }
320  DLLLOCAL Datasource* operator->() const { return ds; }
321 };
322 
323 #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:354
the base class for accessing databases in Qore through a Qore DBI driver
Definition: Datasource.h:55
DLLEXPORT QoreValue getOption(const char *opt, ExceptionSink *xsink)
Returns the current value for the given option.
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 DBI driver
DLLEXPORT QoreHashNode * getOptionHash() const
returns the valid options for this driver with descriptions and current values for the current dataso...
DLLEXPORT const DBIDriver * getDriver() const
returns the DBIDriver pointer used for this object
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
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:275
DLLEXPORT void discard(ExceptionSink *xsink)
dereferences any contained AbstractQoreNode pointer and sets to 0; does not modify other values