ruby-dm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/SYSDBA) +0 -0
- data/dm-0.1.0.gem +0 -0
- data/dm.gemspec +29 -0
- data/ext/client.c +520 -0
- data/ext/client.h +40 -0
- data/ext/dm_enc_name_to_ruby.h +63 -0
- data/ext/dm_ext.c +17 -0
- data/ext/dm_ext.h +43 -0
- data/ext/dm_lib_path.rb +3 -0
- data/ext/extconf.h +11 -0
- data/ext/extconf.rb +123 -0
- data/ext/result.c +1052 -0
- data/ext/result.h +39 -0
- data/ext/statement.c +666 -0
- data/ext/statement.h +36 -0
- data/lib/dm/client.rb +54 -0
- data/lib/dm/console.rb +5 -0
- data/lib/dm/error.rb +59 -0
- data/lib/dm/field.rb +3 -0
- data/lib/dm/result.rb +7 -0
- data/lib/dm/statement.rb +9 -0
- data/lib/dm/version.rb +5 -0
- data/lib/dm.rb +65 -0
- metadata +94 -0
data/ext/result.c
ADDED
@@ -0,0 +1,1052 @@
|
|
1
|
+
#include <dm_ext.h>
|
2
|
+
|
3
|
+
static rb_encoding *binaryEncoding;
|
4
|
+
|
5
|
+
/* on 64bit platforms we can handle dates way outside 2038-01-19T03:14:07
|
6
|
+
*
|
7
|
+
* (9999*31557600) + (12*2592000) + (31*86400) + (11*3600) + (59*60) + 59
|
8
|
+
*/
|
9
|
+
#define dm_MAX_TIME 315578267999ULL
|
10
|
+
|
11
|
+
/* 0000-1-1 00:00:00 UTC
|
12
|
+
*
|
13
|
+
* (0*31557600) + (1*2592000) + (1*86400) + (0*3600) + (0*60) + 0
|
14
|
+
*/
|
15
|
+
#define dm_MIN_TIME 2678400ULL
|
16
|
+
|
17
|
+
#define dm_MAX_BYTES_PER_CHAR 3
|
18
|
+
|
19
|
+
/* From dm documentations:
|
20
|
+
* To distinguish between binary and nonbinary data for string data types,
|
21
|
+
* check whether the charsetnr value is 63. If so, the character set is binary,
|
22
|
+
* which indicates binary rather than nonbinary data. This enables you to distinguish BINARY
|
23
|
+
* from CHAR, VARBINARY from VARCHAR, and the BLOB types from the TEXT types.
|
24
|
+
*/
|
25
|
+
#define dm_BINARY_CHARSET 63
|
26
|
+
|
27
|
+
#ifndef DSQL_JSON
|
28
|
+
#define DSQL_JSON 245
|
29
|
+
#endif
|
30
|
+
|
31
|
+
#ifndef NEW_TYPEDDATA_WRAPPER
|
32
|
+
#define TypedData_Get_Struct(obj, type, ignore, sval) Data_Get_Struct(obj, type, sval)
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#define GET_RESULT(self) \
|
36
|
+
dm_result_wrapper *wrapper; \
|
37
|
+
TypedData_Get_Struct(self, dm_result_wrapper, &rb_dm_result_type, wrapper);
|
38
|
+
|
39
|
+
typedef struct {
|
40
|
+
int symbolizeKeys;
|
41
|
+
int asArray;
|
42
|
+
int castBool;
|
43
|
+
int cacheRows;
|
44
|
+
int cast;
|
45
|
+
int streaming;
|
46
|
+
ID db_timezone;
|
47
|
+
ID app_timezone;
|
48
|
+
int block_given; /* boolean */
|
49
|
+
} result_each_args;
|
50
|
+
|
51
|
+
extern VALUE mdm, cdmClient, cdmError;
|
52
|
+
static VALUE cdmResult, cDateTime, cDate;
|
53
|
+
static VALUE opt_decimal_zero, opt_float_zero, opt_time_year, opt_time_month, opt_utc_offset;
|
54
|
+
static ID intern_new, intern_utc, intern_local, intern_localtime, intern_local_offset,
|
55
|
+
intern_civil, intern_new_offset, intern_merge, intern_BigDecimal,
|
56
|
+
intern_query_options;
|
57
|
+
static VALUE sym_symbolize_keys, sym_as, sym_array, sym_database_timezone,
|
58
|
+
sym_application_timezone, sym_local, sym_utc, sym_cast_booleans,
|
59
|
+
sym_cache_rows, sym_cast, sym_stream, sym_name;
|
60
|
+
|
61
|
+
struct nogvl_get_col_args
|
62
|
+
{
|
63
|
+
dm_result_wrapper *wrapper;
|
64
|
+
};
|
65
|
+
|
66
|
+
static void *nogvl_get_col_desc(void *ptr) {
|
67
|
+
struct nogvl_get_col_args* ptr1 = ptr;
|
68
|
+
dm_result_wrapper *wrapper = ptr1->wrapper;
|
69
|
+
DPIRETURN rt;
|
70
|
+
dhdesc hdesc_col;
|
71
|
+
int iParam;
|
72
|
+
|
73
|
+
rt = dpi_number_columns(wrapper->statement, &(wrapper->numberOfFields));
|
74
|
+
if(!DSQL_SUCCEEDED(rt))
|
75
|
+
return (void*)Qfalse;
|
76
|
+
|
77
|
+
rt = dpi_row_count(wrapper->statement, &(wrapper->numberOfRows));
|
78
|
+
if(!DSQL_SUCCEEDED(rt))
|
79
|
+
return (void*)Qfalse;
|
80
|
+
|
81
|
+
wrapper->lobs = (dhloblctr*)xcalloc(wrapper->numberOfFields, sizeof(dhloblctr));
|
82
|
+
wrapper->length = (slength*)xcalloc(wrapper->numberOfFields, sizeof(slength));
|
83
|
+
wrapper->col_desc = (DmColDesc*)xcalloc(wrapper->numberOfFields, sizeof(DmColDesc));
|
84
|
+
wrapper->result = (char**)xcalloc(wrapper->numberOfFields, sizeof(char*));
|
85
|
+
|
86
|
+
rt = dpi_get_stmt_attr(wrapper->statement, DSQL_ATTR_IMP_ROW_DESC,
|
87
|
+
(dpointer)&hdesc_col, 0, NULL);
|
88
|
+
if(!DSQL_SUCCEEDED(rt))
|
89
|
+
return (void*)Qfalse;
|
90
|
+
|
91
|
+
for (iParam = 0; iParam < wrapper->numberOfFields; iParam++)
|
92
|
+
{
|
93
|
+
rt = dpi_desc_column(wrapper->statement, (sdint2)iParam + 1, wrapper->col_desc[iParam].name,
|
94
|
+
sizeof(wrapper->col_desc[iParam].name), &wrapper->col_desc[iParam].name_length,
|
95
|
+
&wrapper->col_desc[iParam].sql_type, &wrapper->col_desc[iParam].prec, &wrapper->col_desc[iParam].scale,
|
96
|
+
&wrapper->col_desc[iParam].nullable);
|
97
|
+
if(!DSQL_SUCCEEDED(rt))
|
98
|
+
return (void*)Qfalse;
|
99
|
+
if (wrapper->col_desc[iParam].sql_type == DSQL_BLOB || wrapper->col_desc[iParam].sql_type == DSQL_CLOB)
|
100
|
+
{
|
101
|
+
rt = dpi_alloc_lob_locator(wrapper->statement, &(wrapper->lobs[iParam]));
|
102
|
+
if(!DSQL_SUCCEEDED(rt))
|
103
|
+
return (void*)Qfalse;
|
104
|
+
rt = dpi_bind_col(wrapper->statement, (udint2)iParam + 1, DSQL_C_LOB_HANDLE,
|
105
|
+
&(wrapper->lobs[iParam]),sizeof(wrapper->lobs[iParam]), &wrapper->length[iParam]);
|
106
|
+
}
|
107
|
+
else
|
108
|
+
{
|
109
|
+
rt = dpi_get_desc_field(
|
110
|
+
hdesc_col, (sdint2)iParam + 1, DSQL_DESC_DISPLAY_SIZE,
|
111
|
+
(dpointer)&(wrapper->col_desc[iParam].length), 0, NULL);
|
112
|
+
if(!DSQL_SUCCEEDED(rt))
|
113
|
+
return (void*)Qfalse;
|
114
|
+
wrapper->result[iParam] = (char*)xmalloc(wrapper->col_desc[iParam].length+2);
|
115
|
+
if(wrapper->col_desc[iParam].sql_type == DSQL_BINARY || wrapper->col_desc[iParam].sql_type == DSQL_VARBINARY)
|
116
|
+
{
|
117
|
+
rt = dpi_bind_col(wrapper->statement, (udint2)iParam + 1, DSQL_C_BINARY,
|
118
|
+
(dpointer)wrapper->result[iParam],
|
119
|
+
wrapper->col_desc[iParam].length + 1, &wrapper->length[iParam]);
|
120
|
+
}
|
121
|
+
else
|
122
|
+
{
|
123
|
+
rt = dpi_bind_col(wrapper->statement, (udint2)iParam + 1, DSQL_C_NCHAR,
|
124
|
+
(dpointer)wrapper->result[iParam],
|
125
|
+
wrapper->col_desc[iParam].length + 1, &wrapper->length[iParam]);
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
wrapper->is_bind = 1;
|
130
|
+
return (void*)Qtrue;
|
131
|
+
}
|
132
|
+
|
133
|
+
/* Mark any VALUEs that are only referenced in C, so the GC won't get them. */
|
134
|
+
static void rb_dm_result_mark(void * wrapper) {
|
135
|
+
dm_result_wrapper * w = wrapper;
|
136
|
+
if (w) {
|
137
|
+
rb_gc_mark_movable(w->fields);
|
138
|
+
rb_gc_mark_movable(w->rows);
|
139
|
+
rb_gc_mark_movable(w->encoding);
|
140
|
+
rb_gc_mark_movable(w->client);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
/* this may be called manually or during GC */
|
145
|
+
static void rb_dm_result_free_result(dm_result_wrapper * wrapper) {
|
146
|
+
if (!wrapper) return;
|
147
|
+
int i = 0;
|
148
|
+
if (wrapper->resultFreed != 1 && wrapper->is_bind == 1)
|
149
|
+
{
|
150
|
+
|
151
|
+
for(i = 0; i < wrapper->numberOfFields; i++)
|
152
|
+
{
|
153
|
+
if(wrapper->col_desc[i].sql_type == DSQL_BLOB || wrapper->col_desc[i].sql_type == DSQL_CLOB)
|
154
|
+
dpi_free_lob_locator(wrapper->lobs[i]);
|
155
|
+
if(wrapper->result[i])
|
156
|
+
{
|
157
|
+
xfree(wrapper->result[i]);
|
158
|
+
wrapper->result[i] = NULL;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
xfree(wrapper->result);
|
163
|
+
xfree(wrapper->length);
|
164
|
+
xfree(wrapper->lobs);
|
165
|
+
xfree(wrapper->col_desc);
|
166
|
+
/* FIXME: this may call flush_use_result, which can hit the socket */
|
167
|
+
/* For prepared statements, wrapper->result is the result metadata */
|
168
|
+
}
|
169
|
+
wrapper->resultFreed = 1;
|
170
|
+
}
|
171
|
+
|
172
|
+
/* this is called during GC */
|
173
|
+
static void rb_dm_result_free(void *ptr) {
|
174
|
+
dm_result_wrapper *wrapper = ptr;
|
175
|
+
if (wrapper->statement != NULL && wrapper->is_prepare == 0) {
|
176
|
+
dpi_free_stmt(wrapper->statement);
|
177
|
+
wrapper->statement = NULL;
|
178
|
+
}
|
179
|
+
rb_dm_result_free_result(wrapper);
|
180
|
+
|
181
|
+
// If the GC gets to client first it will be nil
|
182
|
+
if (wrapper->client != Qnil) {
|
183
|
+
decr_dm_client(wrapper->client_wrapper);
|
184
|
+
}
|
185
|
+
|
186
|
+
xfree(wrapper);
|
187
|
+
}
|
188
|
+
|
189
|
+
static size_t rb_dm_result_memsize(const void * wrapper) {
|
190
|
+
const dm_result_wrapper * w = wrapper;
|
191
|
+
size_t memsize = sizeof(*w);
|
192
|
+
if (w->client_wrapper) {
|
193
|
+
memsize += sizeof(*w->client_wrapper);
|
194
|
+
}
|
195
|
+
return memsize;
|
196
|
+
}
|
197
|
+
|
198
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
199
|
+
static void rb_dm_result_compact(void * wrapper) {
|
200
|
+
dm_result_wrapper * w = wrapper;
|
201
|
+
if (w) {
|
202
|
+
rb_dm_gc_location(w->fields);
|
203
|
+
rb_dm_gc_location(w->rows);
|
204
|
+
rb_dm_gc_location(w->encoding);
|
205
|
+
rb_dm_gc_location(w->client);
|
206
|
+
}
|
207
|
+
}
|
208
|
+
#endif
|
209
|
+
|
210
|
+
static const rb_data_type_t rb_dm_result_type = {
|
211
|
+
"rb_dm_result",
|
212
|
+
{
|
213
|
+
rb_dm_result_mark,
|
214
|
+
rb_dm_result_free,
|
215
|
+
rb_dm_result_memsize,
|
216
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
217
|
+
rb_dm_result_compact,
|
218
|
+
#endif
|
219
|
+
},
|
220
|
+
0,
|
221
|
+
0,
|
222
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
223
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
224
|
+
#endif
|
225
|
+
};
|
226
|
+
|
227
|
+
static VALUE rb_dm_result_free_(VALUE self) {
|
228
|
+
GET_RESULT(self);
|
229
|
+
|
230
|
+
if (wrapper->statement != NULL && wrapper->is_prepare == 0) {
|
231
|
+
dpi_free_stmt(wrapper->statement);
|
232
|
+
wrapper->statement = NULL;
|
233
|
+
}
|
234
|
+
|
235
|
+
rb_dm_result_free_result(wrapper);
|
236
|
+
wrapper->resultFreed = 1;
|
237
|
+
return Qnil;
|
238
|
+
}
|
239
|
+
|
240
|
+
/*
|
241
|
+
* for small results, this won't hit the network, but there's no
|
242
|
+
* reliable way for us to tell this so we'll always release the GVL
|
243
|
+
* to be safe
|
244
|
+
*/
|
245
|
+
static void* nogvl_stmt_fetch(void *ptr) {
|
246
|
+
dm_result_wrapper* wrapper = ptr;
|
247
|
+
DPIRETURN rt = DSQL_SUCCESS;
|
248
|
+
ulength row_num;
|
249
|
+
slength data_get = 0;
|
250
|
+
int iparam;
|
251
|
+
rt = dpi_fetch(wrapper->statement, &row_num);
|
252
|
+
if(rt == DSQL_NO_DATA || row_num == 0)
|
253
|
+
return (void*)Qnil;
|
254
|
+
if(!DSQL_SUCCEEDED(rt))
|
255
|
+
return (void*)Qfalse;
|
256
|
+
for(iparam = 0; iparam < wrapper->numberOfFields; iparam++)
|
257
|
+
{
|
258
|
+
if(wrapper->col_desc[iparam].sql_type == DSQL_BLOB || wrapper->col_desc[iparam].sql_type == DSQL_CLOB)
|
259
|
+
{
|
260
|
+
rt = dpi_lob_get_length((dhloblctr)(wrapper->lobs[iparam]), &wrapper->length[iparam]);
|
261
|
+
if(wrapper->result[iparam])
|
262
|
+
xfree(wrapper->result[iparam]);
|
263
|
+
if(!DSQL_SUCCEEDED(rt) || wrapper->length[iparam] == -1)
|
264
|
+
{
|
265
|
+
wrapper->result[iparam] = NULL;
|
266
|
+
continue;
|
267
|
+
}
|
268
|
+
if(wrapper->col_desc[iparam].sql_type == DSQL_CLOB)
|
269
|
+
wrapper->result[iparam] = xmalloc(wrapper->length[iparam] * 4 + 2);
|
270
|
+
else
|
271
|
+
wrapper->result[iparam] = xmalloc(wrapper->length[iparam] + 2);
|
272
|
+
if(wrapper->col_desc[iparam].sql_type == DSQL_BLOB)
|
273
|
+
{
|
274
|
+
rt = dpi_lob_read((dhloblctr)(wrapper->lobs[iparam]), 1, DSQL_C_BINARY,
|
275
|
+
0, wrapper->result[iparam], wrapper->length[iparam] + 1, NULL);
|
276
|
+
}
|
277
|
+
else
|
278
|
+
{
|
279
|
+
rt = dpi_lob_read((dhloblctr)(wrapper->lobs[iparam]), 1, DSQL_C_NCHAR,
|
280
|
+
0, wrapper->result[iparam], wrapper->length[iparam] * 4 + 1, &data_get);
|
281
|
+
}
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
return (void*)Qtrue;
|
286
|
+
}
|
287
|
+
|
288
|
+
static VALUE rb_dm_result_fetch_field(VALUE self, unsigned int idx, int symbolize_keys) {
|
289
|
+
VALUE rb_field;
|
290
|
+
VALUE rt;
|
291
|
+
GET_RESULT(self);
|
292
|
+
if(wrapper->is_bind == 0)
|
293
|
+
{
|
294
|
+
struct nogvl_get_col_args args;
|
295
|
+
args.wrapper = wrapper;
|
296
|
+
rt = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
297
|
+
if (rt == Qfalse)
|
298
|
+
rb_raise(cdmError, "failed to get col_desc");
|
299
|
+
}
|
300
|
+
|
301
|
+
if (wrapper->fields == Qnil) {
|
302
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
303
|
+
}
|
304
|
+
|
305
|
+
rb_field = rb_ary_entry(wrapper->fields, idx);
|
306
|
+
if (rb_field == Qnil) {
|
307
|
+
DmColDesc field;
|
308
|
+
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
309
|
+
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
310
|
+
|
311
|
+
field = wrapper->col_desc[idx];
|
312
|
+
if (symbolize_keys) {
|
313
|
+
rb_field = rb_intern3(field.name, field.name_length, rb_utf8_encoding());
|
314
|
+
rb_field = ID2SYM(rb_field);
|
315
|
+
} else {
|
316
|
+
#ifdef HAVE_RB_ENC_INTERNED_STR
|
317
|
+
rb_field = rb_enc_interned_str(field.name, field.name_length, conn_enc);
|
318
|
+
if (default_internal_enc && default_internal_enc != conn_enc) {
|
319
|
+
rb_field = rb_str_to_interned_str(rb_str_export_to_enc(rb_field, default_internal_enc));
|
320
|
+
}
|
321
|
+
#else
|
322
|
+
rb_field = rb_enc_str_new(field.name, field.name_length, conn_enc);
|
323
|
+
if (default_internal_enc && default_internal_enc != conn_enc) {
|
324
|
+
rb_field = rb_str_export_to_enc(rb_field, default_internal_enc);
|
325
|
+
}
|
326
|
+
rb_obj_freeze(rb_field);
|
327
|
+
#endif
|
328
|
+
}
|
329
|
+
rb_ary_store(wrapper->fields, idx, rb_field);
|
330
|
+
}
|
331
|
+
|
332
|
+
return rb_field;
|
333
|
+
}
|
334
|
+
|
335
|
+
static VALUE rb_dm_result_fetch_field_type(VALUE self, unsigned int idx) {
|
336
|
+
VALUE rb_field_type;
|
337
|
+
VALUE rv;
|
338
|
+
GET_RESULT(self);
|
339
|
+
struct nogvl_get_col_args args;
|
340
|
+
args.wrapper = wrapper;
|
341
|
+
|
342
|
+
if(wrapper->is_bind == 0)
|
343
|
+
{
|
344
|
+
struct nogvl_get_col_args args;
|
345
|
+
args.wrapper = wrapper;
|
346
|
+
rv = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
347
|
+
if (rv == Qfalse)
|
348
|
+
rb_raise(cdmError, "failed to get col_desc");
|
349
|
+
}
|
350
|
+
|
351
|
+
if (wrapper->fieldTypes == Qnil) {
|
352
|
+
wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
|
353
|
+
}
|
354
|
+
|
355
|
+
rb_field_type = rb_ary_entry(wrapper->fieldTypes, idx);
|
356
|
+
if (rb_field_type == Qnil) {
|
357
|
+
DmColDesc field ;
|
358
|
+
rb_encoding *default_internal_enc = rb_default_internal_encoding();
|
359
|
+
rb_encoding *conn_enc = rb_to_encoding(wrapper->encoding);
|
360
|
+
int precision;
|
361
|
+
|
362
|
+
field = wrapper->col_desc[idx];
|
363
|
+
|
364
|
+
switch(field.sql_type) {
|
365
|
+
case DSQL_TINYINT: // signed char
|
366
|
+
rb_field_type = rb_sprintf("tinyint(%ld)", field.length);
|
367
|
+
break;
|
368
|
+
case DSQL_SMALLINT: // short int
|
369
|
+
rb_field_type = rb_sprintf("smallint(%ld)", field.length);
|
370
|
+
break;
|
371
|
+
case DSQL_INTERVAL_YEAR: // short int
|
372
|
+
rb_field_type = rb_str_new_cstr("year");
|
373
|
+
break;
|
374
|
+
case DSQL_INTERVAL_MONTH: // short int
|
375
|
+
rb_field_type = rb_str_new_cstr("month");
|
376
|
+
break;
|
377
|
+
case DSQL_INTERVAL_DAY: // short int
|
378
|
+
rb_field_type = rb_str_new_cstr("day");
|
379
|
+
break;
|
380
|
+
case DSQL_INTERVAL_HOUR: // short int
|
381
|
+
rb_field_type = rb_str_new_cstr("hour");
|
382
|
+
break;
|
383
|
+
case DSQL_INTERVAL_MINUTE: // short int
|
384
|
+
rb_field_type = rb_str_new_cstr("minute");
|
385
|
+
break;
|
386
|
+
case DSQL_INTERVAL_SECOND: // short int
|
387
|
+
rb_field_type = rb_str_new_cstr("second");
|
388
|
+
break;
|
389
|
+
case DSQL_INTERVAL_YEAR_TO_MONTH: // short int
|
390
|
+
rb_field_type = rb_str_new_cstr("year to month");
|
391
|
+
break;
|
392
|
+
case DSQL_INTERVAL_DAY_TO_HOUR: // short int
|
393
|
+
rb_field_type = rb_str_new_cstr("day to hour");
|
394
|
+
break;
|
395
|
+
case DSQL_INTERVAL_DAY_TO_MINUTE: // short int
|
396
|
+
rb_field_type = rb_str_new_cstr("day to minute");
|
397
|
+
break;
|
398
|
+
case DSQL_INTERVAL_DAY_TO_SECOND: // short int
|
399
|
+
rb_field_type = rb_str_new_cstr("day to second");
|
400
|
+
break;
|
401
|
+
case DSQL_INTERVAL_HOUR_TO_MINUTE: // short int
|
402
|
+
rb_field_type = rb_str_new_cstr("hour to minute");
|
403
|
+
break;
|
404
|
+
case DSQL_INTERVAL_HOUR_TO_SECOND: // short int
|
405
|
+
rb_field_type = rb_str_new_cstr("hour to second");
|
406
|
+
break;
|
407
|
+
case DSQL_INTERVAL_MINUTE_TO_SECOND: // short int
|
408
|
+
rb_field_type = rb_str_new_cstr("minute to second");
|
409
|
+
break;
|
410
|
+
case DSQL_INT: // int
|
411
|
+
rb_field_type = rb_sprintf("int(%ld)", field.length);
|
412
|
+
break;
|
413
|
+
case DSQL_BIGINT: // long long int
|
414
|
+
rb_field_type = rb_sprintf("bigint(%ld)", field.length);
|
415
|
+
break;
|
416
|
+
case DSQL_FLOAT: // float
|
417
|
+
rb_field_type = rb_sprintf("float(%ld,%d)", field.prec, field.scale);
|
418
|
+
break;
|
419
|
+
case DSQL_DOUBLE: // double
|
420
|
+
rb_field_type = rb_sprintf("double(%ld,%d)", field.prec, field.scale);
|
421
|
+
break;
|
422
|
+
case DSQL_TIME: // dm_TIME
|
423
|
+
rb_field_type = rb_str_new_cstr("time");
|
424
|
+
break;
|
425
|
+
case DSQL_DATE: // dm_TIME
|
426
|
+
rb_field_type = rb_str_new_cstr("date");
|
427
|
+
break;
|
428
|
+
case DSQL_TIMESTAMP: // dm_TIME
|
429
|
+
rb_field_type = rb_str_new_cstr("timestamp");
|
430
|
+
break;
|
431
|
+
case DSQL_TIME_TZ: // dm_TIME
|
432
|
+
rb_field_type = rb_str_new_cstr("time with time zone");
|
433
|
+
break;
|
434
|
+
case DSQL_TIMESTAMP_TZ: // dm_TIME
|
435
|
+
rb_field_type = rb_str_new_cstr("timestamp with time zone");
|
436
|
+
break;
|
437
|
+
case DSQL_DEC: // char[]
|
438
|
+
rb_field_type = rb_sprintf("decimal(%d,%d)", field.prec, field.scale);
|
439
|
+
break;
|
440
|
+
case DSQL_BINARY: // char[]
|
441
|
+
rb_field_type = rb_str_new_cstr("binary");
|
442
|
+
break;
|
443
|
+
case DSQL_VARBINARY: // char[]
|
444
|
+
rb_field_type = rb_str_new_cstr("varbinary");
|
445
|
+
break;
|
446
|
+
case DSQL_VARCHAR: // char[]
|
447
|
+
rb_field_type = rb_sprintf("varchar(%ld)", field.length);
|
448
|
+
break;
|
449
|
+
case DSQL_CHAR: // char[]
|
450
|
+
rb_field_type = rb_sprintf("char(%ld)", field.length);
|
451
|
+
break;
|
452
|
+
case DSQL_BLOB: // char[]
|
453
|
+
rb_field_type = rb_str_new_cstr("blob");
|
454
|
+
break;
|
455
|
+
case DSQL_CLOB: // char[]
|
456
|
+
rb_field_type = rb_str_new_cstr("clob");
|
457
|
+
break;
|
458
|
+
case DSQL_BIT: // char[]
|
459
|
+
rb_field_type = rb_sprintf("bit(%ld)", field.length);
|
460
|
+
break;
|
461
|
+
case DSQL_ROWID: // char[]
|
462
|
+
rb_field_type = rb_str_new_cstr("rowid");
|
463
|
+
break;
|
464
|
+
default:
|
465
|
+
rb_field_type = rb_str_new_cstr("unknown");
|
466
|
+
break;
|
467
|
+
}
|
468
|
+
|
469
|
+
rb_enc_associate(rb_field_type, conn_enc);
|
470
|
+
if (default_internal_enc) {
|
471
|
+
rb_field_type = rb_str_export_to_enc(rb_field_type, default_internal_enc);
|
472
|
+
}
|
473
|
+
|
474
|
+
rb_ary_store(wrapper->fieldTypes, idx, rb_field_type);
|
475
|
+
}
|
476
|
+
|
477
|
+
return rb_field_type;
|
478
|
+
}
|
479
|
+
|
480
|
+
static VALUE dm_set_field_string_encoding(VALUE val, rb_encoding *default_internal_enc, rb_encoding *conn_enc)
|
481
|
+
{
|
482
|
+
rb_enc_associate(val, conn_enc);
|
483
|
+
|
484
|
+
if (default_internal_enc) {
|
485
|
+
val = rb_str_export_to_enc(val, default_internal_enc);
|
486
|
+
}
|
487
|
+
|
488
|
+
return val;
|
489
|
+
}
|
490
|
+
|
491
|
+
/* Interpret microseconds digits left-aligned in fixed-width field.
|
492
|
+
* e.g. 10.123 seconds means 10 seconds and 123000 microseconds,
|
493
|
+
* because the microseconds are to the right of the decimal point.
|
494
|
+
*/
|
495
|
+
static unsigned int msec_char_to_uint(char *msec_char, size_t len)
|
496
|
+
{
|
497
|
+
size_t i;
|
498
|
+
for (i = 0; i < (len - 1); i++) {
|
499
|
+
if (msec_char[i] == '\0') {
|
500
|
+
msec_char[i] = '0';
|
501
|
+
}
|
502
|
+
}
|
503
|
+
return (unsigned int)strtoul(msec_char, NULL, 10);
|
504
|
+
}
|
505
|
+
|
506
|
+
static VALUE rb_dm_result_fetch_row(VALUE self,const result_each_args *args)
|
507
|
+
{
|
508
|
+
VALUE rowVal;
|
509
|
+
char** row;
|
510
|
+
unsigned int i = 0;
|
511
|
+
slength * fieldLengths;
|
512
|
+
void * ptr;
|
513
|
+
rb_encoding *default_internal_enc;
|
514
|
+
rb_encoding *conn_enc;
|
515
|
+
VALUE rv;
|
516
|
+
GET_RESULT(self);
|
517
|
+
|
518
|
+
default_internal_enc = rb_default_internal_encoding();
|
519
|
+
conn_enc = rb_to_encoding(wrapper->encoding);
|
520
|
+
|
521
|
+
if(wrapper->is_bind == 0)
|
522
|
+
{
|
523
|
+
struct nogvl_get_col_args args;
|
524
|
+
args.wrapper = wrapper;
|
525
|
+
rv = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
526
|
+
if (rv == Qfalse)
|
527
|
+
rb_raise(cdmError, "failed to get col_desc");
|
528
|
+
}
|
529
|
+
|
530
|
+
ptr = wrapper->result;
|
531
|
+
{
|
532
|
+
rv = (VALUE)rb_thread_call_without_gvl(nogvl_stmt_fetch, wrapper, RUBY_UBF_IO, 0);
|
533
|
+
if(rv == Qnil)
|
534
|
+
return Qnil;
|
535
|
+
else if(rv == Qfalse)
|
536
|
+
rb_raise(cdmError, "failed to fetch row");
|
537
|
+
}
|
538
|
+
|
539
|
+
row = wrapper->result;
|
540
|
+
|
541
|
+
if (wrapper->fields == Qnil) {
|
542
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
543
|
+
}
|
544
|
+
if (args->asArray) {
|
545
|
+
rowVal = rb_ary_new2(wrapper->numberOfFields);
|
546
|
+
} else {
|
547
|
+
rowVal = rb_hash_new();
|
548
|
+
}
|
549
|
+
fieldLengths = wrapper->length;
|
550
|
+
|
551
|
+
for (i = 0; i < wrapper->numberOfFields; i++) {
|
552
|
+
VALUE field = rb_dm_result_fetch_field(self, i, args->symbolizeKeys);
|
553
|
+
if (row[i] && fieldLengths[i]>0) {
|
554
|
+
VALUE val = Qnil;
|
555
|
+
sdint2 type = wrapper->col_desc[i].sql_type;
|
556
|
+
|
557
|
+
if (!args->cast) {
|
558
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
559
|
+
val = dm_set_field_string_encoding(val, default_internal_enc, conn_enc);
|
560
|
+
} else {
|
561
|
+
switch(type) {
|
562
|
+
case DSQL_BIT:
|
563
|
+
if (args->castBool && fieldLengths[i] == 1) {
|
564
|
+
val = *row[i] == '1' ? Qtrue : Qfalse;
|
565
|
+
}else{
|
566
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
567
|
+
}
|
568
|
+
break;
|
569
|
+
case DSQL_TINYINT: /* TINYINT field */
|
570
|
+
if (args->castBool && fieldLengths[i] == 1) {
|
571
|
+
val = *row[i] != '0' ? Qtrue : Qfalse;
|
572
|
+
break;
|
573
|
+
}
|
574
|
+
case DSQL_SMALLINT: /* SMALLINT field */
|
575
|
+
case DSQL_INT: /* INTEGER field */
|
576
|
+
case DSQL_BIGINT: /* BIGINT field */
|
577
|
+
val = rb_cstr2inum(row[i], 10);
|
578
|
+
break;
|
579
|
+
case DSQL_DEC: /* DECIMAL or NUMERIC field */
|
580
|
+
if (wrapper->col_desc[i].scale == 0) {
|
581
|
+
val = rb_cstr2inum(row[i], 10);
|
582
|
+
} else if (strtod(row[i], NULL) == 0.000000){
|
583
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, opt_decimal_zero);
|
584
|
+
}else{
|
585
|
+
val = rb_funcall(rb_mKernel, intern_BigDecimal, 1, rb_str_new(row[i], fieldLengths[i]));
|
586
|
+
}
|
587
|
+
break;
|
588
|
+
case DSQL_FLOAT: /* FLOAT field */
|
589
|
+
case DSQL_DOUBLE: { /* DOUBLE or REAL field */
|
590
|
+
double column_to_double;
|
591
|
+
column_to_double = strtod(row[i], NULL);
|
592
|
+
if (column_to_double == 0.000000){
|
593
|
+
val = opt_float_zero;
|
594
|
+
}else{
|
595
|
+
val = rb_float_new(column_to_double);
|
596
|
+
}
|
597
|
+
break;
|
598
|
+
}
|
599
|
+
case DSQL_TIME: { /* TIME field */
|
600
|
+
int tokens;
|
601
|
+
unsigned int hour=0, min=0, sec=0, msec=0;
|
602
|
+
char msec_char[7] = {'0','0','0','0','0','0','\0'};
|
603
|
+
|
604
|
+
tokens = sscanf(row[i], "%2u:%2u:%2u.%6s", &hour, &min, &sec, msec_char);
|
605
|
+
if (tokens < 3) {
|
606
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
607
|
+
val = dm_set_field_string_encoding(val, default_internal_enc, conn_enc);
|
608
|
+
break;
|
609
|
+
}
|
610
|
+
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
611
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, opt_time_year, opt_time_month, opt_time_month, UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
612
|
+
if (!NIL_P(args->app_timezone)) {
|
613
|
+
if (args->app_timezone == intern_local) {
|
614
|
+
val = rb_funcall(val, intern_localtime, 0);
|
615
|
+
} else { /* utc */
|
616
|
+
val = rb_funcall(val, intern_utc, 0);
|
617
|
+
}
|
618
|
+
}
|
619
|
+
break;
|
620
|
+
}
|
621
|
+
case DSQL_TIMESTAMP: {/* TIMESTAMP field */
|
622
|
+
int tokens;
|
623
|
+
unsigned int year=0, month=0, day=0, hour=0, min=0, sec=0, msec=0;
|
624
|
+
char msec_char[7] = {'0','0','0','0','0','0','\0'};
|
625
|
+
uint64_t seconds;
|
626
|
+
|
627
|
+
tokens = sscanf(row[i], "%4u-%2u-%2u %2u:%2u:%2u.%6s", &year, &month, &day, &hour, &min, &sec, msec_char);
|
628
|
+
if (tokens < 6) { /* msec might be empty */
|
629
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
630
|
+
val = dm_set_field_string_encoding(val, default_internal_enc, conn_enc);
|
631
|
+
break;
|
632
|
+
}
|
633
|
+
seconds = (year*31557600ULL) + (month*2592000ULL) + (day*86400ULL) + (hour*3600ULL) + (min*60ULL) + sec;
|
634
|
+
|
635
|
+
if (seconds == 0) {
|
636
|
+
val = Qnil;
|
637
|
+
} else {
|
638
|
+
if (month < 1 || day < 1) {
|
639
|
+
rb_raise(cdmError, "Invalid date in field '%.*s': %s", wrapper->col_desc[i].name_length, wrapper->col_desc[i].name, row[i]);
|
640
|
+
val = Qnil;
|
641
|
+
} else {
|
642
|
+
if (seconds < dm_MIN_TIME || seconds > dm_MAX_TIME) { /* use DateTime for larger date range, does not support microseconds */
|
643
|
+
VALUE offset = INT2NUM(0);
|
644
|
+
if (args->db_timezone == intern_local) {
|
645
|
+
offset = rb_funcall(cdmClient, intern_local_offset, 0);
|
646
|
+
}
|
647
|
+
val = rb_funcall(cDateTime, intern_civil, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), offset);
|
648
|
+
if (!NIL_P(args->app_timezone)) {
|
649
|
+
if (args->app_timezone == intern_local) {
|
650
|
+
offset = rb_funcall(cdmClient, intern_local_offset, 0);
|
651
|
+
val = rb_funcall(val, intern_new_offset, 1, offset);
|
652
|
+
} else { /* utc */
|
653
|
+
val = rb_funcall(val, intern_new_offset, 1, opt_utc_offset);
|
654
|
+
}
|
655
|
+
}
|
656
|
+
} else {
|
657
|
+
msec = msec_char_to_uint(msec_char, sizeof(msec_char));
|
658
|
+
val = rb_funcall(rb_cTime, args->db_timezone, 7, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day), UINT2NUM(hour), UINT2NUM(min), UINT2NUM(sec), UINT2NUM(msec));
|
659
|
+
if (!NIL_P(args->app_timezone)) {
|
660
|
+
if (args->app_timezone == intern_local) {
|
661
|
+
val = rb_funcall(val, intern_localtime, 0);
|
662
|
+
} else { /* utc */
|
663
|
+
val = rb_funcall(val, intern_utc, 0);
|
664
|
+
}
|
665
|
+
}
|
666
|
+
}
|
667
|
+
}
|
668
|
+
}
|
669
|
+
break;
|
670
|
+
}
|
671
|
+
case DSQL_DATE:{ /* DATE field */
|
672
|
+
int tokens;
|
673
|
+
unsigned int year=0, month=0, day=0;
|
674
|
+
tokens = sscanf(row[i], "%4u-%2u-%2u", &year, &month, &day);
|
675
|
+
if (tokens < 3) {
|
676
|
+
val = rb_str_new(row[i], fieldLengths[i]);
|
677
|
+
val = dm_set_field_string_encoding(val, default_internal_enc, conn_enc);
|
678
|
+
break;
|
679
|
+
}
|
680
|
+
if (year+month+day == 0) {
|
681
|
+
val = Qnil;
|
682
|
+
} else {
|
683
|
+
if (month < 1 || day < 1) {
|
684
|
+
rb_raise(cdmError, "Invalid date in field '%.*s': %s", wrapper->col_desc[i].name_length, wrapper->col_desc[i].name, row[i]);
|
685
|
+
val = Qnil;
|
686
|
+
} else {
|
687
|
+
val = rb_funcall(cDate, intern_new, 3, UINT2NUM(year), UINT2NUM(month), UINT2NUM(day));
|
688
|
+
}
|
689
|
+
}
|
690
|
+
break;
|
691
|
+
}
|
692
|
+
case DSQL_BLOB:
|
693
|
+
case DSQL_VARCHAR:
|
694
|
+
default:
|
695
|
+
val = rb_str_new(row[i], strlen(row[i]));
|
696
|
+
val = dm_set_field_string_encoding(val, default_internal_enc, conn_enc);
|
697
|
+
break;
|
698
|
+
}
|
699
|
+
}
|
700
|
+
if (args->asArray) {
|
701
|
+
rb_ary_push(rowVal, val);
|
702
|
+
} else {
|
703
|
+
rb_hash_aset(rowVal, field, val);
|
704
|
+
}
|
705
|
+
} else {
|
706
|
+
if (args->asArray) {
|
707
|
+
rb_ary_push(rowVal, Qnil);
|
708
|
+
} else {
|
709
|
+
rb_hash_aset(rowVal, field, Qnil);
|
710
|
+
}
|
711
|
+
}
|
712
|
+
}
|
713
|
+
return rowVal;
|
714
|
+
}
|
715
|
+
|
716
|
+
static VALUE rb_dm_result_fetch_fields(VALUE self) {
|
717
|
+
unsigned int i = 0;
|
718
|
+
short int symbolizeKeys = 0;
|
719
|
+
VALUE rv;
|
720
|
+
|
721
|
+
GET_RESULT(self);
|
722
|
+
|
723
|
+
if(wrapper->resultFreed)
|
724
|
+
{
|
725
|
+
rb_raise(cdmError, "result is freed");
|
726
|
+
}
|
727
|
+
|
728
|
+
if(wrapper->is_bind == 0)
|
729
|
+
{
|
730
|
+
struct nogvl_get_col_args args;
|
731
|
+
args.wrapper = wrapper;
|
732
|
+
rv = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
733
|
+
if (rv == Qfalse)
|
734
|
+
rb_raise(cdmError, "failed to get col_desc");
|
735
|
+
}
|
736
|
+
|
737
|
+
if (wrapper->fields == Qnil) {
|
738
|
+
wrapper->fields = rb_ary_new2(wrapper->numberOfFields);
|
739
|
+
}
|
740
|
+
|
741
|
+
if (RARRAY_LEN(wrapper->fields) != wrapper->numberOfFields) {
|
742
|
+
for (i=0; i<wrapper->numberOfFields; i++) {
|
743
|
+
rb_dm_result_fetch_field(self, i, symbolizeKeys);
|
744
|
+
}
|
745
|
+
}
|
746
|
+
|
747
|
+
return wrapper->fields;
|
748
|
+
}
|
749
|
+
|
750
|
+
static VALUE rb_dm_result_fetch_field_types(VALUE self) {
|
751
|
+
unsigned int i = 0;
|
752
|
+
VALUE rv;
|
753
|
+
GET_RESULT(self);
|
754
|
+
|
755
|
+
if(wrapper->resultFreed)
|
756
|
+
{
|
757
|
+
rb_raise(cdmError, "result is freed");
|
758
|
+
}
|
759
|
+
|
760
|
+
if(wrapper->is_bind == 0)
|
761
|
+
{
|
762
|
+
struct nogvl_get_col_args args;
|
763
|
+
args.wrapper = wrapper;
|
764
|
+
rv = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
765
|
+
if (rv == Qfalse)
|
766
|
+
rb_raise(cdmError, "failed to get col_desc");
|
767
|
+
}
|
768
|
+
|
769
|
+
if (wrapper->fieldTypes == Qnil) {
|
770
|
+
wrapper->fieldTypes = rb_ary_new2(wrapper->numberOfFields);
|
771
|
+
}
|
772
|
+
|
773
|
+
if (RARRAY_LEN(wrapper->fieldTypes) != wrapper->numberOfFields) {
|
774
|
+
for (i=0; i<wrapper->numberOfFields; i++) {
|
775
|
+
rb_dm_result_fetch_field_type(self, i);
|
776
|
+
}
|
777
|
+
}
|
778
|
+
|
779
|
+
return wrapper->fieldTypes;
|
780
|
+
}
|
781
|
+
|
782
|
+
static VALUE rb_dm_result_each_(VALUE self,
|
783
|
+
VALUE(*fetch_row_func)(VALUE, const result_each_args *args),
|
784
|
+
const result_each_args *args)
|
785
|
+
{
|
786
|
+
unsigned long i;
|
787
|
+
const char *errstr;
|
788
|
+
|
789
|
+
GET_RESULT(self);
|
790
|
+
|
791
|
+
if (args->cacheRows && wrapper->lastRowProcessed == wrapper->numberOfRows) {
|
792
|
+
/* we've already read the entire dataset from the C result into our */
|
793
|
+
/* internal array. Lets hand that over to the user since it's ready to go */
|
794
|
+
for (i = 0; i < wrapper->numberOfRows; i++) {
|
795
|
+
rb_yield(rb_ary_entry(wrapper->rows, i));
|
796
|
+
}
|
797
|
+
} else {
|
798
|
+
unsigned long rowsProcessed = 0;
|
799
|
+
rowsProcessed = RARRAY_LEN(wrapper->rows);
|
800
|
+
|
801
|
+
for (i = 0; i < wrapper->numberOfRows; i++) {
|
802
|
+
VALUE row;
|
803
|
+
if (args->cacheRows && i < rowsProcessed) {
|
804
|
+
row = rb_ary_entry(wrapper->rows, i);
|
805
|
+
} else {
|
806
|
+
row = fetch_row_func(self, args);
|
807
|
+
if (args->cacheRows) {
|
808
|
+
rb_ary_store(wrapper->rows, i, row);
|
809
|
+
}
|
810
|
+
wrapper->lastRowProcessed++;
|
811
|
+
}
|
812
|
+
|
813
|
+
if (row == Qnil) {
|
814
|
+
/* we don't need the dm C dataset around anymore, peace it */
|
815
|
+
return Qnil;
|
816
|
+
}
|
817
|
+
|
818
|
+
if (args->block_given) {
|
819
|
+
rb_yield(row);
|
820
|
+
}
|
821
|
+
}
|
822
|
+
}
|
823
|
+
|
824
|
+
// FIXME return Enumerator instead?
|
825
|
+
// return rb_ary_each(wrapper->rows);
|
826
|
+
return wrapper->rows;
|
827
|
+
}
|
828
|
+
|
829
|
+
static VALUE rb_dm_result_each(int argc, VALUE * argv, VALUE self) {
|
830
|
+
result_each_args args;
|
831
|
+
VALUE rv;
|
832
|
+
VALUE defaults, opts, (*fetch_row_func)(VALUE, const result_each_args *args);
|
833
|
+
ID db_timezone, app_timezone, dbTz, appTz;
|
834
|
+
int symbolizeKeys, asArray, castBool, cacheRows, cast;
|
835
|
+
|
836
|
+
GET_RESULT(self);
|
837
|
+
|
838
|
+
if(wrapper->resultFreed)
|
839
|
+
{
|
840
|
+
rb_raise(cdmError, "result is freed");
|
841
|
+
}
|
842
|
+
|
843
|
+
if (!wrapper->statement) {
|
844
|
+
rb_raise(cdmError, "Statement handle already closed");
|
845
|
+
}
|
846
|
+
|
847
|
+
if(wrapper->is_bind == 0)
|
848
|
+
{
|
849
|
+
struct nogvl_get_col_args args;
|
850
|
+
args.wrapper = wrapper;
|
851
|
+
rv = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
852
|
+
if (rv == Qfalse)
|
853
|
+
rb_raise(cdmError, "failed to get col_desc");
|
854
|
+
}
|
855
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
856
|
+
// A block can be passed to this method, but since we don't call the block directly from C,
|
857
|
+
// we don't need to capture it into a variable here with the "&" scan arg.
|
858
|
+
if (rb_scan_args(argc, argv, "01", &opts) == 1)
|
859
|
+
{
|
860
|
+
opts = rb_funcall(defaults, intern_merge, 1, opts);
|
861
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
862
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
863
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
864
|
+
cacheRows = RTEST(rb_hash_aref(opts, sym_cache_rows));
|
865
|
+
cast = RTEST(rb_hash_aref(opts, sym_cast));
|
866
|
+
}
|
867
|
+
else if(defaults != Qnil)
|
868
|
+
{
|
869
|
+
opts = defaults;
|
870
|
+
symbolizeKeys = RTEST(rb_hash_aref(opts, sym_symbolize_keys));
|
871
|
+
asArray = rb_hash_aref(opts, sym_as) == sym_array;
|
872
|
+
castBool = RTEST(rb_hash_aref(opts, sym_cast_booleans));
|
873
|
+
cacheRows = 1;
|
874
|
+
cast = 1;
|
875
|
+
}
|
876
|
+
else
|
877
|
+
{
|
878
|
+
|
879
|
+
symbolizeKeys = 0;
|
880
|
+
asArray = 0;
|
881
|
+
castBool = 0;
|
882
|
+
cacheRows = 1;
|
883
|
+
cast = 1;
|
884
|
+
}
|
885
|
+
|
886
|
+
db_timezone = intern_local;
|
887
|
+
app_timezone = Qnil;
|
888
|
+
|
889
|
+
if (wrapper->rows == Qnil ) {
|
890
|
+
wrapper->rows = rb_ary_new2(wrapper->numberOfRows);
|
891
|
+
}
|
892
|
+
|
893
|
+
// Backward compat
|
894
|
+
args.symbolizeKeys = symbolizeKeys;
|
895
|
+
args.asArray = asArray;
|
896
|
+
args.castBool = castBool;
|
897
|
+
args.cacheRows = cacheRows;
|
898
|
+
args.cast = cast;
|
899
|
+
args.db_timezone = db_timezone;
|
900
|
+
args.app_timezone = app_timezone;
|
901
|
+
args.block_given = rb_block_given_p();
|
902
|
+
|
903
|
+
|
904
|
+
fetch_row_func = rb_dm_result_fetch_row;
|
905
|
+
|
906
|
+
return rb_dm_result_each_(self, fetch_row_func, &args);
|
907
|
+
}
|
908
|
+
|
909
|
+
static VALUE rb_dm_result_count(VALUE self) {
|
910
|
+
GET_RESULT(self);
|
911
|
+
VALUE rt;
|
912
|
+
if(wrapper->resultFreed)
|
913
|
+
{
|
914
|
+
rb_raise(cdmError, "result is freed");
|
915
|
+
}
|
916
|
+
if(wrapper->is_bind == 0)
|
917
|
+
{
|
918
|
+
struct nogvl_get_col_args args;
|
919
|
+
args.wrapper = wrapper;
|
920
|
+
rt = (VALUE)rb_thread_call_without_gvl(nogvl_get_col_desc, &args, RUBY_UBF_IO, 0);
|
921
|
+
if (rt == Qfalse)
|
922
|
+
rb_raise(cdmError, "failed to get col_desc");
|
923
|
+
}
|
924
|
+
|
925
|
+
return ULONG2NUM(wrapper->numberOfRows);
|
926
|
+
}
|
927
|
+
|
928
|
+
static void *nogvl_get_next_result(void *ptr) {
|
929
|
+
dm_result_wrapper *wrapper = ptr;
|
930
|
+
DPIRETURN rt;
|
931
|
+
rt = dpi_more_results(wrapper->statement);
|
932
|
+
if(rt == DSQL_NO_DATA)
|
933
|
+
return (void*)Qnil;
|
934
|
+
else if(!DSQL_SUCCEEDED(rt))
|
935
|
+
return (void*)Qfalse;
|
936
|
+
else
|
937
|
+
return (void*)Qtrue;
|
938
|
+
|
939
|
+
}
|
940
|
+
|
941
|
+
static VALUE rb_dm_next_result(VALUE self) {
|
942
|
+
GET_RESULT(self);
|
943
|
+
VALUE rt,defaults,obj;
|
944
|
+
|
945
|
+
if(wrapper->resultFreed)
|
946
|
+
{
|
947
|
+
rb_raise(cdmError, "result is freed");
|
948
|
+
}
|
949
|
+
|
950
|
+
rt = (VALUE)rb_thread_call_without_gvl(nogvl_get_next_result, wrapper, RUBY_UBF_IO, 0);
|
951
|
+
if(rt == Qnil)
|
952
|
+
return Qnil;
|
953
|
+
else if(rt == Qfalse)
|
954
|
+
rb_raise(cdmError, "failed to get next result");
|
955
|
+
|
956
|
+
defaults = rb_ivar_get(self, intern_query_options);
|
957
|
+
obj = rb_dm_result_to_obj(wrapper->client, wrapper->encoding, wrapper->statement, 1, defaults);
|
958
|
+
return obj;
|
959
|
+
}
|
960
|
+
|
961
|
+
/* dm::Result */
|
962
|
+
VALUE rb_dm_result_to_obj(VALUE client, VALUE encoding, dhstmt statement, int flag, VALUE options) {
|
963
|
+
VALUE obj;
|
964
|
+
dm_result_wrapper * wrapper;
|
965
|
+
DPIRETURN rt;
|
966
|
+
|
967
|
+
#ifdef NEW_TYPEDDATA_WRAPPER
|
968
|
+
obj = TypedData_Make_Struct(cdmResult, dm_result_wrapper, &rb_dm_result_type, wrapper);
|
969
|
+
#else
|
970
|
+
obj = Data_Make_Struct(cdmResult, dm_result_wrapper, rb_dm_result_mark, rb_dm_result_free, wrapper);
|
971
|
+
#endif
|
972
|
+
wrapper->numberOfFields = 0;
|
973
|
+
wrapper->numberOfRows = 0;
|
974
|
+
wrapper->resultFreed = 0;
|
975
|
+
wrapper->fields = Qnil;
|
976
|
+
wrapper->fieldTypes = Qnil;
|
977
|
+
wrapper->rows = Qnil;
|
978
|
+
wrapper->encoding = encoding;
|
979
|
+
wrapper->client = client;
|
980
|
+
wrapper->client_wrapper = DATA_PTR(client);
|
981
|
+
wrapper->client_wrapper->refcount++;
|
982
|
+
wrapper->result = NULL;
|
983
|
+
wrapper->length = NULL;
|
984
|
+
wrapper->lobs = NULL;
|
985
|
+
wrapper->col_desc = NULL;
|
986
|
+
wrapper->is_bind = 0;
|
987
|
+
/* Keep a handle to the Statement to ensure it doesn't get garbage collected first */
|
988
|
+
wrapper->statement = statement;
|
989
|
+
wrapper->is_prepare = flag;
|
990
|
+
|
991
|
+
rb_obj_call_init(obj, 0, NULL);
|
992
|
+
rb_ivar_set(obj, intern_query_options, options);
|
993
|
+
|
994
|
+
/* Options that cannot be changed in results.each(...) { |row| }
|
995
|
+
* should be processed here. */
|
996
|
+
|
997
|
+
return obj;
|
998
|
+
}
|
999
|
+
|
1000
|
+
void init_dm_result() {
|
1001
|
+
cDate = rb_const_get(rb_cObject, rb_intern("Date"));
|
1002
|
+
rb_global_variable(&cDate);
|
1003
|
+
cDateTime = rb_const_get(rb_cObject, rb_intern("DateTime"));
|
1004
|
+
rb_global_variable(&cDateTime);
|
1005
|
+
|
1006
|
+
cdmResult = rb_define_class_under(mdm, "Result", rb_cObject);
|
1007
|
+
rb_undef_alloc_func(cdmResult);
|
1008
|
+
rb_global_variable(&cdmResult);
|
1009
|
+
|
1010
|
+
rb_define_method(cdmResult, "each", rb_dm_result_each, -1);
|
1011
|
+
rb_define_method(cdmResult, "fields", rb_dm_result_fetch_fields, 0);
|
1012
|
+
rb_define_method(cdmResult, "field_types", rb_dm_result_fetch_field_types, 0);
|
1013
|
+
rb_define_method(cdmResult, "free", rb_dm_result_free_, 0);
|
1014
|
+
rb_define_method(cdmResult, "count", rb_dm_result_count, 0);
|
1015
|
+
rb_define_alias(cdmResult, "size", "count");
|
1016
|
+
rb_define_method(cdmResult, "next_result", rb_dm_next_result, 0);
|
1017
|
+
|
1018
|
+
intern_new = rb_intern("new");
|
1019
|
+
intern_utc = rb_intern("utc");
|
1020
|
+
intern_local = rb_intern("local");
|
1021
|
+
intern_merge = rb_intern("merge");
|
1022
|
+
intern_localtime = rb_intern("localtime");
|
1023
|
+
|
1024
|
+
intern_local_offset = rb_intern("local_offset");
|
1025
|
+
intern_civil = rb_intern("civil");
|
1026
|
+
intern_new_offset = rb_intern("new_offset");
|
1027
|
+
intern_BigDecimal = rb_intern("BigDecimal");
|
1028
|
+
intern_query_options = rb_intern("@query_options");
|
1029
|
+
|
1030
|
+
sym_symbolize_keys = ID2SYM(rb_intern("symbolize_keys"));
|
1031
|
+
sym_as = ID2SYM(rb_intern("as"));
|
1032
|
+
sym_array = ID2SYM(rb_intern("array"));
|
1033
|
+
sym_local = ID2SYM(rb_intern("local"));
|
1034
|
+
sym_utc = ID2SYM(rb_intern("utc"));
|
1035
|
+
sym_cast_booleans = ID2SYM(rb_intern("cast_booleans"));
|
1036
|
+
sym_database_timezone = ID2SYM(rb_intern("database_timezone"));
|
1037
|
+
sym_application_timezone = ID2SYM(rb_intern("application_timezone"));
|
1038
|
+
sym_cache_rows = ID2SYM(rb_intern("cache_rows"));
|
1039
|
+
sym_cast = ID2SYM(rb_intern("cast"));
|
1040
|
+
sym_stream = ID2SYM(rb_intern("stream"));
|
1041
|
+
sym_name = ID2SYM(rb_intern("name"));
|
1042
|
+
|
1043
|
+
opt_decimal_zero = rb_str_new2("0.0");
|
1044
|
+
rb_global_variable(&opt_decimal_zero); /*never GC */
|
1045
|
+
opt_float_zero = rb_float_new((double)0);
|
1046
|
+
rb_global_variable(&opt_float_zero);
|
1047
|
+
opt_time_year = INT2NUM(2000);
|
1048
|
+
opt_time_month = INT2NUM(1);
|
1049
|
+
opt_utc_offset = INT2NUM(0);
|
1050
|
+
|
1051
|
+
binaryEncoding = rb_enc_find("binary");
|
1052
|
+
}
|