rubyfb 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/CHANGELOG +6 -0
  2. data/LICENSE +411 -0
  3. data/Manifest +73 -0
  4. data/README +460 -0
  5. data/Rakefile +20 -0
  6. data/examples/example01.rb +65 -0
  7. data/ext/AddUser.c +464 -0
  8. data/ext/AddUser.h +37 -0
  9. data/ext/Backup.c +783 -0
  10. data/ext/Backup.h +37 -0
  11. data/ext/Blob.c +421 -0
  12. data/ext/Blob.h +65 -0
  13. data/ext/Common.c +54 -0
  14. data/ext/Common.h +37 -0
  15. data/ext/Connection.c +863 -0
  16. data/ext/Connection.h +50 -0
  17. data/ext/DataArea.c +274 -0
  18. data/ext/DataArea.h +38 -0
  19. data/ext/Database.c +449 -0
  20. data/ext/Database.h +48 -0
  21. data/ext/FireRuby.c +240 -0
  22. data/ext/FireRuby.h +50 -0
  23. data/ext/FireRubyException.c +268 -0
  24. data/ext/FireRubyException.h +51 -0
  25. data/ext/Generator.c +689 -0
  26. data/ext/Generator.h +53 -0
  27. data/ext/RemoveUser.c +212 -0
  28. data/ext/RemoveUser.h +37 -0
  29. data/ext/Restore.c +855 -0
  30. data/ext/Restore.h +37 -0
  31. data/ext/ResultSet.c +809 -0
  32. data/ext/ResultSet.h +60 -0
  33. data/ext/Row.c +965 -0
  34. data/ext/Row.h +55 -0
  35. data/ext/ServiceManager.c +316 -0
  36. data/ext/ServiceManager.h +48 -0
  37. data/ext/Services.c +124 -0
  38. data/ext/Services.h +42 -0
  39. data/ext/Statement.c +785 -0
  40. data/ext/Statement.h +62 -0
  41. data/ext/Transaction.c +684 -0
  42. data/ext/Transaction.h +50 -0
  43. data/ext/TypeMap.c +1182 -0
  44. data/ext/TypeMap.h +51 -0
  45. data/ext/extconf.rb +28 -0
  46. data/ext/mkmf.bat +1 -0
  47. data/lib/SQLType.rb +224 -0
  48. data/lib/active_record/connection_adapters/rubyfb_adapter.rb +805 -0
  49. data/lib/mkdoc +1 -0
  50. data/lib/rubyfb.rb +2 -0
  51. data/lib/rubyfb_lib.so +0 -0
  52. data/lib/src.rb +1800 -0
  53. data/rubyfb.gemspec +31 -0
  54. data/test/AddRemoveUserTest.rb +56 -0
  55. data/test/BackupRestoreTest.rb +99 -0
  56. data/test/BlobTest.rb +57 -0
  57. data/test/CharacterSetTest.rb +63 -0
  58. data/test/ConnectionTest.rb +111 -0
  59. data/test/DDLTest.rb +54 -0
  60. data/test/DatabaseTest.rb +83 -0
  61. data/test/GeneratorTest.rb +50 -0
  62. data/test/KeyTest.rb +140 -0
  63. data/test/ResultSetTest.rb +162 -0
  64. data/test/RoleTest.rb +73 -0
  65. data/test/RowCountTest.rb +65 -0
  66. data/test/RowTest.rb +203 -0
  67. data/test/SQLTest.rb +182 -0
  68. data/test/SQLTypeTest.rb +101 -0
  69. data/test/ServiceManagerTest.rb +29 -0
  70. data/test/StatementTest.rb +135 -0
  71. data/test/TestSetup.rb +11 -0
  72. data/test/TransactionTest.rb +112 -0
  73. data/test/TypeTest.rb +92 -0
  74. data/test/UnitTest.rb +65 -0
  75. metadata +149 -0
