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