sedna 0.3.0 → 0.4.0

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.
Files changed (7) hide show
  1. data/CHANGES +18 -0
  2. data/README +35 -13
  3. data/Rakefile +3 -3
  4. data/ext/extconf.rb +4 -9
  5. data/ext/sedna.c +429 -118
  6. data/test/sedna_test.rb +447 -317
  7. metadata +2 -2
data/CHANGES CHANGED
@@ -1,3 +1,21 @@
1
+ === 0.4.0
2
+
3
+ * Released on January 4th, 2009.
4
+ * Added Sedna#reset, which closes the current connection and reconnects.
5
+ * Added Sedna#connected?, which returns whether or not the current connection
6
+ is still active.
7
+ * Sedna#transaction can now also be used without a block. Use the newly added
8
+ methods Sedna#commit and Sedna#rollback to finish such a declarative
9
+ transaction. Note that this style is not recommended if a transaction can
10
+ be wrapped in a block.
11
+ * Sedna.connect no longer blocks other threads in Ruby 1.9.1+. Use Sedna.blocking?
12
+ to discover if support for non-blocking behaviour is enabled.
13
+ * Strings in the result Array of Sedna#execute are now in UTF-8 encoding in
14
+ Ruby 1.9, rather than binary.
15
+ * Fully tested in Ruby 1.8.5+ (including the latest version, 1.9.1 RC1).
16
+ * Now also compiles with Ruby 1.9.0.x, but non-blocking behaviour is disabled
17
+ for these versions.
18
+
1
19
  === 0.3.0
2
20
 
3
21
  * Released on December 21st, 2008.
data/README CHANGED
@@ -16,7 +16,7 @@ at http://modis.ispras.ru/sedna
16
16
 
17
17
  Author: Rolf Timmermans (r.timmermans <i>at</i> voormedia.com)
18
18
 
19
- Copyright 2008 Voormedia B.V.
19
+ Copyright 2008, 2009 Voormedia B.V.
20
20
 
21
21
  Licensed under the Apache License, Version 2.0 (the "License");
22
22
  you may not use this file except in compliance with the License.
@@ -32,9 +32,8 @@ limitations under the License.
32
32
 
33
33
  === Current version
34
34
 
35
- The current version of this library is 0.3.0. This release is a <b>beta version</b>.
36
- The API may change significantly between minor versions. For a complete overview
37
- all recent and previous changes, see CHANGES.
35
+ The current version of this library is 0.4.0. This release is a <b>beta version</b>.
36
+ For a complete overview all recent and previous changes, see CHANGES.
38
37
 
39
38
  === Requirements
40
39
 
@@ -43,8 +42,7 @@ header files of the C driver. They are shipped and installed as part of the bina
43
42
  distribution of \Sedna. When installing, choose either <tt>/usr/local/sedna</tt> or
44
43
  <tt>/opt/sedna</tt> as target locations.
45
44
 
46
- The library has been tested with Ruby 1.8.5 and above, including Ruby 1.9.1
47
- (preview 2), but excluding Ruby 1.9.0.
45
+ The library has been tested with Ruby 1.8.5 and above, including Ruby 1.9.
48
46
 
49
47
  === Installation
50
48
 
@@ -69,7 +67,8 @@ using it.
69
67
 
70
68
  To start querying a database, create a new connection with the Sedna.connect
71
69
  method. When a block is given, the Sedna connection object will be returned
72
- which can be used to execute database statements.
70
+ which can be used to execute database statements. See the documentation of the
71
+ Sedna class for more details.
73
72
 
74
73
  connection_details = {
75
74
  :database => "my_db",
@@ -77,13 +76,36 @@ which can be used to execute database statements.
77
76
  :username => "SYSTEM",
78
77
  :password => "MANAGER",
79
78
  }
80
- Sedna.connect(connection_details) do |sedna|
79
+
80
+ Sedna.connect connection_details do |sedna|
81
81
  # Start querying the database.
82
- sedna.execute("create document 'mydoc'") #=> nil
83
- sedna.execute("update insert <msg>Hello world!</msg> into doc('mydoc')") #=> nil
84
- sedna.execute("doc('mydoc')/msg/text()") #=> ["Hello world!"]
82
+ sedna.execute "create document 'my_doc'" #=> nil
83
+ sedna.execute "update insert <msg>Hello world!</msg> into doc('my_doc')" #=> nil
84
+ sedna.execute "doc('my_doc')/msg/text()" #=> ["Hello world!"]
85
85
  # The connection is closed automatically for us.
86
86
  end
87
87
 
