do_oracle 0.10.1-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,903 @@
1
+ #ifdef _WIN32
2
+ #define do_int64 signed __int64
3
+ #else
4
+ #define do_int64 signed long long int
5
+ #endif
6
+
7
+ #include <ruby.h>
8
+ #include <string.h>
9
+ #include <math.h>
10
+ #include <ctype.h>
11
+ #include <time.h>
12
+
13
+ #define ID_CONST_GET rb_intern("const_get")
14
+
15
+ #define RUBY_STRING(char_ptr) rb_str_new2(char_ptr)
16
+ #define TAINTED_STRING(name, length) rb_tainted_str_new(name, length)
17
+ #define CONST_GET(scope, constant) (rb_funcall(scope, ID_CONST_GET, 1, rb_str_new2(constant)))
18
+ #define ORACLE_CLASS(klass, parent) (rb_define_class_under(mOracle, klass, parent))
19
+ #define DEBUG(value) data_objects_debug(value)
20
+ #define RUBY_CLASS(name) rb_const_get(rb_cObject, rb_intern(name))
21
+
22
+ #ifndef RSTRING_PTR
23
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
24
+ #endif
25
+
26
+ #ifndef RSTRING_LEN
27
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
28
+ #endif
29
+
30
+ #ifndef RARRAY_LEN
31
+ #define RARRAY_LEN(a) RARRAY(a)->len
32
+ #endif
33
+
34
+
35
+ // To store rb_intern values
36
+ static ID ID_NEW;
37
+ static ID ID_NEW_DATE;
38
+ static ID ID_LOGGER;
39
+ static ID ID_DEBUG;
40
+ static ID ID_LEVEL;
41
+ static ID ID_TO_S;
42
+ static ID ID_RATIONAL;
43
+
44
+ static ID ID_NAME;
45
+
46
+ static ID ID_NUMBER;
47
+ static ID ID_VARCHAR2;
48
+ static ID ID_CHAR;
49
+ static ID ID_DATE;
50
+ static ID ID_TIMESTAMP;
51
+ static ID ID_TIMESTAMP_TZ;
52
+ static ID ID_TIMESTAMP_LTZ;
53
+ static ID ID_CLOB;
54
+ static ID ID_BLOB;
55
+ static ID ID_LONG;
56
+ static ID ID_RAW;
57
+ static ID ID_LONG_RAW;
58
+ static ID ID_BFILE;
59
+ static ID ID_BINARY_FLOAT;
60
+ static ID ID_BINARY_DOUBLE;
61
+
62
+ static ID ID_TO_A;
63
+ static ID ID_TO_I;
64
+ static ID ID_TO_S;
65
+ static ID ID_TO_F;
66
+
67
+ static ID ID_UTC_OFFSET;
68
+ static ID ID_FULL_CONST_GET;
69
+
70
+ static ID ID_PARSE;
71
+ static ID ID_FETCH;
72
+ static ID ID_TYPE;
73
+ static ID ID_EXECUTE;
74
+ static ID ID_EXEC;
75
+
76
+ static ID ID_SELECT_STMT;
77
+ static ID ID_COLUMN_METADATA;
78
+ static ID ID_PRECISION;
79
+ static ID ID_SCALE;
80
+ static ID ID_BIND_PARAM;
81
+ static ID ID_ELEM;
82
+ static ID ID_READ;
83
+
84
+ static ID ID_CLOSE;
85
+ static ID ID_LOGOFF;
86
+
87
+ static VALUE mExtlib;
88
+ static VALUE mDO;
89
+ static VALUE cDO_Quoting;
90
+ static VALUE cDO_Connection;
91
+ static VALUE cDO_Command;
92
+ static VALUE cDO_Result;
93
+ static VALUE cDO_Reader;
94
+
95
+ static VALUE rb_cDate;
96
+ static VALUE rb_cDateTime;
97
+ static VALUE rb_cBigDecimal;
98
+ static VALUE rb_cByteArray;
99
+
100
+ static VALUE cOCI8;
101
+ static VALUE cOCI8_Cursor;
102
+ static VALUE cOCI8_BLOB;
103
+ static VALUE cOCI8_CLOB;
104
+
105
+ static VALUE mOracle;
106
+ static VALUE cConnection;
107
+ static VALUE cCommand;
108
+ static VALUE cResult;
109
+ static VALUE cReader;
110
+
111
+ static VALUE eArgumentError;
112
+ static VALUE eSQLError;
113
+ static VALUE eConnectionError;
114
+ static VALUE eDataError;
115
+
116
+ static void data_objects_debug(VALUE string, struct timeval* start) {
117
+ struct timeval stop;
118
+ char *message;
119
+
120
+ char *query = RSTRING_PTR(string);
121
+ int length = RSTRING_LEN(string);
122
+ char total_time[32];
123
+ do_int64 duration = 0;
124
+
125
+ VALUE logger = rb_funcall(mOracle, ID_LOGGER, 0);
126
+ int log_level = NUM2INT(rb_funcall(logger, ID_LEVEL, 0));
127
+
128
+ if (0 == log_level) {
129
+ gettimeofday(&stop, NULL);
130
+
131
+ duration = (stop.tv_sec - start->tv_sec) * 1000000 + stop.tv_usec - start->tv_usec;
132
+
133
+ snprintf(total_time, 32, "%.6f", duration / 1000000.0);
134
+ message = (char *)calloc(length + strlen(total_time) + 4, sizeof(char));
135
+ snprintf(message, length + strlen(total_time) + 4, "(%s) %s", total_time, query);
136
+ rb_funcall(logger, ID_DEBUG, 1, rb_str_new(message, length + strlen(total_time) + 3));
137
+ free(message);
138
+ }
139
+ }
140
+
141
+ static char * get_uri_option(VALUE query_hash, char * key) {
142
+ VALUE query_value;
143
+ char * value = NULL;
144
+
145
+ if(!rb_obj_is_kind_of(query_hash, rb_cHash)) { return NULL; }
146
+
147
+ query_value = rb_hash_aref(query_hash, RUBY_STRING(key));
148
+
149
+ if (Qnil != query_value) {
150
+ value = StringValuePtr(query_value);
151
+ }
152
+
153
+ return value;
154
+ }
155
+
156
+ /* ====== Time/Date Parsing Helper Functions ====== */
157
+ static void reduce( do_int64 *numerator, do_int64 *denominator ) {
158
+ do_int64 a, b, c;
159
+ a = *numerator;
160
+ b = *denominator;
161
+ while ( a != 0 ) {
162
+ c = a; a = b % a; b = c;
163
+ }
164
+ *numerator = *numerator / b;
165
+ *denominator = *denominator / b;
166
+ }
167
+
168
+ // Generate the date integer which Date.civil_to_jd returns
169
+ static int jd_from_date(int year, int month, int day) {
170
+ int a, b;
171
+ if ( month <= 2 ) {
172
+ year -= 1;
173
+ month += 12;
174
+ }
175
+ a = year / 100;
176
+ b = 2 - a + (a / 4);
177
+ return floor(365.25 * (year + 4716)) + floor(30.6001 * (month + 1)) + day + b - 1524;
178
+ }
179
+
180
+ // Creates a Rational for use as a Timezone offset to be passed to DateTime.new!
181
+ static VALUE seconds_to_offset(do_int64 num) {
182
+ do_int64 den = 86400;
183
+ reduce(&num, &den);
184
+ return rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ll2inum(num), rb_ll2inum(den));
185
+ }
186
+
187
+ static VALUE timezone_to_offset(int hour_offset, int minute_offset) {
188
+ do_int64 seconds = 0;
189
+
190
+ seconds += hour_offset * 3600;
191
+ seconds += minute_offset * 60;
192
+
193
+ return seconds_to_offset(seconds);
194
+ }
195
+
196
+ // Implementation using C functions
197
+
198
+ static VALUE parse_date(VALUE r_value) {
199
+ VALUE time_array;
200
+ int year, month, day;
201
+ int jd, ajd;
202
+ VALUE rational;
203
+
204
+ if (rb_obj_class(r_value) == rb_cTime) {
205
+ time_array = rb_funcall(r_value, ID_TO_A, 0);
206
+ year = NUM2INT(rb_ary_entry(time_array, 5));
207
+ month = NUM2INT(rb_ary_entry(time_array, 4));
208
+ day = NUM2INT(rb_ary_entry(time_array, 3));
209
+
210
+ jd = jd_from_date(year, month, day);
211
+
212
+ // Math from Date.jd_to_ajd
213
+ ajd = jd * 2 - 1;
214
+ rational = rb_funcall(rb_mKernel, ID_RATIONAL, 2, INT2NUM(ajd), INT2NUM(2));
215
+
216
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
217
+
218
+ } else if (rb_obj_class(r_value) == rb_cDate) {
219
+ return r_value;
220
+
221
+ } else if (rb_obj_class(r_value) == rb_cDateTime) {
222
+ rational = rb_iv_get(r_value, "@ajd");
223
+ return rb_funcall(rb_cDate, ID_NEW_DATE, 3, rational, INT2NUM(0), INT2NUM(2299161));
224
+
225
+ } else {
226
+ // Something went terribly wrong
227
+ rb_raise(eDataError, "Couldn't parse date from class %s object", rb_obj_classname(r_value));
228
+ }
229
+ }
230
+
231
+ // Implementation using C functions
232
+
233
+ static VALUE parse_date_time(VALUE r_value) {
234
+ VALUE ajd, offset;
235
+
236
+ VALUE time_array;
237
+ int year, month, day, hour, min, sec, hour_offset, minute_offset;
238
+ // int usec;
239
+ int jd;
240
+ do_int64 num, den;
241
+
242
+ long int gmt_offset;
243
+ int is_dst;
244
+
245
+ // time_t rawtime;
246
+ // struct tm * timeinfo;
247
+
248
+ // int tokens_read, max_tokens;
249
+
250
+ if (rb_obj_class(r_value) == rb_cDateTime) {
251
+ return r_value;
252
+ } else if (rb_obj_class(r_value) == rb_cTime) {
253
+ time_array = rb_funcall(r_value, ID_TO_A, 0);
254
+ year = NUM2INT(rb_ary_entry(time_array, 5));
255
+ month = NUM2INT(rb_ary_entry(time_array, 4));
256
+ day = NUM2INT(rb_ary_entry(time_array, 3));
257
+ hour = NUM2INT(rb_ary_entry(time_array, 2));
258
+ min = NUM2INT(rb_ary_entry(time_array, 1));
259
+ sec = NUM2INT(rb_ary_entry(time_array, 0));
260
+
261
+ is_dst = rb_ary_entry(time_array, 8) == Qtrue ? 3600 : 0;
262
+ gmt_offset = NUM2INT(rb_funcall(r_value, ID_UTC_OFFSET, 0 ));
263
+
264
+ if ( is_dst > 0 )
265
+ gmt_offset -= is_dst;
266
+
267
+ hour_offset = -(gmt_offset / 3600);
268
+ minute_offset = -(gmt_offset % 3600 / 60);
269
+
270
+ jd = jd_from_date(year, month, day);
271
+
272
+ // Generate ajd with fractional days for the time
273
+ // Extracted from Date#jd_to_ajd, Date#day_fraction_to_time, and Rational#+ and #-
274
+ num = (hour * 1440) + (min * 24);
275
+
276
+ // Modify the numerator so when we apply the timezone everything works out
277
+ num -= (hour_offset * 1440) + (minute_offset * 24);
278
+
279
+ den = (24 * 1440);
280
+ reduce(&num, &den);
281
+
282
+ num = (num * 86400) + (sec * den);
283
+ den = den * 86400;
284
+ reduce(&num, &den);
285
+
286
+ num = (jd * den) + num;
287
+
288
+ num = num * 2;
289
+ num = num - den;
290
+ den = den * 2;
291
+
292
+ reduce(&num, &den);
293
+
294
+ ajd = rb_funcall(rb_mKernel, ID_RATIONAL, 2, rb_ull2inum(num), rb_ull2inum(den));
295
+ offset = timezone_to_offset(hour_offset, minute_offset);
296
+
297
+ return rb_funcall(rb_cDateTime, ID_NEW_DATE, 3, ajd, offset, INT2NUM(2299161));
298
+ } else {
299
+ // Something went terribly wrong
300
+ rb_raise(eDataError, "Couldn't parse datetime from class %s object", rb_obj_classname(r_value));
301
+ }
302
+
303
+ }
304
+
305
+ static VALUE parse_time(VALUE r_value) {
306
+ if (rb_obj_class(r_value) == rb_cTime) {
307
+ return r_value;
308
+ } else {
309
+ // Something went terribly wrong
310
+ rb_raise(eDataError, "Couldn't parse time from class %s object", rb_obj_classname(r_value));
311
+ }
312
+ }
313
+
314
+ static VALUE parse_boolean(VALUE r_value) {
315
+ if (TYPE(r_value) == T_FIXNUM || TYPE(r_value) == T_BIGNUM) {
316
+ return NUM2INT(r_value) >= 1 ? Qtrue : Qfalse;
317
+ } else if (TYPE(r_value) == T_STRING) {
318
+ char value = NIL_P(r_value) || RSTRING_LEN(r_value) == 0 ? '\0' : *(RSTRING_PTR(r_value));
319
+ return value == 'Y' || value == 'y' || value == 'T' || value == 't' ? Qtrue : Qfalse;
320
+ } else {
321
+ // Something went terribly wrong
322
+ rb_raise(eDataError, "Couldn't parse boolean from class %s object", rb_obj_classname(r_value));
323
+ }
324
+ }
325
+
326
+ /* ===== Typecasting Functions ===== */
327
+
328
+ static VALUE infer_ruby_type(VALUE type, VALUE precision, VALUE scale) {
329
+ ID type_id = SYM2ID(type);
330
+
331
+ if (type_id == ID_NUMBER)
332
+ return scale != Qnil && NUM2INT(scale) == 0 ?
333
+ (NUM2INT(precision) == 1 ? rb_cTrueClass : rb_cInteger) : rb_cBigDecimal;
334
+ else if (type_id == ID_VARCHAR2 || type_id == ID_CHAR || type_id == ID_CLOB || type_id == ID_LONG)
335
+ return rb_cString;
336
+ else if (type_id == ID_DATE)
337
+ // return rb_cDateTime;
338
+ // by default map DATE type to Time class as it is much faster than DateTime class
339
+ return rb_cTime;
340
+ else if (type_id == ID_TIMESTAMP || type_id == ID_TIMESTAMP_TZ || type_id == ID_TIMESTAMP_LTZ)
341
+ // return rb_cDateTime;
342
+ // by default map TIMESTAMP type to Time class as it is much faster than DateTime class
343
+ return rb_cTime;
344
+ else if (type_id == ID_BLOB || type_id == ID_RAW || type_id == ID_LONG_RAW || type_id == ID_BFILE)
345
+ return rb_cByteArray;
346
+ else if (type_id == ID_BINARY_FLOAT || type_id == ID_BINARY_DOUBLE)
347
+ return rb_cFloat;
348
+ else
349
+ return rb_cString;
350
+ }
351
+
352
+ static VALUE typecast(VALUE r_value, const VALUE type) {
353
+ VALUE r_data;
354
+
355
+ if (type == rb_cInteger) {
356
+ return TYPE(r_value) == T_FIXNUM || TYPE(r_value) == T_BIGNUM ? r_value : rb_funcall(r_value, ID_TO_I, 0);
357
+
358
+ } else if (type == rb_cString) {
359
+ if (TYPE(r_value) == T_STRING)
360
+ return r_value;
361
+ else if (rb_obj_class(r_value) == cOCI8_CLOB)
362
+ return rb_funcall(r_value, ID_READ, 0);
363
+ else
364
+ return rb_funcall(r_value, ID_TO_S, 0);
365
+
366
+ } else if (type == rb_cFloat) {
367
+ return TYPE(r_value) == T_FLOAT ? r_value : rb_funcall(r_value, ID_TO_F, 0);
368
+
369
+ } else if (type == rb_cBigDecimal) {
370
+ VALUE r_string = TYPE(r_value) == T_STRING ? r_value : rb_funcall(r_value, ID_TO_S, 0);
371
+ return rb_funcall(rb_cBigDecimal, ID_NEW, 1, r_string);
372
+
373
+ } else if (type == rb_cDate) {
374
+ return parse_date(r_value);
375
+
376
+ } else if (type == rb_cDateTime) {
377
+ return parse_date_time(r_value);
378
+
379
+ } else if (type == rb_cTime) {
380
+ return parse_time(r_value);
381
+
382
+ } else if (type == rb_cTrueClass) {
383
+ return parse_boolean(r_value);
384
+
385
+ } else if (type == rb_cByteArray) {
386
+ if (rb_obj_class(r_value) == cOCI8_BLOB)
387
+ r_data = rb_funcall(r_value, ID_READ, 0);
388
+ else
389
+ r_data = r_value;
390
+ return rb_funcall(rb_cByteArray, ID_NEW, 1, r_data);
391
+
392
+ } else if (type == rb_cClass) {
393
+ return rb_funcall(mDO, ID_FULL_CONST_GET, 1, r_value);
394
+
395
+ // TODO: where are tests for this mapping?
396
+ } else if (type == rb_cObject) {
397
+ if (rb_obj_class(r_value) == cOCI8_CLOB)
398
+ return rb_marshal_load(rb_funcall(r_value, ID_READ, 0));
399
+ else
400
+ return rb_marshal_load(r_value);
401
+
402
+ } else if (type == rb_cNilClass) {
403
+ return Qnil;
404
+
405
+ } else {
406
+ if (rb_obj_class(r_value) == cOCI8_CLOB)
407
+ return rb_funcall(r_value, ID_READ, 0);
408
+ else
409
+ return r_value;
410
+ }
411
+
412
+ }
413
+
414
+ static VALUE typecast_bind_value(VALUE oci8_conn, VALUE r_value) {
415
+ VALUE r_class = rb_obj_class(r_value);
416
+ // replace nil value with '' as otherwise OCI8 cannot get bind variable type
417
+ // '' will be inserted as NULL by Oracle
418
+ if (NIL_P(r_value))
419
+ return RUBY_STRING("");
420
+ else if (r_class == rb_cString)
421
+ // if string is longer than 4000 characters then convert to CLOB
422
+ return RSTRING_LEN(r_value) <= 4000 ? r_value : rb_funcall(cOCI8_CLOB, ID_NEW, 2, oci8_conn, r_value);
423
+ else if (r_class == rb_cBigDecimal)
424
+ return rb_funcall(r_value, ID_TO_S, 1, RUBY_STRING("F"));
425
+ else if (r_class == rb_cTrueClass)
426
+ return INT2NUM(1);
427
+ else if (r_class == rb_cFalseClass)
428
+ return INT2NUM(0);
429
+ else if (r_class == rb_cByteArray)
430
+ return rb_funcall(cOCI8_BLOB, ID_NEW, 2, oci8_conn, r_value);
431
+ else if (r_class == rb_cClass)
432
+ return rb_funcall(r_value, ID_TO_S, 0);
433
+ else
434
+ return r_value;
435
+ }
436
+
437
+ /* ====== Public API ======= */
438
+ static VALUE cConnection_dispose(VALUE self) {
439
+ VALUE oci8_conn = rb_iv_get(self, "@connection");
440
+
441
+ if (Qnil == oci8_conn)
442
+ return Qfalse;
443
+
444
+ rb_funcall(oci8_conn, ID_LOGOFF, 0);
445
+
446
+ rb_iv_set(self, "@connection", Qnil);
447
+
448
+ return Qtrue;
449
+ }
450
+
451
+ static VALUE cCommand_set_types(int argc, VALUE *argv, VALUE self) {
452
+ VALUE type_strings = rb_ary_new();
453
+ VALUE array = rb_ary_new();
454
+
455
+ int i, j;
456
+
457
+ for ( i = 0; i < argc; i++) {
458
+ rb_ary_push(array, argv[i]);
459
+ }
460
+
461
+ for (i = 0; i < RARRAY_LEN(array); i++) {
462
+ VALUE entry = rb_ary_entry(array, i);
463
+ if(TYPE(entry) == T_CLASS) {
464
+ rb_ary_push(type_strings, entry);
465
+ } else if (TYPE(entry) == T_ARRAY) {
466
+ for (j = 0; j < RARRAY_LEN(entry); j++) {
467
+ VALUE sub_entry = rb_ary_entry(entry, j);
468
+ if(TYPE(sub_entry) == T_CLASS) {
469
+ rb_ary_push(type_strings, sub_entry);
470
+ } else {
471
+ rb_raise(eArgumentError, "Invalid type given");
472
+ }
473
+ }
474
+ } else {
475
+ rb_raise(eArgumentError, "Invalid type given");
476
+ }
477
+ }
478
+
479
+ rb_iv_set(self, "@field_types", type_strings);
480
+
481
+ return array;
482
+ }
483
+
484
+ typedef struct {
485
+ VALUE self;
486
+ VALUE oci8_conn;
487
+ VALUE cursor;
488
+ VALUE statement_type;
489
+ VALUE args;
490
+ VALUE sql;
491
+ struct timeval start;
492
+ } cCommand_execute_try_t;
493
+
494
+ static VALUE cCommand_execute_try(cCommand_execute_try_t *arg);
495
+ static VALUE cCommand_execute_ensure(cCommand_execute_try_t *arg);
496
+
497
+ // called by Command#execute that is written in Ruby
498
+ static VALUE cCommand_execute_internal(VALUE self, VALUE oci8_conn, VALUE sql, VALUE args) {
499
+ cCommand_execute_try_t arg;
500
+ arg.self = self;
501
+ arg.oci8_conn = oci8_conn;
502
+ arg.sql = sql;
503
+ // store start time before SQL parsing
504
+ gettimeofday(&arg.start, NULL);
505
+ arg.cursor = rb_funcall(oci8_conn, ID_PARSE, 1, sql);
506
+ arg.statement_type = rb_funcall(arg.cursor, ID_TYPE, 0);
507
+ arg.args = args;
508
+
509
+ return rb_ensure(cCommand_execute_try, (VALUE)&arg, cCommand_execute_ensure, (VALUE)&arg);
510
+ }
511
+
512
+ // wrapper for simple SQL calls without arguments
513
+ static VALUE execute_sql(VALUE oci8_conn, VALUE sql) {
514
+ return cCommand_execute_internal(Qnil, oci8_conn, sql, Qnil);
515
+ }
516
+
517
+ static VALUE cCommand_execute_try(cCommand_execute_try_t *arg) {
518
+ VALUE result = Qnil;
519
+ int insert_id_present;
520
+
521
+ // no arguments given
522
+ if NIL_P(arg->args) {
523
+ result = rb_funcall(arg->cursor, ID_EXEC, 0);
524
+ // arguments given - need to typecast
525
+ } else {
526
+ insert_id_present = (!NIL_P(arg->self) && rb_iv_get(arg->self, "@insert_id_present") == Qtrue);
527
+
528
+ if (insert_id_present)
529
+ rb_funcall(arg->cursor, ID_BIND_PARAM, 2, RUBY_STRING(":insert_id"), INT2NUM(0));
530
+
531
+ int i;
532
+ VALUE r_orig_value, r_new_value;
533
+ for (i = 0; i < RARRAY_LEN(arg->args); i++) {
534
+ r_orig_value = rb_ary_entry(arg->args, i);
535
+ r_new_value = typecast_bind_value(arg->oci8_conn, r_orig_value);
536
+ if (r_orig_value != r_new_value)
537
+ rb_ary_store(arg->args, i, r_new_value);
538
+ }
539
+
540
+ result = rb_apply(arg->cursor, ID_EXEC, arg->args);
541
+
542
+ if (insert_id_present) {
543
+ VALUE insert_id = rb_funcall(arg->cursor, ID_ELEM, 1, RUBY_STRING(":insert_id"));
544
+ rb_iv_set(arg->self, "@insert_id", insert_id);
545
+ }
546
+ }
547
+
548
+ if (SYM2ID(arg->statement_type) == ID_SELECT_STMT)
549
+ return arg->cursor;
550
+ else {
551
+ return result;
552
+ }
553
+
554
+ }
555
+
556
+ static VALUE cCommand_execute_ensure(cCommand_execute_try_t *arg) {
557
+ if (SYM2ID(arg->statement_type) != ID_SELECT_STMT)
558
+ rb_funcall(arg->cursor, ID_CLOSE, 0);
559
+ // Log SQL and execution time
560
+ data_objects_debug(arg->sql, &(arg->start));
561
+ return Qnil;
562
+ }
563
+
564
+ static VALUE cConnection_initialize(VALUE self, VALUE uri) {
565
+ VALUE r_host, r_port, r_path, r_user, r_password;
566
+ VALUE r_query, r_time_zone;
567
+ char *non_blocking = NULL;
568
+ char *time_zone = NULL;
569
+ char set_time_zone_command[80];
570
+
571
+ char *host = "localhost", *port = "1521", *path = NULL;
572
+ char *connect_string;
573
+ int connect_string_length;
574
+ VALUE oci8_conn;
575
+
576
+ r_user = rb_funcall(uri, rb_intern("user"), 0);
577
+ r_password = rb_funcall(uri, rb_intern("password"), 0);
578
+
579
+ r_host = rb_funcall(uri, rb_intern("host"), 0);
580
+ if ( Qnil != r_host && RSTRING_LEN(r_host) > 0) {
581
+ host = StringValuePtr(r_host);
582
+ }
583
+
584
+ r_port = rb_funcall(uri, rb_intern("port"), 0);
585
+ if ( Qnil != r_port ) {
586
+ r_port = rb_funcall(r_port, ID_TO_S, 0);
587
+ port = StringValuePtr(r_port);
588
+ }
589
+
590
+ r_path = rb_funcall(uri, rb_intern("path"), 0);
591
+ path = StringValuePtr(r_path);
592
+
593
+ // If just host name is specified then use it as TNS names alias
594
+ if ((r_host != Qnil && RSTRING_LEN(r_host) > 0) &&
595
+ (r_port == Qnil) &&
596
+ (r_path == Qnil || RSTRING_LEN(r_path) == 0)) {
597
+ connect_string = host;
598
+ // If database name is specified in path (in format "/database")
599
+ } else if (strlen(path) > 1) {
600
+ connect_string_length = strlen(host) + strlen(port) + strlen(path) + 4;
601
+ connect_string = (char *)calloc(connect_string_length, sizeof(char));
602
+ snprintf(connect_string, connect_string_length, "//%s:%s%s", host, port, path);
603
+ } else {
604
+ rb_raise(eConnectionError, "Database must be specified");
605
+ }
606
+
607
+ // oci8_conn = rb_funcall(cOCI8, ID_NEW, 3, r_user, r_password, RUBY_STRING(connect_string));
608
+ oci8_conn = rb_funcall(cConnection, rb_intern("oci8_new"), 3, r_user, r_password, RUBY_STRING(connect_string));
609
+
610
+ // Pull the querystring off the URI
611
+ r_query = rb_funcall(uri, rb_intern("query"), 0);
612
+
613
+ non_blocking = get_uri_option(r_query, "non_blocking");
614
+ // Enable non-blocking mode
615
+ if (non_blocking != NULL && strcmp(non_blocking, "true") == 0)
616
+ rb_funcall(oci8_conn, rb_intern("non_blocking="), 1, Qtrue);
617
+ // By default enable auto-commit mode
618
+ rb_funcall(oci8_conn, rb_intern("autocommit="), 1, Qtrue);
619
+ // Set prefetch rows to 100 to increase fetching performance SELECTs with many rows
620
+ rb_funcall(oci8_conn, rb_intern("prefetch_rows="), 1, INT2NUM(100));
621
+
622
+ // Set session time zone
623
+ // at first look for option in connection string
624
+ time_zone = get_uri_option(r_query, "time_zone");
625
+ // if no option specified then look in ENV['TZ']
626
+ if (time_zone == NULL) {
627
+ r_time_zone = rb_funcall(cConnection, rb_intern("ruby_time_zone"), 0);
628
+ if (!NIL_P(r_time_zone))
629
+ time_zone = StringValuePtr(r_time_zone);
630
+ }
631
+ if (time_zone) {
632
+ snprintf(set_time_zone_command, 80, "alter session set time_zone = '%s'", time_zone);
633
+ execute_sql(oci8_conn, RUBY_STRING(set_time_zone_command));
634
+ }
635
+
636
+ execute_sql(oci8_conn, RUBY_STRING("alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'"));
637
+ execute_sql(oci8_conn, RUBY_STRING("alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF'"));
638
+ execute_sql(oci8_conn, RUBY_STRING("alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF TZH:TZM'"));
639
+
640
+ rb_iv_set(self, "@uri", uri);
641
+ rb_iv_set(self, "@connection", oci8_conn);
642
+
643
+ return Qtrue;
644
+ }
645
+
646
+ static VALUE cCommand_execute_non_query(int argc, VALUE *argv[], VALUE self) {
647
+ VALUE affected_rows = rb_funcall2(self, ID_EXECUTE, argc, (VALUE *)argv);
648
+ if (affected_rows == Qtrue)
649
+ affected_rows = INT2NUM(0);
650
+
651
+ VALUE insert_id = rb_iv_get(self, "@insert_id");
652
+
653
+ return rb_funcall(cResult, ID_NEW, 3, self, affected_rows, insert_id);
654
+ }
655
+
656
+ static VALUE cCommand_execute_reader(int argc, VALUE *argv[], VALUE self) {
657
+ VALUE reader, query;
658
+ VALUE field_names, field_types;
659
+ VALUE column_metadata, column, column_name;
660
+
661
+ int i;
662
+ int field_count;
663
+ int infer_types = 0;
664
+
665
+ VALUE cursor = rb_funcall2(self, ID_EXECUTE, argc, (VALUE *)argv);
666
+
667
+ if (rb_obj_class(cursor) != cOCI8_Cursor) {
668
+ rb_raise(eArgumentError, "\"%s\" is invalid SELECT query", StringValuePtr(query));
669
+ }
670
+
671
+ column_metadata = rb_funcall(cursor, ID_COLUMN_METADATA, 0);
672
+ field_count = RARRAY_LEN(column_metadata);
673
+ // reduce field_count by 1 if RAW_RNUM_ is present as last column
674
+ // (generated by DataMapper to simulate LIMIT and OFFSET)
675
+ column = rb_ary_entry(column_metadata, field_count-1);
676
+ column_name = rb_funcall(column, ID_NAME, 0);
677
+ if (strncmp(RSTRING_PTR(column_name), "RAW_RNUM_", RSTRING_LEN(column_name)) == 0)
678
+ field_count--;
679
+
680
+ reader = rb_funcall(cReader, ID_NEW, 0);
681
+ rb_iv_set(reader, "@reader", cursor);
682
+ rb_iv_set(reader, "@field_count", INT2NUM(field_count));
683
+
684
+ field_names = rb_ary_new();
685
+ field_types = rb_iv_get(self, "@field_types");
686
+
687
+ if ( field_types == Qnil || 0 == RARRAY_LEN(field_types) ) {
688
+ field_types = rb_ary_new();
689
+ infer_types = 1;
690
+ } else if (RARRAY_LEN(field_types) != field_count) {
691
+ // Whoops... wrong number of types passed to set_types. Close the reader and raise
692
+ // and error
693
+ rb_funcall(reader, ID_CLOSE, 0);
694
+ rb_raise(eArgumentError, "Field-count mismatch. Expected %ld fields, but the query yielded %d", RARRAY_LEN(field_types), field_count);
695
+ }
696
+
697
+ for ( i = 0; i < field_count; i++ ) {
698
+ column = rb_ary_entry(column_metadata, i);
699
+ column_name = rb_funcall(column, ID_NAME, 0);
700
+ rb_ary_push(field_names, column_name);
701
+ if ( infer_types == 1 ) {
702
+ rb_ary_push(field_types,
703
+ infer_ruby_type(rb_iv_get(column, "@data_type"),
704
+ rb_funcall(column, ID_PRECISION, 0),
705
+ rb_funcall(column, ID_SCALE, 0))
706
+ );
707
+ }
708
+ }
709
+
710
+ rb_iv_set(reader, "@position", INT2NUM(0));
711
+ rb_iv_set(reader, "@fields", field_names);
712
+ rb_iv_set(reader, "@field_types", field_types);
713
+
714
+ rb_iv_set(reader, "@last_row", Qfalse);
715
+
716
+ return reader;
717
+ }
718
+
719
+ static VALUE cReader_close(VALUE self) {
720
+ VALUE cursor = rb_iv_get(self, "@reader");
721
+
722
+ if (Qnil == cursor)
723
+ return Qfalse;
724
+
725
+ rb_funcall(cursor, ID_CLOSE, 0);
726
+
727
+ rb_iv_set(self, "@reader", Qnil);
728
+ return Qtrue;
729
+ }
730
+
731
+ static VALUE cReader_next(VALUE self) {
732
+ VALUE cursor = rb_iv_get(self, "@reader");
733
+
734
+ int field_count;
735
+ int i;
736
+
737
+ if (Qnil == cursor || Qtrue == rb_iv_get(self, "@last_row"))
738
+ return Qfalse;
739
+
740
+ VALUE row = rb_ary_new();
741
+ VALUE field_types, field_type;
742
+ VALUE value;
743
+
744
+ VALUE fetch_result = rb_funcall(cursor, ID_FETCH, 0);
745
+
746
+ if (Qnil == fetch_result) {
747
+ rb_iv_set(self, "@values", Qnil);
748
+ rb_iv_set(self, "@last_row", Qtrue);
749
+ return Qfalse;
750
+ }
751
+
752
+ field_count = NUM2INT(rb_iv_get(self, "@field_count"));
753
+ field_types = rb_iv_get(self, "@field_types");
754
+
755
+ for ( i = 0; i < field_count; i++ ) {
756
+ field_type = rb_ary_entry(field_types, i);
757
+ value = rb_ary_entry(fetch_result, i);
758
+ // Always return nil if the value returned from Oracle is null
759
+ if (Qnil != value) {
760
+ value = typecast(value, field_type);
761
+ }
762
+
763
+ rb_ary_push(row, value);
764
+ }
765
+
766
+ rb_iv_set(self, "@values", row);
767
+ return Qtrue;
768
+ }
769
+
770
+ static VALUE cReader_values(VALUE self) {
771
+
772
+ VALUE values = rb_iv_get(self, "@values");
773
+ if(values == Qnil) {
774
+ rb_raise(eDataError, "Reader not initialized");
775
+ return Qnil;
776
+ } else {
777
+ return values;
778
+ }
779
+ }
780
+
781
+ static VALUE cReader_fields(VALUE self) {
782
+ return rb_iv_get(self, "@fields");
783
+ }
784
+
785
+ static VALUE cReader_field_count(VALUE self) {
786
+ return rb_iv_get(self, "@field_count");
787
+ }
788
+
789
+
790
+ void Init_do_oracle() {
791
+ // rb_require("oci8");
792
+ rb_require("date");
793
+ rb_require("bigdecimal");
794
+
795
+ // Get references classes needed for Date/Time parsing
796
+ rb_cDate = CONST_GET(rb_mKernel, "Date");
797
+ rb_cDateTime = CONST_GET(rb_mKernel, "DateTime");
798
+ rb_cBigDecimal = CONST_GET(rb_mKernel, "BigDecimal");
799
+
800
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("data_objects"));
801
+
802
+ ID_NEW = rb_intern("new");
803
+ #ifdef RUBY_LESS_THAN_186
804
+ ID_NEW_DATE = rb_intern("new0");
805
+ #else
806
+ ID_NEW_DATE = rb_intern("new!");
807
+ #endif
808
+ ID_LOGGER = rb_intern("logger");
809
+ ID_DEBUG = rb_intern("debug");
810
+ ID_LEVEL = rb_intern("level");
811
+ ID_TO_S = rb_intern("to_s");
812
+ ID_RATIONAL = rb_intern("Rational");
813
+
814
+ ID_NAME = rb_intern("name");
815
+
816
+ ID_NUMBER = rb_intern("number");
817
+ ID_VARCHAR2 = rb_intern("varchar2");
818
+ ID_CHAR = rb_intern("char");
819
+ ID_DATE = rb_intern("date");
820
+ ID_TIMESTAMP = rb_intern("timestamp");
821
+ ID_TIMESTAMP_TZ = rb_intern("timestamp_tz");
822
+ ID_TIMESTAMP_LTZ = rb_intern("timestamp_ltz");
823
+ ID_CLOB = rb_intern("clob");
824
+ ID_BLOB = rb_intern("blob");
825
+ ID_LONG = rb_intern("long");
826
+ ID_RAW = rb_intern("raw");
827
+ ID_LONG_RAW = rb_intern("long_raw");
828
+ ID_BFILE = rb_intern("bfile");
829
+ ID_BINARY_FLOAT = rb_intern("binary_float");
830
+ ID_BINARY_DOUBLE = rb_intern("binary_double");
831
+
832
+ ID_TO_A = rb_intern("to_a");
833
+ ID_TO_I = rb_intern("to_i");
834
+ ID_TO_S = rb_intern("to_s");
835
+ ID_TO_F = rb_intern("to_f");
836
+
837
+ ID_UTC_OFFSET = rb_intern("utc_offset");
838
+ ID_FULL_CONST_GET = rb_intern("full_const_get");
839
+
840
+ ID_PARSE = rb_intern("parse");
841
+ ID_FETCH = rb_intern("fetch");
842
+ ID_TYPE = rb_intern("type");
843
+ ID_EXECUTE = rb_intern("execute");
844
+ ID_EXEC = rb_intern("exec");
845
+
846
+ ID_SELECT_STMT = rb_intern("select_stmt");
847
+ ID_COLUMN_METADATA = rb_intern("column_metadata");
848
+ ID_PRECISION = rb_intern("precision");
849
+ ID_SCALE = rb_intern("scale");
850
+ ID_BIND_PARAM = rb_intern("bind_param");
851
+ ID_ELEM = rb_intern("[]");
852
+ ID_READ = rb_intern("read");
853
+
854
+ ID_CLOSE = rb_intern("close");
855
+ ID_LOGOFF = rb_intern("logoff");
856
+
857
+ // Get references to the Extlib module
858
+ mExtlib = CONST_GET(rb_mKernel, "Extlib");
859
+ rb_cByteArray = CONST_GET(mExtlib, "ByteArray");
860
+
861
+ // Get reference to OCI8 class
862
+ cOCI8 = CONST_GET(rb_mKernel, "OCI8");
863
+ cOCI8_Cursor = CONST_GET(cOCI8, "Cursor");
864
+ cOCI8_BLOB = CONST_GET(cOCI8, "BLOB");
865
+ cOCI8_CLOB = CONST_GET(cOCI8, "CLOB");
866
+
867
+ // Get references to the DataObjects module and its classes
868
+ mDO = CONST_GET(rb_mKernel, "DataObjects");
869
+ cDO_Quoting = CONST_GET(mDO, "Quoting");
870
+ cDO_Connection = CONST_GET(mDO, "Connection");
871
+ cDO_Command = CONST_GET(mDO, "Command");
872
+ cDO_Result = CONST_GET(mDO, "Result");
873
+ cDO_Reader = CONST_GET(mDO, "Reader");
874
+
875
+ // Top Level Module that all the classes live under
876
+ mOracle = rb_define_module_under(mDO, "Oracle");
877
+
878
+ eArgumentError = CONST_GET(rb_mKernel, "ArgumentError");
879
+ eSQLError = CONST_GET(mDO, "SQLError");
880
+ eConnectionError = CONST_GET(mDO, "ConnectionError");
881
+ eDataError = CONST_GET(mDO, "DataError");
882
+ // eOracleError = rb_define_class("OracleError", rb_eStandardError);
883
+
884
+ cConnection = ORACLE_CLASS("Connection", cDO_Connection);
885
+ rb_define_method(cConnection, "initialize", cConnection_initialize, 1);
886
+ rb_define_method(cConnection, "dispose", cConnection_dispose, 0);
887
+
888
+ cCommand = ORACLE_CLASS("Command", cDO_Command);
889
+ rb_define_method(cCommand, "set_types", cCommand_set_types, -1);
890
+ rb_define_method(cCommand, "execute_non_query", cCommand_execute_non_query, -1);
891
+ rb_define_method(cCommand, "execute_reader", cCommand_execute_reader, -1);
892
+ rb_define_method(cCommand, "execute_internal", cCommand_execute_internal, 3);
893
+
894
+ cResult = ORACLE_CLASS("Result", cDO_Result);
895
+
896
+ cReader = ORACLE_CLASS("Reader", cDO_Reader);
897
+ rb_define_method(cReader, "close", cReader_close, 0);
898
+ rb_define_method(cReader, "next!", cReader_next, 0);
899
+ rb_define_method(cReader, "values", cReader_values, 0);
900
+ rb_define_method(cReader, "fields", cReader_fields, 0);
901
+ rb_define_method(cReader, "field_count", cReader_field_count, 0);
902
+
903
+ }