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/ResultSet.c ADDED
@@ -0,0 +1,809 @@
1
+ /*------------------------------------------------------------------------------
2
+ * ResultSet.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 "ResultSet.h"
28
+ #include "Common.h"
29
+ #include "Statement.h"
30
+ #include "Connection.h"
31
+ #include "Transaction.h"
32
+ #include "DataArea.h"
33
+ #include "Row.h"
34
+ #include "TypeMap.h"
35
+ #include "ruby.h"
36
+
37
+ /* Function prototypes. */
38
+ static VALUE allocateResultSet(VALUE);
39
+ static VALUE initializeResultSet(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE);
40
+ static VALUE fetchResultSetEntry(VALUE);
41
+ static VALUE closeResultSet(VALUE);
42
+ static VALUE getResultSetCount(VALUE);
43
+ static VALUE getResultSetConnection(VALUE);
44
+ static VALUE getResultSetTransaction(VALUE);
45
+ static VALUE getResultSetSQL(VALUE);
46
+ static VALUE getResultSetDialect(VALUE);
47
+ static VALUE getResultSetColumnCount(VALUE);
48
+ static VALUE getResultSetColumnName(VALUE, VALUE);
49
+ static VALUE getResultSetColumnAlias(VALUE, VALUE);
50
+ static VALUE getResultSetColumnTable(VALUE, VALUE);
51
+ static VALUE getResultSetColumnType(VALUE, VALUE);
52
+ static VALUE eachResultSetRow(VALUE);
53
+ static VALUE isResultSetExhausted(VALUE);
54
+ static void resultSetMark(void *);
55
+ static void cleanupHandle(isc_stmt_handle *handle);
56
+ static void resolveResultsTransaction(ResultsHandle *results, char *resolveMethod);
57
+
58
+
59
+ /* Globals. */
60
+ VALUE cResultSet;
61
+
62
+ /**
63
+ * This method allocates a new ResultSet object.
64
+ *
65
+ * @param klass A reference to the ResultSet Class object.
66
+ *
67
+ * @return A reference to the newly allocated ResultSet object.
68
+ *
69
+ */
70
+ VALUE allocateResultSet(VALUE klass)
71
+ {
72
+ ResultsHandle *results = ALLOC(ResultsHandle);
73
+
74
+ if(results == NULL)
75
+ {
76
+ rb_raise(rb_eNoMemError,
77
+ "Memory allocation failure allocating a result set.");
78
+ }
79
+ results->handle = 0;
80
+ results->output = NULL;
81
+ results->exhausted = 0;
82
+ results->fetched = 0;
83
+ results->dialect = 0;
84
+ results->procedure_output_fetch_state = -1;
85
+ results->transaction = Qnil;
86
+
87
+ return(Data_Wrap_Struct(klass, resultSetMark, resultSetFree, results));
88
+ }
89
+
90
+
91
+ /**
92
+ * This function provides the initialize method for the ResultSet class.
93
+ *
94
+ * @param self A reference to the ResultSet object to be initialized.
95
+ * @param connection A reference to a Connection object that will be used
96
+ * in executing the statement.
97
+ * @param transaction A reference to a Transaction object that will be used
98
+ * in executing the statement.
99
+ * @param sql A reference to a String containing the SQL statement to
100
+ * be executed. Must be a query.
101
+ * @param dialect A reference to an integer containing the SQL dialect to
102
+ * be used in executing the query.
103
+ * @param parameters A reference to an array containing the parameters to be
104
+ * used in executing the query.
105
+ *
106
+ * @return A reference to the newly initialize ResultSet object.
107
+ *
108
+ */
109
+ VALUE initializeResultSet(VALUE self, VALUE connection, VALUE transaction,
110
+ VALUE sql, VALUE dialect, VALUE parameters)
111
+ {
112
+ short setting = 0;
113
+ int type = 0,
114
+ inputs = 0,
115
+ outputs = 0;
116
+ long affected = 0;
117
+ ResultsHandle *results = NULL;
118
+ VALUE value = Qnil;
119
+ ConnectionHandle *cHandle = NULL;
120
+ TransactionHandle *tHandle = NULL;
121
+ XSQLDA *params = NULL;
122
+
123
+ /* Validate the inputs. */
124
+ if(TYPE(connection) == T_DATA &&
125
+ RDATA(connection)->dfree == (RUBY_DATA_FUNC)connectionFree)
126
+ {
127
+ if(rb_funcall(connection, rb_intern("open?"), 0) == Qfalse)
128
+ {
129
+ rb_fireruby_raise(NULL, "Closed connection specified for result set.");
130
+ }
131
+ }
132
+ else
133
+ {
134
+ rb_fireruby_raise(NULL, "Invalid connection specified for result set.");
135
+ }
136
+
137
+ if(TYPE(transaction) == T_DATA &&
138
+ RDATA(transaction)->dfree == (RUBY_DATA_FUNC)transactionFree)
139
+ {
140
+ if(rb_funcall(transaction, rb_intern("active?"), 0) == Qfalse)
141
+ {
142
+ rb_fireruby_raise(NULL, "Inactive transaction specified for result set.");
143
+ }
144
+ }
145
+ else
146
+ {
147
+ rb_fireruby_raise(NULL, "Invalid transaction specified for result set.");
148
+ }
149
+
150
+ value = rb_funcall(dialect, rb_intern("to_i"), 0);
151
+ if(TYPE(value) == T_FIXNUM)
152
+ {
153
+ setting = FIX2INT(value);
154
+ if(setting < 1 || setting > 3)
155
+ {
156
+ rb_fireruby_raise(NULL,
157
+ "Invalid dialect value specified for result set. "\
158
+ "The dialect value must be between 1 and 3.");
159
+ }
160
+ }
161
+ else
162
+ {
163
+ rb_fireruby_raise(NULL,
164
+ "Invalid dialect value specified for result set. The "\
165
+ "dialect value must be between 1 and 3.");
166
+ }
167
+
168
+ /* Prepare the result set. */
169
+ Data_Get_Struct(connection, ConnectionHandle, cHandle);
170
+ Data_Get_Struct(transaction, TransactionHandle, tHandle);
171
+ Data_Get_Struct(self, ResultsHandle, results);
172
+ prepare(&cHandle->handle, &tHandle->handle, STR2CSTR(sql), &results->handle,
173
+ setting, &type, &inputs, &outputs);
174
+
175
+ if(type != isc_info_sql_stmt_select &&
176
+ type != isc_info_sql_stmt_select_for_upd &&
177
+ type != isc_info_sql_stmt_exec_procedure)
178
+ {
179
+ cleanupHandle(&results->handle);
180
+ rb_fireruby_raise(NULL,
181
+ "Non-query SQL statement specified for result set.");
182
+ }
183
+
184
+ rb_iv_set(self, "@connection", connection);
185
+ rb_iv_set(self, "@transaction", transaction);
186
+ rb_iv_set(self, "@sql", rb_funcall(sql, rb_intern("to_s"), 0));
187
+ rb_iv_set(self, "@dialect", value);
188
+ results->dialect = setting;
189
+
190
+ /* UNCOMMENT FOR DEBUGGING PURPOSES! */
191
+ /*strcpy(results->sql, STR2CSTR(sql));*/
192
+
193
+ /* Check if input parameters are needed. */
194
+ if(inputs > 0)
195
+ {
196
+ VALUE value = Qnil;
197
+ int size = 0;
198
+
199
+ if(parameters == Qnil)
200
+ {
201
+ cleanupHandle(&results->handle);
202
+ rb_fireruby_raise(NULL,
203
+ "Empty parameter list specified for result set.");
204
+ }
205
+
206
+ value = rb_funcall(parameters, rb_intern("size"), 0);
207
+ size = TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value);
208
+ if(size < inputs)
209
+ {
210
+ cleanupHandle(&results->handle);
211
+ rb_fireruby_raise(NULL,
212
+ "Insufficient parameters specified for result set.");
213
+ }
214
+
215
+ /* Allocate the XSQLDA and populate it. */
216
+ params = allocateInXSQLDA(inputs, &results->handle, setting);
217
+ prepareDataArea(params);
218
+ setParameters(params, parameters, self);
219
+ }
220
+
221
+ /* Allocate output storage. */
222
+ results->output = allocateOutXSQLDA(outputs, &results->handle, setting);
223
+ prepareDataArea(results->output);
224
+
225
+ /* Execute the statement and clean up. */
226
+ if(type == isc_info_sql_stmt_exec_procedure) {
227
+ /* Execute with output params */
228
+ execute_2(&tHandle->handle, &results->handle, setting, params, type,
229
+ &affected, results->output);
230
+ /* Initialize procedure output fetch */
231
+ results->procedure_output_fetch_state = 0;
232
+ /* Early resolve transaction */
233
+ resolveResultsTransaction (results, "commit");
234
+ } else {
235
+ execute(&tHandle->handle, &results->handle, setting, params, type,
236
+ &affected);
237
+ }
238
+ if(params != NULL)
239
+ {
240
+ releaseDataArea(params);
241
+ }
242
+
243
+ return(self);
244
+ }
245
+
246
+
247
+ /**
248
+ * This function provides the fetch method for the ResultSet class.
249
+ *
250
+ * @param self A reference to the ResultSet object to make the call on.
251
+ *
252
+ * @return Either a reference to a Row object or nil.
253
+ *
254
+ */
255
+ VALUE fetchResultSetEntry(VALUE self)
256
+ {
257
+ VALUE row = Qnil;
258
+ ResultsHandle *results = NULL;
259
+ ISC_STATUS status[20],
260
+ value;
261
+
262
+ Data_Get_Struct(self, ResultsHandle, results);
263
+ if(results->handle != 0)
264
+ {
265
+ value = results->procedure_output_fetch_state;
266
+ if(value < 0) {
267
+ value = isc_dsql_fetch(status, &results->handle, results->dialect,
268
+ results->output);
269
+ } else {
270
+ /* move procedure_output_fetch_state ahead - fetch only one row */
271
+ results->procedure_output_fetch_state = 100;
272
+ }
273
+ VALUE array = Qnil,
274
+ number = Qnil;
275
+ switch(value)
276
+ {
277
+ case 0 :
278
+ array = toArray(self);
279
+ number = INT2NUM(++(results->fetched));
280
+ row = rb_row_new(self, array, number);
281
+ break;
282
+ case 100 :
283
+ results->exhausted = 1;
284
+ resolveResultsTransaction(results, "commit");
285
+ break;
286
+ default :
287
+ rb_fireruby_raise(status, "Error fetching query row.");
288
+ }
289
+ }
290
+
291
+ return(row);
292
+ }
293
+
294
+
295
+ /**
296
+ * This function provides the close method for the ResultSet class, releasing
297
+ * resources associated with the ResultSet.
298
+ *
299
+ * @param self A reference to the ResultSet object to be closed.
300
+ *
301
+ * @return A reference to the closed ResultSet object.
302
+ *
303
+ */
304
+ VALUE closeResultSet(VALUE self)
305
+ {
306
+ ResultsHandle *results = NULL;
307
+
308
+ Data_Get_Struct(self, ResultsHandle, results);
309
+ if(results->handle != 0)
310
+ {
311
+ ISC_STATUS status[20];
312
+
313
+ if(isc_dsql_free_statement(status, &results->handle, DSQL_drop))
314
+ {
315
+ rb_fireruby_raise(status, "Error closing result set.");
316
+ }
317
+ results->handle = 0;
318
+ }
319
+
320
+ if(results->output != NULL)
321
+ {
322
+ releaseDataArea(results->output);
323
+ results->output = NULL;
324
+ }
325
+
326
+ resolveResultsTransaction(results, "commit");
327
+
328
+ return(self);
329
+ }
330
+
331
+
332
+ /**
333
+ * This function provides the count method for the ResultSet class.
334
+ *
335
+ * @param self A reference to the ResultSet object to fetch the current row
336
+ * count for.
337
+ *
338
+ * @return A reference to an integer containing a count of the number of rows
339
+ * fetched from the ResultSet so far.
340
+ *
341
+ */
342
+ VALUE getResultSetCount(VALUE self)
343
+ {
344
+ ResultsHandle *results = NULL;
345
+
346
+ Data_Get_Struct(self, ResultsHandle, results);
347
+
348
+ return(INT2NUM(results->fetched));
349
+ }
350
+
351
+
352
+ /**
353
+ * This method provides the accessor method for the connection ResultSet
354
+ * attribute.
355
+ *
356
+ * @param self A reference to the ResultSet object to fetch the attribute
357
+ * from.
358
+ *
359
+ * @return A reference to the requested attribute object.
360
+ *
361
+ */
362
+ VALUE getResultSetConnection(VALUE self)
363
+ {
364
+ return(rb_iv_get(self, "@connection"));
365
+ }
366
+
367
+
368
+ /**
369
+ * This method provides the accessor method for the transaction ResultSet
370
+ * attribute.
371
+ *
372
+ * @param self A reference to the ResultSet object to fetch the attribute
373
+ * from.
374
+ *
375
+ * @return A reference to the requested attribute object.
376
+ *
377
+ */
378
+ VALUE getResultSetTransaction(VALUE self)
379
+ {
380
+ return(rb_iv_get(self, "@transaction"));
381
+ }
382
+
383
+
384
+ /**
385
+ * This method provides the accessor method for the SQL ResultSet attribute.
386
+ *
387
+ * @param self A reference to the ResultSet object to fetch the attribute
388
+ * from.
389
+ *
390
+ * @return A reference to the requested attribute object.
391
+ *
392
+ */
393
+ VALUE getResultSetSQL(VALUE self)
394
+ {
395
+ return(rb_iv_get(self, "@sql"));
396
+ }
397
+
398
+
399
+ /**
400
+ * This method provides the accessor method for the dialect ResultSet attribute.
401
+ *
402
+ * @param self A reference to the ResultSet object to fetch the attribute
403
+ * from.
404
+ *
405
+ * @return A reference to the requested attribute object.
406
+ *
407
+ */
408
+ VALUE getResultSetDialect(VALUE self)
409
+ {
410
+ return(rb_iv_get(self, "@dialect"));
411
+ }
412
+
413
+
414
+ /**
415
+ * This function provides the column_count attribute accessor for the ResultSet
416
+ * class.
417
+ *
418
+ * @param self A reference to the ResultSet object to fetch the attribute for.
419
+ *
420
+ * @return A reference to an integer containing the column count.
421
+ *
422
+ */
423
+ VALUE getResultSetColumnCount(VALUE self)
424
+ {
425
+ VALUE count = Qnil;
426
+ ResultsHandle *results = NULL;
427
+
428
+ Data_Get_Struct(self, ResultsHandle, results);
429
+ if(results != NULL)
430
+ {
431
+ count = INT2NUM(results->output->sqld);
432
+ }
433
+
434
+ return(count);
435
+ }
436
+
437
+
438
+ /**
439
+ * This function provides the column_name method for the ResultSet class.
440
+ *
441
+ * @param self A reference to the ResultSet object to retrieve the column
442
+ * name from.
443
+ * @param column An offset to the column to retrieve the name for.
444
+ *
445
+ * @return A String containing the column name or nil if an invalid column
446
+ * was specified.
447
+ *
448
+ */
449
+ static VALUE getResultSetColumnName(VALUE self, VALUE column)
450
+ {
451
+ VALUE name = Qnil;
452
+ ResultsHandle *results = NULL;
453
+
454
+ Data_Get_Struct(self, ResultsHandle, results);
455
+ if(results != NULL)
456
+ {
457
+ int offset = 0;
458
+
459
+ offset = (TYPE(column) == T_FIXNUM ? FIX2INT(column) : NUM2INT(column));
460
+ if(offset >= 0 && offset < results->output->sqld)
461
+ {
462
+ XSQLVAR *var = results->output->sqlvar;
463
+ int index;
464
+
465
+ for(index = 0; index < offset; index++, var++);
466
+ name = rb_str_new(var->sqlname, var->sqlname_length);
467
+ }
468
+ }
469
+
470
+ return(name);
471
+ }
472
+
473
+
474
+ /**
475
+ * This function provides the column_alias method for the ResultSet class.
476
+ *
477
+ * @param self A reference to the ResultSet object to retrieve the column
478
+ * alias from.
479
+ * @param column An offset to the column to retrieve the alias for.
480
+ *
481
+ * @return A String containing the column alias or nil if an invalid column
482
+ * was specified.
483
+ *
484
+ */
485
+ static VALUE getResultSetColumnAlias(VALUE self, VALUE column)
486
+ {
487
+ VALUE alias = Qnil;
488
+ ResultsHandle *results = NULL;
489
+
490
+ Data_Get_Struct(self, ResultsHandle, results);
491
+ if(results != NULL)
492
+ {
493
+ int offset = 0;
494
+
495
+ offset = (TYPE(column) == T_FIXNUM ? FIX2INT(column) : NUM2INT(column));
496
+ if(offset >= 0 && offset < results->output->sqld)
497
+ {
498
+ XSQLVAR *var = results->output->sqlvar;
499
+ int index;
500
+
501
+ for(index = 0; index < offset; index++, var++);
502
+ alias = rb_str_new(var->aliasname, var->aliasname_length);
503
+ }
504
+ }
505
+
506
+ return(alias);
507
+ }
508
+
509
+
510
+ /**
511
+ * This function provides the column_table method for the ResultSet class.
512
+ *
513
+ * @param self A reference to the ResultSet object to retrieve the column
514
+ * table name from.
515
+ * @param column An offset to the column to retrieve the table name for.
516
+ *
517
+ * @return A String containing the column table name or nil if an invalid
518
+ * column was specified.
519
+ *
520
+ */
521
+ static VALUE getResultSetColumnTable(VALUE self, VALUE column)
522
+ {
523
+ VALUE name = Qnil;
524
+ ResultsHandle *results = NULL;
525
+
526
+ Data_Get_Struct(self, ResultsHandle, results);
527
+ if(results != NULL)
528
+ {
529
+ int offset = 0;
530
+
531
+ offset = (TYPE(column) == T_FIXNUM ? FIX2INT(column) : NUM2INT(column));
532
+ if(offset >= 0 && offset < results->output->sqld)
533
+ {
534
+ XSQLVAR *var = results->output->sqlvar;
535
+ int index;
536
+
537
+ for(index = 0; index < offset; index++, var++);
538
+ name = rb_str_new(var->relname, var->relname_length);
539
+ }
540
+ }
541
+
542
+ return(name);
543
+ }
544
+
545
+
546
+ /**
547
+ * This function attempts to extract basic type information for a column within
548
+ * a ResultSet object.
549
+ *
550
+ * @param self A reference to the ResultSet to execute on.
551
+ * @param column A integer offset for the column to extract the type of.
552
+ *
553
+ * @return A Symbol representing the basic data type.
554
+ *
555
+ */
556
+ static VALUE getResultSetColumnType(VALUE self, VALUE column)
557
+ {
558
+ VALUE type = toSymbol("UNKNOWN");
559
+
560
+ if(TYPE(column) == T_FIXNUM)
561
+ {
562
+ ResultsHandle *handle = NULL;
563
+
564
+ Data_Get_Struct(self, ResultsHandle, handle);
565
+ if(handle != NULL)
566
+ {
567
+ int index = FIX2INT(column);
568
+
569
+ /* Fix negative index values. */
570
+ if(index < 0)
571
+ {
572
+ index = handle->output->sqln + index;
573
+ }
574
+
575
+ if(index >= 0 && index < handle->output->sqln)
576
+ {
577
+ type = getColumnType(&handle->output->sqlvar[index]);
578
+ }
579
+ }
580
+ }
581
+
582
+
583
+ return(type);
584
+ }
585
+
586
+
587
+ /**
588
+ * This function provides the each method for the ResultSet class.
589
+ *
590
+ * @param self A reference to the ResultSet object to execute the method for.
591
+ *
592
+ * @return A reference to the last value returned by the associated block or
593
+ * nil.
594
+ *
595
+ */
596
+ VALUE eachResultSetRow(VALUE self)
597
+ {
598
+ VALUE result = Qnil;
599
+
600
+ /* Check if a block was provided. */
601
+ if(rb_block_given_p())
602
+ {
603
+ VALUE row;
604
+
605
+ while((row = fetchResultSetEntry(self)) != Qnil)
606
+ {
607
+ result = rb_yield(row);
608
+ }
609
+ }
610
+
611
+ return(result);
612
+ }
613
+
614
+
615
+ /**
616
+ * This function provides the exhausted? method of the ResultSet class.
617
+ *
618
+ * @param self A reference to the ResultSet object to make the call for.
619
+ *
620
+ * @return True if all rows have been fetched from the result set, false
621
+ * if they haven't.
622
+ *
623
+ */
624
+ VALUE isResultSetExhausted(VALUE self)
625
+ {
626
+ VALUE exhausted = Qfalse;
627
+ ResultsHandle *results = NULL;
628
+
629
+ Data_Get_Struct(self, ResultsHandle, results);
630
+ if(results->exhausted)
631
+ {
632
+ exhausted = Qtrue;
633
+ }
634
+
635
+ return(exhausted);
636
+ }
637
+
638
+
639
+ /**
640
+ * This method integrates with the Ruby garbage collector to insure that, if
641
+ * a ResultSet contains a reference to a Transaction object, it doesn't get
642
+ * collected by the garbage collector unnecessarily.
643
+ *
644
+ * @param handle A void pointer to the structure associated with the object
645
+ * being scanned by the garbage collector.
646
+ *
647
+ */
648
+ void resultSetMark(void *handle)
649
+ {
650
+ ResultsHandle *results = (ResultsHandle *)handle;
651
+
652
+ if(results != NULL)
653
+ {
654
+ if(results->transaction != Qnil)
655
+ {
656
+ rb_gc_mark(results->transaction);
657
+ }
658
+ }
659
+ }
660
+
661
+
662
+ /**
663
+ * This function provides a programmatic means of creating a ResultSet object.
664
+ *
665
+ * @param connection A reference to a Connection object that will be used
666
+ * in executing the statement.
667
+ * @param transaction A reference to a Transaction object that will be used
668
+ * in executing the statement.
669
+ * @param sql A reference to a String containing the SQL statement to
670
+ * be executed. Must be a query.
671
+ * @param dialect A reference to an integer containing the SQL dialect to
672
+ * be used in executing the query.
673
+ * @param parameters A reference to an array containing the parameters to be
674
+ * used in executing the query.
675
+ *
676
+ * @return A reference to the newly created ResultSet object.
677
+ *
678
+ */
679
+ VALUE rb_result_set_new(VALUE connection, VALUE transaction, VALUE sql,
680
+ VALUE dialect, VALUE parameters)
681
+ {
682
+ VALUE instance = allocateResultSet(cResultSet);
683
+
684
+ initializeResultSet(instance, connection, transaction, sql, dialect,
685
+ parameters);
686
+
687
+ return(instance);
688
+ }
689
+
690
+
691
+ /**
692
+ * This function assigns an anonymous transaction to a ResultSet, giving the
693
+ * object responsibility for closing it.
694
+ *
695
+ * @param set A reference to the ResultSet object that will be taking
696
+ * ownership of the Transaction.
697
+ * @param transaction A reference to the Transaction object that the ResultSet
698
+ * is assuming responsibility for.
699
+ *
700
+ */
701
+ void rb_assign_transaction(VALUE set, VALUE transaction)
702
+ {
703
+ ResultsHandle *results = NULL;
704
+
705
+ Data_Get_Struct(set, ResultsHandle, results);
706
+ results->transaction = transaction;
707
+ }
708
+
709
+
710
+ /**
711
+ * This function integrates with the Ruby garbage collector to free all of the
712
+ * resources for a ResultSet object that is being collected.
713
+ *
714
+ * @param handle A pointer to the ResultsHandle structure associated with the
715
+ * object being collected.
716
+ *
717
+ */
718
+ void resultSetFree(void *handle)
719
+ {
720
+ if(handle != NULL)
721
+ {
722
+ ResultsHandle *results = (ResultsHandle *)handle;
723
+
724
+ if(results->handle != 0)
725
+ {
726
+ ISC_STATUS status[20];
727
+
728
+ /* UNCOMMENT FOR DEBUG PURPOSES! */
729
+ /*fprintf(stderr, "Releasing statement handle for...\n%s\n", results->sql);*/
730
+ isc_dsql_free_statement(status, &results->handle, DSQL_drop);
731
+ }
732
+
733
+ if(results->output != NULL)
734
+ {
735
+ releaseDataArea(results->output);
736
+ }
737
+
738
+ resolveResultsTransaction(results, "rollback");
739
+ free(results);
740
+ }
741
+ }
742
+
743
+
744
+ /**
745
+ * This is a simple function to aid in the clean up of statement handles.
746
+ *
747
+ * @param handle A pointer to the statement handle to be cleaned up.
748
+ *
749
+ */
750
+ void cleanupHandle(isc_stmt_handle *handle)
751
+ {
752
+ if(*handle != 0)
753
+ {
754
+ ISC_STATUS status[20];
755
+
756
+ /* UNCOMMENT FOR DEBUG PURPOSES! */
757
+ /*fprintf(stderr, "Cleaning up a statement handle.\n");*/
758
+ isc_dsql_free_statement(status, handle, DSQL_drop);
759
+ }
760
+ }
761
+
762
+
763
+ /**
764
+ * Simple helper function to resolve ResultsHandle associated transaction
765
+ *
766
+ * @param results - the ResultsHandle handle
767
+ *
768
+ * @param resolveMethod - transaction resolving method - eg. commit, rollback
769
+ *
770
+ */
771
+ void resolveResultsTransaction(ResultsHandle *results, char *resolveMethod)
772
+ {
773
+ if(results->transaction != Qnil)
774
+ {
775
+ rb_funcall(results->transaction, rb_intern(resolveMethod), 0);
776
+ results->transaction = Qnil;
777
+ }
778
+ }
779
+
780
+
781
+ /**
782
+ * This function initializes the ResultSet class within the Ruby environment.
783
+ * The class is established under the module specified to the function.
784
+ *
785
+ * @param module A reference to the module to create the class within.
786
+ *
787
+ */
788
+ void Init_ResultSet(VALUE module)
789
+ {
790
+ cResultSet = rb_define_class_under(module, "ResultSet", rb_cObject);
791
+ rb_define_alloc_func(cResultSet, allocateResultSet);
792
+ rb_include_module(cResultSet, rb_mEnumerable);
793
+ rb_define_method(cResultSet, "initialize", initializeResultSet, 5);
794
+ rb_define_method(cResultSet, "initialize_copy", forbidObjectCopy, 1);
795
+ rb_define_method(cResultSet, "row_count", getResultSetCount, 0);
796
+ rb_define_method(cResultSet, "fetch", fetchResultSetEntry, 0);
797
+ rb_define_method(cResultSet, "close", closeResultSet, 0);
798
+ rb_define_method(cResultSet, "connection", getResultSetConnection, 0);
799
+ rb_define_method(cResultSet, "transaction", getResultSetTransaction, 0);
800
+ rb_define_method(cResultSet, "sql", getResultSetSQL, 0);
801
+ rb_define_method(cResultSet, "dialect", getResultSetDialect, 0);
802
+ rb_define_method(cResultSet, "each", eachResultSetRow, 0);
803
+ rb_define_method(cResultSet, "column_name", getResultSetColumnName, 1);
804
+ rb_define_method(cResultSet, "column_alias", getResultSetColumnAlias, 1);
805
+ rb_define_method(cResultSet, "column_table", getResultSetColumnTable, 1);
806
+ rb_define_method(cResultSet, "column_count", getResultSetColumnCount, 0);
807
+ rb_define_method(cResultSet, "exhausted?", isResultSetExhausted, 0);
808
+ rb_define_method(cResultSet, "get_base_type", getResultSetColumnType, 1);
809
+ }