88
- See the documentation of the Sedna class -- the methods Sedna.connect and
89
- Sedna#execute in particular -- for more details.
88
+ Use Sedna#load_document to load a document into the database from a string or
89
+ file.
90
+
91
+ sedna.load_document "<msg>Hello world!</msg>", "my_doc", "my_collection"
92
+ sedna.execute "doc('my_doc', 'my_collection')/msg/text()" #=> ["Hello world!"]
93
+
94
+ File.open "document.xml" do |file|
95
+ sedna.load_document file, "my_doc2", "my_collection"
96
+ end
97
+
98
+ Use Sedna#transaction to wrap multiple database statements in a transaction.
99
+ This ensures that either all or none of these statements are executed. If
100
+ something goes wrong halfway, the previously executed statements that are part
101
+ of the transaction will be rolled back.
102
+
103
+ sedna.transaction do
104
+ amount = 100
105
+ sedna.execute "update replace $balance in doc('my_account')/balance
106
+ with <balance>{$balance - #{amount}}</balance>"
107
+ sedna.execute "update replace $balance in doc('your_account')/balance
108
+ with <balance>{$balance + #{amount}}</balance>"
109
+ # If the second statement fails, the first will be rolled back. Only if
110
+ # both statements succeed will the changes become permanent.
111
+ end
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2008 Voormedia B.V.
1
+ # Copyright 2008, 2009 Voormedia B.V.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@ RDOC_FILES = FileList["[A-Z][A-Z]*", "ext/**/*.c"].to_a
30
30
  task :default => [:rebuild, :test]
31
31
 
32
32
  task :multi do
33
- exec "export EXCLUDED_VERSIONS=v1_9_0 && multiruby -S rake"
33
+ exec "multiruby -S rake"
34
34
  end
35
35
 
36
36
  desc "Build the Ruby extension"
@@ -57,7 +57,7 @@ end
57
57
 
58
58
  gem_spec = Gem::Specification.new do |s|
59
59
  s.name = "sedna"
60
- s.version = "0.3.0"
60
+ s.version = "0.4.0"
61
61
 
62
62
  s.summary = "Sedna XML DBMS client library."
63
63
  s.description = %{Ruby extension that provides a client library for the Sedna XML DBMS, making use of the official C driver of the Sedna project.}
data/ext/extconf.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2008 Voormedia B.V.
1
+ # Copyright 2008, 2009 Voormedia B.V.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -68,13 +68,8 @@ passing the -fPIC option to gcc.
68
68
  end
69
69
  end
70
70
 
71
- if have_func("rb_thread_blocking_region") and !have_func("rb_mutex_synchronize")
72
- $stderr.write %{==============================================================================
73
- This Ruby version incorrectly defines Mutex#synchronize. Please upgrade to a
74
- newer version of Ruby (1.9.1+).
75
- ==============================================================================
76
- }
77
- exit 5
78
- end
71
+ have_func "rb_thread_blocking_region"
72
+ have_func "rb_mutex_synchronize"
73
+ have_func "rb_enc_str_buf_cat"
79
74
 
80
75
  create_makefile "sedna"
data/ext/sedna.c CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2008 Voormedia B.V.
2
+ * Copyright 2008, 2009 Voormedia B.V.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -49,31 +49,73 @@
49
49
  #define PROTO_TO_STRING(s) PROTO_STRINGIFY(s)
50
50
  #define PROTOCOL_VERSION PROTO_TO_STRING(SE_CURRENT_SOCKET_PROTOCOL_VERSION_MAJOR) "." PROTO_TO_STRING(SE_CURRENT_SOCKET_PROTOCOL_VERSION_MINOR)
51
51
 
52
- // Default connection arguments
52
+ // Default connection arguments.
53
53
  #define DEFAULT_HOST "localhost"
54
- #define DEFAULT_DATABASE "test"
54
+ #define DEFAULT_DB "test"
55
55
  #define DEFAULT_USER "SYSTEM"
56
- #define DEFAULT_PASSWORD "MANAGER"
56
+ #define DEFAULT_PW "MANAGER"
57
+
58
+ // Instance variable names.
59
+ #define IV_HOST "@host"
60
+ #define IV_DB "@database"
61
+ #define IV_USER "@username"
62
+ #define IV_PW "@password"
63
+ #define IV_AUTOCOMMIT "@autocommit"
64
+ #define IV_MUTEX "@mutex"
57
65
 
58
66
  // Define a shorthand for the common SednaConnection structure.
59
67
  typedef struct SednaConnection SC;
60
68
 
61
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
62
- #define NON_BLOCKING
63
- #define SEDNA_BLOCKING Qfalse
69
+ // Define a struct for database queries.
70
+ struct SednaQuery {
71
+ void *conn;
72
+ void *query;
73
+ };
74
+ typedef struct SednaQuery SQ;
75
+
76
+ // Define a struct for database connection arguments.
77
+ struct SednaConnArgs {
78
+ void *conn;
79
+ void *host;
80
+ void *db;
81
+ void *user;
82
+ void *pw;
83
+ };
84
+ typedef struct SednaConnArgs SCA;
85
+
86
+ // Always create UTF-8 strings with STR_CAT, if supported (Ruby 1.9).
87
+ #ifdef HAVE_RB_ENC_STR_BUF_CAT
88
+ #ifndef RUBY_ENCODING_H
89
+ #include "ruby/encoding.h"
90
+ #endif
91
+ #define STR_CAT(str, buf, bytes) rb_enc_str_buf_cat(str, buf, bytes, rb_utf8_encoding())
92
+ #else
93
+ #define STR_CAT(str, buf, bytes) rb_str_buf_cat(str, buf, bytes)
94
+ #endif
64
95
 
65
- struct SednaQuery {
66
- void *conn;
67
- void *query;
68
- };
69
- typedef struct SednaQuery SQ;
96
+ // Define whether or not non-blocking behaviour will be built in.
97
+ #if defined(HAVE_RB_THREAD_BLOCKING_REGION) && defined(HAVE_RB_MUTEX_SYNCHRONIZE)
98
+ #define NON_BLOCKING 1
99
+ #define SEDNA_BLOCKING Qfalse
70
100
  #else
71
101
  #define SEDNA_BLOCKING Qtrue
72
102
  #endif
73
103
 
104
+ // Define execute and connect functions.
105
+ #ifdef NON_BLOCKING
106
+ // Non-blocking variants for >= 1.9.
107
+ // Synchronize across threads using this instance and execute.
108
+ #define SEDNA_CONNECT(self, c) rb_mutex_synchronize(rb_iv_get(self, IV_MUTEX), (void*)sedna_non_blocking_connect, (VALUE)c);
109
+ #define SEDNA_EXECUTE(self, q) rb_mutex_synchronize(rb_iv_get(self, IV_MUTEX), (void*)sedna_non_blocking_execute, (VALUE)q);
110
+ #else
111
+ // Blocking variants for < 1.9.
112
+ #define SEDNA_CONNECT(self, c) sedna_blocking_connect(c);
113
+ #define SEDNA_EXECUTE(self, q) sedna_blocking_execute(q);
114
+ #endif
115
+
74
116
  // Ruby classes.
75
117
  static VALUE cSedna;
76
- //static VALUE cSednaSet; //Unused so far.
118
+ //static VALUE cSednaSet; // Stick to Array for result sets.
77
119
  static VALUE cSednaException;
78
120
  static VALUE cSednaAuthError;
79
121
  static VALUE cSednaConnError;
@@ -146,18 +188,44 @@ static void sedna_free(SC *conn)
146
188
  static void sedna_mark(SC *conn)
147
189
  { /* Unused. */ }
148
190
 
149
- #ifdef NON_BLOCKING
150
- static int sedna_blocking_execute(SQ *q)
191
+ // Connect to the server.
192
+ static int sedna_blocking_connect(SCA *c)
151
193
  {
152
- return SEexecute(q->conn, q->query);
194
+ return SEconnect(c->conn, c->host, c->db, c->user, c->pw);
153
195
  }
154
196
 
155
- static int sedna_execute(SQ *q)
197
+ #ifdef NON_BLOCKING
198
+ static int sedna_non_blocking_connect(SCA *c)
156
199
  {
157
- return rb_thread_blocking_region((void*)sedna_blocking_execute, q, RUBY_UBF_IO, NULL);
200
+ return rb_thread_blocking_region((void*)sedna_blocking_connect, c, RUBY_UBF_IO, NULL);
158
201
  }
159
202
  #endif
160
203
 
204
+ static void sedna_connect(VALUE self, SCA *c)
205
+ {
206
+ int res = SEDNA_CONNECT(self, c);
207
+ if(res != SEDNA_SESSION_OPEN) {
208
+ // We have to set the connection status to closed explicitly here,
209
+ // because the GC routine sedna_free() will test for this status, but
210
+ // the socket is already closed by SEconnect(). If we do not change the
211
+ // status, sedna_free() will attempt to close the connection again by
212
+ // calling SEclose(), which will definitely lead to unpredictable
213
+ // results.
214
+ ((SC*)c->conn)->isConnectionOk = SEDNA_CONNECTION_CLOSED;
215
+ sedna_err(c->conn, res);
216
+ }
217
+ }
218
+
219
+ // Close the connection to the server.
220
+ static void sedna_close(SC *conn)
221
+ {
222
+ int res;
223
+ if(SEconnectionStatus(conn) != SEDNA_CONNECTION_CLOSED) {
224
+ res = SEclose(conn);
225
+ VERIFY_RES(SEDNA_SESSION_CLOSED, res, conn);
226
+ }
227
+ }
228
+
161
229
  // Read one record completely and return it as a Ruby String object.
162
230
  static VALUE sedna_read(SC *conn, int strip_n)
163
231
  {
@@ -177,11 +245,11 @@ static VALUE sedna_read(SC *conn, int strip_n)
177
245
  // except the first. Strip them! This a known issue in the
178
246
  // network protocol and serialization mechanism.
179
247
  // See: http://sourceforge.net/mailarchive/forum.php?thread_name=3034886f0812030132v3bbd8e2erd86480d3dc640664%40mail.gmail.com&forum_name=sedna-discussion
180
- rb_str_buf_cat(str, buffer + 1, bytes_read - 1);
248
+ STR_CAT(str, buffer + 1, bytes_read - 1);
181
249
  // Do not strip newlines from subsequent buffer reads.
182
250
  strip_n = 0;
183
251
  } else {
184
- rb_str_buf_cat(str, buffer, bytes_read);
252
+ STR_CAT(str, buffer, bytes_read);
185
253
  }
186
254
  }
187
255
  }