data/ext/TypeMap.c ADDED
@@ -0,0 +1,1182 @@
1
+ /*------------------------------------------------------------------------------
2
+ * TypeMap.c
3
+ *----------------------------------------------------------------------------*/
4
+ /**
5
+ * Copyright � Peter Wood, 2005
6
+ *
7
+ * The contents of this file are subject to the Mozilla Public License Version
8
+ * 1.1 (the "License"); you may not use this file except in compliance with the
9
+ * License. You may obtain a copy of the License at
10
+ *
11
+ * http://www.mozilla.org/MPL/
12
+ *
13
+ * Software distributed under the License is distributed on an "AS IS" basis,
14
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
15
+ * the specificlanguage governing rights and limitations under the License.
16
+ *
17
+ * The Original Code is the FireRuby extension for the Ruby language.
18
+ *
19
+ * The Initial Developer of the Original Code is Peter Wood. All Rights
20
+ * Reserved.
21
+ *
22
+ * @author Peter Wood
23
+ * @version 1.0
24
+ */
25
+
26
+ /* Includes. */
27
+ #include "TypeMap.h"
28
+ #include <time.h>
29
+ #include <math.h>
30
+ #include <limits.h>
31
+ #include "Blob.h"
32
+ #include "Connection.h"
33
+ #include "Transaction.h"
34
+ #include "ResultSet.h"
35
+ #include "Statement.h"
36
+ #include "FireRuby.h"
37
+
38
+ #ifdef OS_UNIX
39
+ #include <inttypes.h>
40
+ #else
41
+ typedef short int16_t;
42
+ typedef long int32_t;
43
+ typedef long long int64_t;
44
+ #endif
45
+
46
+ /* Function prototypes. */
47
+ VALUE createDate(const struct tm *);
48
+ VALUE createDateTime(VALUE dt);
49
+ VALUE createTime(VALUE dt);
50
+ VALUE createSafeTime(const struct tm*);
51
+ VALUE getConstant(const char *, VALUE);
52
+ VALUE toDateTime(VALUE);
53
+ VALUE rescueConvert(VALUE, VALUE);
54
+ void storeBlob(VALUE, XSQLVAR *, ConnectionHandle *, TransactionHandle *);
55
+ void populateBlobField(VALUE, XSQLVAR *, VALUE);
56
+ void populateDoubleField(VALUE, XSQLVAR *);
57
+ void populateFloatField(VALUE, XSQLVAR *);
58
+ void populateInt64Field(VALUE, XSQLVAR *);
59
+ void populateLongField(VALUE, XSQLVAR *);
60
+ void populateShortField(VALUE, XSQLVAR *);
61
+ void populateTextField(VALUE, XSQLVAR *);
62
+ void populateDateField(VALUE, XSQLVAR *);
63
+ void populateTimeField(VALUE, XSQLVAR *);
64
+ void populateTimestampField(VALUE, XSQLVAR *);
65
+
66
+
67
+ /**
68
+ * This function converts a single XSQLVAR entry to a Ruby VALUE type.
69
+ *
70
+ * @param entry A pointer to the SQLVAR type containing the data to be
71
+ * converted.
72
+ * @param database A pointer to the database handle relating to the data.
73
+ * @param transaction A pointer to the transaction handle relating to the
74
+ * data.
75
+ *
76
+ * @return A Ruby type for the XSQLVAR entry. The actual type will depend on
77
+ * the field type referenced.
78
+ *
79
+ */
80
+ VALUE toValue(XSQLVAR *entry,
81
+ isc_db_handle *database,
82
+ isc_tr_handle *transaction)
83
+ {
84
+ VALUE value = rb_ary_new();
85
+
86
+ /* Check for NULL values. */
87
+ if(!((entry->sqltype & 1) && (*entry->sqlind < 0)))
88
+ {
89
+ int type = (entry->sqltype & ~1);
90
+ char *array = NULL,
91
+ column[256],
92
+ table[256];
93
+ struct tm datetime;
94
+ short length;
95
+ double actual;
96
+ BlobHandle *blob = NULL;
97
+ VALUE setting = getFireRubySetting("DATE_AS_DATE"),
98
+ working = Qnil;
99
+
100
+ switch(type)
101
+ {
102
+ case SQL_ARRAY : /* Type: ARRAY */
103
+ /* TO BE DONE! */
104
+ break;
105
+
106
+ case SQL_BLOB: /* Type: BLOB */
107
+ memset(column, 0, 256);
108
+ memset(table, 0, 256);
109
+ memcpy(column, entry->sqlname, entry->sqlname_length);
110
+ memcpy(table, entry->relname, entry->relname_length);
111
+ blob = openBlob(*(ISC_QUAD *)entry->sqldata, column, table, database,
112
+ transaction);
113
+ working = Data_Wrap_Struct(cBlob, NULL, blobFree, blob);
114
+ rb_ary_push(value, initializeBlob(working));
115
+ rb_ary_push(value, getColumnType(entry));
116
+ break;
117
+
118
+ case SQL_TYPE_DATE : /* Type: DATE */
119
+ memset(&datetime, 0, sizeof(struct tm));
120
+ isc_decode_sql_date((ISC_DATE *)entry->sqldata, &datetime);
121
+ datetime.tm_sec = 0;
122
+ datetime.tm_min = 0;
123
+ datetime.tm_hour = 0;
124
+ if(setting == Qtrue)
125
+ {
126
+ rb_ary_push(value, createDate(&datetime));
127
+ }
128
+ else
129
+ {
130
+ rb_ary_push(value, createSafeTime(&datetime));
131
+ }
132
+ rb_ary_push(value, getColumnType(entry));
133
+ break;
134
+
135
+ case SQL_DOUBLE : /* Type: DOUBLE PRECISION, DECIMAL, NUMERIC */
136
+ rb_ary_push(value, rb_float_new(*((double *)entry->sqldata)));
137
+ rb_ary_push(value, getColumnType(entry));
138
+ break;
139
+
140
+ case SQL_FLOAT : /* Type: FLOAT */
141
+ rb_ary_push(value, rb_float_new(*((float *)entry->sqldata)));
142
+ rb_ary_push(value, getColumnType(entry));
143
+ break;
144
+
145
+ case SQL_INT64 : /* Type: DECIMAL, NUMERIC */
146
+ if(entry->sqlscale != 0)
147
+ {
148
+ double divisor = pow(10, abs(entry->sqlscale));
149
+
150
+ actual = *((long long *)entry->sqldata);
151
+ rb_ary_push(value, rb_float_new(actual / divisor));
152
+ }
153
+ else
154
+ {
155
+ rb_ary_push(value, LL2NUM(*((long long *)entry->sqldata)));
156
+ }
157
+ rb_ary_push(value, getColumnType(entry));
158
+ break;
159
+
160
+ case SQL_LONG : /* Type: INTEGER, DECIMAL, NUMERIC */
161
+ if(entry->sqlscale != 0)
162
+ {
163
+ double divisor = pow(10, abs(entry->sqlscale));
164
+
165
+ actual = *((int32_t *)entry->sqldata);
166
+ rb_ary_push(value, rb_float_new(actual / divisor));
167
+ }
168
+ else
169
+ {
170
+ rb_ary_push(value, LONG2NUM(*((int32_t *)entry->sqldata)));
171
+ }
172
+ rb_ary_push(value, getColumnType(entry));
173
+ break;
174
+
175
+ case SQL_SHORT : /* Type: SMALLINT, DECIMAL, NUMERIC */
176
+ if(entry->sqlscale != 0)
177
+ {
178
+ double divisor = pow(10, abs(entry->sqlscale));
179
+
180
+ actual = *((short *)entry->sqldata);
181
+ rb_ary_push(value, rb_float_new(actual / divisor));
182
+ }
183
+ else
184
+ {
185
+ rb_ary_push(value, INT2NUM(*((short *)entry->sqldata)));
186
+ }
187
+ rb_ary_push(value, getColumnType(entry));
188
+ break;
189
+
190
+ case SQL_TEXT : /* Type: CHAR */
191
+ array = ALLOC_N(char, entry->sqllen + 1);
192
+ if(array != NULL)
193
+ {
194
+ memset(array, 0, entry->sqllen + 1);
195
+ memcpy(array, entry->sqldata, entry->sqllen);
196
+ rb_ary_push(value, rb_str_new2(array));
197
+ rb_ary_push(value, getColumnType(entry));
198
+ free(array);
199
+ }
200
+ break;
201
+
202
+ case SQL_TYPE_TIME : /* Type: TIME */
203
+ isc_decode_sql_time((ISC_TIME *)entry->sqldata, &datetime);
204
+ datetime.tm_year = 70;
205
+ datetime.tm_mon = 0;
206
+ datetime.tm_mday = 1;
207
+ rb_ary_push(value, createSafeTime(&datetime));
208
+ rb_ary_push(value, getColumnType(entry));
209
+ break;
210
+
211
+ case SQL_TIMESTAMP : /* Type: TIMESTAMP */
212
+ isc_decode_timestamp((ISC_TIMESTAMP *)entry->sqldata, &datetime);
213
+ rb_ary_push(value, createSafeTime(&datetime));
214
+ rb_ary_push(value, getColumnType(entry));
215
+ break;
216
+
217
+ case SQL_VARYING :
218
+ memcpy(&length, entry->sqldata, 2);
219
+ if(length >= 0)
220
+ {
221
+ array = ALLOC_N(char, length + 1);
222
+ if(array != NULL)
223
+ {
224
+ memset(array, 0, length + 1);
225
+ memcpy(array, &entry->sqldata[2], length);
226
+ rb_ary_push(value, rb_str_new2(array));
227
+ rb_ary_push(value, getColumnType(entry));
228
+ free(array);
229
+ }
230
+ }
231
+ break;
232
+
233
+ default :
234
+ rb_ary_push(value, Qnil);
235
+ rb_ary_push(value, Qnil);
236
+ } /* End of switch. */
237
+ }
238
+ else
239
+ {
240
+ rb_ary_push(value, Qnil);
241
+ rb_ary_push(value, getColumnType(entry));
242
+ }
243
+
244
+ return(value);
245
+ }
246
+
247
+
248
+ /**
249
+ * This function attempts to convert the data contents of a XSQLDA to a Ruby
250
+ * array of values.
251
+ *
252
+ * @param results A reference to the ResultSet object to extract the data row
253
+ * from.
254
+ *
255
+ * @return A reference to the array containing the row data from the XSQLDA.
256
+ *
257
+ */
258
+ VALUE toArray(VALUE results)
259
+ {
260
+ VALUE array = rb_ary_new(),
261
+ transaction = rb_iv_get(results, "@transaction"),
262
+ connection = rb_iv_get(results, "@connection");
263
+ XSQLVAR *entry = NULL;
264
+ ConnectionHandle *cHandle = NULL;
265
+ ResultsHandle *rHandle = NULL;
266
+ TransactionHandle *tHandle = NULL;
267
+ int i;
268
+
269
+ Data_Get_Struct(connection, ConnectionHandle, cHandle);
270
+ Data_Get_Struct(results, ResultsHandle, rHandle);
271
+ Data_Get_Struct(transaction, TransactionHandle, tHandle);
272
+ entry = rHandle->output->sqlvar;
273
+ for(i = 0; i < rHandle->output->sqln; i++, entry++)
274
+ {
275
+ VALUE value = toValue(entry, &cHandle->handle, &tHandle->handle);
276
+
277
+ rb_ary_push(array, value);
278
+ }
279
+
280
+ return(array);
281
+ }
282
+
283
+
284
+ /**
285
+ * This function takes an array of parameters and populates the parameter set
286
+ * for a Statement object with the details. Attempts are made to convert the
287
+ * parameter types where appropriate but, if this isn't possible then an
288
+ * exception is generated.
289
+ *
290
+ * @param parameters A pointer to the XSQLDA area that will be used to
291
+ * hold the parameter data.
292
+ * @param array A reference to an array containing the parameter data to
293
+ * be used.
294
+ * @param source Either a Statement or ResultSet object that can be used
295
+ * to get connection and transaction details.
296
+ *
297
+ */
298
+ void setParameters(XSQLDA *parameters, VALUE array, VALUE source)
299
+ {
300
+ VALUE value;
301
+ int index,
302
+ size;
303
+ XSQLVAR *parameter = NULL;
304
+
305
+ /* Check that sufficient parameters have been provided. */
306
+ value = rb_funcall(array, rb_intern("size"), 0);
307
+ size = (TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value));
308
+ parameter = parameters->sqlvar;
309
+ if(size != parameters->sqld)
310
+ {
311
+ rb_raise(rb_eException,
312
+ "Parameter set mismatch. Too many or too few parameters "\
313
+ "specified for a SQL statement.");
314
+ }
315
+ parameters->sqln = parameters->sqld;
316
+ parameters->version = 1;
317
+
318
+ /* Populate the parameters from the array's contents. */
319
+ for(index = 0; index < size; index++, parameter++)
320
+ {
321
+ int type = (parameter->sqltype & ~1);
322
+
323
+ value = rb_ary_entry(array, index);
324
+ /* Check for nils to indicate null values. */
325
+ if(value != Qnil)
326
+ {
327
+ VALUE name = rb_funcall(value, rb_intern("class"), 0);
328
+
329
+ parameter->sqlind = 0;
330
+ name = rb_funcall(name, rb_intern("name"), 0);
331
+ switch(type)
332
+ {
333
+ case SQL_ARRAY : /* Type: ARRAY */
334
+ /* TO BE DONE! */
335
+ break;
336
+
337
+ case SQL_BLOB: /* Type: BLOB */
338
+ populateBlobField(value, parameter, source);
339
+ break;
340
+
341
+ case SQL_DOUBLE : /* Type: DOUBLE PRECISION, DECIMAL, NUMERIC */
342
+ populateDoubleField(value, parameter);
343
+ break;
344
+
345
+ case SQL_FLOAT : /* Type: FLOAT */
346
+ populateFloatField(value, parameter);
347
+ break;
348
+
349
+ case SQL_INT64 : /* Type: DECIMAL, NUMERIC */
350
+ populateInt64Field(value, parameter);
351
+ break;
352
+
353
+ case SQL_LONG : /* Type: INTEGER, DECIMAL, NUMERIC */
354
+ populateLongField(value, parameter);
355
+ break;
356
+
357
+ case SQL_SHORT : /* Type: SMALLINT, DECIMAL, NUMERIC */
358
+ populateShortField(value, parameter);
359
+ break;
360
+
361
+ case SQL_TEXT : /* Type: CHAR */
362
+ populateTextField(value, parameter);
363
+ break;
364
+
365
+ case SQL_TYPE_DATE : /* Type: DATE */
366
+ populateDateField(value, parameter);
367
+ break;
368
+
369
+ case SQL_TYPE_TIME : /* Type: TIME */
370
+ populateTimeField(value, parameter);
371
+ break;
372
+
373
+ case SQL_TIMESTAMP : /* Type: TIMESTAMP */
374
+ populateTimestampField(value, parameter);
375
+ break;
376
+
377
+ case SQL_VARYING : /* Type: VARCHAR */
378
+ populateTextField(value, parameter);
379
+ break;
380
+
381
+ default :
382
+ rb_raise(rb_eException,
383
+ "Unknown SQL type encountered in statement parameter "\
384
+ "set.");
385
+ } /* End of the switch statement. */
386
+ }
387
+ else
388
+ {
389
+ /* Mark the field as a NULL value. */
390
+ memset(parameter->sqldata, 0, parameter->sqllen);
391
+ *parameter->sqlind = -1;
392
+ }
393
+ }
394
+ }
395
+
396
+
397
+ /**
398
+ * This function converts a struct tm to a Ruby Date instance.
399
+ *
400
+ * @param date A structure containing the date details.
401
+ *
402
+ * @return A Ruby Date object.
403
+ *
404
+ */
405
+ VALUE createDate(const struct tm *date)
406
+ {
407
+ VALUE result = Qnil,
408
+ klass = Qnil;
409
+
410
+ klass = getClass("Date");
411
+
412
+ /* Check if we need to require date. */
413
+ if(klass == Qnil)
414
+ {
415
+ rb_require("date");
416
+ klass = getClass("Date");
417
+ }
418
+
419
+ /* Check that we got the Date class. */
420
+ if(klass != Qnil)
421
+ {
422
+ VALUE arguments[3];
423
+
424
+ /* Prepare the arguments. */
425
+ arguments[0] = INT2FIX(date->tm_year + 1900);
426
+ arguments[1] = INT2FIX(date->tm_mon + 1);
427
+ arguments[2] = INT2FIX(date->tm_mday);
428
+
429
+ /* Create the class instance. */
430
+ result = rb_funcall2(klass, rb_intern("new"), 3, arguments);
431
+ }
432
+
433
+
434
+ return(result);
435
+ }
436
+
437
+
438
+ /**
439
+ * This function converts a struct tm to a Ruby DateTime instance.
440
+ *
441
+ * @param datetime A structure containing the date/time details.
442
+ *
443
+ * @return A Ruby DateTime object.
444
+ *
445
+ */
446
+ VALUE createDateTime(VALUE dt)
447
+ {
448
+ VALUE result = Qnil,
449
+ klass = Qnil;
450
+
451
+ struct tm *datetime;
452
+ Data_Get_Struct(dt, struct tm, datetime);
453
+
454
+ klass = getClass("DateTime");
455
+
456
+ /* Check if we need to require date. */
457
+ if(klass == Qnil)
458
+ {
459
+ rb_require("date");
460
+ klass = getClass("DateTime");
461
+ }
462
+
463
+ /* Check that we got the DateTime class. */
464
+ if(klass != Qnil)
465
+ {
466
+ VALUE arguments[7];
467
+
468
+ /* Prepare the arguments. */
469
+ arguments[0] = INT2FIX(datetime->tm_year + 1900);
470
+ arguments[1] = INT2FIX(datetime->tm_mon + 1);
471
+ arguments[2] = INT2FIX(datetime->tm_mday);
472
+ arguments[3] = INT2FIX(datetime->tm_hour);
473
+ arguments[4] = INT2FIX(datetime->tm_min);
474
+ arguments[5] = INT2FIX(datetime->tm_sec);
475
+ arguments[6] = rb_funcall(rb_funcall(klass, rb_intern("now"), 0), rb_intern("offset"), 0);
476
+
477
+ /* Create the class instance. */
478
+ result = rb_funcall2(klass, rb_intern("new"), 7, arguments);
479
+ }
480
+
481
+ return(result);
482
+ }
483
+
484
+
485
+ /**
486
+ * This function converts a struct tm to a Ruby Time instance.
487
+ *
488
+ * @param datetime A structure containing the date/time details.
489
+ *
490
+ * @return A Ruby Time object.
491
+ *
492
+ */
493
+ VALUE createTime(VALUE dt)
494
+ {
495
+ VALUE result = Qnil,
496
+ klass = Qnil;
497
+
498
+ struct tm *datetime;
499
+ Data_Get_Struct(dt, struct tm, datetime);
500
+
501
+ klass = getClass("Time");
502
+
503
+ /* Check that we got the Time class. */
504
+ if(klass != Qnil)
505
+ {
506
+ VALUE arguments[6];
507
+
508
+ /* Prepare the arguments. */
509
+ /*fprintf(stderr, "%d-%d-%d %d:%d:%d\n", datetime->tm_year + 1900,
510
+ datetime->tm_mon + 1, datetime->tm_mday, datetime->tm_hour,
511
+ datetime->tm_min, datetime->tm_sec);*/
512
+ arguments[0] = INT2FIX(datetime->tm_year + 1900);
513
+ arguments[1] = INT2FIX(datetime->tm_mon + 1);
514
+ arguments[2] = INT2FIX(datetime->tm_mday);
515
+ arguments[3] = INT2FIX(datetime->tm_hour);
516
+ arguments[4] = INT2FIX(datetime->tm_min);
517
+ arguments[5] = INT2FIX(datetime->tm_sec);
518
+
519
+ /* Create the class instance. */
520
+ result = rb_funcall2(klass, rb_intern("local"), 6, arguments);
521
+ }
522
+
523
+ return(result);
524
+ }
525
+
526
+ /**
527
+ * This function converts a struct tm to a Ruby Time instance.
528
+ * If the conversion process results in an out of range error then
529
+ * it will convert the struct to a DateTime instance.
530
+ *
531
+ * @param datetime A structure containing the date/time details.
532
+ *
533
+ * @return A Ruby Time object if the arguments are in range, otherwise
534
+ * a Ruby DateTime object.
535
+ *
536
+ */
537
+ VALUE createSafeTime(const struct tm *datetime)
538
+ {
539
+ VALUE dt = Data_Wrap_Struct(rb_cObject, NULL, NULL, datetime);
540
+ return rb_rescue(createTime, dt, createDateTime, dt);
541
+ }
542
+
543
+ /**
544
+ * This method fetches a Ruby constant definition. If the module specified to
545
+ * the function is nil then the top level is assume
546
+ *
547
+ * @param name The name of the constant to be retrieved.
548
+ * @param module A reference to the Ruby module that should contain the
549
+ * constant.
550
+ *
551
+ * @return A Ruby VALUE representing the constant.
552
+ *
553
+ */
554
+ VALUE getConstant(const char *name, VALUE module)
555
+ {
556
+ VALUE owner = module,
557
+ constants,
558
+ string,
559
+ exists,
560
+ entry;
561
+
562
+ /* Check that we've got somewhere to look. */
563
+ if(owner == Qnil)
564
+ {
565
+ owner = rb_cModule;
566
+ }
567
+ constants = rb_funcall(owner, rb_intern("constants"), 0),
568
+ string = rb_str_new2(name),
569
+ exists = rb_funcall(constants, rb_intern("include?"), 1, string);
570
+
571
+ if(exists != Qfalse)
572
+ {
573
+ ID id = rb_intern(name);
574
+ VALUE symbol = ID2SYM(id);
575
+
576
+ entry = rb_funcall(owner, rb_intern("const_get"), 1, symbol);
577
+ }
578
+
579
+ return(entry);
580
+ }
581
+
582
+
583
+ /**
584
+ * This method fetches a Ruby module definition object based on a class name.
585
+ * The method is assumed to have been defined at the top level.
586
+ *
587
+ * @return A Ruby VALUE representing the Module requested, or nil if the
588
+ * module could not be located.
589
+ *
590
+ */
591
+ VALUE getModule(const char *name)
592
+ {
593
+ VALUE module = getConstant(name, Qnil);
594
+
595
+ if(module != Qnil)
596
+ {
597
+ VALUE type = rb_funcall(module, rb_intern("class"), 0);
598
+
599
+ if(type != rb_cModule)
600
+ {
601
+ module = Qnil;
602
+ }
603
+ }
604
+
605
+ return(module);
606
+ }
607
+
608
+
609
+ /**
610
+ * This method fetches a Ruby class definition object based on a class name.
611
+ * The class is assumed to have been defined at the top level.
612
+ *
613
+ * @return A Ruby VALUE representing the requested class, or nil if the class
614
+ * could not be found.
615
+ *
616
+ */
617
+ VALUE getClass(const char *name)
618
+ {
619
+ VALUE klass = getConstant(name, Qnil);
620
+
621
+ if(klass != Qnil)
622
+ {
623
+ VALUE type = rb_funcall(klass, rb_intern("class"), 0);
624
+
625
+ if(type != rb_cClass)
626
+ {
627
+ klass = Qnil;
628
+ }
629
+ }
630
+
631
+ return(klass);
632
+ }
633
+
634
+
635
+ /**
636
+ * This method fetches a module from a specified module.
637
+ *
638
+ * @param name The name of the class to fetch.
639
+ * @param owner The module to search for the module in.
640
+ *
641
+ * @return A Ruby VALUE representing the requested module, or nil if it could
642
+ * not be located.
643
+ *
644
+ */
645
+ VALUE getModuleInModule(const char *name, VALUE owner)
646
+ {
647
+ VALUE module = getConstant(name, owner);
648
+
649
+ if(module != Qnil)
650
+ {
651
+ VALUE type = rb_funcall(module, rb_intern("class"), 0);
652
+
653
+ if(type != rb_cModule)
654
+ {
655
+ module = Qnil;
656
+ }
657
+ }
658
+
659
+ return(module);
660
+ }
661
+
662
+
663
+ /**
664
+ * This function fetches a class from a specified module.
665
+ *
666
+ * @param name The name of the class to be retrieved.
667
+ * @param owner The module to search for the class in.
668
+ *
669
+ * @return A Ruby VALUE representing the requested module, or nil if it could
670
+ * not be located.
671
+ *
672
+ */
673
+ VALUE getClassInModule(const char *name, VALUE owner)
674
+ {
675
+ VALUE klass = getConstant(name, owner);
676
+
677
+ if(klass != Qnil)
678
+ {
679
+ VALUE type = rb_funcall(klass, rb_intern("class"), 0);
680
+
681
+ if(type != rb_cClass)
682
+ {
683
+ klass = Qnil;
684
+ }
685
+ }
686
+
687
+ return(klass);
688
+ }
689
+
690
+
691
+ /**
692
+ * This function attempts to convert a VALUE to a series of date/time values.
693
+ *
694
+ * @param value A reference to the value to be converted.
695
+ *
696
+ * @return A VALUE that represents an array containing the date/time details
697
+ * in the order year, month, day of month, hours, minutes and seconds.
698
+ *
699
+ */
700
+ VALUE toDateTime(VALUE value)
701
+ {
702
+ VALUE result,
703
+ klass = rb_funcall(value, rb_intern("class"), 0);
704
+
705
+ if(klass == rb_cTime)
706
+ {
707
+ VALUE data;
708
+
709
+ result = rb_ary_new();
710
+
711
+ data = rb_funcall(value, rb_intern("year"), 0);
712
+ rb_ary_push(result, INT2FIX(FIX2INT(data) - 1900));
713
+ data = rb_funcall(value, rb_intern("month"), 0);
714
+ rb_ary_push(result, INT2FIX(FIX2INT(data) - 1));
715
+ rb_ary_push(result, rb_funcall(value, rb_intern("day"), 0));
716
+ rb_ary_push(result, rb_funcall(value, rb_intern("hour"), 0));
717
+ rb_ary_push(result, rb_funcall(value, rb_intern("min"), 0));
718
+ rb_ary_push(result, rb_funcall(value, rb_intern("sec"), 0));
719
+ }
720
+ else if(klass == getClass("Date"))
721
+ {
722
+ VALUE data;
723
+
724
+ result = rb_ary_new();
725
+
726
+ data = rb_funcall(value, rb_intern("year"), 0);
727
+ rb_ary_push(result, INT2FIX(FIX2INT(data) - 1900));
728
+ data = rb_funcall(value, rb_intern("month"), 0);
729
+ rb_ary_push(result, INT2FIX(FIX2INT(data) - 1));
730
+ rb_ary_push(result, rb_funcall(value, rb_intern("mday"), 0));
731
+ rb_ary_push(result, INT2FIX(0));
732
+ rb_ary_push(result, INT2FIX(0));
733
+ rb_ary_push(result, INT2FIX(0));
734
+ }
735
+ else
736
+ {
737
+ rb_raise(rb_eException, "Value conversion error.");
738
+ }
739
+
740
+ return(result);
741
+ }
742
+
743
+
744
+ /**
745
+ * This function represents the rescue block for data conversions.
746
+ *
747
+ * @param arguments An array of VALUEs. The first is expected to contain the
748
+ * field offset of the value being converted. The second a
749
+ * string containing the name of the data type being
750
+ * converted from and the third a string containing the name
751
+ * of the data type being converted to.
752
+ * @param error Will be populates with error details of the exception
753
+ * raised.
754
+ *
755
+ */
756
+ VALUE rescueConvert(VALUE arguments, VALUE error)
757
+ {
758
+ VALUE message;
759
+ char text[512];
760
+
761
+ sprintf(text, "Error converting input column %d from a %s to a %s.",
762
+ FIX2INT(rb_ary_entry(arguments, 0)),
763
+ STR2CSTR(rb_ary_entry(arguments, 1)),
764
+ STR2CSTR(rb_ary_entry(arguments, 2)));
765
+ message = rb_str_new2(text);
766
+
767
+ return(rb_funcall(rb_eException, rb_intern("exception"), 1, &message));
768
+ }
769
+
770
+
771
+ /**
772
+ * This function creates a new blob and returns the identifier for it.
773
+ *
774
+ * @param info A pointer to a string containing the blob data.
775
+ * @param field The field that the blob identifier needs to be inserted
776
+ * into.
777
+ * @param database A pointer to the database handle to be used in creating
778
+ * the blob.
779
+ * @param transaction A pointer to the transaction handle to be used in
780
+ * creating the blob.
781
+ *
782
+ * @return An ISC_QUAD value containing the identifier for the blob created.
783
+ *
784
+ */
785
+ void storeBlob(VALUE info,
786
+ XSQLVAR *field,
787
+ ConnectionHandle *connection,
788
+ TransactionHandle *transaction)
789
+ {
790
+ ISC_STATUS status[20];
791
+ isc_blob_handle handle = 0;
792
+ ISC_QUAD *blobId = (ISC_QUAD *)field->sqldata;
793
+ VALUE number = rb_funcall(info, rb_intern("length"), 0);
794
+ long length = 0;
795
+ char *data = STR2CSTR(info);
796
+
797
+ length = TYPE(number) == T_FIXNUM ? FIX2INT(number) : NUM2INT(number);
798
+ field->sqltype = SQL_BLOB;
799
+ if(isc_create_blob(status, &connection->handle, &transaction->handle,
800
+ &handle, blobId) == 0)
801
+ {
802
+ long offset = 0;
803
+ unsigned short size = 0;
804
+
805
+ while(offset < length)
806
+ {
807
+ char *buffer = &data[offset];
808
+
809
+ size = (length - offset) > USHRT_MAX ? USHRT_MAX : length - offset;
810
+ if(isc_put_segment(status, &handle, size, buffer) != 0)
811
+ {
812
+ ISC_STATUS other[20];
813
+
814
+ isc_close_blob(other, &handle);
815
+ rb_fireruby_raise(status, "Error writing blob data.");
816
+ }
817
+
818
+ offset = offset + size;
819
+ }
820
+
821
+ if(isc_close_blob(status, &handle) != 0)
822
+ {
823
+ rb_fireruby_raise(status, "Error closing blob.");
824
+ }
825
+ }
826
+ else
827
+ {
828
+ rb_fireruby_raise(status, "Error storing blob data.");
829
+ }
830
+ }
831
+
832
+
833
+ /**
834
+ * This function populates a blob output parameter.
835
+ *
836
+ * @param value The value to be insert into the blob.
837
+ * @param field A pointer to the output field to be populated.
838
+ * @param source A reference to either a Statement or ResultSet object that
839
+ * contains the connection and transaction details.
840
+ *
841
+ */
842
+ void populateBlobField(VALUE value, XSQLVAR *field, VALUE source)
843
+ {
844
+ VALUE attribute;
845
+ ConnectionHandle *connection = NULL;
846
+ TransactionHandle *transaction = NULL;
847
+
848
+ if(TYPE(value) != T_STRING)
849
+ {
850
+ rb_fireruby_raise(NULL, "Error converting input parameter to blob.");
851
+ }
852
+
853
+ /* Fetch the connection and transaction details. */
854
+ attribute = rb_iv_get(source, "@connection");
855
+ Data_Get_Struct(attribute, ConnectionHandle, connection);
856
+ attribute = rb_iv_get(source, "@transaction");
857
+ Data_Get_Struct(attribute, TransactionHandle, transaction);
858
+ storeBlob(value, field, connection, transaction);
859
+ field->sqltype = SQL_BLOB;
860
+ }
861
+
862
+
863
+ /**
864
+ * This method populates a date output field.
865
+ *
866
+ * @param value A reference to the value to be used to populate the field.
867
+ * @param field A pointer to the output field to be populated.
868
+ *
869
+ */
870
+ void populateDateField(VALUE value, XSQLVAR *field)
871
+ {
872
+ struct tm datetime;
873
+ VALUE arguments = rb_ary_new();
874
+
875
+ rb_ary_push(arguments, rb_str_new2("date"));
876
+ value = rb_rescue(toDateTime, value, rescueConvert, arguments);
877
+ if(TYPE(value) != T_ARRAY)
878
+ {
879
+ VALUE message = rb_funcall(value, rb_intern("message"), 0);
880
+ rb_fireruby_raise(NULL, STR2CSTR(message));
881
+ }
882
+ datetime.tm_year = FIX2INT(rb_ary_entry(value, 0));
883
+ datetime.tm_mon = FIX2INT(rb_ary_entry(value, 1));
884
+ datetime.tm_mday = FIX2INT(rb_ary_entry(value, 2));
885
+ isc_encode_sql_date(&datetime, (ISC_DATE *)field->sqldata);
886
+ field->sqltype = SQL_TYPE_DATE;
887
+ }
888
+
889
+
890
+ /**
891
+ * This method populates a double output field.
892
+ *
893
+ * @param value A reference to the value to be used to populate the field.
894
+ * @param field A pointer to the output field to be populated.
895
+ *
896
+ */
897
+ void populateDoubleField(VALUE value, XSQLVAR *field)
898
+ {
899
+ double store;
900
+ VALUE actual;
901
+
902
+ if(TYPE(value) != T_FLOAT)
903
+ {
904
+ if(rb_obj_is_kind_of(value, rb_cNumeric) || TYPE(value) == T_STRING)
905
+ {
906
+ actual = rb_funcall(value, rb_intern("to_f"), 0);
907
+ }
908
+ else
909
+ {
910
+ rb_fireruby_raise(NULL,
911
+ "Error converting input parameter to double.");
912
+ }
913
+ }
914
+
915
+ store = NUM2DBL(value);
916
+ memcpy(field->sqldata, &store, field->sqllen);
917
+ field->sqltype = SQL_DOUBLE;
918
+ }
919
+
920
+
921
+ /**
922
+ * This method populates a double output field.
923
+ *
924
+ * @param value A reference to the value to be used to populate the field.
925
+ * @param field A pointer to the output field to be populated.
926
+ *
927
+ */
928
+ void populateFloatField(VALUE value, XSQLVAR *field)
929
+ {
930
+ double full = 0.0;
931
+ float store = 0.0;
932
+ VALUE actual;
933
+
934
+ if(TYPE(value) != T_FLOAT)
935
+ {
936
+ if(rb_obj_is_kind_of(value, rb_cNumeric) || TYPE(value) == T_STRING)
937
+ {
938
+ actual = rb_funcall(value, rb_intern("to_f"), 0);
939
+ }
940
+ else
941
+ {
942
+ rb_fireruby_raise(NULL,
943
+ "Error converting input parameter to double.");
944
+ }
945
+ }
946
+
947
+ full = NUM2DBL(value);
948
+ store = (float)full;
949
+ memcpy(field->sqldata, &store, field->sqllen);
950
+ field->sqltype = SQL_FLOAT;
951
+ }
952
+
953
+
954
+ /**
955
+ * This function populates a output parameter field with the data of a Ruby
956
+ * value.
957
+ *
958
+ * @param value A reference to the Ruby value to be inserted into the field.
959
+ * @param field A pointer to the XSQLVAR field that the value will go into.
960
+ *
961
+ */
962
+ void populateInt64Field(VALUE value, XSQLVAR *field)
963
+ {
964
+ VALUE actual = Qnil;
965
+ long long store = 0;
966
+
967
+ if(rb_obj_is_kind_of(value, rb_cInteger))
968
+ {
969
+ actual = value;
970
+ }
971
+ else if(TYPE(value) == T_FLOAT)
972
+ {
973
+ double number = NUM2DBL(value);
974
+
975
+ if(field->sqlscale != 0)
976
+ {
977
+ number = number * pow(10, abs(field->sqlscale));
978
+ actual = INT2NUM((long)number);
979
+ }
980
+ }
981
+ else if(TYPE(value) == T_STRING)
982
+ {
983
+ actual = rb_funcall(value, rb_intern("to_i"), 0);
984
+ }
985
+ else
986
+ {
987
+ rb_fireruby_raise(NULL,
988
+ "Error converting input parameter to 64 bit integer.");
989
+ }
990
+
991
+ store = TYPE(actual) == T_FIXNUM ? FIX2INT(actual) : NUM2INT(actual);
992
+ memcpy(field->sqldata, &store, field->sqllen);
993
+ field->sqltype = SQL_INT64;
994
+ }
995
+
996
+
997
+ /**
998
+ * This function populates a output parameter field with the data of a Ruby
999
+ * value.
1000
+ *
1001
+ * @param value A reference to the Ruby value to be inserted into the field.
1002
+ * @param field A pointer to the XSQLVAR field that the value will go into.
1003
+ *
1004
+ */
1005
+ void populateLongField(VALUE value, XSQLVAR *field)
1006
+ {
1007
+ VALUE actual = Qnil;
1008
+ long long full = 0;
1009
+ long store = 0;
1010
+
1011
+ if(rb_obj_is_kind_of(value, rb_cInteger))
1012
+ {
1013
+ actual = value;
1014
+ }
1015
+ else if(TYPE(value) == T_FLOAT)
1016
+ {
1017
+ double number = NUM2DBL(value);
1018
+
1019
+ if(field->sqlscale != 0)
1020
+ {
1021
+ number = number * pow(10, abs(field->sqlscale));
1022
+ actual = INT2NUM((long)number);
1023
+ }
1024
+ }
1025
+ else if(TYPE(value) == T_STRING)
1026
+ {
1027
+ actual = rb_funcall(value, rb_intern("to_i"), 0);
1028
+ }
1029
+ else
1030
+ {
1031
+ rb_fireruby_raise(NULL,
1032
+ "Error converting input parameter to long integer.");
1033
+ }
1034
+
1035
+ full = TYPE(actual) == T_FIXNUM ? FIX2INT(actual) : NUM2INT(actual);
1036
+ store = (long)full;
1037
+ memcpy(field->sqldata, &store, field->sqllen);
1038
+ field->sqltype = SQL_LONG;
1039
+ }
1040
+
1041
+
1042
+ /**
1043
+ * This function populates a output parameter field with the data of a Ruby
1044
+ * value.
1045
+ *
1046
+ * @param value A reference to the Ruby value to be inserted into the field.
1047
+ * @param field A pointer to the XSQLVAR field that the value will go into.
1048
+ *
1049
+ */
1050
+ void populateShortField(VALUE value, XSQLVAR *field)
1051
+ {
1052
+ VALUE actual = Qnil;
1053
+ long long full = 0;
1054
+ short store = 0;
1055
+
1056
+ if(rb_obj_is_kind_of(value, rb_cInteger))
1057
+ {
1058
+ actual = value;
1059
+ }
1060
+ else if(TYPE(value) == T_FLOAT)
1061
+ {
1062
+ double number = NUM2DBL(value);
1063
+
1064
+ if(field->sqlscale != 0)
1065
+ {
1066
+ number = number * pow(10, abs(field->sqlscale));
1067
+ actual = INT2NUM((long)number);
1068
+ }
1069
+ }
1070
+ else if(TYPE(value) == T_STRING)
1071
+ {
1072
+ actual = rb_funcall(value, rb_intern("to_i"), 0);
1073
+ }
1074
+ else
1075
+ {
1076
+ rb_fireruby_raise(NULL,
1077
+ "Error converting input parameter to short integer.");
1078
+ }
1079
+
1080
+ full = TYPE(actual) == T_FIXNUM ? FIX2INT(actual) : NUM2INT(actual);
1081
+ store = (short)full;
1082
+ memcpy(field->sqldata, &store, field->sqllen);
1083
+ field->sqltype = SQL_SHORT;
1084
+ }
1085
+
1086
+
1087
+ /**
1088
+ * This function populates a output parameter field with the data of a Ruby
1089
+ * value.
1090
+ *
1091
+ * @param value A reference to the Ruby value to be inserted into the field.
1092
+ * @param field A pointer to the XSQLVAR field that the value will go into.
1093
+ *
1094
+ */
1095
+ void populateTextField(VALUE value, XSQLVAR *field)
1096
+ {
1097
+ VALUE actual;
1098
+ char *text = NULL;
1099
+ short length = 0;
1100
+
1101
+ if(TYPE(value) != T_STRING)
1102
+ {
1103
+ actual = value;
1104
+ }
1105
+ else
1106
+ {
1107
+ actual = rb_funcall(value, rb_intern("to_s"), 0);
1108
+ }
1109
+
1110
+ text = STR2CSTR(actual);
1111
+ length = strlen(text) > field->sqllen ? field->sqllen : strlen(text);
1112
+
1113
+ if((field->sqltype & ~1) == SQL_TEXT)
1114
+ {
1115
+ memcpy(field->sqldata, text, length);
1116
+ field->sqltype = SQL_TEXT;
1117
+ }
1118
+ else
1119
+ {
1120
+ memcpy(field->sqldata, &length, sizeof(short));
1121
+ memcpy(&field->sqldata[sizeof(short)], text, length);
1122
+ field->sqltype = SQL_VARYING;
1123
+ }
1124
+ field->sqllen = length;
1125
+ }
1126
+
1127
+
1128
+ /**
1129
+ * This method populates a date output field.
1130
+ *
1131
+ * @param value A reference to the value to be used to populate the field.
1132
+ * @param field A pointer to the output field to be populated.
1133
+ *
1134
+ */
1135
+ void populateTimeField(VALUE value, XSQLVAR *field)
1136
+ {
1137
+ struct tm datetime;
1138
+ VALUE arguments = rb_ary_new();
1139
+
1140
+ rb_ary_push(arguments, rb_str_new2("time"));
1141
+ value = rb_rescue(toDateTime, value, rescueConvert, arguments);
1142
+ if(TYPE(value) != T_ARRAY)
1143
+ {
1144
+ VALUE message = rb_funcall(value, rb_intern("message"), 0);
1145
+ rb_fireruby_raise(NULL, STR2CSTR(message));
1146
+ }
1147
+ datetime.tm_hour = FIX2INT(rb_ary_entry(value, 3));
1148
+ datetime.tm_min = FIX2INT(rb_ary_entry(value, 4));
1149
+ datetime.tm_sec = FIX2INT(rb_ary_entry(value, 5));
1150
+ isc_encode_sql_time(&datetime, (ISC_TIME *)field->sqldata);
1151
+ field->sqltype = SQL_TYPE_TIME;
1152
+ }
1153
+
1154
+
1155
+ /**
1156
+ * This method populates a date output field.
1157
+ *
1158
+ * @param value A reference to the value to be used to populate the field.
1159
+ * @param field A pointer to the output field to be populated.
1160
+ *
1161
+ */
1162
+ void populateTimestampField(VALUE value, XSQLVAR *field)
1163
+ {
1164
+ struct tm datetime;
1165
+ VALUE arguments = rb_ary_new();
1166
+
1167
+ rb_ary_push(arguments, rb_str_new2("timestamp"));
1168
+ value = rb_rescue(toDateTime, value, rescueConvert, arguments);
1169
+ if(TYPE(value) != T_ARRAY)
1170
+ {
1171
+ VALUE message = rb_funcall(value, rb_intern("message"), 0);
1172
+ rb_fireruby_raise(NULL, STR2CSTR(message));
1173
+ }
1174
+ datetime.tm_year = FIX2INT(rb_ary_entry(value, 0));
1175
+ datetime.tm_mon = FIX2INT(rb_ary_entry(value, 1));
1176
+ datetime.tm_mday = FIX2INT(rb_ary_entry(value, 2));
1177
+ datetime.tm_hour = FIX2INT(rb_ary_entry(value, 3));
1178
+ datetime.tm_min = FIX2INT(rb_ary_entry(value, 4));
1179
+ datetime.tm_sec = FIX2INT(rb_ary_entry(value, 5));
1180
+ isc_encode_timestamp(&datetime, (ISC_TIMESTAMP *)field->sqldata);
1181
+ field->sqltype = SQL_TIMESTAMP;
1182
+ }