rubyfb 0.5.2-x86-linux
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 +6 -0
- data/LICENSE +411 -0
- data/Manifest +73 -0
- data/README +460 -0
- data/Rakefile +20 -0
- data/examples/example01.rb +65 -0
- data/ext/AddUser.c +464 -0
- data/ext/AddUser.h +37 -0
- data/ext/Backup.c +783 -0
- data/ext/Backup.h +37 -0
- data/ext/Blob.c +421 -0
- data/ext/Blob.h +65 -0
- data/ext/Common.c +54 -0
- data/ext/Common.h +37 -0
- data/ext/Connection.c +863 -0
- data/ext/Connection.h +50 -0
- data/ext/DataArea.c +274 -0
- data/ext/DataArea.h +38 -0
- data/ext/Database.c +449 -0
- data/ext/Database.h +48 -0
- data/ext/FireRuby.c +240 -0
- data/ext/FireRuby.h +50 -0
- data/ext/FireRubyException.c +268 -0
- data/ext/FireRubyException.h +51 -0
- data/ext/Generator.c +689 -0
- data/ext/Generator.h +53 -0
- data/ext/RemoveUser.c +212 -0
- data/ext/RemoveUser.h +37 -0
- data/ext/Restore.c +855 -0
- data/ext/Restore.h +37 -0
- data/ext/ResultSet.c +809 -0
- data/ext/ResultSet.h +60 -0
- data/ext/Row.c +965 -0
- data/ext/Row.h +55 -0
- data/ext/ServiceManager.c +316 -0
- data/ext/ServiceManager.h +48 -0
- data/ext/Services.c +124 -0
- data/ext/Services.h +42 -0
- data/ext/Statement.c +785 -0
- data/ext/Statement.h +62 -0
- data/ext/Transaction.c +684 -0
- data/ext/Transaction.h +50 -0
- data/ext/TypeMap.c +1182 -0
- data/ext/TypeMap.h +51 -0
- data/ext/extconf.rb +28 -0
- data/ext/mkmf.bat +1 -0
- data/lib/SQLType.rb +224 -0
- data/lib/active_record/connection_adapters/rubyfb_adapter.rb +805 -0
- data/lib/mkdoc +1 -0
- data/lib/rubyfb.rb +2 -0
- data/lib/rubyfb_lib.so +0 -0
- data/lib/src.rb +1800 -0
- data/rubyfb.gemspec +31 -0
- data/test/AddRemoveUserTest.rb +56 -0
- data/test/BackupRestoreTest.rb +99 -0
- data/test/BlobTest.rb +57 -0
- data/test/CharacterSetTest.rb +63 -0
- data/test/ConnectionTest.rb +111 -0
- data/test/DDLTest.rb +54 -0
- data/test/DatabaseTest.rb +83 -0
- data/test/GeneratorTest.rb +50 -0
- data/test/KeyTest.rb +140 -0
- data/test/ResultSetTest.rb +162 -0
- data/test/RoleTest.rb +73 -0
- data/test/RowCountTest.rb +65 -0
- data/test/RowTest.rb +203 -0
- data/test/SQLTest.rb +182 -0
- data/test/SQLTypeTest.rb +101 -0
- data/test/ServiceManagerTest.rb +29 -0
- data/test/StatementTest.rb +135 -0
- data/test/TestSetup.rb +11 -0
- data/test/TransactionTest.rb +112 -0
- data/test/TypeTest.rb +92 -0
- data/test/UnitTest.rb +65 -0
- metadata +149 -0
data/ext/Transaction.c
ADDED
@@ -0,0 +1,684 @@
|
|
1
|
+
/*------------------------------------------------------------------------------
|
2
|
+
* Transaction.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 "Transaction.h"
|
28
|
+
#include "Common.h"
|
29
|
+
#include "Connection.h"
|
30
|
+
#include "ResultSet.h"
|
31
|
+
#include "Statement.h"
|
32
|
+
|
33
|
+
/* Function prototypes. */
|
34
|
+
static VALUE allocateTransaction(VALUE);
|
35
|
+
static VALUE commitTransaction(VALUE);
|
36
|
+
static VALUE rollbackTransaction(VALUE);
|
37
|
+
static VALUE getTransactionConnections(VALUE);
|
38
|
+
static VALUE isTransactionFor(VALUE, VALUE);
|
39
|
+
static VALUE executeOnTransaction(VALUE, VALUE);
|
40
|
+
static VALUE createTransaction(VALUE, VALUE, VALUE);
|
41
|
+
void startTransaction(TransactionHandle *, VALUE, long, char *);
|
42
|
+
void transactionFree(void *);
|
43
|
+
|
44
|
+
/* Globals. */
|
45
|
+
VALUE cTransaction;
|
46
|
+
|
47
|
+
/* Type definitions. */
|
48
|
+
typedef struct
|
49
|
+
{
|
50
|
+
isc_db_handle *database;
|
51
|
+
long length;
|
52
|
+
char *tpb;
|
53
|
+
} ISC_TEB;
|
54
|
+
|
55
|
+
static char DEFAULT_TEB[] = {isc_tpb_version3,
|
56
|
+
isc_tpb_write,
|
57
|
+
isc_tpb_wait,
|
58
|
+
isc_tpb_rec_version,
|
59
|
+
isc_tpb_read_committed};
|
60
|
+
static int DEFAULT_TEB_SIZE = 5;
|
61
|
+
|
62
|
+
|
63
|
+
/**
|
64
|
+
* This function provides for the allocation of a new object of the Transaction
|
65
|
+
* class.
|
66
|
+
*
|
67
|
+
* @param klass A reference to the Transaction Class object
|
68
|
+
*
|
69
|
+
* @return A reference to the newly allocated object.
|
70
|
+
*
|
71
|
+
*/
|
72
|
+
static VALUE allocateTransaction(VALUE klass)
|
73
|
+
{
|
74
|
+
VALUE transaction = Qnil;
|
75
|
+
TransactionHandle *handle = ALLOC(TransactionHandle);
|
76
|
+
|
77
|
+
if(handle != NULL)
|
78
|
+
{
|
79
|
+
handle->handle = 0;
|
80
|
+
transaction = Data_Wrap_Struct(klass, NULL, transactionFree, handle);
|
81
|
+
}
|
82
|
+
else
|
83
|
+
{
|
84
|
+
rb_raise(rb_eNoMemError,
|
85
|
+
"Memory allocation failure allocating a transaction.");
|
86
|
+
}
|
87
|
+
|
88
|
+
return(transaction);
|
89
|
+
}
|
90
|
+
|
91
|
+
|
92
|
+
/**
|
93
|
+
* This function provides the initialize method for the Transaction class.
|
94
|
+
*
|
95
|
+
* @param self A reference to the new Transaction class instance.
|
96
|
+
* @param connections Either a reference to a single Connection object or to
|
97
|
+
* an array of Connection objects that the transaction
|
98
|
+
* will apply to.
|
99
|
+
*
|
100
|
+
*/
|
101
|
+
static VALUE transactionInitialize(VALUE self, VALUE connections)
|
102
|
+
{
|
103
|
+
TransactionHandle *transaction = NULL;
|
104
|
+
VALUE array = Qnil;
|
105
|
+
|
106
|
+
/* Determine if an array has been passed as a parameter. */
|
107
|
+
if(TYPE(connections) == T_ARRAY)
|
108
|
+
{
|
109
|
+
array = connections;
|
110
|
+
}
|
111
|
+
else if(TYPE(connections) == T_DATA &&
|
112
|
+
RDATA(connections)->dfree == (RUBY_DATA_FUNC)connectionFree)
|
113
|
+
{
|
114
|
+
array = rb_ary_new();
|
115
|
+
rb_ary_push(array, connections);
|
116
|
+
}
|
117
|
+
else
|
118
|
+
{
|
119
|
+
rb_fireruby_raise(NULL,
|
120
|
+
"Invalid connection parameter(s) for transaction.");
|
121
|
+
}
|
122
|
+
|
123
|
+
/* Store the database details. */
|
124
|
+
rb_iv_set(self, "@connections", array);
|
125
|
+
|
126
|
+
/* Fetch the data structure and start the transaction. */
|
127
|
+
Data_Get_Struct(self, TransactionHandle, transaction);
|
128
|
+
startTransaction(transaction, array, 0, NULL);
|
129
|
+
rb_tx_started(self, array);
|
130
|
+
|
131
|
+
return(self);
|
132
|
+
}
|
133
|
+
|
134
|
+
|
135
|
+
/**
|
136
|
+
* This function provides the commit method for the Transaction class.
|
137
|
+
*
|
138
|
+
* @param self A reference to the Transaction object being committed.
|
139
|
+
*
|
140
|
+
* @return A reference to self if successful, nil otherwise.
|
141
|
+
*
|
142
|
+
*/
|
143
|
+
static VALUE commitTransaction(VALUE self)
|
144
|
+
{
|
145
|
+
TransactionHandle *transaction = NULL;
|
146
|
+
|
147
|
+
Data_Get_Struct(self, TransactionHandle, transaction);
|
148
|
+
|
149
|
+
/* Commit the transaction. */
|
150
|
+
if(transaction->handle != 0)
|
151
|
+
{
|
152
|
+
ISC_STATUS status[20];
|
153
|
+
|
154
|
+
if(isc_commit_transaction(status, &transaction->handle) != 0)
|
155
|
+
{
|
156
|
+
/* Generate an error. */
|
157
|
+
rb_fireruby_raise(status, "Error committing transaction.");
|
158
|
+
}
|
159
|
+
transaction->handle = 0;
|
160
|
+
}
|
161
|
+
else
|
162
|
+
{
|
163
|
+
/* Generate an error. */
|
164
|
+
rb_fireruby_raise(NULL, "1. Transaction is not active.");
|
165
|
+
}
|
166
|
+
|
167
|
+
/* Notify each connection of the transactions end. */
|
168
|
+
rb_tx_released(rb_iv_get(self, "@connections"), self);
|
169
|
+
|
170
|
+
/* Clear the connections list. */
|
171
|
+
rb_iv_set(self, "@connections", rb_ary_new());
|
172
|
+
|
173
|
+
return(self);
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
/**
|
178
|
+
* This function provides the rollback method for the Transaction class.
|
179
|
+
*
|
180
|
+
* @param self A reference to the Transaction object being rolled back.
|
181
|
+
*
|
182
|
+
* @return A reference to self if successful, nil otherwise.
|
183
|
+
*
|
184
|
+
*/
|
185
|
+
static VALUE rollbackTransaction(VALUE self)
|
186
|
+
{
|
187
|
+
TransactionHandle *transaction = NULL;
|
188
|
+
|
189
|
+
Data_Get_Struct(self, TransactionHandle, transaction);
|
190
|
+
|
191
|
+
/* Roll back the transaction. */
|
192
|
+
if(transaction->handle != 0)
|
193
|
+
{
|
194
|
+
ISC_STATUS status[20];
|
195
|
+
|
196
|
+
if(isc_rollback_transaction(status, &transaction->handle) != 0)
|
197
|
+
{
|
198
|
+
/* Generate an error. */
|
199
|
+
rb_fireruby_raise(status, "Error rolling back transaction.");
|
200
|
+
}
|
201
|
+
transaction->handle = 0;
|
202
|
+
}
|
203
|
+
else
|
204
|
+
{
|
205
|
+
/* Generate an error. */
|
206
|
+
rb_fireruby_raise(NULL, "2. Transaction is not active.");
|
207
|
+
}
|
208
|
+
|
209
|
+
/* Notify each connection of the transactions end. */
|
210
|
+
rb_tx_released(rb_iv_get(self, "@connections"), self);
|
211
|
+
|
212
|
+
/* Clear the connections list. */
|
213
|
+
rb_iv_set(self, "@connections", rb_ary_new());
|
214
|
+
|
215
|
+
return(self);
|
216
|
+
}
|
217
|
+
|
218
|
+
|
219
|
+
/**
|
220
|
+
* This function provides the active? method for the Transaction class.
|
221
|
+
*
|
222
|
+
* @param self A reference to the Transcation object to perform the check for.
|
223
|
+
*
|
224
|
+
* @return Qtrue if the transaction is active, Qfalse otherwise.
|
225
|
+
*
|
226
|
+
*/
|
227
|
+
static VALUE transactionIsActive(VALUE self)
|
228
|
+
{
|
229
|
+
VALUE result = Qfalse;
|
230
|
+
TransactionHandle *transaction = NULL;
|
231
|
+
|
232
|
+
Data_Get_Struct(self, TransactionHandle, transaction);
|
233
|
+
if(transaction->handle != 0)
|
234
|
+
{
|
235
|
+
result = Qtrue;
|
236
|
+
}
|
237
|
+
|
238
|
+
return(result);
|
239
|
+
}
|
240
|
+
|
241
|
+
|
242
|
+
/**
|
243
|
+
* This function provides the accessor for the connections Transaction class
|
244
|
+
* attribute.
|
245
|
+
*
|
246
|
+
* @param self A reference to the Transaction object to fetch the connections
|
247
|
+
* for.
|
248
|
+
*
|
249
|
+
* @return An Array containing the connections associated with the Transaction
|
250
|
+
* object.
|
251
|
+
*
|
252
|
+
*/
|
253
|
+
static VALUE getTransactionConnections(VALUE self)
|
254
|
+
{
|
255
|
+
return(rb_iv_get(self, "@connections"));
|
256
|
+
}
|
257
|
+
|
258
|
+
|
259
|
+
/**
|
260
|
+
* This function provides the for? method of the Transaction class.
|
261
|
+
*
|
262
|
+
* @param self A reference to the Transaction object to make the check
|
263
|
+
* on
|
264
|
+
* @param connection A reference to the Database object to make the check for.
|
265
|
+
*
|
266
|
+
* @return Qtrue if the specified database is covered by the Transaction,
|
267
|
+
* Qfalse otherwise.
|
268
|
+
*
|
269
|
+
*/
|
270
|
+
static VALUE isTransactionFor(VALUE self, VALUE connection)
|
271
|
+
{
|
272
|
+
VALUE result = Qfalse,
|
273
|
+
array = rb_iv_get(self, "@connections"),
|
274
|
+
value = rb_funcall(array, rb_intern("size"), 0);
|
275
|
+
int size = 0,
|
276
|
+
index;
|
277
|
+
ConnectionHandle *instance = NULL;
|
278
|
+
TransactionHandle *transaction = NULL;
|
279
|
+
|
280
|
+
size = (TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value));
|
281
|
+
Data_Get_Struct(self, TransactionHandle, transaction);
|
282
|
+
Data_Get_Struct(connection, ConnectionHandle, instance);
|
283
|
+
|
284
|
+
for(index = 0; index < size && result == Qfalse; index++)
|
285
|
+
{
|
286
|
+
ConnectionHandle *handle;
|
287
|
+
|
288
|
+
value = rb_ary_entry(array, index);
|
289
|
+
Data_Get_Struct(value, ConnectionHandle, handle);
|
290
|
+
result = (handle == instance ? Qtrue : Qfalse);
|
291
|
+
}
|
292
|
+
|
293
|
+
return(result);
|
294
|
+
}
|
295
|
+
|
296
|
+
|
297
|
+
/**
|
298
|
+
* This function provides the execute method for the Transaction class. This
|
299
|
+
* method only works when a transaction applies to a single connection.
|
300
|
+
*
|
301
|
+
* @param self A reference to the Transaction object that the execute has
|
302
|
+
* been called for.
|
303
|
+
* @param sql A reference to the SQL statement to be executed.
|
304
|
+
*
|
305
|
+
* @return A reference to a result set of the SQL statement represents a
|
306
|
+
* query, nil otherwise.
|
307
|
+
*
|
308
|
+
*/
|
309
|
+
static VALUE executeOnTransaction(VALUE self, VALUE sql)
|
310
|
+
{
|
311
|
+
VALUE results = Qnil,
|
312
|
+
list = rb_iv_get(self, "@connections"),
|
313
|
+
value = 0,
|
314
|
+
connection = Qnil,
|
315
|
+
statement = Qnil;
|
316
|
+
TransactionHandle *transaction = NULL;
|
317
|
+
int size = 0;
|
318
|
+
|
319
|
+
/* Check that the transaction is active. */
|
320
|
+
Data_Get_Struct(self, TransactionHandle, transaction);
|
321
|
+
if(transaction->handle == 0)
|
322
|
+
{
|
323
|
+
rb_fireruby_raise(NULL, "Executed called on inactive transaction.");
|
324
|
+
}
|
325
|
+
|
326
|
+
/* Check that we have only one connection for the transaction. */
|
327
|
+
value = rb_funcall(list, rb_intern("size"), 0);
|
328
|
+
size = (TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value));
|
329
|
+
if(size > 1)
|
330
|
+
{
|
331
|
+
rb_fireruby_raise(NULL,
|
332
|
+
"Execute called on a transaction that spans multiple "\
|
333
|
+
"connections. Unable to determine which connection to "\
|
334
|
+
"execute the SQL statement through.");
|
335
|
+
}
|
336
|
+
|
337
|
+
connection = rb_ary_entry(list, 0);
|
338
|
+
statement = rb_statement_new(connection, self, sql, INT2FIX(3));
|
339
|
+
results = rb_execute_statement(statement);
|
340
|
+
|
341
|
+
if(results != Qnil && rb_obj_is_kind_of(results, rb_cInteger) == Qfalse)
|
342
|
+
{
|
343
|
+
if(rb_block_given_p())
|
344
|
+
{
|
345
|
+
VALUE row = rb_funcall(results, rb_intern("fetch"), 0),
|
346
|
+
last = Qnil;
|
347
|
+
|
348
|
+
while(row != Qnil)
|
349
|
+
{
|
350
|
+
last = rb_yield(row);
|
351
|
+
row = rb_funcall(results, rb_intern("fetch"), 0);
|
352
|
+
}
|
353
|
+
rb_funcall(results, rb_intern("close"), 0);
|
354
|
+
results = last;
|
355
|
+
}
|
356
|
+
}
|
357
|
+
rb_statement_close(statement);
|
358
|
+
|
359
|
+
return(results);
|
360
|
+
}
|
361
|
+
|
362
|
+
|
363
|
+
/**
|
364
|
+
* This function creates a new Transaction object for a connection. This method
|
365
|
+
* provides the create class method for the Transaction class and allows for the
|
366
|
+
* specification of transaction parameters.
|
367
|
+
*
|
368
|
+
* @param unused Like it says, not used.
|
369
|
+
* @param connections A reference to the array of Connection objects that the
|
370
|
+
* transaction will be associated with.
|
371
|
+
* @param parameters A reference to an array containing the parameters to be
|
372
|
+
* used in creating the transaction.
|
373
|
+
*
|
374
|
+
* @return A reference to the newly created Transaction object.
|
375
|
+
*
|
376
|
+
*/
|
377
|
+
static VALUE createTransaction(VALUE unused, VALUE connections, VALUE parameters)
|
378
|
+
{
|
379
|
+
VALUE instance = Qnil,
|
380
|
+
list = Qnil;
|
381
|
+
TransactionHandle *transaction = ALLOC(TransactionHandle);
|
382
|
+
|
383
|
+
if(transaction == NULL)
|
384
|
+
{
|
385
|
+
rb_raise(rb_eNoMemError,
|
386
|
+
"Memory allocation failure allocating a transaction.");
|
387
|
+
}
|
388
|
+
transaction->handle = 0;
|
389
|
+
|
390
|
+
if(TYPE(connections) != T_ARRAY)
|
391
|
+
{
|
392
|
+
list = rb_ary_new();
|
393
|
+
rb_ary_push(list, connections);
|
394
|
+
}
|
395
|
+
else
|
396
|
+
{
|
397
|
+
list = connections;
|
398
|
+
}
|
399
|
+
|
400
|
+
if(TYPE(parameters) != T_ARRAY)
|
401
|
+
{
|
402
|
+
rb_fireruby_raise(NULL,
|
403
|
+
"Invalid transaction parameter set specified.");
|
404
|
+
}
|
405
|
+
|
406
|
+
if(transaction != NULL)
|
407
|
+
{
|
408
|
+
VALUE value = rb_funcall(parameters, rb_intern("size"), 0);
|
409
|
+
long size = 0,
|
410
|
+
index;
|
411
|
+
char *buffer = NULL;
|
412
|
+
|
413
|
+
size = TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value);
|
414
|
+
if((buffer = ALLOC_N(char, size)) != NULL)
|
415
|
+
{
|
416
|
+
for(index = 0; index < size; index++)
|
417
|
+
{
|
418
|
+
VALUE entry = rb_ary_entry(parameters, index);
|
419
|
+
int number = 0;
|
420
|
+
|
421
|
+
number = TYPE(entry) == T_FIXNUM ? FIX2INT(entry) : NUM2INT(entry);
|
422
|
+
buffer[index] = number;
|
423
|
+
}
|
424
|
+
|
425
|
+
startTransaction(transaction, list, size, buffer);
|
426
|
+
free(buffer);
|
427
|
+
|
428
|
+
instance = Data_Wrap_Struct(cTransaction, NULL, transactionFree,
|
429
|
+
transaction);
|
430
|
+
rb_iv_set(instance, "@connections", list);
|
431
|
+
rb_tx_started(instance, connections);
|
432
|
+
}
|
433
|
+
else
|
434
|
+
{
|
435
|
+
rb_fireruby_raise(NULL,
|
436
|
+
"Memory allocation failure allocating transaction "\
|
437
|
+
"parameter buffer.");
|
438
|
+
}
|
439
|
+
}
|
440
|
+
else
|
441
|
+
{
|
442
|
+
rb_raise(rb_eNoMemError,
|
443
|
+
"Memory allocation failure allocating transaction.");
|
444
|
+
}
|
445
|
+
|
446
|
+
return(instance);
|
447
|
+
}
|
448
|
+
|
449
|
+
|
450
|
+
/**
|
451
|
+
* This function begins a transaction on all of the database associated with
|
452
|
+
* the Transaction object.
|
453
|
+
*
|
454
|
+
* @param transaction A reference to the Transaction object being started.
|
455
|
+
* @param connections An array of the databases that the transaction applies
|
456
|
+
* to.
|
457
|
+
* @param size The length of the transaction parameter buffer passed
|
458
|
+
* to the function.
|
459
|
+
* @param buffer The transaction parameter buffer to be used in creating
|
460
|
+
* the transaction.
|
461
|
+
*
|
462
|
+
*/
|
463
|
+
void startTransaction(TransactionHandle *transaction,
|
464
|
+
VALUE connections,
|
465
|
+
long size,
|
466
|
+
char *buffer)
|
467
|
+
{
|
468
|
+
ISC_TEB *teb = NULL;
|
469
|
+
VALUE value = rb_funcall(connections, rb_intern("size"), 0),
|
470
|
+
head = rb_ary_entry(connections, 0);
|
471
|
+
short length = 0;
|
472
|
+
ConnectionHandle *first = NULL;
|
473
|
+
|
474
|
+
/* Attempt a retrieval of the first connection. */
|
475
|
+
if(value != Qnil)
|
476
|
+
{
|
477
|
+
/* Check that we have a connection. */
|
478
|
+
if(TYPE(head) == T_DATA &&
|
479
|
+
RDATA(head)->dfree == (RUBY_DATA_FUNC)connectionFree)
|
480
|
+
{
|
481
|
+
Data_Get_Struct(head, ConnectionHandle, first);
|
482
|
+
}
|
483
|
+
}
|
484
|
+
|
485
|
+
/* Generate the list of connections. */
|
486
|
+
length = (TYPE(value) == T_FIXNUM ? FIX2INT(value) : NUM2INT(value));
|
487
|
+
if(length > 0)
|
488
|
+
{
|
489
|
+
if((teb = ALLOC_N(ISC_TEB, length)) != NULL)
|
490
|
+
{
|
491
|
+
int i;
|
492
|
+
|
493
|
+
for(i = 0; i < length; i++)
|
494
|
+
{
|
495
|
+
VALUE entry = rb_ary_entry(connections, i);
|
496
|
+
|
497
|
+
/* Check that we have a connection. */
|
498
|
+
if(TYPE(entry) == T_DATA &&
|
499
|
+
RDATA(entry)->dfree == (RUBY_DATA_FUNC)connectionFree)
|
500
|
+
{
|
501
|
+
ConnectionHandle *connection = NULL;
|
502
|
+
|
503
|
+
Data_Get_Struct(entry, ConnectionHandle, connection);
|
504
|
+
if(connection->handle != 0)
|
505
|
+
{
|
506
|
+
/* Store the connection details. */
|
507
|
+
teb[i].database = &connection->handle;
|
508
|
+
if(size > 0)
|
509
|
+
{
|
510
|
+
teb[i].length = size;
|
511
|
+
teb[i].tpb = buffer;
|
512
|
+
}
|
513
|
+
else
|
514
|
+
{
|
515
|
+
teb[i].length = DEFAULT_TEB_SIZE;
|
516
|
+
teb[i].tpb = DEFAULT_TEB;
|
517
|
+
}
|
518
|
+
}
|
519
|
+
else
|
520
|
+
{
|
521
|
+
/* Clean up and raise an exception. */
|
522
|
+
free(teb);
|
523
|
+
rb_fireruby_raise(NULL,
|
524
|
+
"Disconnected connection specified "\
|
525
|
+
"starting a transaction.");
|
526
|
+
}
|
527
|
+
}
|
528
|
+
else
|
529
|
+
{
|
530
|
+
/* Clean up and thrown an exception. */
|
531
|
+
free(teb);
|
532
|
+
rb_fireruby_raise(NULL,
|
533
|
+
"Invalid connection specified starting a "\
|
534
|
+
"transaction.");
|
535
|
+
}
|
536
|
+
}
|
537
|
+
}
|
538
|
+
else
|
539
|
+
{
|
540
|
+
rb_raise(rb_eNoMemError,
|
541
|
+
"Memory allocation error starting transaction.");
|
542
|
+
}
|
543
|
+
}
|
544
|
+
else
|
545
|
+
{
|
546
|
+
/* Generate an exception. */
|
547
|
+
rb_fireruby_raise(NULL, "No connections specified for transaction.");
|
548
|
+
}
|
549
|
+
|
550
|
+
/* Check that theres been no errors and that we have a connection list. */
|
551
|
+
if(teb != NULL)
|
552
|
+
{
|
553
|
+
ISC_STATUS status[20];
|
554
|
+
|
555
|
+
/* Attempt a transaction start. */
|
556
|
+
if(isc_start_multiple(status, &transaction->handle, length, teb) != 0)
|
557
|
+
{
|
558
|
+
/* Generate an error. */
|
559
|
+
rb_fireruby_raise(status, "Error starting transaction.");
|
560
|
+
}
|
561
|
+
}
|
562
|
+
|
563
|
+
/* Free the database details list if need be. */
|
564
|
+
if(teb != NULL)
|
565
|
+
{
|
566
|
+
free(teb);
|
567
|
+
}
|
568
|
+
}
|
569
|
+
|
570
|
+
|
571
|
+
/**
|
572
|
+
* This function is used to integrate with the Ruby garbage collector to insure
|
573
|
+
* that the resources associated with a Transaction object are released when the
|
574
|
+
* collector comes a calling.
|
575
|
+
*
|
576
|
+
* @param transaction A pointer to the TransactionHandle structure associated
|
577
|
+
* with the Transaction object being collected.
|
578
|
+
*
|
579
|
+
*/
|
580
|
+
void transactionFree(void *transaction)
|
581
|
+
{
|
582
|
+
if(transaction != NULL)
|
583
|
+
{
|
584
|
+
TransactionHandle *handle = (TransactionHandle *)transaction;
|
585
|
+
|
586
|
+
if(handle->handle != 0)
|
587
|
+
{
|
588
|
+
ISC_STATUS status[20];
|
589
|
+
|
590
|
+
isc_rollback_transaction(status, &handle->handle);
|
591
|
+
}
|
592
|
+
free(handle);
|
593
|
+
}
|
594
|
+
}
|
595
|
+
|
596
|
+
|
597
|
+
/**
|
598
|
+
* This function provides a programmatic method of creating a Transaction
|
599
|
+
* object.
|
600
|
+
*
|
601
|
+
* @param connections Either an single Connection object or an array of
|
602
|
+
* Connection objects that the transaction will apply
|
603
|
+
* to.
|
604
|
+
*
|
605
|
+
* @return A reference to the Transaction object, or nil if an error occurs.
|
606
|
+
*
|
607
|
+
*/
|
608
|
+
VALUE rb_transaction_new(VALUE connections)
|
609
|
+
{
|
610
|
+
VALUE transaction = allocateTransaction(cTransaction);
|
611
|
+
|
612
|
+
transactionInitialize(transaction, connections);
|
613
|
+
|
614
|
+
return(transaction);
|
615
|
+
}
|
616
|
+
|
617
|
+
/**
|
618
|
+
* This function provides a convenient means of checking whether a connection
|
619
|
+
* is covered by a transaction.
|
620
|
+
*
|
621
|
+
* @param transaction A reference to the transaction to perform the check on.
|
622
|
+
* @param connection A reference to the connection to perform the check for.
|
623
|
+
*
|
624
|
+
* @return Non-zero if the connection is covered by the transaction, zero if
|
625
|
+
* it is not.
|
626
|
+
*
|
627
|
+
*/
|
628
|
+
int coversConnection(VALUE transaction, VALUE connection)
|
629
|
+
{
|
630
|
+
int result = 0;
|
631
|
+
VALUE boolean = isTransactionFor(transaction, connection);
|
632
|
+
|
633
|
+
if(boolean == Qtrue)
|
634
|
+
{
|
635
|
+
result = 1;
|
636
|
+
}
|
637
|
+
|
638
|
+
return(result);
|
639
|
+
}
|
640
|
+
|
641
|
+
|
642
|
+
/**
|
643
|
+
* This function initializes the Transaction class within the Ruby environment.
|
644
|
+
* The class is established under the module specified to the function.
|
645
|
+
*
|
646
|
+
* @param module A reference to the module to create the class within.
|
647
|
+
*
|
648
|
+
*/
|
649
|
+
void Init_Transaction(VALUE module)
|
650
|
+
{
|
651
|
+
cTransaction = rb_define_class_under(module, "Transaction", rb_cObject);
|
652
|
+
rb_define_alloc_func(cTransaction, allocateTransaction);
|
653
|
+
rb_define_method(cTransaction, "initialize", transactionInitialize, 1);
|
654
|
+
rb_define_method(cTransaction, "initialize_copy", forbidObjectCopy, 1);
|
655
|
+
rb_define_method(cTransaction, "active?", transactionIsActive, 0);
|
656
|
+
rb_define_method(cTransaction, "commit", commitTransaction, 0);
|
657
|
+
rb_define_method(cTransaction, "rollback", rollbackTransaction, 0);
|
658
|
+
rb_define_method(cTransaction, "connections", getTransactionConnections, 0);
|
659
|
+
rb_define_method(cTransaction, "for_connection?", isTransactionFor, 1);
|
660
|
+
rb_define_module_function(cTransaction, "create", createTransaction, 2);
|
661
|
+
rb_define_method(cTransaction, "execute", executeOnTransaction, 1);
|
662
|
+
rb_define_const(cTransaction, "TPB_VERSION_1", INT2FIX(isc_tpb_version1));
|
663
|
+
rb_define_const(cTransaction, "TPB_VERSION_3", INT2FIX(isc_tpb_version3));
|
664
|
+
rb_define_const(cTransaction, "TPB_CONSISTENCY", INT2FIX(isc_tpb_consistency));
|
665
|
+
rb_define_const(cTransaction, "TPB_CONCURRENCY", INT2FIX(isc_tpb_concurrency));
|
666
|
+
rb_define_const(cTransaction, "TPB_SHARED", INT2FIX(isc_tpb_shared));
|
667
|
+
rb_define_const(cTransaction, "TPB_PROTECTED", INT2FIX(isc_tpb_protected));
|
668
|
+
rb_define_const(cTransaction, "TPB_EXCLUSIVE", INT2FIX(isc_tpb_exclusive));
|
669
|
+
rb_define_const(cTransaction, "TPB_WAIT", INT2FIX(isc_tpb_wait));
|
670
|
+
rb_define_const(cTransaction, "TPB_NO_WAIT", INT2FIX(isc_tpb_nowait));
|
671
|
+
rb_define_const(cTransaction, "TPB_READ", INT2FIX(isc_tpb_read));
|
672
|
+
rb_define_const(cTransaction, "TPB_WRITE", INT2FIX(isc_tpb_write));
|
673
|
+
rb_define_const(cTransaction, "TPB_LOCK_READ", INT2FIX(isc_tpb_lock_read));
|
674
|
+
rb_define_const(cTransaction, "TPB_LOCK_WRITE", INT2FIX(isc_tpb_lock_write));
|
675
|
+
rb_define_const(cTransaction, "TPB_VERB_TIME", INT2FIX(isc_tpb_verb_time));
|
676
|
+
rb_define_const(cTransaction, "TPB_COMMIT_TIME", INT2FIX(isc_tpb_commit_time));
|
677
|
+
rb_define_const(cTransaction, "TPB_IGNORE_LIMBO", INT2FIX(isc_tpb_ignore_limbo));
|
678
|
+
rb_define_const(cTransaction, "TPB_READ_COMMITTED", INT2FIX(isc_tpb_read_committed));
|
679
|
+
rb_define_const(cTransaction, "TPB_AUTO_COMMIT", INT2FIX(isc_tpb_autocommit));
|
680
|
+
rb_define_const(cTransaction, "TPB_REC_VERSION", INT2FIX(isc_tpb_rec_version));
|
681
|
+
rb_define_const(cTransaction, "TPB_NO_REC_VERSION", INT2FIX(isc_tpb_no_rec_version));
|
682
|
+
rb_define_const(cTransaction, "TPB_RESTART_REQUESTS", INT2FIX(isc_tpb_restart_requests));
|
683
|
+
rb_define_const(cTransaction, "TPB_NO_AUTO_UNDO", INT2FIX(isc_tpb_no_auto_undo));
|
684
|
+
}
|