@@ -194,6 +262,7 @@ static VALUE sedna_read(SC *conn, int strip_n)
194
262
  static VALUE sedna_get_results(SC *conn)
195
263
  {
196
264
  int res, strip_n = 0;
265
+ // Can be replaced with: rb_funcall(cSednaSet, rb_intern("new"), 0, NULL);
197
266
  VALUE set = rb_ary_new();
198
267
 
199
268
  while((res = SEnext(conn)) != SEDNA_RESULT_END) {
@@ -207,6 +276,18 @@ static VALUE sedna_get_results(SC *conn)
207
276
  return set;
208
277
  }
209
278
 
279
+ static int sedna_blocking_execute(SQ *q)
280
+ {
281
+ return SEexecute(q->conn, q->query);
282
+ }
283
+
284
+ #ifdef NON_BLOCKING
285
+ static int sedna_non_blocking_execute(SQ *q)
286
+ {
287
+ return rb_thread_blocking_region((void*)sedna_blocking_execute, q, RUBY_UBF_IO, NULL);
288
+ }
289
+ #endif
290
+
210
291
  // Enable or disable autocommit.
211
292
  static void sedna_autocommit(SC *conn, int value)
212
293
  {
@@ -215,24 +296,73 @@ static void sedna_autocommit(SC *conn, int value)
215
296
  }
216
297
 
217
298
  // Begin a transaction.
218
- static void sedna_tr_begin(SC *conn)
299
+ static void sedna_begin(SC *conn)
219
300
  {
220
- int res = SEbegin(conn);
301
+ int res;
302
+
303
+ // Disable autocommit mode.
304
+ SEDNA_AUTOCOMMIT_DISABLE(conn);
305
+
306
+ // Start the transaction.
307
+ res = SEbegin(conn);
221
308
  VERIFY_RES(SEDNA_BEGIN_TRANSACTION_SUCCEEDED, res, conn);
222
309
  }
223
310
 
224
311
  // Commit a transaction.
225
312
  static void sedna_tr_commit(SC *conn)
226
313
  {
227
- int res = SEcommit(conn);
228
- VERIFY_RES(SEDNA_COMMIT_TRANSACTION_SUCCEEDED, res, conn);
314
+ int res;
315
+
316
+ if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) {
317
+ // Commit if a transaction was in progres.
318
+ res = SEcommit(conn);
319
+ VERIFY_RES(SEDNA_COMMIT_TRANSACTION_SUCCEEDED, res, conn);
320
+ } else {
321
+ // If there is no current transaction, raise an error.
322
+ rb_raise(cSednaTrnError, "No transaction in progress, cannot commit.");
323
+ }
324
+ }
325
+
326
+ // Commit a transaction and reset autocommit mode.
327
+ static void sedna_commit(SC *conn, VALUE self)
328
+ {
329
+ int status;
330
+
331
+ // Roll back.
332
+ rb_protect((void*)sedna_tr_commit, (VALUE)conn, &status);
333
+
334
+ // Turn autocommit back on if it was set.
335
+ SWITCH_SEDNA_AUTOCOMMIT(conn, rb_iv_get(self, IV_AUTOCOMMIT));
336
+
337
+ // Re-raise any exception.
338
+ if(status != 0) rb_jump_tag(status);
229
339
  }
230
340
 
231
341
  // Rollback a transaction.
