activerecord-rdb-adapter 0.7.4 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/extconf.rb +93 -0
  3. data/fb.c +3081 -0
  4. data/fb_extensions.rb +21 -0
  5. metadata +7 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f71a7f43889ccb34453a8ae98854cf95b966cd1866e8e5c156081d80472ddb5
4
- data.tar.gz: 381147052286bd4b4179ed7d9e209f4ff7d31e93e28b490c973fb30521e16e89
3
+ metadata.gz: 29962e4968f5597099dbfb993d10018e660a232340d9867c3a0056c363925fa6
4
+ data.tar.gz: bc0e46387f5f3b4ec8edd3828d933e11e32f5dc0d1adc9f262499107b0260de6
5
5
  SHA512:
6
- metadata.gz: 1515d2fcb162e54b6cd431b08bd9742ba96faf292aa61945ece6d255ae806f8b82cf014ee9114fff043fd71feb7b4a693ea1cf4e8a77a21fca7686e29c70d72d
7
- data.tar.gz: b38a3010b7a77fa5931ef6a7edb727673c7bb1da6e34581b0649b8fd20adf000989b359abac3ffc5a72f164a2c11887bdeed4bc3b8dafe70c5ffc51658551f9e
6
+ metadata.gz: b88f9435c63e6d6ca457fafa3046da5a79be57e0b331b44fa2c1d8ddd8b9cd7420e6a2d0f4c1d5534e08c754b42263e107504cbf86add2def969a2c3860a6801
7
+ data.tar.gz: 7f0d624807fe4063ff5c0525eb5185d49141cc46450d54e216a23f908f409787caf5ae084ed865bfc16f6b203aa788fa3227f753e54fc9bdf39f9e34730a31f9
data/extconf.rb ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # = Windows
3
+ # === Sample of Makefile creation:
4
+ # <tt>ruby extconf.rb --with-opt-dir=C:/Progra~1/Firebird/Firebird_2_5</tt>
5
+ # === Notes
6
+ # * Windows is known to build with Ruby from rubyinstaller.org.
7
+ # * New in this release is automatically finding your Firebird install under Program Files.
8
+ # * If your install is some place non-standard (or on a non-English version of Windows), you'll need to run extconf.rb manually as above.
9
+ # * mkmf doesn't like directories with spaces, hence the 8.3 notation in the example above.
10
+ # = Linux
11
+ # === Notes
12
+ # * Build seems to "just work."
13
+ # * Unit tests take about 10 times as long to complete using Firebird Classic. Default xinetd.conf settings may not allow the tests to complete due to the frequency with which new attachments are made.
14
+ # = Mac OS X (Intel)
15
+ # * Works
16
+
17
+ def unquote(string)
18
+ string.sub(/\A(['"])?(.*?)\1?\z/m, '\2') unless string.nil?
19
+ end
20
+
21
+ def key_exists?(path)
22
+ begin
23
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(path, ::Win32::Registry::KEY_READ)
24
+ return true
25
+ rescue
26
+ return false
27
+ end
28
+ end
29
+
30
+ def read_firebird_registry
31
+ require 'win32/registry'
32
+ if key_exists?('SOFTWARE\Firebird Project\Firebird Server\Instances')
33
+ Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Firebird Project\Firebird Server\Instances', Win32::Registry::Constants::KEY_READ) do |reg|
34
+ return reg.read_s('DefaultInstance') rescue nil
35
+ end
36
+ else
37
+ return false
38
+ end
39
+ end
40
+
41
+ def search_firebird_path
42
+ program_files = ENV['ProgramFiles'].gsub('\\', '/').gsub(/(\w+\s+[\w\s]+)/) { |s| s.size > 8 ? s[0,6] + '~1' : s }
43
+ program_files_x86 = ENV['ProgramFiles'].gsub('\\', '/').gsub(/(\w+\s+[\w\s]+)/) { |s| s.size > 8 ? s[0,6] + '~2' : s }
44
+ result = Dir["#{program_files}/Firebird/Firebird_*"].sort.last || Dir["#{program_files_x86}/Firebird/Firebird_*"].sort.last
45
+ end
46
+
47
+ if RUBY_PLATFORM =~ /(mingw32|mswin32)/ and ARGV.grep(/^--with-opt-dir=/).empty?
48
+ opt = unquote(ENV['FIREBIRD'])
49
+ opt = opt || read_firebird_registry
50
+ opt = opt || search_firebird_path
51
+ if opt
52
+ ARGV << "--with-opt-dir=#{opt}"
53
+ else
54
+ puts "No any Firebird instances found in system."
55
+ exit
56
+ end
57
+ end
58
+
59
+ require 'mkmf'
60
+
61
+ libs = %w/ fbclient gds /
62
+
63
+ case RUBY_PLATFORM
64
+ when /bccwin32/
65
+ libs.push "fbclient_bor"
66
+ when /mswin32/, /mingw32/
67
+ $CFLAGS = $CFLAGS + " -DOS_WIN32"
68
+ libs.push "fbclient_ms"
69
+ when /darwin/
70
+ # hosttype = `uname -m`.chomp
71
+ $CFLAGS += " -DOS_UNIX"
72
+ # $CFLAGS.gsub!(/-arch (\w+)/) { |m| $1 == hosttype ? m : '' }
73
+ # $LDFLAGS.gsub!(/-arch (\w+)/) { |m| $1 == hosttype ? m : '' }
74
+ # CONFIG['LDSHARED'].gsub!(/-arch (\w+)/) { |m| $1 == hosttype ? m : '' }
75
+ $CPPFLAGS += " -I/Library/Frameworks/Firebird.framework/Headers"
76
+ $LDFLAGS += " -framework Firebird"
77
+ when /linux/
78
+ $CFLAGS = $CFLAGS + " -DOS_UNIX"
79
+ end
80
+
81
+ dir_config("firebird")
82
+
83
+ test_func = "isc_attach_database"
84
+
85
+ case RUBY_PLATFORM
86
+ when /mswin32/, /mingw32/
87
+ libs.find {|lib| have_library(lib) } and
88
+ have_func(test_func, ["ibase.h"])
89
+ else
90
+ libs.find {|lib| have_library(lib, test_func) }
91
+ end
92
+
93
+ create_makefile("fb")
data/fb.c ADDED
@@ -0,0 +1,3081 @@
1
+ #include "ruby.h"
2
+
3
+ #ifdef HAVE_RUBY_REGEX_H
4
+ # include "ruby/re.h"
5
+ #else
6
+ # include "re.h"
7
+ #endif
8
+
9
+ #ifdef ONIGURUMA_H
10
+ #define IGNORECASE ONIG_OPTION_IGNORECASE
11
+ #else
12
+ #define IGNORECASE RE_OPTION_IGNORECASE
13
+ #endif
14
+
15
+ #include <stdio.h>
16
+ #include <string.h>
17
+ #include <limits.h>
18
+ #include <ibase.h>
19
+ #include <float.h>
20
+ #include <time.h>
21
+ #include <stdbool.h>
22
+
23
+
24
+ #define SQLDA_COLSINIT 50
25
+ #define SQLCODE_NOMORE 100
26
+ #define TPBBUFF_ALLOC 64
27
+ #define CMND_DELIMIT " \t\n\r\f"
28
+ #define LIST_DELIMIT ", \t\n\r\f"
29
+ #define META_NAME_MAX 31
30
+
31
+ /* Statement type */
32
+ #define STATEMENT_DDL 1
33
+ #define STATEMENT_DML 0
34
+
35
+ /* Execute process flag */
36
+ #define EXECF_EXECDML 0
37
+ #define EXECF_SETPARM 1
38
+
39
+ static VALUE rb_mFb;
40
+ static VALUE rb_cFbDatabase;
41
+ static VALUE rb_cFbConnection;
42
+ static VALUE rb_cFbCursor;
43
+ static VALUE rb_cFbSqlType;
44
+ /* static VALUE rb_cFbGlobal; */
45
+ static VALUE rb_eFbError;
46
+ static VALUE rb_sFbField;
47
+ static VALUE rb_sFbIndex;
48
+ static VALUE rb_sFbColumn;
49
+ static VALUE rb_cDate;
50
+
51
+ static ID id_matches;
52
+ static ID id_downcase_bang;
53
+ static VALUE re_lowercase;
54
+ static ID id_rstrip_bang;
55
+ static ID id_sub_bang;
56
+ static ID id_force_encoding;
57
+
58
+ /* static char isc_info_stmt[] = { isc_info_sql_stmt_type }; */
59
+ /* static char isc_info_buff[16]; */
60
+ static char isc_tpb_0[] = {
61
+ isc_tpb_version3, isc_tpb_write,
62
+ isc_tpb_read_committed, isc_tpb_nowait
63
+ };
64
+
65
+ /* structs */
66
+
67
+ /* DB handle and TR parameter block list structure */
68
+ typedef struct
69
+ {
70
+ isc_db_handle *dbb_ptr ;
71
+ long tpb_len ;
72
+ char *tpb_ptr ;
73
+ } ISC_TEB ; /* transaction existence block */
74
+
75
+ /* InterBase varchar structure */
76
+ typedef struct
77
+ {
78
+ short vary_length;
79
+ char vary_string[1];
80
+ } VARY;
81
+
82
+ struct FbConnection {
83
+ isc_db_handle db; /* DB handle */
84
+ isc_tr_handle transact; /* transaction handle */
85
+ VALUE cursor;
86
+ unsigned short dialect;
87
+ unsigned short db_dialect;
88
+ short downcase_names;
89
+ VALUE encoding;
90
+ short readonly_selects;
91
+ int dropped;
92
+ ISC_STATUS isc_status[20];
93
+ /* struct FbConnection *next; */
94
+ };
95
+
96
+ /* static struct FbConnection *fb_connection_list; */
97
+
98
+ struct FbCursor {
99
+ int open;
100
+ int eof;
101
+ isc_tr_handle auto_transact;
102
+ isc_stmt_handle stmt;
103
+ XSQLDA *i_sqlda;
104
+ XSQLDA *o_sqlda;
105
+ char *i_buffer;
106
+ long i_buffer_size;
107
+ char *o_buffer;
108
+ long o_buffer_size;
109
+ VALUE fields_ary;
110
+ VALUE fields_hash;
111
+ VALUE connection;
112
+ };
113
+
114
+ typedef struct trans_opts
115
+ {
116
+ const char *option1;
117
+ const char *option2;
118
+ char optval;
119
+ short position;
120
+ struct trans_opts *sub_opts;
121
+ } trans_opts;
122
+
123
+ /* global utilities */
124
+
125
+ #define FB_ALIGN(n, b) ((n + b - 1) & ~(b - 1))
126
+ #define UPPER(c) (((c) >= 'a' && (c)<= 'z') ? (c) - 'a' + 'A' : (c))
127
+ #define FREE(p) if (p) { xfree(p); p = 0; }
128
+ #define SETNULL(p) if (p && strlen(p) == 0) { p = 0; }
129
+ // #define HERE(s) printf("%s\n", s)
130
+ #define HERE(s)
131
+
132
+ static long calculate_buffsize(XSQLDA *sqlda)
133
+ {
134
+ XSQLVAR *var;
135
+ long cols;
136
+ short dtp;
137
+ long offset = 0;
138
+ long alignment;
139
+ long length;
140
+ long count;
141
+
142
+ cols = sqlda->sqld;
143
+ var = sqlda->sqlvar;
144
+ for (count = 0; count < cols; var++,count++) {
145
+ length = alignment = var->sqllen;
146
+ dtp = var->sqltype & ~1;
147
+
148
+ if (dtp == SQL_TEXT) {
149
+ alignment = 1;
150
+ } else if (dtp == SQL_VARYING) {
151
+ length += sizeof(short);
152
+ alignment = sizeof(short);
153
+ }
154
+
155
+ offset = FB_ALIGN((size_t)offset, alignment);
156
+ offset += length;
157
+ offset = FB_ALIGN((size_t)offset, sizeof(short));
158
+ offset += sizeof(short);
159
+ }
160
+
161
+ return offset + sizeof(short);
162
+ }
163
+
164
+ #if (FB_API_VER >= 20)
165
+ static VALUE fb_error_msg(const ISC_STATUS *isc_status)
166
+ {
167
+ char msg[1024];
168
+ VALUE result = rb_str_new(NULL, 0);
169
+ const size_t suffixLen = strlen("\n");
170
+ while (fb_interpret(msg, 1024, &isc_status))
171
+ {
172
+ result = rb_str_cat(result, msg, strlen(msg));
173
+ result = rb_str_cat(result, "\n", suffixLen);
174
+ }
175
+ return result;
176
+ }
177
+ #else
178
+ static VALUE fb_error_msg(ISC_STATUS *isc_status)
179
+ {
180
+ char msg[1024];
181
+ VALUE result = rb_str_new(NULL, 0);
182
+ while (isc_interprete(msg, &isc_status))
183
+ {
184
+ result = rb_str_cat(result, msg, strlen(msg));
185
+ result = rb_str_cat(result, "\n", strlen("\n"));
186
+ }
187
+ return result;
188
+ }
189
+ #endif
190
+
191
+ struct time_object {
192
+ struct timeval tv;
193
+ struct tm tm;
194
+ int gmt;
195
+ int tm_got;
196
+ };
197
+
198
+ #define GetTimeval(obj, tobj) \
199
+ Data_Get_Struct(obj, struct time_object, tobj)
200
+
201
+ static VALUE fb_mktime(struct tm *tm, const char *which)
202
+ {
203
+ #if defined(_WIN32)
204
+ if (tm->tm_year + 1900 < 1970)
205
+ {
206
+ tm->tm_year = 70;
207
+ tm->tm_mon = 0;
208
+ tm->tm_mday = 1;
209
+ tm->tm_hour = 0;
210
+ tm->tm_min = 0;
211
+ tm->tm_sec = 0;
212
+ }
213
+ #endif
214
+ #if defined(_LP64) || defined(__LP64__) || defined(__arch64__)
215
+ // No need to floor time on 64-bit Unix.
216
+ #else
217
+ if (tm->tm_year + 1900 < 1902)
218
+ {
219
+ tm->tm_year = 2;
220
+ tm->tm_mon = 0;
221
+ tm->tm_mday = 1;
222
+ tm->tm_hour = 0;
223
+ tm->tm_min = 0;
224
+ tm->tm_sec = 0;
225
+ }
226
+ #endif
227
+ return rb_funcall(
228
+ rb_cTime, rb_intern(which), 6,
229
+ INT2FIX(tm->tm_year + 1900), INT2FIX(tm->tm_mon + 1), INT2FIX(tm->tm_mday),
230
+ INT2FIX(tm->tm_hour), INT2FIX(tm->tm_min), INT2FIX(tm->tm_sec));
231
+ }
232
+
233
+ static VALUE fb_mkdate(struct tm *tm)
234
+ {
235
+ return rb_funcall(
236
+ rb_cDate, rb_intern("civil"), 3,
237
+ INT2FIX(1900 + tm->tm_year), INT2FIX(tm->tm_mon + 1), INT2FIX(tm->tm_mday));
238
+ }
239
+
240
+ static int responds_like_date(VALUE obj)
241
+ {
242
+ return rb_respond_to(obj, rb_intern("year")) &&
243
+ rb_respond_to(obj, rb_intern("month")) &&
244
+ rb_respond_to(obj, rb_intern("day"));
245
+ }
246
+ static void tm_from_date(struct tm *tm, VALUE date)
247
+ {
248
+ VALUE year, month, day;
249
+
250
+ if (!responds_like_date(date)) {
251
+ VALUE s = rb_funcall(date, rb_intern("to_s"), 0);
252
+ date = rb_funcall(rb_cDate, rb_intern("parse"), 1, s);
253
+ }
254
+ year = rb_funcall(date, rb_intern("year"), 0);
255
+ month = rb_funcall(date, rb_intern("month"), 0);
256
+ day = rb_funcall(date, rb_intern("day"), 0);
257
+ memset(tm, 0, sizeof(struct tm));
258
+ tm->tm_year = FIX2INT(year) - 1900;
259
+ tm->tm_mon = FIX2INT(month) - 1;
260
+ tm->tm_mday = FIX2INT(day);
261
+ }
262
+
263
+ static void tm_from_timestamp(struct tm *tm, VALUE obj)
264
+ {
265
+ #ifdef TypedData_Get_Struct
266
+ VALUE year, month, day, hour, min, sec;
267
+ #else
268
+ struct time_object *tobj;
269
+ #endif
270
+
271
+ if (!rb_obj_is_kind_of(obj, rb_cTime))
272
+ {
273
+ if (rb_respond_to(obj, rb_intern("to_str")))
274
+ {
275
+ VALUE s = rb_funcall(obj, rb_intern("to_str"), 0);
276
+ obj = rb_funcall(rb_cTime, rb_intern("parse"), 1, s);
277
+ }
278
+ else
279
+ {
280
+ rb_raise(rb_eTypeError, "Expecting Time object or string.");
281
+ }
282
+ }
283
+
284
+ #ifdef TypedData_Get_Struct
285
+ year = rb_funcall(obj, rb_intern("year"), 0);
286
+ month = rb_funcall(obj, rb_intern("month"), 0);
287
+ day = rb_funcall(obj, rb_intern("day"), 0);
288
+ hour = rb_funcall(obj, rb_intern("hour"), 0);
289
+ min = rb_funcall(obj, rb_intern("min"), 0);
290
+ sec = rb_funcall(obj, rb_intern("sec"), 0);
291
+ memset(tm, 0, sizeof(struct tm));
292
+ tm->tm_year = FIX2INT(year) - 1900;
293
+ tm->tm_mon = FIX2INT(month) - 1;
294
+ tm->tm_mday = FIX2INT(day);
295
+ tm->tm_hour = FIX2INT(hour);
296
+ tm->tm_min = FIX2INT(min);
297
+ tm->tm_sec = FIX2INT(sec);
298
+ #else
299
+ GetTimeval(obj, tobj);
300
+ *tm = tobj->tm;
301
+ #endif
302
+ }
303
+
304
+ static VALUE object_to_fixnum(VALUE object)
305
+ {
306
+ if (TYPE(object) != T_FIXNUM && TYPE(object) != T_BIGNUM)
307
+ {
308
+ if (TYPE(object) == T_FLOAT || !strcmp(rb_class2name(CLASS_OF(object)), "BigDecimal"))
309
+ object = rb_funcall(object, rb_intern("round"), 0);
310
+ else if (TYPE(object) == T_STRING)
311
+ object = rb_funcall(rb_funcall(rb_mKernel, rb_intern("BigDecimal"), 1, object), rb_intern("round"), 0);
312
+ else if (!strcmp(rb_class2name(CLASS_OF(object)), "Time"))
313
+ rb_raise(rb_eTypeError, "Time value not allowed as Integer");
314
+ else if (rb_respond_to(object, rb_intern("to_i")))
315
+ object = rb_funcall(object, rb_intern("to_i"), 0);
316
+ else
317
+ rb_raise(rb_eTypeError, "Value doesn't respond to 'to_i' for conversion");
318
+ }
319
+ return (object);
320
+ }
321
+
322
+ static VALUE double_from_obj(VALUE obj)
323
+ {
324
+ if (TYPE(obj) == T_STRING)
325
+ {
326
+ obj = rb_funcall(obj, rb_intern("to_f"), 0);
327
+ }
328
+ return obj;
329
+ }
330
+
331
+ static VALUE rb_ro_trans_p()
332
+ {
333
+ return rb_str_new2("READ ONLY NO WAIT ISOLATION LEVEL READ COMMITTED RECORD_VERSION");
334
+ }
335
+
336
+ static VALUE fb_sql_type_from_code(int code, int subtype)
337
+ {
338
+ const char *sql_type = NULL;
339
+ switch(code) {
340
+ case SQL_TEXT:
341
+ case blr_text:
342
+ sql_type = "CHAR";
343
+ break;
344
+ case SQL_VARYING:
345
+ case blr_varying:
346
+ sql_type = "VARCHAR";
347
+ break;
348
+ case SQL_SHORT:
349
+ case blr_short:
350
+ switch (subtype) {
351
+ case 0: sql_type = "SMALLINT"; break;
352
+ case 1: sql_type = "NUMERIC"; break;
353
+ case 2: sql_type = "DECIMAL"; break;
354
+ }
355
+ break;
356
+ case SQL_LONG:
357
+ case blr_long:
358
+ switch (subtype) {
359
+ case 0: sql_type = "INTEGER"; break;
360
+ case 1: sql_type = "NUMERIC"; break;
361
+ case 2: sql_type = "DECIMAL"; break;
362
+ }
363
+ break;
364
+ case SQL_FLOAT:
365
+ case blr_float:
366
+ sql_type = "FLOAT";
367
+ break;
368
+ case SQL_DOUBLE:
369
+ case blr_double:
370
+ switch (subtype) {
371
+ case 0: sql_type = "DOUBLE PRECISION"; break;
372
+ case 1: sql_type = "NUMERIC"; break;
373
+ case 2: sql_type = "DECIMAL"; break;
374
+ }
375
+ break;
376
+ case SQL_D_FLOAT:
377
+ case blr_d_float:
378
+ sql_type = "DOUBLE PRECISION";
379
+ break;
380
+ case SQL_TIMESTAMP:
381
+ case blr_timestamp:
382
+ sql_type = "TIMESTAMP";
383
+ break;
384
+ case SQL_BLOB:
385
+ case blr_blob:
386
+ sql_type = "BLOB";
387
+ break;
388
+ case SQL_ARRAY:
389
+ sql_type = "ARRAY";
390
+ break;
391
+ #if (FB_API_VER >= 30)
392
+ case SQL_BOOLEAN:
393
+ case blr_bool:
394
+ case blr_boolean:
395
+ sql_type = "BOOLEAN";
396
+ break;
397
+ #endif
398
+ case SQL_QUAD:
399
+ case blr_quad:
400
+ sql_type = "DECIMAL";
401
+ break;
402
+ case SQL_TYPE_TIME:
403
+ case blr_sql_time:
404
+ sql_type = "TIME";
405
+ break;
406
+ case SQL_TYPE_DATE:
407
+ case blr_sql_date:
408
+ sql_type = "DATE";
409
+ break;
410
+ case SQL_INT64:
411
+ case blr_int64:
412
+ switch (subtype) {
413
+ case 0: sql_type = "BIGINT"; break;
414
+ case 1: sql_type = "NUMERIC"; break;
415
+ case 2: sql_type = "DECIMAL"; break;
416
+ }
417
+ break;
418
+ default:
419
+ printf("Unknown: %d, %d\n", code, subtype);
420
+ sql_type = "UNKNOWN";
421
+ break;
422
+ }
423
+ return rb_str_new2(sql_type);
424
+ }
425
+
426
+ /* call-seq:
427
+ * from_code(code, subtype) -> String
428
+ *
429
+ * Returns the SQL type, such as VARCHAR or INTEGER for a given type code and subtype.
430
+ */
431
+ static VALUE sql_type_from_code(VALUE self, VALUE code, VALUE subtype)
432
+ {
433
+ return fb_sql_type_from_code(NUM2INT(code), NUM2INT(subtype));
434
+ }
435
+
436
+ static void fb_error_check(ISC_STATUS *isc_status)
437
+ {
438
+ if (isc_status[0] == 1 && isc_status[1]) {
439
+ char buf[1024];
440
+ VALUE exc, msg, msg1, msg2;
441
+ short code = isc_sqlcode(isc_status);
442
+
443
+ isc_sql_interprete(code, buf, 1024);
444
+ msg1 = rb_str_new2(buf);
445
+ msg2 = fb_error_msg(isc_status);
446
+ msg = rb_str_cat(msg1, "\n", strlen("\n"));
447
+ msg = rb_str_concat(msg, msg2);
448
+
449
+ exc = rb_exc_new3(rb_eFbError, msg);
450
+ rb_iv_set(exc, "error_code", INT2FIX(code));
451
+ rb_exc_raise(exc);
452
+ }
453
+ }
454
+
455
+ static void fb_error_check_warn(ISC_STATUS *isc_status)
456
+ {
457
+ short code = isc_sqlcode(isc_status);
458
+ if (code != 0) {
459
+ char buf[1024];
460
+ isc_sql_interprete(code, buf, 1024);
461
+ rb_warning("%s(%d)", buf, code);
462
+ }
463
+ }
464
+
465
+ static XSQLDA* sqlda_alloc(long cols)
466
+ {
467
+ XSQLDA *sqlda;
468
+
469
+ sqlda = (XSQLDA*)xmalloc(XSQLDA_LENGTH((size_t)cols));
470
+ #ifdef SQLDA_CURRENT_VERSION
471
+ sqlda->version = SQLDA_CURRENT_VERSION;
472
+ #else
473
+ sqlda->version = SQLDA_VERSION1;
474
+ #endif
475
+ sqlda->sqln = cols;
476
+ sqlda->sqld = 0;
477
+ return sqlda;
478
+ }
479
+
480
+ static VALUE cursor_close _((VALUE));
481
+ static VALUE cursor_drop _((VALUE));
482
+ static VALUE cursor_execute _((int, VALUE*, VALUE));
483
+ static VALUE cursor_fetchall _((int, VALUE*, VALUE));
484
+
485
+ static void fb_cursor_mark();
486
+ static void fb_cursor_free();
487
+
488
+ /* connection utilities */
489
+ static void fb_connection_check(struct FbConnection *fb_connection)
490
+ {
491
+ if (fb_connection->db == 0) {
492
+ rb_raise(rb_eFbError, "closed db connection");
493
+ }
494
+ }
495
+
496
+ static void fb_connection_close_cursors(struct FbConnection *fb_connection)
497
+ {
498
+ int i;
499
+ long len = RARRAY_LEN(fb_connection->cursor);
500
+ for (i = 0; i < len; i++) {
501
+ cursor_close(RARRAY_PTR(fb_connection->cursor)[i]);
502
+ }
503
+ }
504
+
505
+ static void fb_connection_drop_cursors(struct FbConnection *fb_connection)
506
+ {
507
+ int i;
508
+ long len = RARRAY_LEN(fb_connection->cursor);
509
+ for (i = 0; i < len; i++) {
510
+ cursor_drop(RARRAY_PTR(fb_connection->cursor)[i]);
511
+ }
512
+ rb_ary_clear(fb_connection->cursor);
513
+ }
514
+
515
+ static void fb_connection_disconnect(struct FbConnection *fb_connection)
516
+ {
517
+ if (fb_connection->transact) {
518
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
519
+ fb_error_check(fb_connection->isc_status);
520
+ }
521
+ if (fb_connection->dropped) {
522
+ isc_drop_database(fb_connection->isc_status, &fb_connection->db);
523
+ } else {
524
+ isc_detach_database(fb_connection->isc_status, &fb_connection->db);
525
+ }
526
+ fb_error_check(fb_connection->isc_status);
527
+ }
528
+
529
+ static void fb_connection_disconnect_warn(struct FbConnection *fb_connection)
530
+ {
531
+ if (fb_connection->transact) {
532
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
533
+ fb_error_check_warn(fb_connection->isc_status);
534
+ }
535
+ isc_detach_database(fb_connection->isc_status, &fb_connection->db);
536
+ fb_error_check_warn(fb_connection->isc_status);
537
+ }
538
+
539
+ static void fb_connection_mark(struct FbConnection *fb_connection)
540
+ {
541
+ rb_gc_mark(fb_connection->cursor);
542
+ }
543
+
544
+ static void fb_connection_free(struct FbConnection *fb_connection)
545
+ {
546
+ if (fb_connection->db) {
547
+ fb_connection_disconnect_warn(fb_connection);
548
+ }
549
+ xfree(fb_connection);
550
+ }
551
+
552
+ /*
553
+ static struct FbConnection* fb_connection_check_retrieve(VALUE data)
554
+ {
555
+ if (TYPE(data) != T_DATA || RDATA(data)->dfree != (void *)fb_connection_free) {
556
+ rb_raise(rb_eTypeError,
557
+ "Wrong argument type %s (expected Fb::Connection)",
558
+ rb_class2name(CLASS_OF(data)));
559
+ }
560
+ return (struct FbConnection*)RDATA(data)->data;
561
+ }
562
+ */
563
+
564
+ static unsigned short fb_connection_db_SQL_Dialect(struct FbConnection *fb_connection)
565
+ {
566
+ long dialect;
567
+ long length;
568
+ char db_info_command = isc_info_db_sql_dialect;
569
+ char isc_info_buff[16];
570
+
571
+ /* Get the db SQL Dialect */
572
+ isc_database_info(fb_connection->isc_status, &fb_connection->db,
573
+ 1, &db_info_command,
574
+ sizeof(isc_info_buff), isc_info_buff);
575
+ fb_error_check(fb_connection->isc_status);
576
+
577
+ if (isc_info_buff[0] == isc_info_db_sql_dialect) {
578
+ length = isc_vax_integer(&isc_info_buff[1], 2);
579
+ dialect = isc_vax_integer(&isc_info_buff[3], (short)length);
580
+ } else {
581
+ dialect = 1;
582
+ }
583
+ return dialect;
584
+ }
585
+
586
+ static unsigned short fb_connection_dialect(struct FbConnection *fb_connection)
587
+ {
588
+ return fb_connection->dialect;
589
+ }
590
+
591
+ /*
592
+ static unsigned short fb_connection_db_dialect(struct FbConnection *fb_connection)
593
+ {
594
+ return fb_connection->db_dialect;
595
+ }
596
+ */
597
+
598
+ /* Transaction option list */
599
+
600
+ static trans_opts rcom_opt_S[] =
601
+ {
602
+ {"NO", "RECORD_VERSION", isc_tpb_no_rec_version, -1, 0},
603
+ {"RECORD_VERSION", 0, isc_tpb_rec_version, -1, 0},
604
+ {"*", 0, isc_tpb_no_rec_version, -1, 0},
605
+ {0, 0, 0, 0, 0}
606
+ };
607
+
608
+
609
+ static trans_opts read_opt_S[] =
610
+ {
611
+ {"WRITE", 0, isc_tpb_write, 1, 0},
612
+ {"ONLY", 0, isc_tpb_read, 1, 0},
613
+ {"COMMITTED", 0, isc_tpb_read_committed, 2, rcom_opt_S},
614
+ {0, 0, 0, 0, 0}
615
+ };
616
+
617
+
618
+ static trans_opts snap_opt_S[] =
619
+ {
620
+ {"TABLE", "STABILITY", isc_tpb_consistency, 2, 0},
621
+ {"*", 0, isc_tpb_concurrency, 2, 0},
622
+ {0, 0, 0, 0, 0}
623
+ };
624
+
625
+
626
+ static trans_opts isol_opt_S[] =
627
+ {
628
+ {"SNAPSHOT", 0, 0, 0, snap_opt_S},
629
+ {"READ", "COMMITTED", isc_tpb_read_committed, 2, rcom_opt_S},
630
+ {0, 0, 0, 0, 0}
631
+ };
632
+
633
+
634
+ static trans_opts trans_opt_S[] =
635
+ {
636
+ {"READ", 0, 0, 0, read_opt_S},
637
+ {"WAIT", 0, isc_tpb_wait, 3, 0},
638
+ {"NO", "WAIT", isc_tpb_nowait, 3, 0},
639
+ {"ISOLATION", "LEVEL", 0, 0, isol_opt_S},
640
+ {"SNAPSHOT", 0, 0, 0, snap_opt_S},
641
+ {"RESERVING", 0, -1, 0, 0},
642
+ {0, 0, 0, 0, 0}
643
+ };
644
+
645
+ /* Name1 Name2 Option value Position Sub-option */
646
+
647
+ #define RESV_TABLEEND "FOR"
648
+ #define RESV_SHARED "SHARED"
649
+ #define RESV_PROTECTD "PROTECTED"
650
+ #define RESV_READ "READ"
651
+ #define RESV_WRITE "WRITE"
652
+ #define RESV_CONTINUE ','
653
+
654
+ static char* trans_parseopts(VALUE opt, long *tpb_len)
655
+ {
656
+ char *s, *trans;
657
+ long used;
658
+ long size;
659
+ char *tpb;
660
+ trans_opts *curr_p;
661
+ trans_opts *target_p;
662
+ char *check1_p;
663
+ char *check2_p;
664
+ int count;
665
+ int next_c;
666
+ char check_f[4];
667
+ char *resv_p;
668
+ char *resend_p;
669
+ char *tblend_p = 0;
670
+ long tbl_len;
671
+ long res_first;
672
+ int res_count;
673
+ long ofs;
674
+ char sp_prm;
675
+ char rw_prm;
676
+ int cont_f;
677
+ const char *desc = 0;
678
+
679
+ /* Initialize */
680
+ s = StringValuePtr(opt);
681
+ trans = ALLOCA_N(char, strlen(s)+1);
682
+ strcpy(trans, s);
683
+ s = trans;
684
+ while (*s) {
685
+ *s = UPPER(*s);
686
+ s++;
687
+ }
688
+
689
+ tpb = NULL;
690
+ memset((void *)check_f, 0, sizeof(check_f));
691
+
692
+ /* Set the default transaction option */
693
+ tpb = (char*)xmalloc(TPBBUFF_ALLOC);
694
+ size = TPBBUFF_ALLOC;
695
+ memcpy((void*)tpb, (void*)isc_tpb_0, sizeof(isc_tpb_0));
696
+ used = sizeof(isc_tpb_0);
697
+
698
+ /* Analyze the transaction option strings */
699
+ curr_p = trans_opt_S;
700
+ check1_p = strtok(trans, CMND_DELIMIT);
701
+ if (check1_p) {
702
+ check2_p = strtok(0, CMND_DELIMIT);
703
+ } else {
704
+ check2_p = 0;
705
+ }
706
+ while (curr_p) {
707
+ target_p = 0;
708
+ next_c = 0;
709
+ for (count = 0; curr_p[count].option1; count++) {
710
+ if (!strcmp(curr_p[count].option1, "*")) {
711
+ target_p = &curr_p[count];
712
+ break;
713
+ } else if (check1_p && !strcmp(check1_p, curr_p[count].option1)) {
714
+ if (!curr_p[count].option2) {
715
+ next_c = 1;
716
+ target_p = &curr_p[count];
717
+ break;
718
+ } else if (check2_p && !strcmp(check2_p, curr_p[count].option2)) {
719
+ next_c = 2;
720
+ target_p = &curr_p[count];
721
+ break;
722
+ }
723
+ }
724
+ }
725
+
726
+ if (!target_p) {
727
+ desc = "Illegal transaction option was specified";
728
+ goto error;
729
+ }
730
+
731
+ /* Set the transaction option */
732
+ if (target_p->optval > '\0') {
733
+ if (target_p->position > 0) {
734
+ if (check_f[target_p->position]) {
735
+ desc = "Duplicate transaction option was specified";
736
+ goto error;
737
+ }
738
+ tpb[target_p->position] = target_p->optval;
739
+ check_f[target_p->position] = 1;
740
+ } else {
741
+ if (used + 1 > size) {
742
+ char *tmp_tpb = (char *)realloc(tpb, (size_t)(size + TPBBUFF_ALLOC));
743
+ if (tmp_tpb == NULL) {
744
+ } else {
745
+ tpb = tmp_tpb;
746
+ }
747
+ size += TPBBUFF_ALLOC;
748
+ }
749
+ tpb[used] = target_p->optval;
750
+ used++;
751
+ }
752
+ } else if (target_p->optval) { /* RESERVING ... FOR */
753
+ if (check_f[0]) {
754
+ desc = "Duplicate transaction option was specified";
755
+ goto error;
756
+ }
757
+ resv_p = check2_p;
758
+ if (!resv_p || !strcmp(resv_p, RESV_TABLEEND)) {
759
+ desc = "RESERVING needs table name list";
760
+ goto error;
761
+ }
762
+ while (resv_p) {
763
+ res_first = used;
764
+ res_count = 0;
765
+ resend_p = strtok(0, CMND_DELIMIT);
766
+ while (resend_p) {
767
+ if (!strcmp(resend_p, RESV_TABLEEND)) {
768
+ break;
769
+ }
770
+ resend_p = strtok(0, CMND_DELIMIT);
771
+ }
772
+
773
+ if (!resend_p) {
774
+ desc = "Illegal transaction option was specified";
775
+ goto error;
776
+ }
777
+
778
+ while (resv_p < resend_p) {
779
+ if (*resv_p == '\0' || (ofs = strspn(resv_p, LIST_DELIMIT)) < 0) {
780
+ resv_p++;
781
+ } else {
782
+ resv_p = &resv_p[(size_t)ofs];
783
+ tblend_p = strpbrk(resv_p, LIST_DELIMIT);
784
+ if (tblend_p) {
785
+ tbl_len = tblend_p - resv_p;
786
+ } else {
787
+ tbl_len = strlen(resv_p);
788
+ }
789
+ if (tbl_len > META_NAME_MAX) {
790
+ desc = "Illegal table name was specified";
791
+ goto error;
792
+ }
793
+
794
+ if (tbl_len > 0) {
795
+ if (used + tbl_len + 3 > size) {
796
+ tpb = (char*)xrealloc(tpb, (size_t)(size+TPBBUFF_ALLOC));
797
+ size += TPBBUFF_ALLOC;
798
+ }
799
+ tpb[used+1] = (char)tbl_len;
800
+ memcpy((void *)&tpb[used+2],resv_p, tbl_len);
801
+ used += tbl_len + 3;
802
+ res_count++;
803
+ }
804
+ resv_p += tbl_len;
805
+ }
806
+ }
807
+
808
+ resv_p = strtok(0, CMND_DELIMIT);
809
+ if (resv_p && !strcmp(resv_p, RESV_SHARED)) {
810
+ sp_prm = isc_tpb_shared;
811
+ } else if (resv_p && !strcmp(resv_p, RESV_PROTECTD)) {
812
+ sp_prm = isc_tpb_protected;
813
+ } else {
814
+ desc = "RESERVING needs {SHARED|PROTECTED} {READ|WRITE}";
815
+ goto error;
816
+ }
817
+
818
+ cont_f = 0;
819
+ resv_p = strtok(0, CMND_DELIMIT);
820
+ if (resv_p) {
821
+ if (resv_p[strlen(resv_p)-1] == RESV_CONTINUE) {
822
+ cont_f = 1;
823
+ resv_p[strlen(resv_p)-1] = '\0';
824
+ } else {
825
+ tblend_p = strpbrk(resv_p, LIST_DELIMIT);
826
+ if (tblend_p) {
827
+ cont_f = 2;
828
+ *tblend_p = '\0';
829
+ }
830
+ }
831
+ }
832
+
833
+ if (resv_p && !strcmp(resv_p, RESV_READ)) {
834
+ rw_prm = isc_tpb_lock_read;
835
+ } else if (resv_p && !strcmp(resv_p, RESV_WRITE)) {
836
+ rw_prm = isc_tpb_lock_write;
837
+ } else {
838
+ desc = "RESERVING needs {SHARED|PROTECTED} {READ|WRITE}";
839
+ goto error;
840
+ }
841
+
842
+ ofs = res_first;
843
+ for (count = 0; count < res_count; count++) {
844
+ tpb[ofs++] = rw_prm;
845
+ ofs += tpb[ofs] + 1;
846
+ tpb[ofs++] = sp_prm;
847
+ }
848
+
849
+ if (cont_f == 1) {
850
+ resv_p = strtok(0, CMND_DELIMIT);
851
+ if (!resv_p) {
852
+ desc = "Unexpected end of command";
853
+ goto error;
854
+ }
855
+ }
856
+ if (cont_f == 2) {
857
+ resv_p = tblend_p + 1;
858
+ } else {
859
+ resv_p = strtok(0, CMND_DELIMIT);
860
+ if (resv_p) {
861
+ if ((int)strlen(resv_p) == 1 && resv_p[0] == RESV_CONTINUE) {
862
+ resv_p = strtok(0, CMND_DELIMIT);
863
+ if (!resv_p) {
864
+ desc = "Unexpected end of command";
865
+ goto error;
866
+ }
867
+ } else if (resv_p[0] == RESV_CONTINUE) {
868
+ resv_p++;
869
+ } else {
870
+ next_c = 1;
871
+ check2_p = resv_p;
872
+ resv_p = 0;
873
+ }
874
+ } else {
875
+ next_c = 0;
876
+ check1_p = check2_p = 0;
877
+ }
878
+ }
879
+ }
880
+
881
+ check_f[0] = 1;
882
+ }
883
+
884
+
885
+ /* Set the next check list */
886
+ curr_p = target_p->sub_opts;
887
+
888
+ for (count = 0; count < next_c; count++) {
889
+ check1_p = check2_p;
890
+ if (check2_p) {
891
+ check2_p = strtok(0, CMND_DELIMIT);
892
+ }
893
+ }
894
+
895
+ if (check1_p && !curr_p) {
896
+ curr_p = trans_opt_S;
897
+ }
898
+ }
899
+
900
+ /* Set the results */
901
+ *tpb_len = used;
902
+ return tpb;
903
+
904
+ error:
905
+ xfree(tpb);
906
+ rb_raise(rb_eFbError, "%s", desc);
907
+ }
908
+
909
+ static void fb_connection_transaction_start(struct FbConnection *fb_connection, VALUE opt)
910
+ {
911
+ char *tpb = 0;
912
+ static char isc_tpb[5] = {isc_tpb_version1,
913
+ isc_tpb_write,
914
+ isc_tpb_read_committed,
915
+ isc_tpb_wait,
916
+ isc_tpb_rec_version};
917
+ long tpb_len;
918
+
919
+ if (fb_connection->transact) {
920
+ rb_raise(rb_eFbError, "A transaction has been already started");
921
+ }
922
+
923
+ if (!NIL_P(opt) && opt != Qnil) {
924
+ tpb = trans_parseopts(opt, &tpb_len);
925
+ isc_start_transaction(fb_connection->isc_status, &fb_connection->transact, 1, &fb_connection->db, tpb_len, tpb);
926
+ } else {
927
+ isc_start_transaction(fb_connection->isc_status, &fb_connection->transact, 1, &fb_connection->db, 5, isc_tpb);
928
+ }
929
+
930
+ xfree(tpb);
931
+ fb_error_check(fb_connection->isc_status);
932
+ }
933
+
934
+ static void fb_connection_commit(struct FbConnection *fb_connection)
935
+ {
936
+ if (fb_connection->transact) {
937
+ fb_connection_close_cursors(fb_connection);
938
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
939
+ fb_error_check(fb_connection->isc_status);
940
+ }
941
+ }
942
+
943
+ static void fb_connection_rollback(struct FbConnection *fb_connection)
944
+ {
945
+ if (fb_connection->transact) {
946
+ fb_connection_close_cursors(fb_connection);
947
+ isc_rollback_transaction(fb_connection->isc_status, &fb_connection->transact);
948
+ fb_error_check(fb_connection->isc_status);
949
+ }
950
+ }
951
+
952
+ /* call-seq:
953
+ * transaction(options) -> true
954
+ * transaction(options) { } -> block result
955
+ *
956
+ * Start a transaction for this connection.
957
+ */
958
+ static VALUE connection_transaction(int argc, VALUE *argv, VALUE self)
959
+ {
960
+ struct FbConnection *fb_connection;
961
+ VALUE opt = Qnil;
962
+
963
+ rb_scan_args(argc, argv, "01", &opt);
964
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
965
+
966
+ fb_connection_transaction_start(fb_connection, opt);
967
+
968
+ if (rb_block_given_p()) {
969
+ int state;
970
+ VALUE result = rb_protect(rb_yield, 0, &state);
971
+ if (state) {
972
+ fb_connection_rollback(fb_connection);
973
+ return rb_funcall(rb_mKernel, rb_intern("raise"), 0);
974
+ } else {
975
+ fb_connection_commit(fb_connection);
976
+ return result;
977
+ }
978
+ } else {
979
+ return Qtrue;
980
+ }
981
+ }
982
+
983
+ /* call-seq:
984
+ * transaction_started()? -> true or false
985
+ *
986
+ * Returns true if a transaction is currently active.
987
+ */
988
+ static VALUE connection_transaction_started(VALUE self)
989
+ {
990
+ struct FbConnection *fb_connection;
991
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
992
+
993
+ return fb_connection->transact ? Qtrue : Qfalse;
994
+ }
995
+
996
+ /* call-seq:
997
+ * commit() -> nil
998
+ *
999
+ * Commit the current transaction.
1000
+ */
1001
+ static VALUE connection_commit(VALUE self)
1002
+ {
1003
+ struct FbConnection *fb_connection;
1004
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1005
+
1006
+ fb_connection_commit(fb_connection);
1007
+ return Qnil;
1008
+ }
1009
+
1010
+ /* call-seq:
1011
+ * rollback() -> nil
1012
+ *
1013
+ * Rollback the current transaction.
1014
+ */
1015
+ static VALUE connection_rollback(VALUE self)
1016
+ {
1017
+ struct FbConnection *fb_connection;
1018
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1019
+
1020
+ fb_connection_rollback(fb_connection);
1021
+ return Qnil;
1022
+ }
1023
+
1024
+ /*
1025
+ * call-seq:
1026
+ * open?() -> true or false
1027
+ *
1028
+ * Current connection status.
1029
+ */
1030
+
1031
+ static VALUE connection_is_open(VALUE self)
1032
+ {
1033
+ struct FbConnection *fb_connection;
1034
+
1035
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1036
+ return (fb_connection->db == 0) ? Qfalse : Qtrue;
1037
+ }
1038
+
1039
+ /* call-seq:
1040
+ * to_s() -> String
1041
+ *
1042
+ * Return current database connection string and either (OPEN) or (CLOSED).
1043
+ */
1044
+ static VALUE connection_to_s(VALUE self)
1045
+ {
1046
+ VALUE is_open = connection_is_open(self);
1047
+ VALUE status = (is_open == Qtrue) ? rb_str_new2(" (OPEN)") : rb_str_new2(" (CLOSED)");
1048
+ VALUE database = rb_iv_get(self, "@database");
1049
+ VALUE s = rb_str_dup(database);
1050
+ return rb_str_concat(s, status);
1051
+ }
1052
+
1053
+ /* call-seq:
1054
+ * cursor() -> Cursor
1055
+ *
1056
+ * Creates a +Cursor+ for the +Connection+ and allocates a statement.
1057
+ * This function is no longer published.
1058
+ */
1059
+ static VALUE connection_cursor(VALUE self)
1060
+ {
1061
+ VALUE c;
1062
+ struct FbConnection *fb_connection;
1063
+ struct FbCursor *fb_cursor;
1064
+
1065
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1066
+ fb_connection_check(fb_connection);
1067
+
1068
+ c = Data_Make_Struct(rb_cFbCursor, struct FbCursor, fb_cursor_mark, fb_cursor_free, fb_cursor);
1069
+ fb_cursor->connection = self;
1070
+ fb_cursor->fields_ary = Qnil;
1071
+ fb_cursor->fields_hash = Qnil;
1072
+ fb_cursor->open = Qfalse;
1073
+ fb_cursor->eof = Qfalse;
1074
+ fb_cursor->stmt = 0;
1075
+ fb_cursor->i_sqlda = sqlda_alloc(SQLDA_COLSINIT);
1076
+ fb_cursor->o_sqlda = sqlda_alloc(SQLDA_COLSINIT);
1077
+ fb_cursor->i_buffer = NULL;
1078
+ fb_cursor->i_buffer_size = 0;
1079
+ fb_cursor->o_buffer = NULL;
1080
+ fb_cursor->o_buffer_size = 0;
1081
+ isc_dsql_alloc_statement2(fb_connection->isc_status, &fb_connection->db, &fb_cursor->stmt);
1082
+ fb_error_check(fb_connection->isc_status);
1083
+
1084
+ return c;
1085
+ }
1086
+
1087
+ /* call-seq:
1088
+ * execute(sql, *args) -> Cursor or rows affected
1089
+ * execute(sql, *args) {|cursor| } -> block result
1090
+ *
1091
+ * Allocates a +Cursor+ and executes the +sql+ statement, matching up the
1092
+ * parameters in +args+ with the place holders in the statement, represented by ?'s.
1093
+ *
1094
+ * If the sql statement returns a result set and a block is provided, the cursor is
1095
+ * yielded to the block before being automatically closed.
1096
+ *
1097
+ * If the sql statement returns a result set and a block is not provided, a +Cursor+
1098
+ * object is returned.
1099
+ *
1100
+ * If the sql statement performs an INSERT, UPDATE or DELETE, the number of rows
1101
+ * affected is returned. Other statements, such as schema updates, return -1.
1102
+ *
1103
+ * +*args+ can be one or more tuples of parameters or an array of tuples (arrays),
1104
+ * in which case the statement will be executed once for each tuple of parameters.
1105
+ *
1106
+ * If no transaction is currently active, a transaction is automatically started
1107
+ * and is closed when the cursor is closed. Note that if additional statements
1108
+ * yielding cursors are started before the first cursor is closed, these cursors will
1109
+ * also be closed when the first one is closed and its transaction committed.
1110
+ */
1111
+ static VALUE connection_execute(int argc, VALUE *argv, VALUE self)
1112
+ {
1113
+ VALUE cursor = connection_cursor(self);
1114
+ VALUE val = cursor_execute(argc, argv, cursor);
1115
+
1116
+ if (NIL_P(val)) {
1117
+ if (rb_block_given_p()) {
1118
+ return rb_ensure(rb_yield,cursor,cursor_close,cursor);
1119
+ } else {
1120
+ HERE("connection_execute Y");
1121
+ return cursor;
1122
+ }
1123
+ } else {
1124
+ cursor_drop(cursor);
1125
+ }
1126
+ return val;
1127
+ }
1128
+
1129
+ /* call-seq:
1130
+ * query(:array, sql, *arg) -> Array of Arrays or nil
1131
+ * query(:hash, sql, *arg) -> Array of Hashes or nil
1132
+ * query(sql, *args) -> Array of Arrays or nil
1133
+ *
1134
+ * For queries returning a result set, an array is returned, containing
1135
+ * either a list of Arrays or Hashes, one for each row.
1136
+ *
1137
+ * If the sql statement performs an INSERT, UPDATE or DELETE, the number of rows
1138
+ * affected is returned. Other statements, such as schema updates, return -1.
1139
+ *
1140
+ * If no transaction is currently active, a transaction is automatically started
1141
+ * and committed. Otherwise, the statement executes within the context of the
1142
+ * current transaction.
1143
+ */
1144
+ static VALUE connection_query(int argc, VALUE *argv, VALUE self)
1145
+ {
1146
+ VALUE format;
1147
+ VALUE cursor;
1148
+ VALUE result;
1149
+
1150
+ if (argc >= 1 && TYPE(argv[0]) == T_SYMBOL) {
1151
+ format = argv[0];
1152
+ argc--; argv++;
1153
+ } else {
1154
+ format = ID2SYM(rb_intern("array"));
1155
+ }
1156
+ cursor = connection_cursor(self);
1157
+ result = cursor_execute(argc, argv, cursor);
1158
+ if (NIL_P(result)) {
1159
+ result = cursor_fetchall(1, &format, cursor);
1160
+ cursor_close(cursor);
1161
+ }
1162
+
1163
+ return result;
1164
+ }
1165
+
1166
+ /* call-seq:
1167
+ * close() -> nil
1168
+ *
1169
+ * Closes connection to database. All open cursors are dropped.
1170
+ */
1171
+ static VALUE connection_close(VALUE self)
1172
+ {
1173
+ struct FbConnection *fb_connection;
1174
+
1175
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1176
+
1177
+ if (fb_connection->dropped) return Qnil;
1178
+
1179
+ fb_connection_check(fb_connection);
1180
+ fb_connection_disconnect(fb_connection);
1181
+ fb_connection_drop_cursors(fb_connection);
1182
+
1183
+ return Qnil;
1184
+ }
1185
+
1186
+ /* call-seq:
1187
+ * drop() -> nil
1188
+ *
1189
+ * Drops connected database. All open cursors are dropped.
1190
+ */
1191
+ static VALUE connection_drop(VALUE self)
1192
+ {
1193
+ struct FbConnection *fb_connection;
1194
+
1195
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1196
+ fb_connection->dropped = 1;
1197
+ fb_connection_disconnect(fb_connection);
1198
+ fb_connection_drop_cursors(fb_connection);
1199
+
1200
+ return Qnil;
1201
+ }
1202
+
1203
+ /* call-seq:
1204
+ * dialect() -> int
1205
+ *
1206
+ * Returns dialect of connection.
1207
+ */
1208
+ static VALUE connection_dialect(VALUE self)
1209
+ {
1210
+ struct FbConnection *fb_connection;
1211
+
1212
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1213
+ fb_connection_check(fb_connection);
1214
+
1215
+ return INT2FIX(fb_connection->dialect);
1216
+ }
1217
+
1218
+ /* call-seq:
1219
+ * db_dialect() -> int
1220
+ *
1221
+ * Returns database dialect.
1222
+ */
1223
+ static VALUE connection_db_dialect(VALUE self)
1224
+ {
1225
+ struct FbConnection *fb_connection;
1226
+
1227
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
1228
+ fb_connection_check(fb_connection);
1229
+
1230
+ return INT2FIX(fb_connection->db_dialect);
1231
+ }
1232
+
1233
+ static void fb_cursor_check(struct FbCursor *fb_cursor)
1234
+ {
1235
+ if (fb_cursor->stmt == 0) {
1236
+ rb_raise(rb_eFbError, "dropped db cursor");
1237
+ }
1238
+ if (!fb_cursor->open) {
1239
+ rb_raise(rb_eFbError, "closed db cursor");
1240
+ }
1241
+ }
1242
+
1243
+ static void fb_cursor_drop(struct FbCursor *fb_cursor)
1244
+ {
1245
+ ISC_STATUS isc_status[20];
1246
+ if (fb_cursor->open) {
1247
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_close);
1248
+ fb_error_check(isc_status);
1249
+ }
1250
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_drop);
1251
+ fb_error_check(isc_status);
1252
+ }
1253
+
1254
+ static void fb_cursor_drop_warn(struct FbCursor *fb_cursor)
1255
+ {
1256
+ ISC_STATUS isc_status[20];
1257
+ if (fb_cursor->open) {
1258
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_close);
1259
+ fb_error_check_warn(isc_status);
1260
+ }
1261
+ isc_dsql_free_statement(isc_status, &fb_cursor->stmt, DSQL_drop);
1262
+ fb_error_check_warn(isc_status);
1263
+ }
1264
+
1265
+ static void fb_cursor_mark(struct FbCursor *fb_cursor)
1266
+ {
1267
+ rb_gc_mark(fb_cursor->connection);
1268
+ rb_gc_mark(fb_cursor->fields_ary);
1269
+ rb_gc_mark(fb_cursor->fields_hash);
1270
+ }
1271
+
1272
+ static void fb_cursor_free(struct FbCursor *fb_cursor)
1273
+ {
1274
+ if (fb_cursor->stmt) {
1275
+ fb_cursor_drop_warn(fb_cursor);
1276
+ }
1277
+ xfree(fb_cursor->i_sqlda);
1278
+ xfree(fb_cursor->o_sqlda);
1279
+ xfree(fb_cursor->i_buffer);
1280
+ xfree(fb_cursor->o_buffer);
1281
+ xfree(fb_cursor);
1282
+ }
1283
+
1284
+ static VALUE sql_decimal_to_bigdecimal(long long sql_data, int scale)
1285
+ {
1286
+ int i;
1287
+ char bigdecimal_buffer[23];
1288
+ int bigdecimal_dot;
1289
+ sprintf(bigdecimal_buffer, "%022lld", sql_data);
1290
+ bigdecimal_dot = strlen(bigdecimal_buffer) + (size_t)scale;
1291
+ for (i = strlen(bigdecimal_buffer); i > bigdecimal_dot; i--)
1292
+ bigdecimal_buffer[i] = bigdecimal_buffer[i-1];
1293
+ bigdecimal_buffer[bigdecimal_dot] = '.';
1294
+ return rb_funcall(rb_path2class("BigDecimal"), rb_intern("new"), 1, rb_str_new2(bigdecimal_buffer));
1295
+ }
1296
+
1297
+ static VALUE object_to_unscaled_bigdecimal(VALUE object, int scale)
1298
+ {
1299
+ int i;
1300
+ long ratio = 1;
1301
+ for (i = 0; i > scale; i--)
1302
+ ratio *= 10;
1303
+ if (TYPE(object) == T_FLOAT)
1304
+ object = rb_funcall(object, rb_intern("to_s"), 0);
1305
+ object = rb_funcall(rb_path2class("BigDecimal"), rb_intern("new"), 1, object);
1306
+ object = rb_funcall(object, rb_intern("*"), 1, LONG2NUM(ratio));
1307
+ return rb_funcall(object, rb_intern("round"), 0);
1308
+ }
1309
+
1310
+ static void fb_cursor_set_inputparams(struct FbCursor *fb_cursor, long argc, VALUE *argv)
1311
+ {
1312
+ struct FbConnection *fb_connection;
1313
+ long count;
1314
+ long offset;
1315
+ /* long type; */
1316
+ short dtp;
1317
+ VALUE obj;
1318
+ long lvalue;
1319
+ ISC_INT64 llvalue;
1320
+ long alignment;
1321
+ double dvalue;
1322
+ double dcheck;
1323
+ VARY *vary;
1324
+ XSQLVAR *var;
1325
+
1326
+ isc_blob_handle blob_handle;
1327
+ ISC_QUAD blob_id;
1328
+ /* static char blob_items[] = { isc_info_blob_max_segment }; */
1329
+ /* char blob_info[16]; */
1330
+ char *p;
1331
+ long length;
1332
+ /* struct time_object *tobj; */
1333
+ struct tm tms;
1334
+
1335
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1336
+
1337
+ /* Check the number of parameters */
1338
+ if (fb_cursor->i_sqlda->sqld != argc) {
1339
+ rb_raise(rb_eFbError, "statement requires %d items; %ld given", fb_cursor->i_sqlda->sqld, argc);
1340
+ }
1341
+
1342
+ /* Get the parameters */
1343
+ for (count = 0,offset = 0; count < argc; count++) {
1344
+ obj = argv[count];
1345
+
1346
+ /* type = TYPE(obj); */
1347
+
1348
+ /* Convert the data type for InterBase */
1349
+ var = &fb_cursor->i_sqlda->sqlvar[count];
1350
+ if (!NIL_P(obj)) {
1351
+ dtp = var->sqltype & ~1; /* Erase null flag */
1352
+ alignment = var->sqllen;
1353
+
1354
+ switch (dtp) {
1355
+ case SQL_TEXT :
1356
+ alignment = 1;
1357
+ offset = FB_ALIGN((size_t)offset, alignment);
1358
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1359
+ obj = rb_obj_as_string(obj);
1360
+ if (RSTRING_LEN(obj) > var->sqllen) {
1361
+ rb_raise(rb_eRangeError, "CHAR overflow: %ld bytes exceeds %d byte(s) allowed.",
1362
+ RSTRING_LEN(obj), var->sqllen);
1363
+ }
1364
+ memcpy(var->sqldata, RSTRING_PTR(obj), (size_t)RSTRING_LEN(obj));
1365
+ var->sqllen = RSTRING_LEN(obj);
1366
+ offset += var->sqllen + 1;
1367
+ break;
1368
+
1369
+ case SQL_VARYING :
1370
+ alignment = sizeof(short);
1371
+ offset = FB_ALIGN((size_t)offset, alignment);
1372
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1373
+ vary = (VARY *)var->sqldata;
1374
+ obj = rb_obj_as_string(obj);
1375
+ if (RSTRING_LEN(obj) > var->sqllen) {
1376
+ rb_raise(rb_eRangeError, "VARCHAR overflow: %ld bytes exceeds %d byte(s) allowed.",
1377
+ RSTRING_LEN(obj), var->sqllen);
1378
+ }
1379
+ memcpy(vary->vary_string, RSTRING_PTR(obj), (size_t)RSTRING_LEN(obj));
1380
+ vary->vary_length = RSTRING_LEN(obj);
1381
+ offset += vary->vary_length + sizeof(short);
1382
+ break;
1383
+
1384
+ case SQL_SHORT :
1385
+ offset = FB_ALIGN((size_t)offset, alignment);
1386
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1387
+ if (var->sqlscale < 0) {
1388
+ lvalue = NUM2LONG(object_to_unscaled_bigdecimal(obj, var->sqlscale));
1389
+ } else {
1390
+ lvalue = NUM2LONG(object_to_fixnum(obj));
1391
+ }
1392
+ if (lvalue < -32768 || lvalue > 32767) {
1393
+ rb_raise(rb_eRangeError, "short integer overflow");
1394
+ }
1395
+ *(short *)var->sqldata = lvalue;
1396
+ offset += alignment;
1397
+ break;
1398
+
1399
+ case SQL_LONG :
1400
+ offset = FB_ALIGN((size_t)offset, alignment);
1401
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1402
+ if (var->sqlscale < 0) {
1403
+ lvalue = NUM2LONG(object_to_unscaled_bigdecimal(obj, var->sqlscale));
1404
+ } else {
1405
+ lvalue = NUM2LONG(object_to_fixnum(obj));
1406
+ }
1407
+ if (lvalue < -2147483648LL || lvalue > 2147483647LL) {
1408
+ rb_raise(rb_eRangeError, "integer overflow");
1409
+ }
1410
+ *(ISC_LONG *)var->sqldata = (ISC_LONG)lvalue;
1411
+ offset += alignment;
1412
+ break;
1413
+
1414
+ case SQL_FLOAT :
1415
+ offset = FB_ALIGN((size_t)offset, alignment);
1416
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1417
+ obj = double_from_obj(obj);
1418
+ dvalue = NUM2DBL(obj);
1419
+ if (dvalue >= 0.0) {
1420
+ dcheck = dvalue;
1421
+ } else {
1422
+ dcheck = dvalue * -1;
1423
+ }
1424
+ if (dcheck != 0.0 && (dcheck < FLT_MIN || dcheck > FLT_MAX)) {
1425
+ rb_raise(rb_eRangeError, "float overflow");
1426
+ }
1427
+ *(float *)var->sqldata = (float)dvalue;
1428
+ offset += alignment;
1429
+ break;
1430
+
1431
+ case SQL_DOUBLE :
1432
+ offset = FB_ALIGN((size_t)offset, alignment);
1433
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1434
+ obj = double_from_obj(obj);
1435
+ dvalue = NUM2DBL(obj);
1436
+ *(double *)var->sqldata = dvalue;
1437
+ offset += alignment;
1438
+ break;
1439
+
1440
+ case SQL_INT64 :
1441
+ offset = FB_ALIGN((size_t)offset, alignment);
1442
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1443
+
1444
+ if (var->sqlscale < 0) {
1445
+ llvalue = NUM2LL(object_to_unscaled_bigdecimal(obj, var->sqlscale));
1446
+ } else {
1447
+ llvalue = NUM2LL(object_to_fixnum(obj));
1448
+ }
1449
+
1450
+ *(ISC_INT64 *)var->sqldata = llvalue;
1451
+ offset += alignment;
1452
+ break;
1453
+
1454
+ case SQL_BLOB :
1455
+ offset = FB_ALIGN((size_t)offset, alignment);
1456
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1457
+ obj = rb_obj_as_string(obj);
1458
+
1459
+ blob_handle = 0;
1460
+ isc_create_blob2(
1461
+ fb_connection->isc_status,&fb_connection->db,&fb_connection->transact,
1462
+ &blob_handle,&blob_id,0,NULL);
1463
+ fb_error_check(fb_connection->isc_status);
1464
+ length = RSTRING_LEN(obj);
1465
+ p = RSTRING_PTR(obj);
1466
+ while (length >= 4096) {
1467
+ isc_put_segment(fb_connection->isc_status,&blob_handle,4096,p);
1468
+ fb_error_check(fb_connection->isc_status);
1469
+ p += 4096;
1470
+ length -= 4096;
1471
+ }
1472
+ if (length) {
1473
+ isc_put_segment(fb_connection->isc_status,&blob_handle,length,p);
1474
+ fb_error_check(fb_connection->isc_status);
1475
+ }
1476
+ isc_close_blob(fb_connection->isc_status,&blob_handle);
1477
+ fb_error_check(fb_connection->isc_status);
1478
+
1479
+ *(ISC_QUAD *)var->sqldata = blob_id;
1480
+ offset += alignment;
1481
+ break;
1482
+
1483
+ case SQL_TIMESTAMP :
1484
+ offset = FB_ALIGN((size_t)offset, alignment);
1485
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1486
+ tm_from_timestamp(&tms, obj);
1487
+ isc_encode_timestamp(&tms, (ISC_TIMESTAMP *)var->sqldata);
1488
+ offset += alignment;
1489
+ break;
1490
+
1491
+ case SQL_TYPE_TIME :
1492
+ offset = FB_ALIGN((size_t)offset, alignment);
1493
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1494
+ tm_from_timestamp(&tms, obj);
1495
+ isc_encode_sql_time(&tms, (ISC_TIME *)var->sqldata);
1496
+ offset += alignment;
1497
+ break;
1498
+
1499
+ case SQL_TYPE_DATE :
1500
+ offset = FB_ALIGN((size_t)offset, alignment);
1501
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1502
+ tm_from_date(&tms, obj);
1503
+ isc_encode_sql_date(&tms, (ISC_DATE *)var->sqldata);
1504
+ offset += alignment;
1505
+ break;
1506
+
1507
+ #if 0
1508
+ case SQL_ARRAY :
1509
+ /* Not supported now
1510
+ offset = FB_ALIGN(offset, alignment);
1511
+ var->sqldata = (char *)(fb_cursor->i_buffer + offset);
1512
+ if (get_arrayvalue(self, type, obj, var))
1513
+ return(STATUS_ABNORMAL);
1514
+ offset += alignment;
1515
+ break;
1516
+ */
1517
+ rb_raise(rb_eArgError, "Arrays not supported");
1518
+ break;
1519
+ #endif
1520
+
1521
+ #if (FB_API_VER >= 30)
1522
+ case SQL_BOOLEAN:
1523
+ case blr_bool:
1524
+ case blr_boolean:
1525
+ offset = FB_ALIGN((size_t)offset, alignment);
1526
+ var->sqldata = (char *)(fb_cursor->i_buffer + (size_t)offset);
1527
+ *(bool *)var->sqldata = obj;
1528
+ offset += alignment;
1529
+ break;
1530
+ #endif
1531
+ default :
1532
+ rb_raise(rb_eFbError, "Specified table includes unsupported datatype (%d)", dtp);
1533
+ }
1534
+
1535
+ if (var->sqltype & 1) {
1536
+ offset = FB_ALIGN((size_t)offset, sizeof(short));
1537
+ var->sqlind = (short *)(fb_cursor->i_buffer + (size_t)offset);
1538
+ *var->sqlind = 0;
1539
+ offset += sizeof(short);
1540
+ }
1541
+ } else if (var->sqltype & 1) {
1542
+ var->sqldata = 0;
1543
+ offset = FB_ALIGN((size_t)offset, sizeof(short));
1544
+ var->sqlind = (short *)(fb_cursor->i_buffer + (size_t)offset);
1545
+ *var->sqlind = -1;
1546
+ offset += sizeof(short);
1547
+ } else {
1548
+ rb_raise(rb_eFbError, "specified column is not permitted to be null");
1549
+ }
1550
+ }
1551
+ }
1552
+
1553
+ static void fb_cursor_execute_withparams(struct FbCursor *fb_cursor, long argc, VALUE *argv)
1554
+ {
1555
+ struct FbConnection *fb_connection;
1556
+
1557
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1558
+ /* Check the first object type of the parameters */
1559
+ if (argc >= 1 && TYPE(argv[0]) == T_ARRAY) {
1560
+ int i;
1561
+ VALUE obj;
1562
+ VALUE ary = argv[0];
1563
+ if (RARRAY_LEN(ary) > 0 && TYPE(RARRAY_PTR(ary)[0]) == T_ARRAY) {
1564
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
1565
+ obj = rb_ary_entry(ary, i);
1566
+ fb_cursor_execute_withparams(fb_cursor, 1, &obj);
1567
+ }
1568
+ } else {
1569
+ for (i = 0; i < argc; i++) {
1570
+ obj = argv[i];
1571
+
1572
+ /* Set the input parameters */
1573
+ Check_Type(obj, T_ARRAY);
1574
+ fb_cursor_set_inputparams(fb_cursor, RARRAY_LEN(obj), RARRAY_PTR(obj));
1575
+
1576
+ /* Execute SQL statement */
1577
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, fb_cursor->i_sqlda, NULL);
1578
+ fb_error_check(fb_connection->isc_status);
1579
+ }
1580
+ }
1581
+ } else {
1582
+ /* Set the input parameters */
1583
+ fb_cursor_set_inputparams(fb_cursor, argc, argv);
1584
+
1585
+ /* Execute SQL statement */
1586
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, fb_cursor->i_sqlda, NULL);
1587
+ fb_error_check(fb_connection->isc_status);
1588
+ }
1589
+ }
1590
+
1591
+ static VALUE precision_from_sqlvar(XSQLVAR *sqlvar)
1592
+ {
1593
+ switch(sqlvar->sqltype & ~1) {
1594
+ case SQL_TEXT: return Qnil;
1595
+ case SQL_VARYING: return Qnil;
1596
+ case SQL_SHORT:
1597
+ switch (sqlvar->sqlsubtype) {
1598
+ case 0: return INT2FIX(0);
1599
+ case 1: return INT2FIX(4);
1600
+ case 2: return INT2FIX(4);
1601
+ }
1602
+ break;
1603
+ case SQL_LONG:
1604
+ switch (sqlvar->sqlsubtype) {
1605
+ case 0: return INT2FIX(0);
1606
+ case 1: return INT2FIX(9);
1607
+ case 2: return INT2FIX(9);
1608
+ }
1609
+ break;
1610
+ case SQL_FLOAT: return Qnil;
1611
+ case SQL_DOUBLE:
1612
+ case SQL_D_FLOAT:
1613
+ switch (sqlvar->sqlsubtype) {
1614
+ case 0: return Qnil;
1615
+ case 1: return INT2FIX(15);
1616
+ case 2: return INT2FIX(15);
1617
+ }
1618
+ break;
1619
+ case SQL_TIMESTAMP: return Qnil;
1620
+ case SQL_BLOB: return Qnil;
1621
+ case SQL_ARRAY: return Qnil;
1622
+ #if (FB_API_VER >= 30)
1623
+ case SQL_BOOLEAN:
1624
+ case blr_bool:
1625
+ case blr_boolean:
1626
+ return Qnil;
1627
+ #endif
1628
+ case SQL_QUAD: return Qnil;
1629
+ case SQL_TYPE_TIME: return Qnil;
1630
+ case SQL_TYPE_DATE: return Qnil;
1631
+ case SQL_INT64:
1632
+ switch (sqlvar->sqlsubtype) {
1633
+ case 0: return INT2FIX(0);
1634
+ case 1: return INT2FIX(18);
1635
+ case 2: return INT2FIX(18);
1636
+ }
1637
+ break;
1638
+ }
1639
+ return Qnil;
1640
+ }
1641
+
1642
+ static int no_lowercase(VALUE value)
1643
+ {
1644
+ VALUE local_value = StringValue(value);
1645
+ int result = rb_funcall(re_lowercase, id_matches, 1, local_value) == Qnil;
1646
+ return result;
1647
+ }
1648
+
1649
+ static VALUE fb_cursor_fields_ary(XSQLDA *sqlda, short downcase_names)
1650
+ {
1651
+ long cols;
1652
+ long count;
1653
+ XSQLVAR *var;
1654
+ short dtp;
1655
+ VALUE ary;
1656
+
1657
+ cols = sqlda->sqld;
1658
+ if (cols == 0) {
1659
+ return Qnil;
1660
+ }
1661
+
1662
+ ary = rb_ary_new();
1663
+ for (count = 0; count < cols; count++) {
1664
+ VALUE field;
1665
+ VALUE name, type_code, sql_type, sql_subtype, display_size, internal_size, precision, scale, nullable;
1666
+
1667
+ var = &sqlda->sqlvar[count];
1668
+ dtp = var->sqltype & ~1;
1669
+
1670
+ if (var->aliasname_length) { /* aliasname always present? */
1671
+ name = rb_tainted_str_new(var->aliasname, var->aliasname_length);
1672
+ } else {
1673
+ name = rb_tainted_str_new(var->sqlname, var->sqlname_length);
1674
+ }
1675
+ if (downcase_names && no_lowercase(name)) {
1676
+ rb_funcall(name, id_downcase_bang, 0);
1677
+ }
1678
+ rb_str_freeze(name);
1679
+ type_code = INT2NUM((long)(var->sqltype & ~1));
1680
+ sql_type = fb_sql_type_from_code(dtp, var->sqlsubtype);
1681
+ sql_subtype = INT2FIX(var->sqlsubtype);
1682
+ display_size = INT2NUM((long)var->sqllen);
1683
+ if (dtp == SQL_VARYING) {
1684
+ internal_size = INT2NUM((long)var->sqllen + sizeof(short));
1685
+ } else {
1686
+ internal_size = INT2NUM((long)var->sqllen);
1687
+ }
1688
+ precision = precision_from_sqlvar(var);
1689
+ scale = INT2NUM((long)var->sqlscale);
1690
+ nullable = (var->sqltype & 1) ? Qtrue : Qfalse;
1691
+
1692
+ field = rb_struct_new(rb_sFbField, name, sql_type, sql_subtype, display_size, internal_size, precision, scale, nullable, type_code);
1693
+ rb_ary_push(ary, field);
1694
+ }
1695
+ rb_ary_freeze(ary);
1696
+ return ary;
1697
+ }
1698
+
1699
+ static VALUE fb_cursor_fields_hash(VALUE fields_ary)
1700
+ {
1701
+ int i;
1702
+ VALUE hash = rb_hash_new();
1703
+
1704
+ for (i = 0; i < RARRAY_LEN(fields_ary); i++) {
1705
+ VALUE field = rb_ary_entry(fields_ary, i);
1706
+ VALUE name = rb_struct_aref(field, LONG2NUM(0));
1707
+ rb_hash_aset(hash, name, field);
1708
+ }
1709
+
1710
+ return hash;
1711
+ }
1712
+
1713
+ static void fb_cursor_fetch_prep(struct FbCursor *fb_cursor)
1714
+ {
1715
+ struct FbConnection *fb_connection;
1716
+ long cols;
1717
+ long count;
1718
+ XSQLVAR *var;
1719
+ short dtp;
1720
+ long length;
1721
+ long alignment;
1722
+ long offset;
1723
+
1724
+ fb_cursor_check(fb_cursor);
1725
+
1726
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1727
+ fb_connection_check(fb_connection);
1728
+
1729
+ /* Check if open cursor */
1730
+ if (!fb_cursor->open) {
1731
+ rb_raise(rb_eFbError, "The cursor has not been opened. Use execute(query)");
1732
+ }
1733
+ /* Describe output SQLDA */
1734
+ isc_dsql_describe(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda);
1735
+ fb_error_check(fb_connection->isc_status);
1736
+
1737
+ /* Set the output SQLDA */
1738
+ cols = fb_cursor->o_sqlda->sqld;
1739
+ for (var = fb_cursor->o_sqlda->sqlvar, offset = 0, count = 0; count < cols; var++, count++) {
1740
+ length = alignment = var->sqllen;
1741
+ dtp = var->sqltype & ~1;
1742
+
1743
+ if (dtp == SQL_TEXT) {
1744
+ alignment = 1;
1745
+ } else if (dtp == SQL_VARYING) {
1746
+ length += sizeof(short);
1747
+ alignment = sizeof(short);
1748
+ }
1749
+ offset = FB_ALIGN((size_t)offset, alignment);
1750
+ var->sqldata = (char*)(fb_cursor->o_buffer + (size_t)offset);
1751
+ offset += length;
1752
+ offset = FB_ALIGN((size_t)offset, sizeof(short));
1753
+ var->sqlind = (short*)(fb_cursor->o_buffer + (size_t)offset);
1754
+ offset += sizeof(short);
1755
+ }
1756
+ }
1757
+
1758
+ static VALUE fb_cursor_fetch(struct FbCursor *fb_cursor)
1759
+ {
1760
+ struct FbConnection *fb_connection;
1761
+ long cols;
1762
+ VALUE ary;
1763
+ long count;
1764
+ XSQLVAR *var;
1765
+ long dtp;
1766
+ VALUE val;
1767
+ VARY *vary;
1768
+ struct tm tms;
1769
+
1770
+ isc_blob_handle blob_handle;
1771
+ ISC_QUAD blob_id;
1772
+ unsigned short actual_seg_len;
1773
+ static char blob_items[] = {
1774
+ isc_info_blob_max_segment,
1775
+ isc_info_blob_num_segments,
1776
+ isc_info_blob_total_length
1777
+ };
1778
+ char blob_info[32];
1779
+ char *p, item;
1780
+ short length;
1781
+ unsigned short max_segment = 0;
1782
+ ISC_LONG num_segments = 0;
1783
+ ISC_LONG total_length = 0;
1784
+
1785
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
1786
+ fb_connection_check(fb_connection);
1787
+
1788
+ if (fb_cursor->eof) {
1789
+ rb_raise(rb_eFbError, "Cursor is past end of data.");
1790
+ }
1791
+ /* Fetch one row */
1792
+ if (isc_dsql_fetch(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda) == SQLCODE_NOMORE) {
1793
+ fb_cursor->eof = Qtrue;
1794
+ return Qnil;
1795
+ }
1796
+ fb_error_check(fb_connection->isc_status);
1797
+
1798
+ /* Create the result tuple object */
1799
+ cols = fb_cursor->o_sqlda->sqld;
1800
+ ary = rb_ary_new2(cols);
1801
+
1802
+ /* Create the result objects for each columns */
1803
+ for (count = 0; count < cols; count++) {
1804
+ var = &fb_cursor->o_sqlda->sqlvar[count];
1805
+ dtp = var->sqltype & ~1;
1806
+
1807
+ /* Check if column is null */
1808
+
1809
+
1810
+ if ((var->sqltype & 1) && (*var->sqlind < 0)) {
1811
+ val = Qnil;
1812
+ } else {
1813
+ /* Set the column value to the result tuple */
1814
+
1815
+ switch (dtp) {
1816
+ case SQL_TEXT:
1817
+ val = rb_tainted_str_new(var->sqldata, var->sqllen);
1818
+ #if HAVE_RUBY_ENCODING_H
1819
+ rb_funcall(val, id_force_encoding, 1, fb_connection->encoding);
1820
+ #endif
1821
+ break;
1822
+
1823
+ case SQL_VARYING:
1824
+ vary = (VARY*)var->sqldata;
1825
+ val = rb_tainted_str_new(vary->vary_string, vary->vary_length);
1826
+ #if HAVE_RUBY_ENCODING_H
1827
+ rb_funcall(val, id_force_encoding, 1, fb_connection->encoding);
1828
+ #endif
1829
+ break;
1830
+
1831
+ case SQL_SHORT:
1832
+ if (var->sqlscale < 0) {
1833
+ val = sql_decimal_to_bigdecimal((long long)*(ISC_SHORT*)var->sqldata, var->sqlscale);
1834
+ } else {
1835
+ val = INT2NUM((long)*(short*)var->sqldata);
1836
+ }
1837
+ break;
1838
+
1839
+ case SQL_LONG:
1840
+ if (var->sqlscale < 0) {
1841
+ val = sql_decimal_to_bigdecimal((long long)*(ISC_LONG*)var->sqldata, var->sqlscale);
1842
+ } else {
1843
+ val = INT2NUM(*(ISC_LONG*)var->sqldata);
1844
+ }
1845
+ break;
1846
+
1847
+ case SQL_FLOAT:
1848
+ val = rb_float_new((double)*(float*)var->sqldata);
1849
+ break;
1850
+
1851
+ case SQL_DOUBLE:
1852
+ val = rb_float_new(*(double*)var->sqldata);
1853
+ break;
1854
+
1855
+ case SQL_INT64:
1856
+ if (var->sqlscale < 0) {
1857
+ val = sql_decimal_to_bigdecimal(*(ISC_INT64*)var->sqldata, var->sqlscale);
1858
+ } else {
1859
+ val = LL2NUM(*(ISC_INT64*)var->sqldata);
1860
+ }
1861
+ break;
1862
+
1863
+ case SQL_TIMESTAMP:
1864
+ isc_decode_timestamp((ISC_TIMESTAMP *)var->sqldata, &tms);
1865
+ val = fb_mktime(&tms, "local");
1866
+ break;
1867
+
1868
+ case SQL_TYPE_TIME:
1869
+ isc_decode_sql_time((ISC_TIME *)var->sqldata, &tms);
1870
+ tms.tm_year = 100;
1871
+ tms.tm_mon = 0;
1872
+ tms.tm_mday = 1;
1873
+ val = fb_mktime(&tms, "utc");
1874
+ break;
1875
+
1876
+ case SQL_TYPE_DATE:
1877
+ isc_decode_sql_date((ISC_DATE *)var->sqldata, &tms);
1878
+ val = fb_mkdate(&tms);
1879
+ break;
1880
+
1881
+ case SQL_BLOB:
1882
+ blob_handle = 0;
1883
+ blob_id = *(ISC_QUAD *)var->sqldata;
1884
+ isc_open_blob2(fb_connection->isc_status, &fb_connection->db, &fb_connection->transact, &blob_handle, &blob_id, 0, NULL);
1885
+ fb_error_check(fb_connection->isc_status);
1886
+ isc_blob_info(
1887
+ fb_connection->isc_status, &blob_handle,
1888
+ sizeof(blob_items), blob_items,
1889
+ sizeof(blob_info), blob_info);
1890
+ fb_error_check(fb_connection->isc_status);
1891
+ for (p = blob_info; *p != isc_info_end; p += length) {
1892
+ item = *p++;
1893
+ length = (short) isc_vax_integer(p,2);
1894
+ p += 2;
1895
+ switch (item) {
1896
+ case isc_info_blob_max_segment:
1897
+ max_segment = isc_vax_integer(p,length);
1898
+ break;
1899
+ case isc_info_blob_num_segments:
1900
+ num_segments = isc_vax_integer(p,length);
1901
+ break;
1902
+ case isc_info_blob_total_length:
1903
+ total_length = isc_vax_integer(p,length);
1904
+ break;
1905
+ }
1906
+ }
1907
+ val = rb_tainted_str_new(NULL,total_length);
1908
+ for (p = RSTRING_PTR(val); num_segments > 0; num_segments--, p += actual_seg_len) {
1909
+ isc_get_segment(fb_connection->isc_status, &blob_handle, &actual_seg_len, max_segment, p);
1910
+ fb_error_check(fb_connection->isc_status);
1911
+ }
1912
+ #if HAVE_RUBY_ENCODING_H
1913
+ rb_funcall(val, id_force_encoding, 1, fb_connection->encoding);
1914
+ #endif
1915
+ isc_close_blob(fb_connection->isc_status, &blob_handle);
1916
+ fb_error_check(fb_connection->isc_status);
1917
+ break;
1918
+
1919
+ case SQL_ARRAY:
1920
+ rb_warn("ARRAY not supported (yet)");
1921
+ val = Qnil;
1922
+ break;
1923
+
1924
+ #if (FB_API_VER >= 30)
1925
+ case SQL_BOOLEAN:
1926
+ case blr_bool:
1927
+ case blr_boolean:
1928
+ val = ((*(bool*)var->sqldata) == true) ? Qtrue : Qfalse;
1929
+ break;
1930
+ #endif
1931
+
1932
+ default:
1933
+ rb_raise(rb_eFbError, "Specified table includes unsupported datatype (%ld)", dtp);
1934
+ break;
1935
+ }
1936
+ }
1937
+ rb_ary_push(ary, val);
1938
+ }
1939
+
1940
+ return ary;
1941
+ }
1942
+
1943
+ static long cursor_rows_affected(struct FbCursor *fb_cursor, long statement_type)
1944
+ {
1945
+ long inserted = 0, selected = 0, updated = 0, deleted = 0;
1946
+ char request[] = { isc_info_sql_records };
1947
+ char response[64], *r;
1948
+ ISC_STATUS isc_status[20];
1949
+
1950
+ isc_dsql_sql_info(isc_status, &fb_cursor->stmt, sizeof(request), request, sizeof(response), response);
1951
+ fb_error_check(isc_status);
1952
+ if (response[0] != isc_info_sql_records) { return -1; }
1953
+
1954
+ r = response + 3; /* skip past first cluster */
1955
+ while (*r != isc_info_end) {
1956
+ char count_type = *r++;
1957
+ short len = isc_vax_integer(r, sizeof(short));
1958
+ r += sizeof(short);
1959
+ switch (count_type) {
1960
+ case isc_info_req_insert_count:
1961
+ inserted = isc_vax_integer(r, len);
1962
+ break;
1963
+ case isc_info_req_select_count:
1964
+ selected = isc_vax_integer(r, len);
1965
+ break;
1966
+ case isc_info_req_update_count:
1967
+ updated = isc_vax_integer(r, len);
1968
+ break;
1969
+ case isc_info_req_delete_count:
1970
+ deleted = isc_vax_integer(r, len);
1971
+ break;
1972
+ }
1973
+ r += len;
1974
+ }
1975
+ switch (statement_type) {
1976
+ case isc_info_sql_stmt_select: return selected;
1977
+ case isc_info_sql_stmt_insert: return inserted;
1978
+ case isc_info_sql_stmt_update: return updated;
1979
+ case isc_info_sql_stmt_delete: return deleted;
1980
+ default: return inserted + selected + updated + deleted;
1981
+ }
1982
+ }
1983
+
1984
+ /* call-seq:
1985
+ * execute2(sql, *args) -> nil or rows affected
1986
+ *
1987
+ * This function is not published.
1988
+ */
1989
+ static VALUE cursor_execute2(VALUE args)
1990
+ {
1991
+ struct FbCursor *fb_cursor;
1992
+ struct FbConnection *fb_connection;
1993
+ char *sql;
1994
+ VALUE rb_sql;
1995
+ long statement;
1996
+ long length;
1997
+ long in_params;
1998
+ long cols;
1999
+ long rows_affected;
2000
+ VALUE result = Qnil;
2001
+ char isc_info_buff[16];
2002
+ char isc_info_stmt[] = { isc_info_sql_stmt_type };
2003
+
2004
+ VALUE self = rb_ary_pop(args);
2005
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2006
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2007
+
2008
+ rb_sql = rb_ary_shift(args);
2009
+ sql = StringValuePtr(rb_sql);
2010
+
2011
+ /* Prepare query */
2012
+ isc_dsql_prepare(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, 0, sql, fb_connection_dialect(fb_connection), fb_cursor->o_sqlda);
2013
+ fb_error_check(fb_connection->isc_status);
2014
+
2015
+ /* Get the statement type */
2016
+ isc_dsql_sql_info(fb_connection->isc_status, &fb_cursor->stmt,
2017
+ sizeof(isc_info_stmt), isc_info_stmt,
2018
+ sizeof(isc_info_buff), isc_info_buff);
2019
+ fb_error_check(fb_connection->isc_status);
2020
+
2021
+ if (isc_info_buff[0] == isc_info_sql_stmt_type) {
2022
+ length = isc_vax_integer(&isc_info_buff[1], 2);
2023
+ statement = isc_vax_integer(&isc_info_buff[3], (short)length);
2024
+ } else {
2025
+ statement = 0;
2026
+ }
2027
+ /* Describe the parameters */
2028
+ isc_dsql_describe_bind(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->i_sqlda);
2029
+ fb_error_check(fb_connection->isc_status);
2030
+
2031
+ isc_dsql_describe(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda);
2032
+ fb_error_check(fb_connection->isc_status);
2033
+
2034
+ /* Get the number of parameters and reallocate the SQLDA */
2035
+ in_params = fb_cursor->i_sqlda->sqld;
2036
+ if (fb_cursor->i_sqlda->sqln < in_params) {
2037
+ xfree(fb_cursor->i_sqlda);
2038
+ fb_cursor->i_sqlda = sqlda_alloc(in_params);
2039
+ /* Describe again */
2040
+ isc_dsql_describe_bind(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->i_sqlda);
2041
+ fb_error_check(fb_connection->isc_status);
2042
+ }
2043
+
2044
+ /* Get the size of parameters buffer and reallocate it */
2045
+ if (in_params) {
2046
+ length = calculate_buffsize(fb_cursor->i_sqlda);
2047
+ if (length > fb_cursor->i_buffer_size) {
2048
+ fb_cursor->i_buffer = xrealloc(fb_cursor->i_buffer, (size_t)length);
2049
+ fb_cursor->i_buffer_size = length;
2050
+ }
2051
+ }
2052
+
2053
+ /* Execute the SQL statement if it is not query */
2054
+ if (!fb_cursor->o_sqlda->sqld) {
2055
+ if (statement == isc_info_sql_stmt_start_trans) {
2056
+ rb_raise(rb_eFbError, "use Fb::Connection#transaction()");
2057
+ } else if (statement == isc_info_sql_stmt_commit) {
2058
+ rb_raise(rb_eFbError, "use Fb::Connection#commit()");
2059
+ } else if (statement == isc_info_sql_stmt_rollback) {
2060
+ rb_raise(rb_eFbError, "use Fb::Connection#rollback()");
2061
+ } else if (in_params) {
2062
+ fb_cursor_execute_withparams(fb_cursor, RARRAY_LEN(args), RARRAY_PTR(args));
2063
+ } else {
2064
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, NULL, NULL);
2065
+ fb_error_check(fb_connection->isc_status);
2066
+ }
2067
+ rows_affected = cursor_rows_affected(fb_cursor, statement);
2068
+ result = INT2NUM(rows_affected);
2069
+ } else {
2070
+ /* Open cursor if the SQL statement is query */
2071
+ /* Get the number of columns and reallocate the SQLDA */
2072
+ cols = fb_cursor->o_sqlda->sqld;
2073
+ if (fb_cursor->o_sqlda->sqln < cols) {
2074
+ xfree(fb_cursor->o_sqlda);
2075
+ fb_cursor->o_sqlda = sqlda_alloc(cols);
2076
+ /* Describe again */
2077
+ isc_dsql_describe(fb_connection->isc_status, &fb_cursor->stmt, 1, fb_cursor->o_sqlda);
2078
+ fb_error_check(fb_connection->isc_status);
2079
+ }
2080
+
2081
+ if (in_params) {
2082
+ fb_cursor_set_inputparams(fb_cursor, RARRAY_LEN(args), RARRAY_PTR(args));
2083
+ }
2084
+
2085
+ /* Open cursor */
2086
+ isc_dsql_execute2(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, SQLDA_VERSION1, in_params ? fb_cursor->i_sqlda : NULL, NULL);
2087
+ fb_error_check(fb_connection->isc_status);
2088
+ fb_cursor->open = Qtrue;
2089
+
2090
+ /* Get the size of results buffer and reallocate it */
2091
+ length = calculate_buffsize(fb_cursor->o_sqlda);
2092
+ if (length > fb_cursor->o_buffer_size) {
2093
+ fb_cursor->o_buffer = xrealloc(fb_cursor->o_buffer, (size_t)length);
2094
+ fb_cursor->o_buffer_size = length;
2095
+ }
2096
+
2097
+ /* Set the description attributes */
2098
+ fb_cursor->fields_ary = fb_cursor_fields_ary(fb_cursor->o_sqlda, fb_connection->downcase_names);
2099
+ fb_cursor->fields_hash = fb_cursor_fields_hash(fb_cursor->fields_ary);
2100
+ }
2101
+ return result;
2102
+ }
2103
+
2104
+ static ISC_LONG fb_statement_type(struct FbConnection *fb_connection, struct FbCursor *fb_cursor, VALUE args)
2105
+ {
2106
+ ISC_LONG statement_type;
2107
+ short length;
2108
+ char isc_info_buff[16];
2109
+ char isc_info_stmt[] = {isc_info_sql_stmt_type};
2110
+ static char isc_tpb[] = {
2111
+ isc_tpb_version3,
2112
+ isc_tpb_read,
2113
+ isc_tpb_nowait,
2114
+ isc_tpb_read_committed,
2115
+ isc_tpb_rec_version
2116
+ };
2117
+
2118
+ VALUE copy_args = rb_ary_dup(args);
2119
+ rb_ary_pop(copy_args); // self
2120
+ VALUE rb_sql = rb_ary_shift(copy_args);
2121
+ char *sql = StringValuePtr(rb_sql);
2122
+
2123
+ isc_start_transaction(fb_connection->isc_status, &fb_connection->transact, 1, &fb_connection->db,
2124
+ (long) sizeof(isc_tpb), isc_tpb);
2125
+ fb_error_check(fb_connection->isc_status);
2126
+
2127
+ isc_dsql_prepare(fb_connection->isc_status, &fb_connection->transact, &fb_cursor->stmt, 0, sql,
2128
+ fb_connection_dialect(fb_connection), NULL);
2129
+ fb_error_check(fb_connection->isc_status);
2130
+
2131
+ isc_dsql_sql_info(fb_connection->isc_status, &fb_cursor->stmt,
2132
+ sizeof(isc_info_stmt), isc_info_stmt,
2133
+ sizeof(isc_info_buff), isc_info_buff);
2134
+ fb_error_check(fb_connection->isc_status);
2135
+
2136
+ if (isc_info_buff[0] == isc_info_sql_stmt_type) {
2137
+ length = (short)isc_vax_integer(&isc_info_buff[1], 2);
2138
+ statement_type = isc_vax_integer(&isc_info_buff[3], length);
2139
+ } else {
2140
+ statement_type = 0;
2141
+ }
2142
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
2143
+ fb_error_check(fb_connection->isc_status);
2144
+
2145
+ return statement_type;
2146
+ }
2147
+
2148
+ /* call-seq:
2149
+ * execute(sql, *args) -> nil or rows affected
2150
+ *
2151
+ * This function is no longer published.
2152
+ */
2153
+ static VALUE cursor_execute(int argc, VALUE* argv, VALUE self)
2154
+ {
2155
+ struct FbCursor *fb_cursor;
2156
+ struct FbConnection *fb_connection;
2157
+ VALUE args;
2158
+
2159
+ if (argc < 1) {
2160
+ rb_raise(rb_eArgError, "At least 1 argument required.");
2161
+ }
2162
+
2163
+ args = rb_ary_new4(argc, argv);
2164
+ rb_ary_push(args, self);
2165
+
2166
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2167
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2168
+ fb_connection_check(fb_connection);
2169
+
2170
+ if (fb_cursor->open) {
2171
+ isc_dsql_free_statement(fb_connection->isc_status, &fb_cursor->stmt, DSQL_close);
2172
+ fb_error_check(fb_connection->isc_status);
2173
+ fb_cursor->open = Qfalse;
2174
+ }
2175
+
2176
+ if (!fb_connection->transact) {
2177
+ VALUE result;
2178
+ int state;
2179
+
2180
+ ISC_LONG statement_type = 0;
2181
+ if (fb_connection->readonly_selects) {
2182
+ statement_type = fb_statement_type(fb_connection, fb_cursor,args);
2183
+ }
2184
+ if (statement_type == isc_info_sql_stmt_select) {
2185
+ fb_connection_transaction_start(fb_connection, rb_ro_trans_p());
2186
+ } else {
2187
+ fb_connection_transaction_start(fb_connection, Qnil);
2188
+ }
2189
+ fb_cursor->auto_transact = fb_connection->transact;
2190
+
2191
+ result = rb_protect(cursor_execute2, args, &state);
2192
+ if (state) {
2193
+ fb_connection_rollback(fb_connection);
2194
+ return rb_funcall(rb_mKernel, rb_intern("raise"), 0);
2195
+ } else if (result != Qnil) {
2196
+ fb_connection_commit(fb_connection);
2197
+ return result;
2198
+ } else {
2199
+ return result;
2200
+ }
2201
+ } else {
2202
+ return cursor_execute2(args);
2203
+ }
2204
+ }
2205
+
2206
+ static VALUE fb_hash_from_ary(VALUE fields, VALUE row)
2207
+ {
2208
+ VALUE hash = rb_hash_new();
2209
+ int i;
2210
+ for (i = 0; i < RARRAY_LEN(fields); i++) {
2211
+ VALUE field = rb_ary_entry(fields, i);
2212
+ VALUE name = rb_struct_aref(field, LONG2NUM(0));
2213
+ VALUE v = rb_ary_entry(row, i);
2214
+ rb_hash_aset(hash, name, v);
2215
+ }
2216
+ return hash;
2217
+ }
2218
+
2219
+ static int hash_format(int argc, VALUE *argv)
2220
+ {
2221
+ if (argc == 0 || argv[0] == ID2SYM(rb_intern("array"))) {
2222
+ return 0;
2223
+ } else if (argv[0] == ID2SYM(rb_intern("hash"))) {
2224
+ return 1;
2225
+ } else {
2226
+ rb_raise(rb_eFbError, "Unknown format");
2227
+ }
2228
+ }
2229
+
2230
+ /* call-seq:
2231
+ * fetch() -> Array
2232
+ * fetch(:array) -> Array
2233
+ * fetch(:hash) -> Hash
2234
+ *
2235
+ * Reads and returns a single row from the open cursor in either an Array or a Hash,
2236
+ * where the column names or aliases from the query form the keys.
2237
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2238
+ * the keys are lower case, except where the column name was mixed case to begin with.
2239
+ */
2240
+ static VALUE cursor_fetch(int argc, VALUE* argv, VALUE self)
2241
+ {
2242
+ VALUE ary;
2243
+ struct FbCursor *fb_cursor;
2244
+
2245
+ int hash_row = hash_format(argc, argv);
2246
+
2247
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2248
+ fb_cursor_fetch_prep(fb_cursor);
2249
+
2250
+ ary = fb_cursor_fetch(fb_cursor);
2251
+ if (NIL_P(ary)) return Qnil;
2252
+ return hash_row ? fb_hash_from_ary(fb_cursor->fields_ary, ary) : ary;
2253
+ }
2254
+
2255
+ /* call-seq:
2256
+ * fetchall() -> Array of Arrays
2257
+ * fetchall(:array) -> Array of Arrays
2258
+ * fetchall(:hash) -> Array of Hashes
2259
+ *
2260
+ * Returns the remainder of the rows from the open cursor, with each row represented
2261
+ * by either an Array or a Hash, where the column names or aliases from the query form the keys.
2262
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2263
+ * the keys are lower case, except where the column name was mixed case to begin with.
2264
+ */
2265
+ static VALUE cursor_fetchall(int argc, VALUE* argv, VALUE self)
2266
+ {
2267
+ VALUE ary, row;
2268
+ struct FbCursor *fb_cursor;
2269
+
2270
+ int hash_rows = hash_format(argc, argv);
2271
+
2272
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2273
+ fb_cursor_fetch_prep(fb_cursor);
2274
+
2275
+ ary = rb_ary_new();
2276
+ for (;;) {
2277
+ row = fb_cursor_fetch(fb_cursor);
2278
+ if (NIL_P(row)) break;
2279
+ if (hash_rows) {
2280
+ rb_ary_push(ary, fb_hash_from_ary(fb_cursor->fields_ary, row));
2281
+ } else {
2282
+ rb_ary_push(ary, row);
2283
+ }
2284
+ }
2285
+
2286
+ return ary;
2287
+ }
2288
+
2289
+ /* call-seq:
2290
+ * each() {|Array| } -> nil
2291
+ * each(:array) {|Array| } -> nil
2292
+ * each(:hash) {|Hash| } -> nil
2293
+ *
2294
+ * Iterates the rows from the open cursor, passing each one to a block in either
2295
+ * an Array or a Hash, where the column names or aliases from the query form the keys.
2296
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2297
+ * the keys are lower case, except where the column name was mixed case to begin with.
2298
+ */
2299
+ static VALUE cursor_each(int argc, VALUE* argv, VALUE self)
2300
+ {
2301
+ VALUE row;
2302
+ struct FbCursor *fb_cursor;
2303
+
2304
+ int hash_rows = hash_format(argc, argv);
2305
+
2306
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2307
+ fb_cursor_fetch_prep(fb_cursor);
2308
+
2309
+ for (;;) {
2310
+ row = fb_cursor_fetch(fb_cursor);
2311
+ if (NIL_P(row)) break;
2312
+ if (hash_rows) {
2313
+ rb_yield(fb_hash_from_ary(fb_cursor->fields_ary, row));
2314
+ } else {
2315
+ rb_yield(row);
2316
+ }
2317
+ }
2318
+
2319
+ return Qnil;
2320
+ }
2321
+
2322
+ /* call-seq:
2323
+ * close(sql, *args) -> nil
2324
+ *
2325
+ * Closes the cursor. If a transaction was automatically started for this cursor, the transaction is commited.
2326
+ */
2327
+ static VALUE cursor_close(VALUE self)
2328
+ {
2329
+ struct FbCursor *fb_cursor;
2330
+ struct FbConnection *fb_connection;
2331
+
2332
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2333
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2334
+ fb_cursor_check(fb_cursor);
2335
+
2336
+ /* Close the cursor */
2337
+ if (fb_cursor->stmt) {
2338
+ isc_dsql_free_statement(fb_connection->isc_status, &fb_cursor->stmt, DSQL_close);
2339
+ fb_error_check_warn(fb_connection->isc_status);
2340
+ isc_dsql_free_statement(fb_connection->isc_status, &fb_cursor->stmt, DSQL_drop);
2341
+ fb_error_check(fb_connection->isc_status);
2342
+ fb_cursor->open = Qfalse;
2343
+ if (fb_connection->transact == fb_cursor->auto_transact) {
2344
+ isc_commit_transaction(fb_connection->isc_status, &fb_connection->transact);
2345
+ fb_cursor->auto_transact = fb_connection->transact;
2346
+ fb_error_check(fb_connection->isc_status);
2347
+ }
2348
+ }
2349
+ fb_cursor->fields_ary = Qnil;
2350
+ fb_cursor->fields_hash = Qnil;
2351
+ return Qnil;
2352
+ }
2353
+
2354
+ /* call-seq:
2355
+ * drop() -> nil
2356
+ *
2357
+ * Drops the cursor.
2358
+ *
2359
+ * TODO: How is this different from close()?
2360
+ */
2361
+ static VALUE cursor_drop(VALUE self)
2362
+ {
2363
+ struct FbCursor *fb_cursor;
2364
+ struct FbConnection *fb_connection;
2365
+ int i;
2366
+
2367
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2368
+ fb_cursor_drop(fb_cursor);
2369
+ fb_cursor->fields_ary = Qnil;
2370
+ fb_cursor->fields_hash = Qnil;
2371
+
2372
+ /* reset the reference from connection */
2373
+ Data_Get_Struct(fb_cursor->connection, struct FbConnection, fb_connection);
2374
+ for (i = 0; i < RARRAY_LEN(fb_connection->cursor); i++) {
2375
+ if (RARRAY_PTR(fb_connection->cursor)[i] == self) {
2376
+ RARRAY_PTR(fb_connection->cursor)[i] = Qnil;
2377
+ }
2378
+ }
2379
+
2380
+ return Qnil;
2381
+ }
2382
+
2383
+ /* call-seq:
2384
+ * fields() -> Array
2385
+ * fields(:array) -> Array
2386
+ * fields(:hash) -> Hash
2387
+ *
2388
+ * Return an array of Field Structs or a hash indexed by field name.
2389
+ * If the +downcase_names+ attribute of the associated connection evaluates to true,
2390
+ * the keys are lower case, except where the column name was mixed case to begin with.
2391
+ */
2392
+ static VALUE cursor_fields(int argc, VALUE* argv, VALUE self)
2393
+ {
2394
+ struct FbCursor *fb_cursor;
2395
+
2396
+ Data_Get_Struct(self, struct FbCursor, fb_cursor);
2397
+ if (argc == 0 || argv[0] == ID2SYM(rb_intern("array"))) {
2398
+ return fb_cursor->fields_ary;
2399
+ } else if (argv[0] == ID2SYM(rb_intern("hash"))) {
2400
+ return fb_cursor->fields_hash;
2401
+ } else {
2402
+ rb_raise(rb_eFbError, "Unknown format");
2403
+ }
2404
+ }
2405
+
2406
+ /* call-seq:
2407
+ * error_code -> int
2408
+ *
2409
+ * Returns the sqlcode associated with the error.
2410
+ */
2411
+ static VALUE error_error_code(VALUE error)
2412
+ {
2413
+ rb_p(error);
2414
+ return rb_iv_get(error, "error_code");
2415
+ }
2416
+
2417
+ static char* dbp_create(long *length)
2418
+ {
2419
+ char *dbp = ALLOC_N(char, 1);
2420
+ *dbp = isc_dpb_version1;
2421
+ *length = 1;
2422
+ return dbp;
2423
+ }
2424
+
2425
+ static char* dbp_add_string(char *dbp, char isc_dbp_code, char *s, long *length)
2426
+ {
2427
+ char *buf;
2428
+ long old_length = *length;
2429
+ long s_len = strlen(s);
2430
+ *length += 2 + s_len;
2431
+ REALLOC_N(dbp, char, *length);
2432
+ buf = dbp + (size_t)old_length;
2433
+ *buf++ = isc_dbp_code;
2434
+ *buf++ = (char)s_len;
2435
+ memcpy(buf, s, (size_t)s_len);
2436
+ return dbp;
2437
+ }
2438
+
2439
+ static char* connection_create_dbp(VALUE self, long *length)
2440
+ {
2441
+ char *dbp;
2442
+ VALUE username, password, charset, role;
2443
+
2444
+ username = rb_iv_get(self, "@username");
2445
+ Check_Type(username, T_STRING);
2446
+ password = rb_iv_get(self, "@password");
2447
+ Check_Type(password, T_STRING);
2448
+ role = rb_iv_get(self, "@role");
2449
+ charset = rb_iv_get(self, "@charset");
2450
+
2451
+ dbp = dbp_create(length);
2452
+ dbp = dbp_add_string(dbp, isc_dpb_user_name, StringValuePtr(username), length);
2453
+ dbp = dbp_add_string(dbp, isc_dpb_password, StringValuePtr(password), length);
2454
+ if (!NIL_P(charset)) {
2455
+ dbp = dbp_add_string(dbp, isc_dpb_lc_ctype, StringValuePtr(charset), length);
2456
+ }
2457
+ if (!NIL_P(role)) {
2458
+ dbp = dbp_add_string(dbp, isc_dpb_sql_role_name, StringValuePtr(role), length);
2459
+ }
2460
+ return dbp;
2461
+ }
2462
+
2463
+ static const char* CONNECTION_PARMS[] = {
2464
+ "@database",
2465
+ "@username",
2466
+ "@password",
2467
+ "@charset",
2468
+ "@role",
2469
+ "@downcase_names",
2470
+ "@encoding",
2471
+ "@readonly_selects",
2472
+ (char *)0
2473
+ };
2474
+
2475
+ static VALUE connection_create(isc_db_handle handle, VALUE db)
2476
+ {
2477
+ unsigned short dialect;
2478
+ unsigned short db_dialect;
2479
+ VALUE downcase_names;
2480
+ VALUE readonly_selects;
2481
+ const char *parm;
2482
+ int i;
2483
+ struct FbConnection *fb_connection;
2484
+ VALUE connection = Data_Make_Struct(rb_cFbConnection, struct FbConnection, fb_connection_mark, fb_connection_free, fb_connection);
2485
+ fb_connection->db = handle;
2486
+ fb_connection->transact = 0;
2487
+ fb_connection->cursor = rb_ary_new();
2488
+ dialect = SQL_DIALECT_CURRENT;
2489
+ db_dialect = fb_connection_db_SQL_Dialect(fb_connection);
2490
+
2491
+ if (db_dialect < dialect) {
2492
+ dialect = db_dialect;
2493
+ /* TODO: downgrade warning */
2494
+ }
2495
+
2496
+ fb_connection->dialect = dialect;
2497
+ fb_connection->db_dialect = db_dialect;
2498
+ downcase_names = rb_iv_get(db, "@downcase_names");
2499
+ fb_connection->downcase_names = RTEST(downcase_names);
2500
+ fb_connection->encoding = rb_iv_get(db, "@encoding");
2501
+ readonly_selects = rb_iv_get(db, "@readonly_selects");
2502
+ fb_connection->readonly_selects = RTEST(readonly_selects);
2503
+
2504
+ for (i = 0; (parm = CONNECTION_PARMS[i]); i++) {
2505
+ rb_iv_set(connection, parm, rb_iv_get(db, parm));
2506
+ }
2507
+
2508
+ return connection;
2509
+ }
2510
+
2511
+ static VALUE connection_names(VALUE self, const char *sql)
2512
+ {
2513
+ VALUE row;
2514
+ VALUE query = rb_str_new2(sql);
2515
+ VALUE cursor = connection_execute(1, &query, self);
2516
+ VALUE names = rb_ary_new();
2517
+ struct FbConnection *fb_connection;
2518
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2519
+
2520
+ while ((row = cursor_fetch(0, NULL, cursor)) != Qnil) {
2521
+ VALUE name = rb_ary_entry(row, 0);
2522
+ if (fb_connection->downcase_names && no_lowercase(name)) {
2523
+ rb_funcall(name, id_downcase_bang, 0);
2524
+ }
2525
+ rb_funcall(name, id_rstrip_bang, 0);
2526
+ rb_ary_push(names, name);
2527
+ }
2528
+
2529
+ cursor_close(cursor);
2530
+ return names;
2531
+ }
2532
+
2533
+ /* call-seq:
2534
+ * table_names() -> array
2535
+ *
2536
+ * Returns sorted array of table names in connected database.
2537
+ */
2538
+ static VALUE connection_table_names(VALUE self)
2539
+ {
2540
+ const char *sql = "SELECT RDB$RELATION_NAME FROM RDB$RELATIONS "
2541
+ "WHERE (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND RDB$VIEW_BLR IS NULL "
2542
+ "ORDER BY RDB$RELATION_NAME";
2543
+ return connection_names(self, sql);
2544
+ }
2545
+
2546
+ /* call-seq:
2547
+ * generator_names() -> array
2548
+ *
2549
+ * Returns sorted array of generator names in connected database.
2550
+ */
2551
+ static VALUE connection_generator_names(VALUE self)
2552
+ {
2553
+ const char *sql = "SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS "
2554
+ "WHERE (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG <> 1) "
2555
+ "ORDER BY RDB$GENERATOR_NAME";
2556
+ return connection_names(self, sql);
2557
+ }
2558
+
2559
+ /* call-seq:
2560
+ * view_names() -> array
2561
+ *
2562
+ * Returns sorted array of view names in connected database.
2563
+ */
2564
+ static VALUE connection_view_names(VALUE self)
2565
+ {
2566
+ const char *sql = "SELECT RDB$RELATION_NAME, RDB$OWNER_NAME, RDB$VIEW_SOURCE FROM RDB$RELATIONS "
2567
+ "WHERE (RDB$SYSTEM_FLAG <> 1 OR RDB$SYSTEM_FLAG IS NULL) AND NOT RDB$VIEW_BLR IS NULL AND RDB$FLAGS = 1 "
2568
+ "ORDER BY RDB$RELATION_ID";
2569
+ return connection_names(self, sql);
2570
+ }
2571
+
2572
+ /* call-seq:
2573
+ * role_names() -> array
2574
+ *
2575
+ * Returns sorted array of role names in connected database.
2576
+ */
2577
+ static VALUE connection_role_names(VALUE self)
2578
+ {
2579
+ const char *sql = "SELECT * FROM RDB$ROLES WHERE RDB$SYSTEM_FLAG = 0 ORDER BY RDB$ROLE_NAME";
2580
+ return connection_names(self, sql);
2581
+ }
2582
+
2583
+ /* call-seq:
2584
+ * procedure_names() -> array
2585
+ *
2586
+ * Returns sorted array of stored procedure names in connected database.
2587
+ */
2588
+ static VALUE connection_procedure_names(VALUE self)
2589
+ {
2590
+ const char *sql = "SELECT RDB$PROCEDURE_NAME FROM RDB$PROCEDURES "
2591
+ "ORDER BY RDB$PROCEDURE_NAME";
2592
+ return connection_names(self, sql);
2593
+ }
2594
+
2595
+ /* call-seq:
2596
+ * trigger_names() -> array
2597
+ *
2598
+ * Returns sorted array of trigger names in connected database.
2599
+ */
2600
+ static VALUE connection_trigger_names(VALUE self)
2601
+ {
2602
+ const char *sql = "SELECT RDB$TRIGGER_NAME FROM RDB$TRIGGERS "
2603
+ "ORDER BY RDB$TRIGGER_NAME";
2604
+ return connection_names(self, sql);
2605
+ }
2606
+
2607
+ /* call-seq:
2608
+ * columns(table_name) -> array
2609
+ *
2610
+ * Returns array of objects describing each column of table_name.
2611
+ */
2612
+
2613
+ static VALUE connection_columns(VALUE self, VALUE table_name)
2614
+ {
2615
+ int i;
2616
+ struct FbConnection *fb_connection;
2617
+ VALUE re_default = rb_reg_new("^\\s*DEFAULT\\s+", strlen("^\\s*DEFAULT\\s+"), IGNORECASE);
2618
+ VALUE re_rdb = rb_reg_new("^RDB\\$", strlen("^RDB\\$"), 0);
2619
+ VALUE empty = rb_str_new(NULL, 0);
2620
+ VALUE columns = rb_ary_new();
2621
+ const char *sql = "SELECT r.rdb$field_name NAME, r.rdb$field_source, f.rdb$field_type, f.rdb$field_sub_type, "
2622
+ "f.rdb$field_length, f.rdb$field_precision, f.rdb$field_scale SCALE, "
2623
+ "COALESCE(r.rdb$default_source, f.rdb$default_source), "
2624
+ "COALESCE(r.rdb$null_flag, f.rdb$null_flag) "
2625
+ "FROM rdb$relation_fields r "
2626
+ "JOIN rdb$fields f ON r.rdb$field_source = f.rdb$field_name "
2627
+ "WHERE UPPER(r.rdb$relation_name) = ? "
2628
+ "ORDER BY r.rdb$field_position";
2629
+ VALUE query = rb_str_new2(sql);
2630
+ VALUE upcase_table_name = rb_funcall(table_name, rb_intern("upcase"), 0);
2631
+ VALUE query_parms[] = { query, upcase_table_name };
2632
+ VALUE rs = connection_query(2, query_parms, self);
2633
+ ID match_id = rb_intern("match");
2634
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2635
+ for (i = 0; i < RARRAY_LEN(rs); i++) {
2636
+ VALUE row = rb_ary_entry(rs, i);
2637
+ VALUE name = rb_ary_entry(row, 0);
2638
+ VALUE domain = rb_ary_entry(row, 1);
2639
+ VALUE sql_type = rb_ary_entry(row, 2);
2640
+ VALUE sql_subtype = rb_ary_entry(row, 3);
2641
+ VALUE length = rb_ary_entry(row, 4);
2642
+ VALUE precision = rb_ary_entry(row, 5);
2643
+ VALUE scale = rb_ary_entry(row, 6);
2644
+ VALUE dflt = rb_ary_entry(row, 7);
2645
+ VALUE not_null = rb_ary_entry(row, 8);
2646
+ VALUE nullable;
2647
+ VALUE column;
2648
+ rb_funcall(name, id_rstrip_bang, 0);
2649
+ rb_funcall(domain, id_rstrip_bang, 0);
2650
+ if (fb_connection->downcase_names && no_lowercase(name)) {
2651
+ rb_funcall(name, id_downcase_bang, 0);
2652
+ }
2653
+ if (rb_funcall(re_rdb, match_id, 1, domain) != Qnil) {
2654
+ domain = Qnil;
2655
+ }
2656
+ if (sql_subtype == Qnil) {
2657
+ sql_subtype = INT2NUM(0);
2658
+ }
2659
+ sql_type = sql_type_from_code(self, sql_type, sql_subtype);
2660
+ if (dflt != Qnil) {
2661
+ rb_funcall(dflt, id_sub_bang, 2, re_default, empty);
2662
+ }
2663
+ nullable = RTEST(not_null) ? Qfalse : Qtrue;
2664
+ column = rb_struct_new(rb_sFbColumn, name, domain, sql_type, sql_subtype, length, precision, scale, dflt, nullable);
2665
+ rb_ary_push(columns, column);
2666
+ }
2667
+ rb_ary_freeze(columns);
2668
+ return columns;
2669
+ }
2670
+
2671
+ char *p(char *prompt, VALUE s)
2672
+ {
2673
+ char *sz;
2674
+ if (TYPE(s) != T_STRING) {
2675
+ s = rb_funcall(s, rb_intern("to_s"), 0);
2676
+ }
2677
+ sz = StringValuePtr(s);
2678
+ printf("%s: %s\n", prompt, sz);
2679
+ return sz;
2680
+ }
2681
+
2682
+ static VALUE connection_index_columns(VALUE self, VALUE index_name)
2683
+ {
2684
+ const char *sql_columns = "SELECT * "
2685
+ "FROM RDB$INDEX_SEGMENTS "
2686
+ "WHERE RDB$INDEX_SEGMENTS.RDB$INDEX_NAME = ? "
2687
+ "ORDER BY RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION";
2688
+ VALUE query_columns = rb_str_new2(sql_columns);
2689
+ VALUE query_parms[] = { query_columns, index_name };
2690
+ VALUE result = connection_query(2, query_parms, self);
2691
+ VALUE columns = rb_ary_new();
2692
+ int i;
2693
+ struct FbConnection *fb_connection;
2694
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2695
+
2696
+ for (i = 0; i < RARRAY_LEN(result); i++) {
2697
+ VALUE row = rb_ary_entry(result, i);
2698
+ VALUE name = rb_ary_entry(row, 1);
2699
+ rb_funcall(name, id_rstrip_bang, 0);
2700
+ if (fb_connection->downcase_names && no_lowercase(name)) {
2701
+ rb_funcall(name, id_downcase_bang, 0);
2702
+ }
2703
+ rb_ary_push(columns, name);
2704
+ }
2705
+ return columns;
2706
+ }
2707
+
2708
+ /* call-seq:
2709
+ * indexes() -> Hash
2710
+ *
2711
+ * Returns a hash of indexes, keyed by index name.
2712
+ */
2713
+ static VALUE connection_indexes(VALUE self)
2714
+ {
2715
+ const char *sql_indexes = "SELECT RDB$INDICES.RDB$RELATION_NAME, RDB$INDICES.RDB$INDEX_NAME, RDB$INDICES.RDB$UNIQUE_FLAG, RDB$INDICES.RDB$INDEX_TYPE "
2716
+ "FROM RDB$INDICES "
2717
+ " JOIN RDB$RELATIONS ON RDB$INDICES.RDB$RELATION_NAME = RDB$RELATIONS.RDB$RELATION_NAME "
2718
+ "WHERE (RDB$RELATIONS.RDB$SYSTEM_FLAG <> 1 OR RDB$RELATIONS.RDB$SYSTEM_FLAG IS NULL) ";
2719
+ VALUE query_indexes = rb_str_new2(sql_indexes);
2720
+ VALUE ary_indexes = connection_query(1, &query_indexes, self);
2721
+ VALUE indexes = rb_hash_new();
2722
+ int i;
2723
+ struct FbConnection *fb_connection;
2724
+ Data_Get_Struct(self, struct FbConnection, fb_connection);
2725
+
2726
+ for (i = 0; i < RARRAY_LEN(ary_indexes); i++) {
2727
+ VALUE index_struct;
2728
+ VALUE row = rb_ary_entry(ary_indexes, i);
2729
+ VALUE table_name = rb_ary_entry(row, 0);
2730
+ VALUE index_name = rb_ary_entry(row, 1);
2731
+ VALUE unique = rb_ary_entry(row, 2);
2732
+ VALUE descending = rb_ary_entry(row, 3);
2733
+ VALUE columns = connection_index_columns(self, index_name);
2734
+
2735
+ rb_funcall(table_name, id_rstrip_bang, 0);
2736
+ rb_funcall(index_name, id_rstrip_bang, 0);
2737
+
2738
+ if (fb_connection->downcase_names) {
2739
+ if (no_lowercase(table_name)) {
2740
+ rb_funcall(table_name, id_downcase_bang, 0);
2741
+ }
2742
+ if (no_lowercase(index_name)) {
2743
+ rb_funcall(index_name, id_downcase_bang, 0);
2744
+ }
2745
+ }
2746
+
2747
+ rb_str_freeze(table_name);
2748
+ rb_str_freeze(index_name);
2749
+
2750
+ unique = (unique == INT2FIX(1)) ? Qtrue : Qfalse;
2751
+ descending = (descending == INT2FIX(1)) ? Qtrue : Qfalse;
2752
+
2753
+ index_struct = rb_struct_new(rb_sFbIndex, table_name, index_name, unique, descending, columns);
2754
+ rb_hash_aset(indexes, index_name, index_struct);
2755
+ }
2756
+ return indexes;
2757
+ }
2758
+
2759
+ /*
2760
+ static void define_attrs(VALUE klass, char **attrs)
2761
+ {
2762
+ char *parm;
2763
+ while ((parm = *attrs))
2764
+ {
2765
+ rb_define_attr(klass, parm+1, 1, 1);
2766
+ attrs++;
2767
+ }
2768
+ }
2769
+ */
2770
+
2771
+ static VALUE default_string(VALUE hash, const char *key, const char *def)
2772
+ {
2773
+ VALUE sym = ID2SYM(rb_intern(key));
2774
+ VALUE val = rb_hash_aref(hash, sym);
2775
+ return NIL_P(val) ? rb_str_new2(def) : StringValue(val);
2776
+ }
2777
+
2778
+ static VALUE default_int(VALUE hash, const char *key, int def)
2779
+ {
2780
+ VALUE sym = ID2SYM(rb_intern(key));
2781
+ VALUE val = rb_hash_aref(hash, sym);
2782
+ return NIL_P(val) ? INT2NUM(def) : val;
2783
+ }
2784
+
2785
+ static VALUE database_allocate_instance(VALUE klass)
2786
+ {
2787
+ NEWOBJ(obj, struct RObject);
2788
+ OBJSETUP((VALUE)obj, klass, T_OBJECT);
2789
+ return (VALUE)obj;
2790
+ }
2791
+
2792
+ static VALUE hash_from_connection_string(VALUE cs)
2793
+ {
2794
+ VALUE hash = rb_hash_new();
2795
+ VALUE re_SemiColon = rb_reg_regcomp(rb_str_new2("\\s*;\\s*"));
2796
+ VALUE re_Equal = rb_reg_regcomp(rb_str_new2("\\s*=\\s*"));
2797
+ ID id_split = rb_intern("split");
2798
+ VALUE pairs = rb_funcall(cs, id_split, 1, re_SemiColon);
2799
+ int i;
2800
+ for (i = 0; i < RARRAY_LEN(pairs); i++) {
2801
+ VALUE pair = rb_ary_entry(pairs, i);
2802
+ VALUE keyValue = rb_funcall(pair, id_split, 1, re_Equal);
2803
+ if (RARRAY_LEN(keyValue) == 2) {
2804
+ VALUE key = rb_ary_entry(keyValue, 0);
2805
+ VALUE val = rb_ary_entry(keyValue, 1);
2806
+ rb_hash_aset(hash, rb_str_intern(key), val);
2807
+ }
2808
+ }
2809
+ return hash;
2810
+ }
2811
+
2812
+ static void check_page_size(int page_size)
2813
+ {
2814
+ if (page_size != 1024 && page_size != 2048 && page_size != 4096 && page_size != 8192 && page_size != 16384) {
2815
+ rb_raise(rb_eFbError, "Invalid page size: %d", page_size);
2816
+ }
2817
+ }
2818
+
2819
+ /* call-seq:
2820
+ * Database.new(options) -> Database
2821
+ *
2822
+ * Initialize Database with Hash of values:
2823
+ * :database:: Full Firebird connection string, e.g. 'localhost:/var/fbdata/drivertest.fdb' (required)
2824
+ * :username:: database username (default: 'sysdba')
2825
+ * :password:: database password (default: 'masterkey')
2826
+ * :charset:: character set to be used with the connection (default: 'NONE')
2827
+ * :role:: database role to connect using (default: nil)
2828
+ * :downcase_names:: Column names are reported in lowercase, unless they were originally mixed case (default: nil).
2829
+ * :page_size:: page size to use when creating a database (default: 4096)
2830
+ */
2831
+ static VALUE database_initialize(int argc, VALUE *argv, VALUE self)
2832
+ {
2833
+ VALUE parms, database;
2834
+
2835
+ if (argc >= 1) {
2836
+ parms = argv[0];
2837
+ if (TYPE(parms) == T_STRING) {
2838
+ parms = hash_from_connection_string(parms);
2839
+ } else {
2840
+ Check_Type(parms, T_HASH);
2841
+ }
2842
+ database = rb_hash_aref(parms, ID2SYM(rb_intern("database")));
2843
+ if (NIL_P(database)) rb_raise(rb_eFbError, "Database must be specified.");
2844
+ rb_iv_set(self, "@database", database);
2845
+ rb_iv_set(self, "@username", default_string(parms, "username", "sysdba"));
2846
+ rb_iv_set(self, "@password", default_string(parms, "password", "masterkey"));
2847
+ rb_iv_set(self, "@charset", default_string(parms, "charset", "NONE"));
2848
+ rb_iv_set(self, "@role", rb_hash_aref(parms, ID2SYM(rb_intern("role"))));
2849
+ rb_iv_set(self, "@downcase_names", rb_hash_aref(parms, ID2SYM(rb_intern("downcase_names"))));
2850
+ rb_iv_set(self, "@encoding", default_string(parms, "encoding", "ASCII-8BIT"));
2851
+ rb_iv_set(self, "@page_size", default_int(parms, "page_size", 4096));
2852
+ }
2853
+ return self;
2854
+ }
2855
+
2856
+ /* call-seq:
2857
+ * create() -> Database
2858
+ * create() {|connection| } -> Database
2859
+ *
2860
+ * Create a database using the current database options.
2861
+ * If a block is provided, an open connection to the new database is passed to it
2862
+ * before being automatically closed.
2863
+ */
2864
+ static VALUE database_create(VALUE self)
2865
+ {
2866
+ ISC_STATUS isc_status[20];
2867
+ isc_db_handle handle = 0;
2868
+ isc_tr_handle local_transact = 0;
2869
+ VALUE parms, fmt, stmt;
2870
+ char *sql;
2871
+
2872
+ VALUE database = rb_iv_get(self, "@database");
2873
+ VALUE username = rb_iv_get(self, "@username");
2874
+ VALUE password = rb_iv_get(self, "@password");
2875
+ VALUE page_size = rb_iv_get(self, "@page_size");
2876
+ VALUE charset = rb_iv_get(self, "@charset");
2877
+
2878
+ check_page_size(NUM2INT(page_size));
2879
+
2880
+ parms = rb_ary_new3(5, database, username, password, page_size, charset);
2881
+
2882
+ fmt = rb_str_new2("CREATE DATABASE '%s' USER '%s' PASSWORD '%s' PAGE_SIZE = %d DEFAULT CHARACTER SET %s;");
2883
+ stmt = rb_funcall(fmt, rb_intern("%"), 1, parms);
2884
+ sql = StringValuePtr(stmt);
2885
+
2886
+ if (isc_dsql_execute_immediate(isc_status, &handle, &local_transact, 0, sql, 3, NULL) != 0) {
2887
+ fb_error_check(isc_status);
2888
+ }
2889
+ if (handle) {
2890
+ if (rb_block_given_p()) {
2891
+ VALUE connection = connection_create(handle, self);
2892
+ rb_ensure(rb_yield,connection,connection_close,connection);
2893
+ } else {
2894
+ isc_detach_database(isc_status, &handle);
2895
+ fb_error_check(isc_status);
2896
+ }
2897
+ }
2898
+
2899
+ return self;
2900
+ }
2901
+
2902
+ /* call-seq:
2903
+ * Database.create(options) -> Database
2904
+ * Database.create(options) {|connection| } -> Database
2905
+ *
2906
+ * Create a database using the specified options (see: Database.new for details of options Hash).
2907
+ * If a block is provided, an open connection to the new database is passed to it
2908
+ * before being automatically closed.
2909
+ */
2910
+ static VALUE database_s_create(int argc, VALUE *argv, VALUE klass)
2911
+ {
2912
+ VALUE obj = database_allocate_instance(klass);
2913
+ database_initialize(argc, argv, obj);
2914
+ return database_create(obj);
2915
+ }
2916
+
2917
+ /* call-seq:
2918
+ * connect() -> Connection
2919
+ * connect() {|connection| } -> nil
2920
+ *
2921
+ * Connect to the database specified by the current database options.
2922
+ *
2923
+ * If a block is provided, the open connection is passed to it before being
2924
+ * automatically closed.
2925
+ */
2926
+ static VALUE database_connect(VALUE self)
2927
+ {
2928
+ ISC_STATUS isc_status[20];
2929
+ char *dbp;
2930
+ long length;
2931
+ isc_db_handle handle = 0;
2932
+ VALUE database = rb_iv_get(self, "@database");
2933
+
2934
+ Check_Type(database, T_STRING);
2935
+ dbp = connection_create_dbp(self, &length);
2936
+ isc_attach_database(isc_status, 0, StringValuePtr(database), &handle, length, dbp);
2937
+ xfree(dbp);
2938
+ fb_error_check(isc_status);
2939
+ {
2940
+ VALUE connection = connection_create(handle, self);
2941
+ if (rb_block_given_p()) {
2942
+ return rb_ensure(rb_yield, connection, connection_close, connection);
2943
+ return Qnil;
2944
+ } else {
2945
+ return connection;
2946
+ }
2947
+ }
2948
+ }
2949
+
2950
+ /* call-seq:
2951
+ * Database.connect(options) -> Connection
2952
+ * Database.connect(options) {|connection| } -> nil
2953
+ *
2954
+ * Connect to a database using the options given (see: Database.new for details of options Hash).
2955
+ *
2956
+ * If a block is provided, the open connection is passed to it before being
2957
+ * automatically closed.
2958
+ */
2959
+ static VALUE database_s_connect(int argc, VALUE *argv, VALUE klass)
2960
+ {
2961
+ VALUE obj = database_allocate_instance(klass);
2962
+ database_initialize(argc, argv, obj);
2963
+ return database_connect(obj);
2964
+ }
2965
+
2966
+ /* call-seq:
2967
+ * drop() -> nil
2968
+ *
2969
+ * Drop the database specified by the current database options.
2970
+ */
2971
+ static VALUE database_drop(VALUE self)
2972
+ {
2973
+ struct FbConnection *fb_connection;
2974
+
2975
+ VALUE connection = database_connect(self);
2976
+ Data_Get_Struct(connection, struct FbConnection, fb_connection);
2977
+ isc_drop_database(fb_connection->isc_status, &fb_connection->db);
2978
+ fb_error_check(fb_connection->isc_status);
2979
+ /* fb_connection_remove(fb_connection); */
2980
+ return Qnil;
2981
+ }
2982
+
2983
+ /* call-seq:
2984
+ * Database.drop(options) -> nil
2985
+ *
2986
+ * Drop the database specified by the options given (see: Database.new for details of options Hash).
2987
+ */
2988
+ static VALUE database_s_drop(int argc, VALUE *argv, VALUE klass)
2989
+ {
2990
+ VALUE obj = database_allocate_instance(klass);
2991
+ database_initialize(argc, argv, obj);
2992
+ return database_drop(obj);
2993
+ }
2994
+
2995
+ void Init_fb()
2996
+ {
2997
+ rb_funcall(rb_mKernel, rb_intern("require"), 1, rb_str_new2("bigdecimal"));
2998
+
2999
+ rb_mFb = rb_define_module("Fb");
3000
+
3001
+ rb_cFbDatabase = rb_define_class_under(rb_mFb, "Database", rb_cData);
3002
+ rb_define_alloc_func(rb_cFbDatabase, database_allocate_instance);
3003
+ rb_define_method(rb_cFbDatabase, "initialize", database_initialize, -1);
3004
+ rb_define_attr(rb_cFbDatabase, "database", 1, 1);
3005
+ rb_define_attr(rb_cFbDatabase, "username", 1, 1);
3006
+ rb_define_attr(rb_cFbDatabase, "password", 1, 1);
3007
+ rb_define_attr(rb_cFbDatabase, "charset", 1, 1);
3008
+ rb_define_attr(rb_cFbDatabase, "role", 1, 1);
3009
+ rb_define_attr(rb_cFbDatabase, "downcase_names", 1, 1);
3010
+ rb_define_attr(rb_cFbDatabase, "encoding", 1, 1);
3011
+ rb_define_attr(rb_cFbDatabase, "readonly_selects", 1, 1);
3012
+ rb_define_attr(rb_cFbDatabase, "page_size", 1, 1);
3013
+ rb_define_method(rb_cFbDatabase, "create", database_create, 0);
3014
+ rb_define_singleton_method(rb_cFbDatabase, "create", database_s_create, -1);
3015
+ rb_define_method(rb_cFbDatabase, "connect", database_connect, 0);
3016
+ rb_define_singleton_method(rb_cFbDatabase, "connect", database_s_connect, -1);
3017
+ rb_define_method(rb_cFbDatabase, "drop", database_drop, 0);
3018
+ rb_define_singleton_method(rb_cFbDatabase, "drop", database_s_drop, -1);
3019
+
3020
+ rb_cFbConnection = rb_define_class_under(rb_mFb, "Connection", rb_cData);
3021
+ rb_define_attr(rb_cFbConnection, "database", 1, 1);
3022
+ rb_define_attr(rb_cFbConnection, "username", 1, 1);
3023
+ rb_define_attr(rb_cFbConnection, "password", 1, 1);
3024
+ rb_define_attr(rb_cFbConnection, "charset", 1, 1);
3025
+ rb_define_attr(rb_cFbConnection, "role", 1, 1);
3026
+ rb_define_attr(rb_cFbConnection, "downcase_names", 1, 1);
3027
+ rb_define_attr(rb_cFbConnection, "encoding", 1, 1);
3028
+ rb_define_attr(rb_cFbConnection, "readonly_selects", 1, 1);
3029
+ rb_define_method(rb_cFbConnection, "to_s", connection_to_s, 0);
3030
+ rb_define_method(rb_cFbConnection, "execute", connection_execute, -1);
3031
+ rb_define_method(rb_cFbConnection, "query", connection_query, -1);
3032
+ rb_define_method(rb_cFbConnection, "transaction", connection_transaction, -1);
3033
+ rb_define_method(rb_cFbConnection, "transaction_started", connection_transaction_started, 0);
3034
+ rb_define_method(rb_cFbConnection, "commit", connection_commit, 0);
3035
+ rb_define_method(rb_cFbConnection, "rollback", connection_rollback, 0);
3036
+ rb_define_method(rb_cFbConnection, "close", connection_close, 0);
3037
+ rb_define_method(rb_cFbConnection, "drop", connection_drop, 0);
3038
+ rb_define_method(rb_cFbConnection, "open?", connection_is_open, 0);
3039
+ rb_define_method(rb_cFbConnection, "dialect", connection_dialect, 0);
3040
+ rb_define_method(rb_cFbConnection, "db_dialect", connection_db_dialect, 0);
3041
+ rb_define_method(rb_cFbConnection, "table_names", connection_table_names, 0);
3042
+ rb_define_method(rb_cFbConnection, "generator_names", connection_generator_names, 0);
3043
+ rb_define_method(rb_cFbConnection, "view_names", connection_view_names, 0);
3044
+ rb_define_method(rb_cFbConnection, "role_names", connection_role_names, 0);
3045
+ rb_define_method(rb_cFbConnection, "procedure_names", connection_procedure_names, 0);
3046
+ rb_define_method(rb_cFbConnection, "trigger_names", connection_trigger_names, 0);
3047
+ rb_define_method(rb_cFbConnection, "indexes", connection_indexes, 0);
3048
+ rb_define_method(rb_cFbConnection, "columns", connection_columns, 1);
3049
+ /* rb_define_method(rb_cFbConnection, "cursor", connection_cursor, 0); */
3050
+
3051
+ rb_cFbCursor = rb_define_class_under(rb_mFb, "Cursor", rb_cData);
3052
+ /* rb_define_method(rb_cFbCursor, "execute", cursor_execute, -1); */
3053
+ rb_define_method(rb_cFbCursor, "fields", cursor_fields, -1);
3054
+ rb_define_method(rb_cFbCursor, "fetch", cursor_fetch, -1);
3055
+ rb_define_method(rb_cFbCursor, "fetchall", cursor_fetchall, -1);
3056
+ rb_define_method(rb_cFbCursor, "each", cursor_each, -1);
3057
+ rb_define_method(rb_cFbCursor, "close", cursor_close, 0);
3058
+ rb_define_method(rb_cFbCursor, "drop", cursor_drop, 0);
3059
+
3060
+ rb_cFbSqlType = rb_define_class_under(rb_mFb, "SqlType", rb_cData);
3061
+ rb_define_singleton_method(rb_cFbSqlType, "from_code", sql_type_from_code, 2);
3062
+
3063
+ rb_eFbError = rb_define_class_under(rb_mFb, "Error", rb_eStandardError);
3064
+ rb_define_method(rb_eFbError, "error_code", error_error_code, 0);
3065
+
3066
+ rb_sFbField = rb_struct_define("FbField", "name", "sql_type", "sql_subtype", "display_size", "internal_size", "precision", "scale", "nullable", "type_code", NULL);
3067
+ rb_sFbIndex = rb_struct_define("FbIndex", "table_name", "index_name", "unique", "descending", "columns", NULL);
3068
+ rb_sFbColumn = rb_struct_define("FbColumn", "name", "domain", "sql_type", "sql_subtype", "length", "precision", "scale", "default", "nullable", NULL);
3069
+
3070
+ rb_require("date");
3071
+ rb_require("time"); /* Needed as of Ruby 1.8.5 */
3072
+ rb_cDate = rb_const_get(rb_cObject, rb_intern("Date"));
3073
+
3074
+ id_matches = rb_intern("=~");
3075
+ id_downcase_bang = rb_intern("downcase!");
3076
+ re_lowercase = rb_reg_regcomp(rb_str_new2("[[:lower:]]"));
3077
+ rb_global_variable(&re_lowercase);
3078
+ id_rstrip_bang = rb_intern("rstrip!");
3079
+ id_sub_bang = rb_intern("sub!");
3080
+ id_force_encoding = rb_intern("force_encoding");
3081
+ }