Qore Programming Language  1.12.0
qore_dbi_private.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  qore_dbi_private.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 _QORE_QORE_DBI_PRIVATE_H
33 #define _QORE_QORE_DBI_PRIVATE_H
34 
35 #include <map>
36 
37 // internal DBI definitions
38 typedef std::map<int, void*> dbi_method_list_t;
39 hashdecl DbiOptInfo {
40  const char* desc = nullptr;
41  const QoreTypeInfo *typeInfo = nullptr;
42 
43  DLLLOCAL DbiOptInfo() {
44  }
45 
46  DLLLOCAL DbiOptInfo(const char* d, const QoreTypeInfo* t) : desc(d), typeInfo(t) {
47  }
48 };
49 typedef std::map<const char*, DbiOptInfo, ltcstrcase> dbi_opt_map_t;
50 
51 hashdecl dbi_driver_stmt {
52  q_dbi_stmt_prepare_t prepare = nullptr;
53  q_dbi_stmt_prepare_raw_t prepare_raw = nullptr;
54  q_dbi_stmt_bind_t bind = nullptr,
55  bind_placeholders = nullptr,
56  bind_values = nullptr;
57  q_dbi_stmt_exec_t exec = nullptr,
58  exec_describe = nullptr;
59  q_dbi_stmt_fetch_row_t fetch_row = nullptr;
60  q_dbi_stmt_fetch_rows_t fetch_rows = nullptr;
61  q_dbi_stmt_fetch_columns_t fetch_columns = nullptr;
62  q_dbi_stmt_fetch_row_t describe = nullptr;
63  q_dbi_stmt_next_t next = nullptr;
64  q_dbi_stmt_define_t define = nullptr;
65  q_dbi_stmt_close_t close = nullptr,
66  free = nullptr;
67  q_dbi_stmt_affected_rows_t affected_rows = nullptr;
68  q_dbi_stmt_get_output_t get_output = nullptr;
69  q_dbi_stmt_get_output_rows_t get_output_rows = nullptr;
70 
71  DLLLOCAL dbi_driver_stmt() {
72  }
73 };
74 
75 hashdecl dbi_driver_opt {
76  q_dbi_option_set_t set = nullptr;
77  q_dbi_option_get_t get = nullptr;
78 
79  DLLLOCAL dbi_driver_opt() {
80  }
81 };
82 
83 hashdecl DBIDriverFunctions {
84  q_dbi_open_t open = nullptr;
85  q_dbi_close_t close = nullptr;
86  q_dbi_select_t select = nullptr;
87  q_dbi_select_rows_t selectRows = nullptr;
88  q_dbi_select_row_t selectRow = nullptr;
89  q_dbi_exec_t execSQL = nullptr;
90  q_dbi_execraw_t execRawSQL = nullptr;
91  q_dbi_describe_t describe = nullptr;
92  q_dbi_commit_t commit = nullptr;
93  q_dbi_rollback_t rollback = nullptr;
94  q_dbi_begin_transaction_t begin_transaction = nullptr; // for DBI drivers that require explicit transaction starts
95  q_dbi_get_server_version_t get_server_version = nullptr;
96  q_dbi_get_client_version_t get_client_version = nullptr;
97 
98  dbi_driver_stmt stmt;
99  dbi_driver_opt opt;
100 
101  DLLLOCAL DBIDriverFunctions() {
102  }
103 };
104 
105 // helper class that will edit argument lists and convert number values to floats if the driver does not support the "number" type (QoreNumberNode)
106 class DbiArgHelper {
107 protected:
108  const QoreListNode* orig;
109  QoreListNode* nl;
110  ExceptionSink* xsink;
111 
112 public:
113  DLLLOCAL DbiArgHelper(const QoreListNode* ol, bool numeric, ExceptionSink* xs);
114 
115  DLLLOCAL ~DbiArgHelper() {
116  if (nl)
117  nl->deref(xsink);
118  }
119 
120  DLLLOCAL const QoreListNode* get() const {
121  return nl ? nl : orig;
122  }
123 
124  DLLLOCAL const QoreListNode* operator*() const {
125  return nl ? nl : orig;
126  }
127 };
128 
129 hashdecl OptInputHelper {
130  ExceptionSink* xsink;
131  QoreValue val;
132  bool tmp;
133 
134  DLLLOCAL OptInputHelper(ExceptionSink* xs, const qore_dbi_private& driver, const char* opt, bool set = false, const QoreValue v = QoreValue());
135 
136  DLLLOCAL ~OptInputHelper() {
137  if (tmp)
138  val.discard(xsink);
139  }
140 
141  DLLLOCAL operator bool() const {
142  assert(xsink);
143  return !*xsink;
144  }
145 };
146 
147 hashdecl qore_dbi_private {
148  const char* name;
149  int caps;
150  DBIDriverFunctions f;
151  dbi_opt_map_t omap;
152 
153  DLLLOCAL qore_dbi_private(const char* nme, const qore_dbi_mlist_private& methods, int cps);
154 
155  DLLLOCAL bool hasStatementAPI() const {
156  return caps & DBI_CAP_HAS_STATEMENT;
157  }
158 
159  DLLLOCAL int init(Datasource* ds, ExceptionSink* xsink) const {
160  assert(xsink);
161  int rc = f.open(ds, xsink);
162  assert((!rc && !*xsink) || (rc && *xsink));
163  // set option if init was successful
164  if (!rc && f.opt.set) {
166  while (hi.next()) {
167  f.opt.set(ds, hi.getKey(), hi.get(), xsink);
168  }
169  }
170  return rc;
171  }
172 
173  DLLLOCAL int close(Datasource* ds) const {
174  return f.close(ds);
175  }
176 
177  DLLLOCAL QoreValue select(Datasource* ds, const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink) const {
178  DbiArgHelper dargs(args, (caps & DBI_CAP_HAS_NUMBER_SUPPORT), xsink);
179  return f.select(ds, sql, *dargs, xsink);
180  }
181 
182  DLLLOCAL QoreValue selectRows(Datasource* ds, const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink) const {
183  DbiArgHelper dargs(args, (caps & DBI_CAP_HAS_NUMBER_SUPPORT), xsink);
184  return f.selectRows(ds, sql, *dargs, xsink);
185  }
186 
187  DLLLOCAL QoreHashNode* selectRow(Datasource* ds, const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink) const {
188  DbiArgHelper dargs(args, (caps & DBI_CAP_HAS_NUMBER_SUPPORT), xsink);
189 
190  if (f.selectRow) {
191  return f.selectRow(ds, sql, *dargs, xsink);
192  }
193 
194  ValueHolder res(f.selectRows(ds, sql, *dargs, xsink), xsink);
195  if (!res) {
196  return nullptr;
197  }
198 
199  if (res->getType() != NT_HASH) {
200  assert(res->getType() == NT_LIST);
201  assert(res->getInternalNode()->reference_count() == 1);
202  QoreListNode* l = res->get<QoreListNode>();
203  assert(l->size() <= 1);
204  QoreValue n = l->shift();
205  assert(n.isNothing() || n.getType() == NT_HASH);
206  return n.get<QoreHashNode>();
207  }
208 
209  return res.release().get<QoreHashNode>();
210  }
211 
212  DLLLOCAL QoreValue execSQL(Datasource* ds, const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink) const {
213  DbiArgHelper dargs(args, (caps & DBI_CAP_HAS_NUMBER_SUPPORT), xsink);
214  return f.execSQL(ds, sql, *dargs, xsink);
215  }
216 
217  DLLLOCAL QoreValue execRawSQL(Datasource* ds, const QoreString* sql, ExceptionSink* xsink) const {
218  if (!f.execRawSQL) {
219  xsink->raiseException("DBI-EXEC-RAW-SQL-ERROR", "this driver does not implement the Datasource::execRawSQL() method");
220  return QoreValue();
221  }
222  return f.execRawSQL(ds, sql, xsink);
223  }
224 
225  DLLLOCAL QoreHashNode* describe(Datasource* ds, const QoreString* sql, const QoreListNode* args, ExceptionSink* xsink) {
226  if (!f.describe) {
227  xsink->raiseException("DBI-DESCRIBE-ERROR", "this driver does not implement the Datasource::describe() method");
228  return nullptr;
229  }
230  DbiArgHelper dargs(args, (caps & DBI_CAP_HAS_NUMBER_SUPPORT), xsink);
231  return f.describe(ds, sql, *dargs, xsink);
232  }
233 
234  DLLLOCAL int commit(Datasource* ds, ExceptionSink* xsink) const {
235  return f.commit(ds, xsink);
236  }
237 
238  DLLLOCAL int rollback(Datasource* ds, ExceptionSink* xsink) const {
239  return f.rollback(ds, xsink);
240  }
241 
242  DLLLOCAL int beginTransaction(Datasource* ds, ExceptionSink* xsink) const {
243  if (f.begin_transaction)
244  return f.begin_transaction(ds, xsink);
245  return 0; // 0 = OK
246  }
247 
248  DLLLOCAL int autoCommit(Datasource* ds, ExceptionSink* xsink) const {
249  // if the driver does not require explicit "begin" statements to
250  // start a transaction, then we have to explicitly call "commit" here
251  if (!f.begin_transaction)
252  return f.commit(ds, xsink);
253 
254  return 0; // 0 = OK
255  }
256 
257  DLLLOCAL QoreValue getServerVersion(Datasource* ds, ExceptionSink* xsink) const {
258  if (f.get_server_version)
259  return f.get_server_version(ds, xsink);
260  return QoreValue();
261  }
262 
263  DLLLOCAL QoreValue getClientVersion(const Datasource* ds, ExceptionSink* xsink) const {
264  if (f.get_client_version)
265  return f.get_client_version(ds, xsink);
266  return QoreValue();
267  }
268 
269  DLLLOCAL int getCaps() const {
270  return caps;
271  }
272 
273  DLLLOCAL QoreListNode* getCapList() const;
274 
275  DLLLOCAL bool hasExecDefine() const {
276  return (bool)f.stmt.exec_describe;
277  }
278 
279  DLLLOCAL int stmt_prepare(SQLStatement* stmt, const QoreString& str, const QoreListNode* args, ExceptionSink* xsink) const {
280  DbiArgHelper dargs(args, (caps & DBI_CAP_HAS_NUMBER_SUPPORT), xsink);
281  return f.stmt.prepare(stmt, str, *dargs, xsink);
282  }
283 
284  DLLLOCAL int stmt_prepare_raw(SQLStatement* stmt, const QoreString& str, ExceptionSink* xsink) const {
285  return f.stmt.prepare_raw(stmt, str, xsink);
286  }
287 
288  DLLLOCAL int stmt_bind(SQLStatement* stmt, const QoreListNode& l, ExceptionSink* xsink) const {
289  return f.stmt.bind(stmt, l, xsink);
290  }
291 
292  DLLLOCAL int stmt_bind_placeholders(SQLStatement* stmt, const QoreListNode& l, ExceptionSink* xsink) const {
293  if (!f.stmt.bind_placeholders) {
294  xsink->raiseException("SQLSTATEMENT-BIND-PLACEHOLDERS-ERROR", "the '%s' driver does not require placeholder buffer specifications so the SQLStatement::bindPlaceholders() method is not supported", name);
295  return -1;
296  }
297 
298  return f.stmt.bind_placeholders(stmt, l, xsink);
299  }
300 
301  DLLLOCAL int stmt_bind_values(SQLStatement* stmt, const QoreListNode& l, ExceptionSink* xsink) const {
302  return f.stmt.bind_values(stmt, l, xsink);
303  }
304 
305  DLLLOCAL int stmt_define(SQLStatement* stmt, ExceptionSink* xsink) const {
306  return f.stmt.define(stmt, xsink);
307  }
308 
309  DLLLOCAL int stmt_exec_describe(SQLStatement* stmt, ExceptionSink* xsink) const {
310  assert(f.stmt.exec_describe);
311  return f.stmt.exec_describe(stmt, xsink);
312  }
313 
314  DLLLOCAL int stmt_exec(SQLStatement* stmt, ExceptionSink* xsink) const {
315  return f.stmt.exec(stmt, xsink);
316  }
317 
318  DLLLOCAL int stmt_affected_rows(SQLStatement* stmt, ExceptionSink* xsink) const {
319  return f.stmt.affected_rows(stmt, xsink);
320  }
321 
322  DLLLOCAL QoreHashNode* stmt_get_output(SQLStatement* stmt, ExceptionSink* xsink) const {
323  return f.stmt.get_output(stmt, xsink);
324  }
325 
326  DLLLOCAL QoreHashNode* stmt_get_output_rows(SQLStatement* stmt, ExceptionSink* xsink) const {
327  return f.stmt.get_output_rows(stmt, xsink);
328  }
329 
330  DLLLOCAL QoreHashNode* stmt_fetch_row(SQLStatement* stmt, ExceptionSink* xsink) const {
331  return f.stmt.fetch_row(stmt, xsink);
332  }
333 
334  DLLLOCAL QoreListNode* stmt_fetch_rows(SQLStatement* stmt, int rows, ExceptionSink* xsink) const {
335  return f.stmt.fetch_rows(stmt, rows, xsink);
336  }
337 
338  DLLLOCAL QoreHashNode* stmt_fetch_columns(SQLStatement* stmt, int rows, ExceptionSink* xsink) const {
339  return f.stmt.fetch_columns(stmt, rows, xsink);
340  }
341 
342  DLLLOCAL QoreHashNode* stmt_describe(SQLStatement* stmt, ExceptionSink* xsink) const {
343  if (!f.stmt.describe) {
344  xsink->raiseException("DBI-DESCRIBE-ERROR", "this driver does not implement the SQLStatement::describe() method");
345  return nullptr;
346  }
347  return f.stmt.describe(stmt, xsink);
348  }
349 
350  DLLLOCAL bool stmt_next(SQLStatement* stmt, ExceptionSink* xsink) const {
351  return f.stmt.next(stmt, xsink);
352  }
353 
354  DLLLOCAL int stmt_close(SQLStatement* stmt, ExceptionSink* xsink) const {
355  return f.stmt.close(stmt, xsink);
356  }
357 
358  DLLLOCAL int stmt_free(SQLStatement* stmt, ExceptionSink* xsink) const {
359  return f.stmt.free ? f.stmt.free(stmt, xsink) : 0;
360  }
361 
362  DLLLOCAL int opt_set(Datasource* ds, const char* opt, const QoreValue val, ExceptionSink* xsink) {
363  OptInputHelper oh(xsink, *this, opt, true, val);
364  if (!oh)
365  return -1;
366 
367  return f.opt.set(ds, opt, oh.val, xsink);
368  }
369 
370  DLLLOCAL QoreValue opt_get(const Datasource* ds, const char* opt, ExceptionSink* xsink) {
371  OptInputHelper oh(xsink, *this, opt);
372  if (!oh)
373  return QoreValue();
374 
375  return f.opt.get(ds, opt);
376  }
377 
378  DLLLOCAL QoreHashNode* getOptionHash(const Datasource* ds) const {
379  QoreHashNode* rv = new QoreHashNode(autoTypeInfo);
380 
381  for (dbi_opt_map_t::const_iterator i = omap.begin(), e = omap.end(); i != e; ++i) {
382  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
383  h->setKeyValue("desc", new QoreStringNode(i->second.desc), 0);
384  h->setKeyValue("type", new QoreStringNode(QoreTypeInfo::getName(i->second.typeInfo)), 0);
385  h->setKeyValue("value", f.opt.get(ds, i->first), 0);
386 
387  rv->setKeyValue(i->first, h, 0);
388  }
389  return rv;
390  }
391 
392  DLLLOCAL QoreHashNode* getOptionHash() const {
393  QoreHashNode* rv = new QoreHashNode(autoTypeInfo);
394 
395  for (dbi_opt_map_t::const_iterator i = omap.begin(), e = omap.end(); i != e; ++i) {
396  QoreHashNode* h = new QoreHashNode(autoTypeInfo);
397  h->setKeyValue("desc", new QoreStringNode(i->second.desc), 0);
398  h->setKeyValue("type", new QoreStringNode(QoreTypeInfo::getName(i->second.typeInfo)), 0);
399 
400  rv->setKeyValue(i->first, h, 0);
401  }
402  return rv;
403  }
404 
405  DLLLOCAL static qore_dbi_private* get(const DBIDriver& d) {
406  return d.priv;
407  }
408 };
409 
410 #endif
int(* q_dbi_rollback_t)(Datasource *ds, ExceptionSink *xsink)
signature for the DBI "rollback" method - must be defined in each DBI driver
Definition: DBI.h:200
QoreValue(* q_dbi_get_client_version_t)(const Datasource *ds, ExceptionSink *xsink)
signature for the "get_client_version" method
Definition: DBI.h:224
QoreHashNode *(* q_dbi_select_row_t)(Datasource *ds, const QoreString *str, const QoreListNode *args, ExceptionSink *xsink)
signature for the DBI "selectRow" method - must be defined in each DBI driver
Definition: DBI.h:165
#define DBI_CAP_HAS_NUMBER_SUPPORT
supports arbitrary-precision numeric support for binding and retrieving values; if this is not true t...
Definition: DBI.h:49
#define DBI_CAP_HAS_STATEMENT
supports the SQLStatement class (set automatically by the Qore library)
Definition: DBI.h:47
QoreValue(* q_dbi_exec_t)(Datasource *ds, const QoreString *str, const QoreListNode *args, ExceptionSink *xsink)
signature for the DBI "execSQL" method - must be defined in each DBI driver
Definition: DBI.h:175
int(* q_dbi_stmt_exec_t)(SQLStatement *stmt, ExceptionSink *xsink)
execute statement
Definition: DBI.h:244
int(* q_dbi_stmt_prepare_raw_t)(SQLStatement *stmt, const QoreString &str, ExceptionSink *xsink)
prepare statement with no bind parsing
Definition: DBI.h:234
QoreValue(* q_dbi_select_t)(Datasource *ds, const QoreString *str, const QoreListNode *args, ExceptionSink *xsink)
signature for the DBI "select" method - must be defined in each DBI driver
Definition: DBI.h:144
QoreValue(* q_dbi_select_rows_t)(Datasource *ds, const QoreString *str, const QoreListNode *args, ExceptionSink *xsink)
signature for the DBI "selectRows" method - must be defined in each DBI driver
Definition: DBI.h:154
QoreHashNode *(* q_dbi_stmt_get_output_t)(SQLStatement *stmt, ExceptionSink *xsink)
get output values, any row sets are returned as a hash of lists
Definition: DBI.h:254
int(* q_dbi_begin_transaction_t)(Datasource *ds, ExceptionSink *xsink)
signature for the DBI "begin_transaction" method, should only be defined for drivers needing this to ...
Definition: DBI.h:208
int(* q_dbi_stmt_bind_t)(SQLStatement *stmt, const QoreListNode &l, ExceptionSink *xsink)
bind input values and optionally describe output parameters
Definition: DBI.h:239
int(* q_dbi_commit_t)(Datasource *ds, ExceptionSink *xsink)
signature for the DBI "commit" method - must be defined in each DBI driver
Definition: DBI.h:192
int(* q_dbi_open_t)(Datasource *ds, ExceptionSink *xsink)
signature for the DBI "open" method - must be defined in each DBI driver
Definition: DBI.h:127
int(* q_dbi_close_t)(Datasource *ds)
signature for the DBI "close" method - must be defined in each DBI driver
Definition: DBI.h:134
QoreValue(* q_dbi_get_server_version_t)(Datasource *ds, ExceptionSink *xsink)
signature for the "get_server_version" method
Definition: DBI.h:216
int(* q_dbi_stmt_affected_rows_t)(SQLStatement *stmt, ExceptionSink *xsink)
get number of affected rows
Definition: DBI.h:249
QoreValue(* q_dbi_execraw_t)(Datasource *ds, const QoreString *str, ExceptionSink *xsink)
signature for the DBI "execRawSQL" method - must be defined in each DBI driver
Definition: DBI.h:184
QoreHashNode *(* q_dbi_describe_t)(Datasource *ds, const QoreString *str, const QoreListNode *args, ExceptionSink *xsink)
signature for the DBI "describe" method
Definition: DBI.h:279
QoreHashNode *(* q_dbi_stmt_get_output_rows_t)(SQLStatement *stmt, ExceptionSink *xsink)
get output values, any row sets are returned as a list of hashes
Definition: DBI.h:259
int(* q_dbi_stmt_prepare_t)(SQLStatement *stmt, const QoreString &str, const QoreListNode *args, ExceptionSink *xsink)
prepare statement and process placeholder specifications and bind parameters
Definition: DBI.h:229
DLLEXPORT void deref(ExceptionSink *xsink)
decrements the reference count and calls derefImpl() if there_can_be_only_one is false,...
constant iterator class for QoreHashNode, to be only created on the stack
Definition: QoreHashNode.h:577
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 const QoreHashNode * getConnectOptions() const
returns the valid options for this driver with descriptions and current values for the current dataso...
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
DLLEXPORT AbstractQoreNode * raiseException(const char *err, const char *fmt,...)
appends a Qore-language exception to the list
This is the hash or associative list container type in Qore, dynamically allocated only,...
Definition: QoreHashNode.h:50
DLLEXPORT int setKeyValue(const char *key, QoreValue value, ExceptionSink *xsink)
sets the value of "key" to "value"
This is the list container type in Qore, dynamically allocated only, reference counted.
Definition: QoreListNode.h:52
DLLEXPORT QoreValue shift()
returns the first element of the list, all other entries are moved down to fill up the first position...
DLLEXPORT size_t size() const
returns the number of elements in the list
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
This is the public class for DBI drivers supporting Qore's new prepared statement API.
Definition: SQLStatement.h:38
holds an object and dereferences it in the destructor
Definition: QoreValue.h:476
const qore_type_t NT_LIST
type value for QoreListNode
Definition: node_types.h:50
const qore_type_t NT_HASH
type value for QoreHashNode
Definition: node_types.h:51
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