232
342
  static void sedna_tr_rollback(SC *conn)
233
343
  {
234
- int res = SErollback(conn);
235
- VERIFY_RES(SEDNA_ROLLBACK_TRANSACTION_SUCCEEDED, res, conn);
344
+ int res;
345
+
346
+ // Roll back if a transaction was in progress.
347
+ if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) {
348
+ res = SErollback(conn);
349
+ VERIFY_RES(SEDNA_ROLLBACK_TRANSACTION_SUCCEEDED, res, conn);
350
+ }
351
+ }
352
+
353
+ // Rollback a transaction and reset autocommit mode.
354
+ static void sedna_rollback(SC *conn, VALUE self)
355
+ {
356
+ int status;
357
+
358
+ // Roll back.
359
+ rb_protect((void*)sedna_tr_rollback, (VALUE)conn, &status);
360
+
361
+ // Turn autocommit back on if it was set.
362
+ SWITCH_SEDNA_AUTOCOMMIT(conn, rb_iv_get(self, IV_AUTOCOMMIT));
363
+
364
+ // Re-raise any exception.
365
+ if(status != 0) rb_jump_tag(status);
236
366
  }
237
367
 
238
368
 
@@ -251,45 +381,47 @@ static VALUE cSedna_s_new(VALUE klass)
251
381
 
252
382
  /* :nodoc:
253
383
  *
254
- * Initialize a new instance of Sedna.
384
+ * Initialize a new instance of Sedna. Undocumented, because Sedna.connect should
385
+ * be used instead.
255
386
  */
256
387
  static VALUE cSedna_initialize(VALUE self, VALUE options)
257
388
  {
258
- int res;
259
389
  VALUE host_k, db_k, user_k, pw_k, host_v, db_v, user_v, pw_v;
260
390
  char *host, *db, *user, *pw;
261
- SC *conn = sedna_struct(self);
262
391
 
392
+ // Ensure the argument is a Hash.
263
393
  Check_Type(options, T_HASH);
394
+
395
+ // Store the symbols of the valid hash keys.
264
396
  host_k = ID2SYM(rb_intern("host"));
265
397
  db_k = ID2SYM(rb_intern("database"));
266
398
  user_k = ID2SYM(rb_intern("username"));
267
399
  pw_k = ID2SYM(rb_intern("password"));
268
400
 
401
+ // Get the connection details or set them to the default values if not given.
269
402
  if(NIL_P(host_v = rb_hash_aref(options, host_k))) host = DEFAULT_HOST; else host = STR2CSTR(host_v);
270
- if(NIL_P(db_v = rb_hash_aref(options, db_k))) db = DEFAULT_DATABASE; else db = STR2CSTR(db_v);
403
+ if(NIL_P(db_v = rb_hash_aref(options, db_k))) db = DEFAULT_DB; else db = STR2CSTR(db_v);
271
404
  if(NIL_P(user_v = rb_hash_aref(options, user_k))) user = DEFAULT_USER; else user = STR2CSTR(user_v);
272
- if(NIL_P(pw_v = rb_hash_aref(options, pw_k))) pw = DEFAULT_PASSWORD; else pw = STR2CSTR(pw_v);
273
-
274
- res = SEconnect(conn, host, db, user, pw);
275
- if(res != SEDNA_SESSION_OPEN) {
276
- // We have to set the connection status to closed explicitly here,
277
- // because the GC routine sedna_free() will test for this status, but
278
- // the socket is already closed by SEconnect(). If we do not change the
279
- // status, sedna_free() will attempt to close the connection again by
280
- // calling SEclose(), which will definitely lead to unpredictable
281
- // results.
282
- conn->isConnectionOk = SEDNA_CONNECTION_CLOSED;
283
- sedna_err(conn, res);
284
- }
285
-
286
- // Initialize @autocommit to true (default argument).
287
- rb_iv_set(self, "@autocommit", Qtrue);
405
+ if(NIL_P(pw_v = rb_hash_aref(options, pw_k))) pw = DEFAULT_PW; else pw = STR2CSTR(pw_v);
406
+
407
+ // Save all connection details to instance variables.
408
+ rb_iv_set(self, IV_HOST, rb_str_new2(host));
409
+ rb_iv_set(self, IV_DB, rb_str_new2(db));
410
+ rb_iv_set(self, IV_USER, rb_str_new2(user));
411
+ rb_iv_set(self, IV_PW, rb_str_new2(pw));
288
412
 
289
413
  #ifdef NON_BLOCKING
290
- rb_iv_set(self, "@mutex", rb_mutex_new());
414
+ // Create a mutex if this build supports non-blocking queries.
415
+ rb_iv_set(self, IV_MUTEX, rb_mutex_new());
291
416
  #endif
292
417
 
418
+ // Connect to the database.
419
+ SCA c = { sedna_struct(self), host, db, user, pw };
420
+ sedna_connect(self, &c);
421
+
422
+ // Initialize @autocommit to true.
423
+ rb_iv_set(self, IV_AUTOCOMMIT, Qtrue);
424
+
293
425
  return self;
294
426
  }
295
427
 
@@ -297,20 +429,48 @@ static VALUE cSedna_initialize(VALUE self, VALUE options)
297
429
  * call-seq:
298
430
  * sedna.close -> nil
299
431
  *
300
- * Closes an open Sedna connection. If the connection was already closed when
432
+ * Closes an open Sedna connection. If the connection is already closed when
301
433
  * this method is called, nothing happens. A Sedna::ConnectionError is raised
302
434
  * if the connection was open but could not be closed.
303
435
  */
304
436
  static VALUE cSedna_close(VALUE self)
