-
Notifications
You must be signed in to change notification settings - Fork 30
/
handles.h
537 lines (459 loc) · 16.6 KB
/
handles.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
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
#ifndef __HANDLES_H__
#define __HANDLES_H__
#include <curl/curl.h>
#include <ujdecode.h>
#include "error.h"
#include "defs.h"
#include "log.h"
/* forward declarations */
struct struct_env;
struct struct_dbc;
struct struct_stmt;
struct struct_desc;
/*
* header structure, embedded in all API handles
* Note: must remain the first declared member.
*/
typedef struct struct_hheader { /* handle header */
/* SQL_HANDLE_ENV / _DBC / _STMT / _DESC */
SQLSMALLINT type;
/* diagnostic/state keeping */
esodbc_diag_st diag;
/* ODBC API multi-threading exclusive lock */
esodbc_mutex_lt mutex;
/* back reference to "parent" structure (in type hierarchy) */
union {
struct struct_env *env;
struct struct_dbc *dbc;
struct struct_stmt *stmt;
void *parent;
};
/* logging helpers */
wstr_st typew; /* ENV/DBC/STMT/DESC as w-string */
esodbc_filelog_st *log; /* logger: owned by a DBC; ENV uses global */
} esodbc_hhdr_st;
/*
* https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/environment-handles :
* """
* The environment's state
* The current environment-level diagnostics
* The handles of connections currently allocated on the environment
* The current settings of each environment attribute
* """
*/
typedef struct struct_env {
esodbc_hhdr_st hdr;
SQLUINTEGER version; /* versions defined as UL (see SQL_OV_ODBC3) */
} esodbc_env_st;
/* meta data types (same for both SQL_C_<t> and SQL_<t> types) */
typedef enum {
METATYPE_UNKNOWN = 0,
METATYPE_EXACT_NUMERIC,
METATYPE_FLOAT_NUMERIC,
METATYPE_STRING,
METATYPE_BIN,
METATYPE_DATETIME,
METATYPE_INTERVAL_WSEC,
METATYPE_INTERVAL_WOSEC,
METATYPE_BIT,
METATYPE_UID,
METATYPE_MAX // SQL_C_DEFAULT, ESODBC_SQL_NULL
} esodbc_metatype_et;
/* Structure mapping one ES/SQL data type. */
typedef struct elasticsearch_type {
/* fields of one row returned in response to 'SYS TYPES' query */
wstr_st type_name;
SQLSMALLINT data_type; /* maps to rec's .concise_type member */
SQLINTEGER column_size;
wstr_st literal_prefix;
wstr_st literal_suffix;
wstr_st create_params;
SQLSMALLINT nullable;
SQLSMALLINT case_sensitive;
SQLSMALLINT searchable;
SQLSMALLINT unsigned_attribute;
SQLSMALLINT fixed_prec_scale;
SQLSMALLINT auto_unique_value;
wstr_st local_type_name;
SQLSMALLINT minimum_scale;
SQLSMALLINT maximum_scale;
SQLSMALLINT sql_data_type; /* :-> rec's .type member */
SQLSMALLINT sql_datetime_sub; /* :-> rec's .datetime_interval_code */
SQLINTEGER num_prec_radix;
SQLSMALLINT interval_precision;
/* number of SYS TYPES result columns mapped over the above members */
#define ESODBC_TYPES_COLUMNS 19
/* SQL C type driver mapping of ES' data_type; this is derived from
* .type_name, rathern than .(sql_)data_type (sice the name is the
* "unique" key and sole identifier in general queries results). */
SQLSMALLINT c_concise_type;
/* There should be no need for a supplemental 'sql_c_type': if
* rec.datetime_interval_code == 0, then this member would equal the
* concise one (above); else, rec.type will contain the right value
* already (i.e. they'd be the same for SQL and SQL C data types). */
/* helper member, to characterize the type */
esodbc_metatype_et meta_type;
SQLLEN display_size;
/* convenience C-string conversion of type_name */
cstr_st type_name_c;
} esodbc_estype_st;
/*
* https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/connection-handles :
* """
* The state of the connection
* The current connection-level diagnostics
* The handles of statements and descriptors currently allocated on the
* connection
* The current settings of each connection attribute
* """
*/
typedef struct struct_dbc {
esodbc_hhdr_st hdr;
wstr_st dsn; /* data source name SQLGetInfo(SQL_DATA_SOURCE_NAME) */
wstr_st server; /* ~ name; requested with SQLGetInfo(SQL_SERVER_NAME) */
union {
wstr_st string; /* version: SQLGetInfo(SQL_DBMS_VER) */
unsigned char checking; /* first letter of DSN config option */
} srv_ver; /* server version */
cstr_st url; /* SQL URL (posts) */
cstr_st root_url; /* root URL (gets) */
enum {
ESODBC_SEC_NONE = 0,
ESODBC_SEC_USE_SSL,
ESODBC_SEC_CHECK_CA,
ESODBC_SEC_CHECK_HOST,
ESODBC_SEC_CHECK_REVOKE,
ESODBC_SEC_MAX /* meta */
} secure;
cstr_st ca_path;
cstr_st uid;
cstr_st pwd;
SQLUINTEGER timeout;
BOOL follow;
struct {
size_t max; /* max fetch size */
char *str; /* as string */
char slen; /* string's length (w/o terminator) */
} fetch;
BOOL pack_json; /* should JSON be used in REST bodies? (vs. CBOR) */
esodbc_estype_st *es_types; /* array with ES types */
SQLULEN no_types; /* number of types in array */
/* maximum precision/length of types using same SQL data type ID */
SQLINTEGER max_float_size;
SQLINTEGER max_varchar_size;
CURL *curl; /* cURL handle */
CURLcode curl_err;
char curl_err_buff[CURL_ERROR_SIZE];
char *abuff; /* buffer holding the answer */
size_t alen; /* size of abuff */
size_t apos; /* current write position in the abuff */
size_t amax; /* maximum length (bytes) that abuff can grow to */
esodbc_mutex_lt curl_mux; /* mutex for above 'networking' members */
/* window handler */
HWND hwin;
/* options */
SQLULEN metadata_id; // default: SQL_FALSE
} esodbc_dbc_st;
typedef struct desc_rec {
/* back ref to owning descriptor */
struct struct_desc *desc;
/* helper member, to characterize the type */
esodbc_metatype_et meta_type;
/* pointer to the ES/SQL type in DBC array
* need to be set for records in IxD descriptors */
esodbc_estype_st *es_type;
/*
* record fields
*/
/* following record fields have been moved into es_type:
* display_size, literal_prefix, literal_suffix, local_type_name,
* type_name, auto_unique_value, case_sensitive, fixed_prec_scale,
* nullable, searchable, usigned */
/* record types (SQL_<t> for IxD, or SQL_C_<t> for AxD) */
SQLSMALLINT concise_type;
SQLSMALLINT type;
SQLSMALLINT datetime_interval_code;
SQLPOINTER data_ptr; /* array, if .array_size > 1 */
wstr_st base_column_name; /* read-only */
wstr_st base_table_name; /* r/o */
wstr_st catalog_name; /* r/o */
wstr_st label; /* r/o */ //alias?
wstr_st name;
wstr_st schema_name; /* r/o */
wstr_st table_name; /* r/o */
SQLLEN *indicator_ptr; /* array, if .array_size > 1 */
SQLLEN *octet_length_ptr; /* array, if .array_size > 1 */
SQLLEN octet_length;
SQLULEN length;
SQLINTEGER datetime_interval_precision; /*TODO: -> es_type? */
SQLINTEGER num_prec_radix; /*TODO: -> es_type? */
SQLSMALLINT parameter_type;
SQLSMALLINT precision;
SQLSMALLINT rowver;
SQLSMALLINT scale;
SQLSMALLINT unnamed;
SQLSMALLINT updatable;
} esodbc_rec_st;
typedef enum {
DESC_TYPE_ANON, /* SQLAllocHandle()'ed */
DESC_TYPE_ARD,
DESC_TYPE_IRD,
DESC_TYPE_APD,
DESC_TYPE_IPD,
} desc_type_et;
/* type is for an application descriptor */
#define DESC_TYPE_IS_APPLICATION(_dtype) \
(_dtype == DESC_TYPE_ARD || _dtype == DESC_TYPE_APD)
/* type is for an implementation descriptor */
#define DESC_TYPE_IS_IMPLEMENTATION(_dtype) \
(_dtype == DESC_TYPE_IRD || _dtype == DESC_TYPE_IPD)
/* type is for a record descriptor */
#define DESC_TYPE_IS_RECORD(_dtype) \
(_dtype == DESC_TYPE_ARD || _dtype == DESC_TYPE_IRD)
/* type is for a parameter descriptor */
#define DESC_TYPE_IS_PARAMETER(_dtype) \
(_dtype == DESC_TYPE_APD || _dtype == DESC_TYPE_IPD)
typedef struct struct_desc {
esodbc_hhdr_st hdr;
desc_type_et type; /* APD, IPD, ARD, IRD */
/* header fields */
/* descriptor was allocated automatically by the driver or explicitly by
* the application */
SQLSMALLINT alloc_type;
/* ARDs: the number of rows returned by each call to SQLFetch* = the rowset
* APDs: the number of values for each parameter. */
SQLULEN array_size;
SQLUSMALLINT *array_status_ptr;
SQLLEN *bind_offset_ptr;
SQLINTEGER bind_type; /* row/col */
SQLSMALLINT count; /* of recs */
SQLULEN *rows_processed_ptr;
/* /header fields */
/* array of records of .count cardinality */
esodbc_rec_st *recs;
} esodbc_desc_st;
/* the ES/SQL type must be set for implementation descriptor records */
#define ASSERT_IXD_HAS_ES_TYPE(_rec) \
assert(DESC_TYPE_IS_IMPLEMENTATION(_rec->desc->type) && _rec->es_type)
typedef struct struct_resultset {
long code; /* code of last response */
char *buff; /* buffer containing the answer to the last request in a STM */
size_t blen; /* length of the answer */
wstr_st ecurs; /* Elastic's cursor object */
void *state; /* top UJSON decoder state */
void *rows_iter; /* UJSON iterator with the rows in result set */
UJObject row_array; /* UJSON object for current row */
size_t nrows; /* (count of) rows in current result set */
size_t vrows; /* (count of) visited rows in current result set */
} resultset_st;
/*
* "The fields of an IRD have a default value only after the statement has
* been prepared or executed and the IRD has been populated, not when the
* statement handle or descriptor has been allocated. Until the IRD has been
* populated, any attempt to gain access to a field of an IRD will return an
* error."
*/
typedef struct struct_stmt {
esodbc_hhdr_st hdr;
/* cache UTF8 JSON serialized SQL: can be (re)used with varying params */
cstr_st u8sql;
/* pointers to the current descriptors */
esodbc_desc_st *ard;
esodbc_desc_st *ird;
esodbc_desc_st *apd;
esodbc_desc_st *ipd;
/* initial implicit descriptors allocated with the statement */
esodbc_desc_st i_ard;
esodbc_desc_st i_ird;
esodbc_desc_st i_apd;
esodbc_desc_st i_ipd;
/* options */
SQLULEN bookmarks; //default: SQL_UB_OFF
SQLULEN metadata_id; // default: copied from connection
/* "the maximum amount of data that the driver returns from a character or
* binary column" */
SQLULEN max_length;
/* "number of seconds to wait for an SQL statement to execute before
* returning to the application." */
SQLULEN query_timeout;
/* [current] result set (= one answer from ES/SQL; can contain a cursor) */
resultset_st rset;
/* total count of fetched rows for one statement (sum(resultset.nrows)) */
size_t tf_rows;
/* SQL data types conversion to SQL C compatibility (IRD.SQL -> ARD.C) */
enum {
CONVERSION_VIOLATION = -2, /* specs disallowed */
CONVERSION_UNSUPPORTED, /* ES/driver not supported */
CONVERSION_UNCHECKED, /* 0 */
CONVERSION_SUPPORTED,
CONVERSION_SKIPPED, /* used with driver's meta queries */
} sql2c_conversion;
/* SQLGetData state members */
SQLINTEGER gd_col; /* current column to get from, if positive */
SQLINTEGER gd_ctype; /* current target type */
SQLLEN gd_offt; /* position in source buffer */
} esodbc_stmt_st;
/* reset total number of fetched rows for a statement */
#define STMT_TFROWS_RESET(_stmt) \
do { \
(_stmt)->tf_rows = 0; \
} while (0)
/* 1-based current row number */
#define STMT_CRR_ROW_NUMBER(_stmt) \
((_stmt)->tf_rows - (_stmt)->rset.nrows + \
(_stmt)->rset.vrows + /*1-based*/1)
/* SQLGetData() state reset */
#define STMT_GD_RESET(_stmt) \
do { \
_stmt->gd_col = -1; \
_stmt->gd_ctype = 0; \
_stmt->gd_offt = 0; \
} while (0)
/* is currently a SQLGetData() call being serviced? */
#define STMT_GD_CALLING(_stmt) (0 <= _stmt->gd_col)
SQLRETURN update_rec_count(esodbc_desc_st *desc, SQLSMALLINT new_count);
esodbc_rec_st *get_record(esodbc_desc_st *desc, SQLSMALLINT rec_no, BOOL grow);
void dump_record(esodbc_rec_st *rec);
void init_dbc(esodbc_dbc_st *dbc, SQLHANDLE InputHandle);
esodbc_desc_st *getdata_set_ard(esodbc_stmt_st *stmt, esodbc_desc_st *gd_ard,
SQLUSMALLINT colno, esodbc_rec_st *recs, SQLUSMALLINT count);
void getdata_reset_ard(esodbc_stmt_st *stmt, esodbc_desc_st *ard,
SQLUSMALLINT colno, esodbc_rec_st *recs, SQLUSMALLINT count);
void concise_to_type_code(SQLSMALLINT concise, SQLSMALLINT *type,
SQLSMALLINT *code);
esodbc_metatype_et concise_to_meta(SQLSMALLINT concise_type,
desc_type_et desc_type);
SQLRETURN EsSQLAllocHandle(SQLSMALLINT HandleType,
SQLHANDLE InputHandle, _Out_ SQLHANDLE *OutputHandle);
SQLRETURN EsSQLFreeHandle(SQLSMALLINT HandleType, SQLHANDLE Handle);
SQLRETURN EsSQLFreeStmt(SQLHSTMT StatementHandle, SQLUSMALLINT Option);
SQLRETURN EsSQLSetEnvAttr(SQLHENV EnvironmentHandle,
SQLINTEGER Attribute,
_In_reads_bytes_opt_(StringLength) SQLPOINTER Value,
SQLINTEGER StringLength);
SQLRETURN EsSQLGetEnvAttr(SQLHENV EnvironmentHandle, SQLINTEGER Attribute,
_Out_writes_(_Inexpressible_(BufferLength)) SQLPOINTER Value,
SQLINTEGER BufferLength, _Out_opt_ SQLINTEGER *StringLength);
SQLRETURN EsSQLSetStmtAttrW(
SQLHSTMT StatementHandle,
SQLINTEGER Attribute,
SQLPOINTER ValuePtr,
SQLINTEGER BufferLength);
SQLRETURN EsSQLGetStmtAttrW(
SQLHSTMT StatementHandle,
SQLINTEGER Attribute,
SQLPOINTER ValuePtr,
SQLINTEGER BufferLength,
SQLINTEGER *StringLengthPtr);
SQLRETURN EsSQLGetDescFieldW(
SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber,
SQLSMALLINT FieldIdentifier,
_Out_writes_opt_(_Inexpressible_(BufferLength))
SQLPOINTER ValuePtr,
SQLINTEGER BufferLength,
SQLINTEGER *StringLengthPtr);
SQLRETURN EsSQLGetDescRecW(
SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber,
_Out_writes_opt_(BufferLength)
SQLWCHAR *Name,
_Out_opt_
SQLSMALLINT BufferLength,
_Out_opt_
SQLSMALLINT *StringLengthPtr,
_Out_opt_
SQLSMALLINT *TypePtr,
_Out_opt_
SQLSMALLINT *SubTypePtr,
_Out_opt_
SQLLEN *LengthPtr,
_Out_opt_
SQLSMALLINT *PrecisionPtr,
_Out_opt_
SQLSMALLINT *ScalePtr,
_Out_opt_
SQLSMALLINT *NullablePtr);
/* use with RecNumber for header fields */
#define NO_REC_NR -1
SQLRETURN EsSQLSetDescFieldW(
SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber,
SQLSMALLINT FieldIdentifier,
SQLPOINTER Value,
SQLINTEGER BufferLength);
SQLRETURN EsSQLSetDescRec(
SQLHDESC DescriptorHandle,
SQLSMALLINT RecNumber,
SQLSMALLINT Type,
SQLSMALLINT SubType,
SQLLEN Length,
SQLSMALLINT Precision,
SQLSMALLINT Scale,
_Inout_updates_bytes_opt_(Length) SQLPOINTER Data,
_Inout_opt_ SQLLEN *StringLength,
_Inout_opt_ SQLLEN *Indicator);
/*
* Macros to convert ODBC API generic handles into implementation types.
*/
#define ENVH(_h) ((esodbc_env_st *)(_h))
#define DBCH(_h) ((esodbc_dbc_st *)(_h))
#define STMH(_h) ((esodbc_stmt_st *)(_h))
#define DSCH(_h) ((esodbc_desc_st *)(_h))
/* this will only work if member stays first in handles (see struct decl). */
#define HDRH(_h) ((esodbc_hhdr_st *)(_h))
/*
* Locking macros for ODBC API calls.
*/
#define HND_LOCK(_h) ESODBC_MUX_LOCK(&HDRH(_h)->mutex)
#define HND_TRYLOCK(_h) ESODBC_MUX_TRYLOCK(&HDRH(_h)->mutex)
#define HND_UNLOCK(_h) ESODBC_MUX_UNLOCK(&HDRH(_h)->mutex)
/* post state into the diagnostic and return state's return code */
#define RET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \
return post_diagnostic(_hp, _s, MK_WPTR(_t), _c)
/* similar to RET_HDIAG, but only post the state */
#define RET_HDIAGS(_hp/*handle ptr*/, _s/*tate*/) \
return post_diagnostic(_hp, _s, NULL, 0)
/* copy the diagnostics from one handle to the other */
#define HDIAG_COPY(_s, _d) (_d)->hdr.diag = (_s)->hdr.diag
/* set a diagnostic to a(ny) handle */
#define SET_HDIAG(_hp/*handle ptr*/, _s/*tate*/, _t/*char text*/, _c/*ode*/) \
post_diagnostic(_hp, _s, MK_WPTR(_t), _c)
/* return the code associated with the given state (and debug-log) */
#define RET_STATE(_s) \
do { \
assert(_s < SQL_STATE_MAX); \
return esodbc_errors[_s].retcode; \
} while (0)
#define STMT_HAS_RESULTSET(stmt) ((stmt)->rset.buff != NULL)
#define STMT_FORCE_NODATA(stmt) (stmt)->rset.blen = (size_t)-1
#define STMT_NODATA_FORCED(stmt) ((stmt)->rset.blen == (size_t)-1)
/* "An application can unbind the data buffer for a column but still have a
* length/indicator buffer bound for the column" */
#define REC_IS_BOUND(rec) ( \
(rec)->data_ptr != NULL || \
(rec)->indicator_ptr != NULL || \
(rec)->octet_length_ptr != NULL)
/*
* Logging with handle
*/
#define LOGH(hnd, lvl, werrn, fmt, ...) \
_LOG(HDRH(hnd)->log, lvl, werrn, "[" LWPDL "@0x%p] " fmt, \
LWSTR(&HDRH(hnd)->typew), hnd, __VA_ARGS__)
#define ERRNH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_ERR, 1, fmt, __VA_ARGS__)
#define ERRH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_ERR, 0, fmt, __VA_ARGS__)
#define WARNH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_WARN, 0, fmt, __VA_ARGS__)
#define INFOH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_INFO, 0, fmt, __VA_ARGS__)
#define DBGH(hnd, fmt, ...) LOGH(hnd, LOG_LEVEL_DBG, 0, fmt, __VA_ARGS__)
#define BUGH(hnd, fmt, ...) \
do { \
ERRH(hnd, "[BUG] " fmt, __VA_ARGS__); \
assert(0); \
} while (0)
#endif /* __HANDLES_H__ */
/* vim: set noet fenc=utf-8 ff=dos sts=0 sw=4 ts=4 : */