305
437
  {
306
- int res;
307
438
  SC *conn = sedna_struct(self);
439
+
440
+ // Ensure the connection is closed.
441
+ sedna_close(conn);
442
+
443
+ // Always return nil if successful.
444
+ return Qnil;
445
+ }
446
+
447
+ /*
448
+ * call-seq:
449
+ * sedna.reset -> nil
450
+ *
451
+ * Closes an open Sedna connection and reconnects. If the connection is already
452
+ * closed when this method is called, the connection is just reestablished. When
453
+ * reconnecting, the same connection details are used that were given when initially
454
+ * connecting with the connect method.
455
+ *
456
+ * If the connection could not be closed or reopened, a Sedna::ConnectionError is
457
+ * raised. If the authentication fails when reconnecting, a
458
+ * Sedna::AuthenticationError is raised.
459
+ */
460
+ static VALUE cSedna_reset(VALUE self)
461
+ {
462
+ SC *conn = sedna_struct(self);
463
+
464
+ // First ensure the current connection is closed.
465
+ sedna_close(conn);
308
466
 
309
- if(SEconnectionStatus(conn) != SEDNA_CONNECTION_CLOSED) {
310
- res = SEclose(conn);
311
- VERIFY_RES(SEDNA_SESSION_CLOSED, res, conn);
312
- }
467
+ // Retrieve stored connection details.
468
+ SCA c = { conn, STR2CSTR(rb_iv_get(self, IV_HOST)), STR2CSTR(rb_iv_get(self, IV_DB)), STR2CSTR(rb_iv_get(self, IV_USER)), STR2CSTR(rb_iv_get(self, IV_PW)) };
469
+
470
+ // Connect to the database.
471
+ sedna_connect(self, &c);
313
472
 
473
+ // Always return nil if successful.
314
474
  return Qnil;
315
475
  }
316
476
 
@@ -320,32 +480,40 @@ static VALUE cSedna_close(VALUE self)
320
480
  * Sedna.connect(details) {|sedna| ... } -> nil
321
481
  *
322
482
  * Establishes a new connection to a \Sedna XML database. Accepts a hash that
323
- * describes which database to \connect to.
483
+ * describes which database to connect to.
324
484
  *
325
485
  * If a block is given, the block is executed if a connection was successfully
326
- * established. The connection is closed at the end of the block. If called
486
+ * established. The connection is closed at the end of the block or if the
487
+ * stack is unwinded (if an exception is raised, for example). If called
327
488
  * without a block, a Sedna object that represents the connection is returned.
328
489
  * The connection should be closed by calling Sedna#close.
329
490
  *
330
491
  * If a connection cannot be initiated, a Sedna::ConnectionError is raised.
331
492
  * If the authentication fails, a Sedna::AuthenticationError is raised.
332
493
  *
494
+ * This method does not block other threads in Ruby 1.9.1+ -- connections that
495
+ * are initiated in different threads will be created concurrently. You can
496
+ * use Sedna.blocking? to verify if the extension supports non-blocking
497
+ * behaviour.
498
+ *
333
499
  * ==== Valid connection details keys
334
500
  *
335
- * * <tt>:host</tt> - Host name or IP address to which to \connect to (defaults to +localhost+).
336
- * * <tt>:database</tt> - Name of the database to \connect to (defaults to +test+).
501
+ * * <tt>:host</tt> - Host name or IP address to which to connect to (defaults to +localhost+).
502
+ * * <tt>:database</tt> - Name of the database to connect to (defaults to +test+).
337
503
  * * <tt>:username</tt> - User name to authenticate with (defaults to +SYSTEM+).
338
504
  * * <tt>:password</tt> - Password to authenticate with (defaults to +MANAGER+).
339
505
  *
340
506
  * ==== Examples
341
507
  *
342
508
  * Call without a block and close the connection afterwards.
343
- * sedna = Sedna.connect(:database => "my_db", :host => "my_host")
509
+ *
510
+ * sedna = Sedna.connect :database => "my_db", :host => "my_host"
344
511
  * # Query the database and close afterwards.
345
512
  * sedna.close
346
513
  *
347
514
  * Call with a block. The connection is closed automatically.
348
- * Sedna.connect(:database => "my_db", :host => "my_host") do |sedna|
515
+ *
516
+ * Sedna.connect :database => "my_db", :host => "my_host" do |sedna|
349
517
  * # Query the database.
350
518
  * # The connection is closed automatically.
351
519
  * end
@@ -353,15 +521,24 @@ static VALUE cSedna_close(VALUE self)
353
521
  static VALUE cSedna_s_connect(VALUE klass, VALUE options)
354
522
  {
355
523
  int status;
524
+
525
+ // Create a new instance.
356
526
  VALUE obj = rb_funcall(klass, rb_intern("new"), 1, options);
357
527
 
358
528
  if(rb_block_given_p()) {
529
+ // If a block is given, yield the instance, and make sure we always return...
359
530
  rb_protect(rb_yield, obj, &status);
531
+
532
+ // ...to ensure that the connection is closed afterwards.
360
533
  cSedna_close(obj);
361
- if(status != 0) rb_jump_tag(status); // Re-raise any exception.
534
+
535
+ // Re-raise any exception.
536
+ if(status != 0) rb_jump_tag(status);
362
537
 
538
+ // Always return nil if successful.
363
539
  return Qnil;
364
540
  } else {
541
+ // If no block is given, simply return the instance.
365
542
  return obj;
366
543
  }
367
544
  }
@@ -381,16 +558,33 @@ static VALUE cSedna_s_version(VALUE klass)
381
558
  * call-seq:
382
559
  * Sedna.blocking? -> true or false
383
560
  *
384
- * Returns +true+ if querying the database with Sedna#execute will block other
385
- * threads. Returns +false+ if multiple queries can be run in different threads
386
- * simultaneously. \Sedna will not block other threads when compiled against
387
- * Ruby 1.9.
561
+ * Returns +true+ if connecting with Sedna.connect or querying the database
562
+ * with Sedna#execute will block other threads. Returns +false+ if multiple
563
+ * queries can be run or multiple connections can be made simultaneously in
564
+ * different threads. \Sedna will not block other threads (this method returns
565
+ * +false+) when compiled against Ruby 1.9.1+.
388
566
  */
389
567
  static VALUE cSedna_s_blocking(VALUE klass)
390
568
  {
391
569
  return SEDNA_BLOCKING;
392
570
  }
393
571
 
572
+ /*
573
+ * call-seq:
574
+ * sedna.connected? -> true or false
575
+ *
576
+ * Returns +true+ if the connection is connected and functioning properly. Returns
577
+ * +false+ if the connection has been closed.
578
+ */
579
+ static VALUE cSedna_connected(VALUE self)
580
+ {
581
+ SC *conn = sedna_struct(self);
582
+
583
+ // Return true if the connection status is OK. This only indicates that the
584
+ // client still thinks it is connected.
585
+ return (SEconnectionStatus(conn) == SEDNA_CONNECTION_OK) ? Qtrue : Qfalse;
586
+ }
587
+
394
588
  /*
395
589
  * call-seq:
396
590
  * sedna.execute(query) -> array or nil
@@ -399,11 +593,11 @@ static VALUE cSedna_s_blocking(VALUE klass)
399
593
  * Executes the given +query+ against a \Sedna database. Returns an array if the
400
594
  * given query is a select query. The elements of the array are strings that
401
595
  * correspond to each result in the result set. If the query is an update query
402
- * or a (bulk) load query, +nil+ is returned. When attempting to \execute a
596
+ * or a (bulk) load query, +nil+ is returned. When attempting to execute a
403
597
  * query on a closed connection, a Sedna::ConnectionError will be raised. A
404
598
  * Sedna::Exception is raised if the query fails or is invalid.
405
599
  *
406
- * This method does not block other threads in Ruby 1.9 -- database queries that
600
+ * This method does not block other threads in Ruby 1.9.1+ -- database queries that
407
601
  * are run in different threads with different connections will run concurrently.
408
602
  * You can use Sedna.blocking? to verify if the extension supports non-blocking
409
603
  * behaviour. Database queries run from different threads, but on the same
@@ -412,12 +606,17 @@ static VALUE cSedna_s_blocking(VALUE klass)
412
606
  * ==== Examples
413
607
  *
414
608
  * Create a new document.
609
+ *
415
610
  * sedna.execute "create document 'mydoc'"
416
611
  * #=> nil
612
+ *
417
613
  * Update the newly created document with a root node.
614
+ *
418
615
  * sedna.execute "update insert <message>Hello world!</message> into doc('mydoc')"
419
616
  * #=> nil
617
+ *
420
618
  * Select a node in a document using XPath.
619
+ *
421
620
  * sedna.execute "doc('mydoc')/message/text()"
422
621
  * #=> ["Hello world!"]
423
622
  *
@@ -429,27 +628,27 @@ static VALUE cSedna_s_blocking(VALUE klass)
429
628
  */
430
629
  static VALUE cSedna_execute(VALUE self, VALUE query)
431
630
  {
432
- int res;
433
631
  SC *conn = sedna_struct(self);
434
632
 
435
- if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
436
-
437
- #ifdef NON_BLOCKING
438
- // Non-blocking variant for >= 1.9.
633
+ // Prepare query arguments.
439
634
  SQ q = { conn, STR2CSTR(query) };
440
- res = rb_mutex_synchronize(rb_iv_get(self, "@mutex"), (void*)sedna_execute, (VALUE)&q);
441
- #else
442
- // Blocking variant for < 1.9.
443
- res = SEexecute(conn, STR2CSTR(query));
444
- #endif
635
+
636
+ // Verify that the connection is OK.
637
+ if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
638
+
639
+ // Execute query.
640
+ int res = SEDNA_EXECUTE(self, &q);
445
641
 
446
642
  switch(res) {
447
643
  case SEDNA_QUERY_SUCCEEDED:
644
+ // Return the results if this was a query.
448
645
  return sedna_get_results(conn);
449
646
  case SEDNA_UPDATE_SUCCEEDED:
450
647
  case SEDNA_BULK_LOAD_SUCCEEDED:
648
+ // Return nil if this was an update or bulk load.
451
649
  return Qnil;
452
650
  default:
651
+ // Raise an exception if something else happened.
453
652
  sedna_err(conn, res);
454
653
  return Qnil;
455
654
  }
@@ -470,7 +669,7 @@ static VALUE cSedna_execute(VALUE self, VALUE query)
470
669
  *
471
670
  * ==== Examples
472
671
  *
473
- * Create a new standalone document and retrieve it.
672
+ * Create a new document and retrieve its contents.
474
673
  *
475
674
  * sedna.load_document "<my_document>Hello world!</my_document>", "my_doc"
476
675
  * #=> nil
@@ -490,29 +689,41 @@ static VALUE cSedna_load_document(int argc, VALUE *argv, VALUE self)
490
689
  VALUE document, doc_name, col_name, buf;
491
690
  char *doc_name_c, *col_name_c;
492
691
 
692
+ // Verify that the connection is OK.
493
693
  if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
494
694
 
495
- rb_scan_args(argc, argv, "21", &document, &doc_name, &col_name); // 2 mandatory args, 1 optional.
695
+ // 2 mandatory arguments, 1 optional.
696
+ rb_scan_args(argc, argv, "21", &document, &doc_name, &col_name);
496
697
  doc_name_c = STR2CSTR(doc_name);
497
698
  col_name_c = NIL_P(col_name) ? NULL : STR2CSTR(col_name);
498
699
 
499
700
  if(TYPE(document) == T_FILE) {
701
+ // If the document is an IO object...
500
702
  while(!NIL_P(buf = rb_funcall(document, rb_intern("read"), 1, INT2NUM(LOAD_BUF_LEN)))) {
703
+ // ...read from it until we reach EOF and load the data.
501
704
  res = SEloadData(conn, STR2CSTR(buf), RSTRING_LEN(buf), doc_name_c, col_name_c);
502
705
  VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
503
706
  }
707
+
708
+ // If there is no data, raise an exception.
504
709
  if(res == 0) rb_raise(cSednaException, "Document is empty.");
505
710
  } else {
711
+ // If the document is not an IO object, verify it is a string instead.
506
712
  Check_Type(document, T_STRING);
713
+
714
+ // If there is no data, raise an exception.
507
715
  if(RSTRING_LEN(document) == 0) rb_raise(cSednaException, "Document is empty.");
508
716
 
717
+ // Load the data.
509
718
  res = SEloadData(conn, STR2CSTR(document), RSTRING_LEN(document), doc_name_c, col_name_c);
510
719
  VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
511
720
  }
512
721
 
722
+ // Signal that we're finished.
513
723
  res = SEendLoadData(conn);
514
724
  VERIFY_RES(SEDNA_BULK_LOAD_SUCCEEDED, res, conn);
515
725
 
726
+ // Always return nil if successful.
516
727
  return Qnil;
517
728
  }
518
729
 
@@ -525,9 +736,13 @@ static VALUE cSedna_autocommit_set(VALUE self, VALUE auto_commit)
525
736
  int val = (RTEST(auto_commit) ? Qtrue : Qfalse);
526
737
  SC *conn = sedna_struct(self);
527
738
 
739
+ // Switch autocommit mode on or off.
528
740
  SWITCH_SEDNA_AUTOCOMMIT(conn, val);
529
- rb_iv_set(self, "@autocommit", val);
741
+
742
+ // Set the instance variable accordingly so it can be re-set after a transaction.
743
+ rb_iv_set(self, IV_AUTOCOMMIT, val);
530
744
 
745
+ // Always return nil if successful.
531
746
  return Qnil;
532
747
  }
533
748
 
@@ -537,74 +752,163 @@ static VALUE cSedna_autocommit_set(VALUE self, VALUE auto_commit)
537
752
  */
538
753
  static VALUE cSedna_autocommit_get(VALUE self)
539
754
  {
540
- return rb_iv_get(self, "@autocommit");
755
+ return rb_iv_get(self, IV_AUTOCOMMIT);
541
756
  }
542
757
 
543
758
  /*
544
759
  * call-seq:
545
760
  * sedna.transaction { ... } -> nil
761
+ * sedna.transaction -> nil
546
762
  *
547
- * Wraps the given block in a \transaction. If the block runs
548
- * completely, the \transaction is committed. If the stack is unwinded
549
- * prematurely, the \transaction is rolled back. This typically happens
550
- * when an \Exception is raised by calling +raise+ or a Symbol is thrown by
551
- * invoking +throw+. Note that Exceptions will not be rescued -- they will be
552
- * re-raised after rolling back the \transaction.
763
+ * Wraps the given block in a transaction. If the block runs completely, the
764
+ * transaction is committed. If the stack is unwinded prematurely, the
765
+ * transaction is rolled back. This typically happens when an exception is
766
+ * raised by calling +raise+ or a Symbol is thrown by invoking +throw+. Note
767
+ * that exceptions will not be rescued -- they will be re-raised after rolling
768
+ * back the transaction.
553
769
  *
554
- * This method returns +nil+ if the \transaction is successfully committed
770
+ * This method returns +nil+ if the transaction is successfully committed
555
771
  * to the database. If the given block completes successfully, but the
556
- * \transaction fails to be committed, a Sedna::TransactionError will
557
- * be raised.
772
+ * transaction fails to be committed, a Sedna::TransactionError will be raised.
773
+ *
774
+ * Transactions cannot be nested or executed simultaneously with the same
775
+ * connection. Calling this method inside a block that is passed to another
776
+ * transaction, or with the same connection in two concurrent threads will
777
+ * raise a Sedna::TransactionError on the second invocation.
558
778
  *
559
- * Transactions cannot be nested or executed simultaneously with the same connection.
560
- * Calling this method inside a block that is passed to another transaction, or
561
- * with the same connection in two concurrent threads will raise a
562
- * Sedna::TransactionError on the second invocation.
779
+ * If no block is given, this method only signals the beginning of a new
780
+ * transaction. A subsequent call to Sedna#commit or Sedna#rollback is required
781
+ * to end the transaction. Note that invoking this method with a block is the
782
+ * preferred way of executing transactions, because any exceptions that may be
783
+ * raised will automatically trigger a proper transaction rollback. Only call
784
+ * +commit+ and +rollback+ directly if you cannot use a block to wrap your
785
+ * transaction in.
563
786
  *
564
787
  * ==== Examples
565
788
  *
789
+ * Transactions are committed after the given block ends.
790
+ *
566
791
  * sedna.transaction do
567
- * count = sedna.execute "count(collection('my_col')/items)" #=> 0
568
- * sedna.execute "update insert <total>#{count}</total> into doc('my_doc')"
792
+ * amount = 100
793
+ * sedna.execute "update replace $balance in doc('my_account')/balance
794
+ * with <balance>{$balance - #{amount}}</balance>"
795
+ * sedna.execute "update replace $balance in doc('your_account')/balance
796
+ * with <balance>{$balance + #{amount}}</balance>"
569
797
  * # ...
570
798
  * end
571
799
  * # Transaction is committed.
572
800
  *
801
+ * Transactions are rolled back if something is thrown from inside the block.
802
+ *
803
+ * sedna.transaction do
804
+ * articles = sedna.execute "for $a in collection('articles')
805
+ * where $a/article/author = 'me' return $a"
806
+ * throw :no_articles if articles.empty?
807
+ * # ... never get here
808
+ * end
809
+ * # Transaction is rolled back.
810
+ *
811
+ * Transactions are also rolled back if an exception is raised inside the block.
812
+ *
573
813
  * sedna.transaction do
574
- * count = sedna.execute "count(collection('my_col')/items)" #=> 0
575
- * throw :no_items if count == 0
814
+ * amount = 100
815
+ * sedna.execute "update replace $balance in doc('my_account')/balance
816
+ * with <balance>{$balance - #{amount}}</balance>"
817
+ * new_balance = sedna.execute "doc('my_account')/balance"
818
+ * raise "Insufficient funds" if new_balance.to_i < 0
576
819
  * # ... never get here
577
820
  * end
578
821
  * # Transaction is rolled back.
822
+ *
823
+ * If you really have to, you can also use transactions declaratively. Make
824
+ * sure to roll back the transaction if something goes wrong!
825
+ *
826
+ * sedna.transaction
827
+ * begin
828
+ * amount = 100
829
+ * sedna.execute "update replace $balance in doc('my_account')/balance
830
+ * with <balance>{$balance - #{amount}}</balance>"
831
+ * sedna.execute "update replace $balance in doc('your_account')/balance
832
+ * with <balance>{$balance + #{amount}}</balance>"
833
+ * rescue Exception
834
+ * sedna.rollback
835
+ * else
836
+ * sedna.commit
837
+ * end
579
838
  */
580
839
  static VALUE cSedna_transaction(VALUE self)
581
840
  {
582
841
  int status;
583
842
  SC *conn = sedna_struct(self);
584
843
 
585
- SEDNA_AUTOCOMMIT_DISABLE(conn);
586
- sedna_tr_begin(conn);
844
+ // Begin the transaction.
845
+ sedna_begin(conn);
587
846
 
588
- rb_protect(rb_yield, Qnil, &status);
847
+ if(rb_block_given_p()) {
848
+ // Yield to the given block and protect it so we can always commit or rollback.
849
+ rb_protect(rb_yield, Qnil, &status);
589
850
 
590
- if(status == 0) {
591
- // Attempt to commit.
592
- if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) sedna_tr_commit(conn);
593
- else rb_raise(cSednaTrnError, "The transaction was prematurely ended, but no error was encountered. Did you rescue an exception inside the transaction?");
851
+ if(status == 0) {
852
+ // Attempt to commit if block completed successfully.
853
+ sedna_commit(conn, self);
854
+ } else {
855
+ // Stack has unwinded, attempt to roll back!
856
+ sedna_rollback(conn, self);
594
857
 
595
- SWITCH_SEDNA_AUTOCOMMIT(conn, rb_iv_get(self, "@autocommit"));
596
- } else {
597
- // Stack has unwinded, attempt to roll back.
598
- if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) sedna_tr_rollback(conn);
858
+ // Re-raise any exception or re-throw whatever was thrown.
859
+ rb_jump_tag(status);
860
+ }
861
+ }
599
862
 
600
- SWITCH_SEDNA_AUTOCOMMIT(conn, rb_iv_get(self, "@autocommit"));
863
+ // Always return nil if successful.
864
+ return Qnil;
865
+ }
601
866
 
602
- rb_jump_tag(status); // Re-raise exception.
603
- }
867
+ /*
868
+ * call-seq:
869
+ * sedna.commit -> nil
870
+ *
871
+ * Commits a currently active transaction. Only use this method if you are
872
+ * specifying a transaction declaratively. Invoking Sedna#transaction with a
873
+ * block will automatically commit the transaction if the block finishes
874
+ * successfully.
875
+ *
876
+ * This method will raise a Sedna::TransactionError if no transaction is in
877
+ * progress when it is called.
878
+ */
879
+ static VALUE cSedna_commit(VALUE self)
880
+ {
881
+ SC *conn = sedna_struct(self);
604
882
 
883
+ // Attempt to commit.
884
+ sedna_commit(conn, self);
885
+
886
+ // Always return nil if successful.
605
887
  return Qnil;
606
888
  }
607
889
 
890
+ /*
891
+ * call-seq:
892
+ * sedna.rollback -> nil
893
+ *
894
+ * Rolls back a currently active transaction. Only use this method if you are
895
+ * specifying a transaction declaratively. Invoking Sedna#transaction with a
896
+ * block will automatically roll back the transaction if an exception is raised
897
+ * or if the stack is unwinded for whatever reason.
898
+ *
899
+ * This method will do nothing if no transaction is in progress when it is
900
+ * called.
901
+ */
902
+ static VALUE cSedna_rollback(VALUE self)
903
+ {
904
+ SC *conn = sedna_struct(self);
905
+
906
+ // Attempt to roll back.
907
+ sedna_rollback(conn, self);
908
+
909
+ // Always return nil if successful.
910
+ return Qnil;
911
+ }
608
912
 
609
913
  // Initialize the extension ==============================================
610
914
 
@@ -621,7 +925,8 @@ void Init_sedna()
621
925
  * :username => "SYSTEM",
622
926
  * :password => "MANAGER",
623
927
  * }
624
- * Sedna.connect(connection_details) do |sedna|
928
+ *
929
+ * Sedna.connect connection_details do |sedna|
625
930
  * # Query the database.
626
931
  * # The connection is closed automatically.
627
932
  * end
@@ -636,19 +941,22 @@ void Init_sedna()
636
941
  rb_define_singleton_method(cSedna, "blocking?", cSedna_s_blocking, 0);
637
942
 
638
943
  rb_define_method(cSedna, "initialize", cSedna_initialize, 1);
639
- rb_define_method(cSedna, "execute", cSedna_execute, 1);
640
- rb_define_method(cSedna, "load_document", cSedna_load_document, -1);
944
+ rb_define_method(cSedna, "connected?", cSedna_connected, 0);
641
945
  rb_define_method(cSedna, "close", cSedna_close, 0);
946
+ rb_define_method(cSedna, "reset", cSedna_reset, 0);
642
947
  rb_define_method(cSedna, "transaction", cSedna_transaction, 0);
643
-
948
+ rb_define_method(cSedna, "commit", cSedna_commit, 0);
949
+ rb_define_method(cSedna, "rollback", cSedna_rollback, 0);
950
+ rb_define_method(cSedna, "execute", cSedna_execute, 1);
644
951
  rb_define_undocumented_alias(cSedna, "query", "execute");
952
+ rb_define_method(cSedna, "load_document", cSedna_load_document, -1);
645
953
 
646
954
  /*
647
955
  * Document-attr: autocommit
648
956
  *
649
957
  * When autocommit is set to +true+ (default), database queries can be run
650
958
  * without explicitly wrapping them in a transaction. Each query that is not
651
- * part of a \transaction is automatically committed to the database.
959
+ * part of a transaction is automatically committed to the database.
652
960
  * Explicit transactions in auto-commit mode will still be committed
653
961
  * atomically.
654
962
  *
@@ -664,9 +972,12 @@ void Init_sedna()
664
972
  rb_define_method(cSedna, "autocommit", cSedna_autocommit_get, 0);
665
973
 
666
974
  /*
667
- * The result set of a database query.
975
+ * The result of a database query is stored in a Sedna::Set object, which
976
+ * is a subclass of Array. Additional details about the executed query, such
977
+ * as timing and debug information, may be added to Sedna::Set objects in
978
+ * future versions of this library.
668
979
  */
669
- // Unused so far...
980
+ // Stick to Array for result sets.
670
981
  //cSednaSet = rb_define_class_under(cSedna, "Set", rb_cArray);
671
982
 
672
983
  /*