sedna 0.2.0 → 0.3.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 +11 -0
  2. data/README +6 -3
  3. data/Rakefile +91 -0
  4. data/ext/extconf.rb +9 -0
  5. data/ext/sedna.c +162 -28
  6. data/test/sedna_test.rb +271 -113
  7. metadata +3 -2
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ === 0.3.0
2
+
3
+ * Released on December 21st, 2008.
4
+ * Compatible with the latest build of \Sedna (3.1.126).
5
+ * Fully tested in Ruby 1.8.5, 1.8.6, 1.8.7, and 1.9.1-preview2. (1.9.0.x does not work.)
6
+ * Sedna#execute no longer blocks other threads in Ruby 1.9. Use Sedna.blocking?
7
+ to discover if support for non-blocking queries is enabled.
8
+ * Strings in the result Array of Sedna#execute are now properly tainted.
9
+ * Thread-safe in both Ruby 1.8 and Ruby 1.9.
10
+ * Added Sedna.version, which returns the current client protocol version.
11
+
1
12
  === 0.2.0
2
13
 
3
14
  * Released on December 15th, 2008.
data/README CHANGED
@@ -32,9 +32,9 @@ limitations under the License.
32
32
 
33
33
  === Current version
34
34
 
35
- The current version of this library is 0.2.0. This version is a <b>development
36
- preview</b>. The API may change significantly between minor versions.
37
- For a complete overview all recent and previous changes, see CHANGES.
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.
38
38
 
39
39
  === Requirements
40
40
 
@@ -43,6 +43,9 @@ header files of the C driver. They are shipped and installed as part of the bina
43
43
  distribution of \Sedna. When installing, choose either <tt>/usr/local/sedna</tt> or
44
44
  <tt>/opt/sedna</tt> as target locations.
45
45
 
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.
48
+
46
49
  === Installation
47
50
 
48
51
  After installing the \Sedna C driver (see above), simply install the Ruby client
data/Rakefile ADDED
@@ -0,0 +1,91 @@
1
+ # Copyright 2008 Voormedia B.V.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Ruby extension library providing a client API to the Sedna native XML
16
+ # database management system, based on the official Sedna C driver.
17
+
18
+ # This file defines how to build a Rubygem for the Sedna client library.
19
+
20
+ require 'rubygems'
21
+ require 'rdoc' # Use latest RDoc version.
22
+ require 'rake'
23
+ require 'rake/testtask'
24
+ require 'rake/gempackagetask'
25
+ require 'rake/rdoctask'
26
+
27
+ RDOC_TITLE = "Sedna XML DBMS client library for Ruby"
28
+ RDOC_FILES = FileList["[A-Z][A-Z]*", "ext/**/*.c"].to_a
29
+
30
+ task :default => [:rebuild, :test]
31
+
32
+ task :multi do
33
+ exec "export EXCLUDED_VERSIONS=v1_9_0 && multiruby -S rake"
34
+ end
35
+
36
+ desc "Build the Ruby extension"
37
+ task :build do
38
+ Dir.chdir "ext"
39
+ ruby "extconf.rb"
40
+ sh "make"
41
+ Dir.chdir ".."
42
+ end
43
+
44
+ desc "Remove build products"
45
+ task :clobber_build do
46
+ sh "rm -f ext/*.{so,o,log}"
47
+ sh "rm -f ext/Makefile"
48
+ end
49
+
50
+ desc "Force a rebuild of the Ruby extension"
51
+ task :rebuild => [:clobber_build, :build]
52
+
53
+ Rake::TestTask.new do |t|
54
+ t.test_files = FileList["test/*_test.rb"]
55
+ t.verbose = true
56
+ end
57
+
58
+ gem_spec = Gem::Specification.new do |s|
59
+ s.name = "sedna"
60
+ s.version = "0.3.0"
61
+
62
+ s.summary = "Sedna XML DBMS client library."
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.}
64
+ s.requirements = %{Sedna XML DBMS C driver (library and headers).}
65
+ s.author = "Rolf Timmermans"
66
+ s.email = "r.timmermans@voormedia.com"
67
+ s.homepage = "http://sedna.rubyforge.org/"
68
+ s.rubyforge_project = "sedna"
69
+
70
+ s.extensions << "ext/extconf.rb"
71
+ s.files = FileList["Rakefile", "ext/extconf.rb", "ext/**/*.c", "test/**/*.rb"].to_a
72
+ s.require_path = "lib"
73
+
74
+ s.has_rdoc = true
75
+ s.extra_rdoc_files = RDOC_FILES
76
+ s.rdoc_options << "--title" << RDOC_TITLE << "--main" << "README"
77
+
78
+ s.test_files = FileList["test/**/*_test.rb"].to_a
79
+ end
80
+
81
+ Rake::GemPackageTask.new gem_spec do |p|
82
+ p.gem_spec = gem_spec
83
+ p.need_tar_gz = true
84
+ p.need_zip = true
85
+ end
86
+
87
+ Rake::RDocTask.new { |rdoc|
88
+ rdoc.rdoc_dir = 'doc'
89
+ rdoc.title = RDOC_TITLE
90
+ rdoc.rdoc_files.include *RDOC_FILES
91
+ }
data/ext/extconf.rb CHANGED
@@ -68,4 +68,13 @@ 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
79
+
71
80
  create_makefile "sedna"
data/ext/sedna.c CHANGED
@@ -13,33 +13,65 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  *
16
+ * ========================================================================
17
+ *
16
18
  * Ruby extension library providing a client API to the Sedna native XML
17
19
  * database management system, based on the official Sedna C driver.
18
20
  *
19
21
  * This file contains the Ruby C-extension.
22
+ *
23
+ * ========================================================================
20
24
  */
21
25
 
22
26
  #include <string.h>
23
27
  #include "ruby.h"
24
28
  #include "libsedna.h"
25
29
 
26
- // Size of the read buffer.
30
+ // Size of the query result read buffer.
27
31
  #define RESULT_BUF_LEN 8192
32
+
33
+ // Size of the load_document buffer.
28
34
  #define LOAD_BUF_LEN 8192
29
35
 
30
- // Use macros to fool RDoc and hide some of our methods/aliases.
36
+ // Use this macro to fool RDoc and hide some aliases.
31
37
  #define rb_define_undocumented_alias(kl, new, old) rb_define_alias(kl, new, old)
32
38
 
33
39
  // Macro for verification of return values of the Sedna API + error handling.
34
- #define verify_res(expected, real, conn) if(real != expected) sedna_err(conn, real)
40
+ #define VERIFY_RES(expected, real, conn) if(real != expected) sedna_err(conn, real)
35
41
 
36
42
  // Macro for setting autocommit values on connection conn.
37
- #define sedna_autocommit_on(conn) sedna_autocommit(conn, SEDNA_AUTOCOMMIT_ON)
38
- #define sedna_autocommit_off(conn) sedna_autocommit(conn, SEDNA_AUTOCOMMIT_OFF)
39
- #define switch_sedna_autocommit(conn, val) if(val) sedna_autocommit_on(conn); else sedna_autocommit_off(conn)
43
+ #define SEDNA_AUTOCOMMIT_ENABLE(conn) sedna_autocommit(conn, SEDNA_AUTOCOMMIT_ON)
44
+ #define SEDNA_AUTOCOMMIT_DISABLE(conn) sedna_autocommit(conn, SEDNA_AUTOCOMMIT_OFF)
45
+ #define SWITCH_SEDNA_AUTOCOMMIT(conn, val) if(val) SEDNA_AUTOCOMMIT_ENABLE(conn); else SEDNA_AUTOCOMMIT_DISABLE(conn)
46
+
47
+ // Define the protocol version number as string.
48
+ #define PROTO_STRINGIFY(s) #s
49
+ #define PROTO_TO_STRING(s) PROTO_STRINGIFY(s)
50
+ #define PROTOCOL_VERSION PROTO_TO_STRING(SE_CURRENT_SOCKET_PROTOCOL_VERSION_MAJOR) "." PROTO_TO_STRING(SE_CURRENT_SOCKET_PROTOCOL_VERSION_MINOR)
51
+
52
+ // Default connection arguments
53
+ #define DEFAULT_HOST "localhost"
54
+ #define DEFAULT_DATABASE "test"
55
+ #define DEFAULT_USER "SYSTEM"
56
+ #define DEFAULT_PASSWORD "MANAGER"
40
57
 
58
+ // Define a shorthand for the common SednaConnection structure.
59
+ typedef struct SednaConnection SC;
60
+
61
+ #ifdef HAVE_RB_THREAD_BLOCKING_REGION
62
+ #define NON_BLOCKING
63
+ #define SEDNA_BLOCKING Qfalse
64
+
65
+ struct SednaQuery {
66
+ void *conn;
67
+ void *query;
68
+ };
69
+ typedef struct SednaQuery SQ;
70
+ #else
71
+ #define SEDNA_BLOCKING Qtrue
72
+ #endif
41
73
 
42
- // Our classes.
74
+ // Ruby classes.
43
75
  static VALUE cSedna;
44
76
  //static VALUE cSednaSet; //Unused so far.
45
77
  static VALUE cSednaException;
@@ -47,9 +79,8 @@ static VALUE cSednaAuthError;
47
79
  static VALUE cSednaConnError;
48
80
  static VALUE cSednaTrnError;
49
81
 
50
- // Define a shorthand for the common SednaConnection structure.
51
- typedef struct SednaConnection SC;
52
82
 
83
+ // Common functions =======================================================
53
84
 
54
85
  // Test the last error message for conn, and raise an exception if there is one.
55
86
  // The type of the exception is based on the result of the function that was
@@ -59,6 +90,7 @@ static void sedna_err(SC *conn, int res)
59
90
  VALUE exception;
60
91
  const char *msg;
61
92
  char *err, *details, *p;
93
+
62
94
  switch(res) {
63
95
  case SEDNA_AUTHENTICATION_FAILED:
64
96
  exception = cSednaAuthError; break;
@@ -73,15 +105,18 @@ static void sedna_err(SC *conn, int res)
73
105
  default:
74
106
  exception = cSednaException;
75
107
  }
108
+
76
109
  msg = SEgetLastErrorMsg(conn);
77
110
  err = strstr(msg, "\n");
78
111
  details = strstr(err, "\nDetails: ");
112
+
79
113
  if(err != NULL) {
80
114
  err++;
81
115
  if((p = strstr(err, "\n")) != NULL) strncpy(p, "\0", 1);
82
116
  } else {
83
117
  err = "Unknown error.";
84
118
  }
119
+
85
120
  if(details != NULL) {
86
121
  details += 10;
87
122
  while((p = strstr(details, "\n")) != NULL) strncpy(p, " ", 1);
@@ -111,12 +146,26 @@ static void sedna_free(SC *conn)
111
146
  static void sedna_mark(SC *conn)
112
147
  { /* Unused. */ }
113
148
 
149
+ #ifdef NON_BLOCKING
150
+ static int sedna_blocking_execute(SQ *q)
151
+ {
152
+ return SEexecute(q->conn, q->query);
153
+ }
154
+
155
+ static int sedna_execute(SQ *q)
156
+ {
157
+ return rb_thread_blocking_region((void*)sedna_blocking_execute, q, RUBY_UBF_IO, NULL);
158
+ }
159
+ #endif
160
+
114
161
  // Read one record completely and return it as a Ruby String object.
115
162
  static VALUE sedna_read(SC *conn, int strip_n)
116
163
  {
117
164
  int bytes_read = 0;
118
165
  char buffer[RESULT_BUF_LEN];
119
166
  VALUE str = rb_str_buf_new(0);
167
+ OBJ_TAINT(str);
168
+
120
169
  do {
121
170
  bytes_read = SEgetData(conn, buffer, RESULT_BUF_LEN - 1);
122
171
  if(bytes_read == SEDNA_ERROR) {
@@ -137,6 +186,7 @@ static VALUE sedna_read(SC *conn, int strip_n)
137
186
  }
138
187
  }
139
188
  } while(bytes_read > 0);
189
+
140
190
  return str;
141
191
  }
142
192
 
@@ -145,6 +195,7 @@ static VALUE sedna_get_results(SC *conn)
145
195
  {
146
196
  int res, strip_n = 0;
147
197
  VALUE set = rb_ary_new();
198
+
148
199
  while((res = SEnext(conn)) != SEDNA_RESULT_END) {
149
200
  if(res == SEDNA_ERROR) sedna_err(conn, res);
150
201
  // Set strip_n to 1 for all results except the first. This will cause
@@ -152,6 +203,7 @@ static VALUE sedna_get_results(SC *conn)
152
203
  rb_ary_push(set, sedna_read(conn, strip_n));
153
204
  if(!strip_n) strip_n = 1;
154
205
  };
206
+
155
207
  return set;
156
208
  }
157
209
 
@@ -159,37 +211,41 @@ static VALUE sedna_get_results(SC *conn)
159
211
  static void sedna_autocommit(SC *conn, int value)
160
212
  {
161
213
  int res = SEsetConnectionAttr(conn, SEDNA_ATTR_AUTOCOMMIT, (void *)&value, sizeof(int));
162
- verify_res(SEDNA_SET_ATTRIBUTE_SUCCEEDED, res, conn);
214
+ VERIFY_RES(SEDNA_SET_ATTRIBUTE_SUCCEEDED, res, conn);
163
215
  }
164
216
 
165
217
  // Begin a transaction.
166
218
  static void sedna_tr_begin(SC *conn)
167
219
  {
168
220
  int res = SEbegin(conn);
169
- verify_res(SEDNA_BEGIN_TRANSACTION_SUCCEEDED, res, conn);
221
+ VERIFY_RES(SEDNA_BEGIN_TRANSACTION_SUCCEEDED, res, conn);
170
222
  }
171
223
 
172
224
  // Commit a transaction.
173
225
  static void sedna_tr_commit(SC *conn)
174
226
  {
175
227
  int res = SEcommit(conn);
176
- verify_res(SEDNA_COMMIT_TRANSACTION_SUCCEEDED, res, conn);
228
+ VERIFY_RES(SEDNA_COMMIT_TRANSACTION_SUCCEEDED, res, conn);
177
229
  }
178
230
 
179
231
  // Rollback a transaction.
180
232
  static void sedna_tr_rollback(SC *conn)
181
233
  {
182
234
  int res = SErollback(conn);
183
- verify_res(SEDNA_ROLLBACK_TRANSACTION_SUCCEEDED, res, conn);
235
+ VERIFY_RES(SEDNA_ROLLBACK_TRANSACTION_SUCCEEDED, res, conn);
184
236
  }
185
237
 
238
+
239
+ // Functions available from Ruby ==========================================
240
+
186
241
  // Alocates memory for a SednaConnection struct.
187
242
  static VALUE cSedna_s_new(VALUE klass)
188
243
  {
189
244
  SC *conn, init = SEDNA_CONNECTION_INITIALIZER;
190
- conn = (SC *)malloc(sizeof(SC));
245
+ conn = ALLOC(SC);
191
246
  if(conn == NULL) rb_raise(rb_eNoMemError, "Could not allocate memory.");
192
247
  memcpy(conn, &init, sizeof(init));
248
+
193
249
  return Data_Wrap_Struct(klass, sedna_mark, sedna_free, conn);
194
250
  }
195
251
 
@@ -210,10 +266,10 @@ static VALUE cSedna_initialize(VALUE self, VALUE options)
210
266
  user_k = ID2SYM(rb_intern("username"));
211
267
  pw_k = ID2SYM(rb_intern("password"));
212
268
 
213
- if(NIL_P(host_v = rb_hash_aref(options, host_k))) host = "localhost"; else host = STR2CSTR(host_v);
214
- if(NIL_P(db_v = rb_hash_aref(options, db_k))) db = "test"; else db = STR2CSTR(db_v);
215
- if(NIL_P(user_v = rb_hash_aref(options, user_k))) user = "SYSTEM"; else user = STR2CSTR(user_v);
216
- if(NIL_P(pw_v = rb_hash_aref(options, pw_k))) pw = "MANAGER"; else pw = STR2CSTR(pw_v);
269
+ 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);
271
+ 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);
217
273
 
218
274
  res = SEconnect(conn, host, db, user, pw);
219
275
  if(res != SEDNA_SESSION_OPEN) {
@@ -230,6 +286,10 @@ static VALUE cSedna_initialize(VALUE self, VALUE options)
230
286
  // Initialize @autocommit to true (default argument).
231
287
  rb_iv_set(self, "@autocommit", Qtrue);
232
288
 
289
+ #ifdef NON_BLOCKING
290
+ rb_iv_set(self, "@mutex", rb_mutex_new());
291
+ #endif
292
+
233
293
  return self;
234
294
  }
235
295
 
@@ -245,10 +305,12 @@ static VALUE cSedna_close(VALUE self)
245
305
  {
246
306
  int res;
247
307
  SC *conn = sedna_struct(self);
308
+
248
309
  if(SEconnectionStatus(conn) != SEDNA_CONNECTION_CLOSED) {
249
310
  res = SEclose(conn);
250
- verify_res(SEDNA_SESSION_CLOSED, res, conn);
311
+ VERIFY_RES(SEDNA_SESSION_CLOSED, res, conn);
251
312
  }
313
+
252
314
  return Qnil;
253
315
  }
254
316
 
@@ -292,16 +354,43 @@ static VALUE cSedna_s_connect(VALUE klass, VALUE options)
292
354
  {
293
355
  int status;
294
356
  VALUE obj = rb_funcall(klass, rb_intern("new"), 1, options);
357
+
295
358
  if(rb_block_given_p()) {
296
359
  rb_protect(rb_yield, obj, &status);
297
360
  cSedna_close(obj);
298
361
  if(status != 0) rb_jump_tag(status); // Re-raise any exception.
362
+
299
363
  return Qnil;
300
364
  } else {
301
365
  return obj;
302
366
  }
303
367
  }
304
368
 
369
+ /*
370
+ * call-seq:
371
+ * Sedna.version -> string
372
+ *
373
+ * Returns the current version of the Sedna client protocol.
374
+ */
375
+ static VALUE cSedna_s_version(VALUE klass)
376
+ {
377
+ return rb_str_new2(PROTOCOL_VERSION);
378
+ }
379
+
380
+ /*
381
+ * call-seq:
382
+ * Sedna.blocking? -> true or false
383
+ *
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.
388
+ */
389
+ static VALUE cSedna_s_blocking(VALUE klass)
390
+ {
391
+ return SEDNA_BLOCKING;
392
+ }
393
+
305
394
  /*
306
395
  * call-seq:
307
396
  * sedna.execute(query) -> array or nil
@@ -314,6 +403,12 @@ static VALUE cSedna_s_connect(VALUE klass, VALUE options)
314
403
  * query on a closed connection, a Sedna::ConnectionError will be raised. A
315
404
  * Sedna::Exception is raised if the query fails or is invalid.
316
405
  *
406
+ * This method does not block other threads in Ruby 1.9 -- database queries that
407
+ * are run in different threads with different connections will run concurrently.
408
+ * You can use Sedna.blocking? to verify if the extension supports non-blocking
409
+ * behaviour. Database queries run from different threads, but on the same
410
+ * connection will still block and be executed serially.
411
+ *
317
412
  * ==== Examples
318
413
  *
319
414
  * Create a new document.
@@ -336,8 +431,18 @@ static VALUE cSedna_execute(VALUE self, VALUE query)
336
431
  {
337
432
  int res;
338
433
  SC *conn = sedna_struct(self);
434
+
339
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.
439
+ 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.
340
443
  res = SEexecute(conn, STR2CSTR(query));
444
+ #endif
445
+
341
446
  switch(res) {
342
447
  case SEDNA_QUERY_SUCCEEDED:
343
448
  return sedna_get_results(conn);
@@ -384,23 +489,30 @@ static VALUE cSedna_load_document(int argc, VALUE *argv, VALUE self)
384
489
  SC *conn = sedna_struct(self);
385
490
  VALUE document, doc_name, col_name, buf;
386
491
  char *doc_name_c, *col_name_c;
492
+
387
493
  if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
494
+
388
495
  rb_scan_args(argc, argv, "21", &document, &doc_name, &col_name); // 2 mandatory args, 1 optional.
389
496
  doc_name_c = STR2CSTR(doc_name);
390
497
  col_name_c = NIL_P(col_name) ? NULL : STR2CSTR(col_name);
498
+
391
499
  if(TYPE(document) == T_FILE) {
392
500
  while(!NIL_P(buf = rb_funcall(document, rb_intern("read"), 1, INT2NUM(LOAD_BUF_LEN)))) {
393
501
  res = SEloadData(conn, STR2CSTR(buf), RSTRING_LEN(buf), doc_name_c, col_name_c);
394
- verify_res(SEDNA_DATA_CHUNK_LOADED, res, conn);
502
+ VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
395
503
  }
396
504
  if(res == 0) rb_raise(cSednaException, "Document is empty.");
397
505
  } else {
506
+ Check_Type(document, T_STRING);
398
507
  if(RSTRING_LEN(document) == 0) rb_raise(cSednaException, "Document is empty.");
508
+
399
509
  res = SEloadData(conn, STR2CSTR(document), RSTRING_LEN(document), doc_name_c, col_name_c);
400
- verify_res(SEDNA_DATA_CHUNK_LOADED, res, conn);
510
+ VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
401
511
  }
512
+
402
513
  res = SEendLoadData(conn);
403
- verify_res(SEDNA_BULK_LOAD_SUCCEEDED, res, conn);
514
+ VERIFY_RES(SEDNA_BULK_LOAD_SUCCEEDED, res, conn);
515
+
404
516
  return Qnil;
405
517
  }
406
518
 
@@ -412,8 +524,10 @@ static VALUE cSedna_autocommit_set(VALUE self, VALUE auto_commit)
412
524
  {
413
525
  int val = (RTEST(auto_commit) ? Qtrue : Qfalse);
414
526
  SC *conn = sedna_struct(self);
415
- switch_sedna_autocommit(conn, val);
527
+
528
+ SWITCH_SEDNA_AUTOCOMMIT(conn, val);
416
529
  rb_iv_set(self, "@autocommit", val);
530
+
417
531
  return Qnil;
418
532
  }
419
533
 
@@ -442,6 +556,11 @@ static VALUE cSedna_autocommit_get(VALUE self)
442
556
  * \transaction fails to be committed, a Sedna::TransactionError will
443
557
  * be raised.
444
558
  *
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.
563
+ *
445
564
  * ==== Examples
446
565
  *
447
566
  * sedna.transaction do
@@ -462,23 +581,33 @@ static VALUE cSedna_transaction(VALUE self)
462
581
  {
463
582
  int status;
464
583
  SC *conn = sedna_struct(self);
465
- sedna_autocommit_off(conn);
584
+
585
+ SEDNA_AUTOCOMMIT_DISABLE(conn);
466
586
  sedna_tr_begin(conn);
587
+
467
588
  rb_protect(rb_yield, Qnil, &status);
589
+
468
590
  if(status == 0) {
469
591
  // Attempt to commit.
470
592
  if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) sedna_tr_commit(conn);
471
593
  else rb_raise(cSednaTrnError, "The transaction was prematurely ended, but no error was encountered. Did you rescue an exception inside the transaction?");
472
- switch_sedna_autocommit(conn, rb_iv_get(self, "@autocommit"));
594
+
595
+ SWITCH_SEDNA_AUTOCOMMIT(conn, rb_iv_get(self, "@autocommit"));
473
596
  } else {
474
597
  // Stack has unwinded, attempt to roll back.
475
598
  if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) sedna_tr_rollback(conn);
476
- switch_sedna_autocommit(conn, rb_iv_get(self, "@autocommit"));
599
+
600
+ SWITCH_SEDNA_AUTOCOMMIT(conn, rb_iv_get(self, "@autocommit"));
601
+
477
602
  rb_jump_tag(status); // Re-raise exception.
478
603
  }
604
+
479
605
  return Qnil;
480
606
  }
481
607
 
608
+
609
+ // Initialize the extension ==============================================
610
+
482
611
  void Init_sedna()
483
612
  {
484
613
  /*
@@ -500,13 +629,19 @@ void Init_sedna()
500
629
  * See the README for a high-level overview of how to use this library.
501
630
  */
502
631
  cSedna = rb_define_class("Sedna", rb_cObject);
632
+
503
633
  rb_define_alloc_func(cSedna, cSedna_s_new);
504
634
  rb_define_singleton_method(cSedna, "connect", cSedna_s_connect, 1);
635
+ rb_define_singleton_method(cSedna, "version", cSedna_s_version, 0);
636
+ rb_define_singleton_method(cSedna, "blocking?", cSedna_s_blocking, 0);
637
+
505
638
  rb_define_method(cSedna, "initialize", cSedna_initialize, 1);
506
639
  rb_define_method(cSedna, "execute", cSedna_execute, 1);
507
640
  rb_define_method(cSedna, "load_document", cSedna_load_document, -1);
508
- rb_define_undocumented_alias(cSedna, "query", "execute");
509
641
  rb_define_method(cSedna, "close", cSedna_close, 0);
642
+ rb_define_method(cSedna, "transaction", cSedna_transaction, 0);
643
+
644
+ rb_define_undocumented_alias(cSedna, "query", "execute");
510
645
 
511
646
  /*
512
647
  * Document-attr: autocommit
@@ -527,7 +662,6 @@ void Init_sedna()
527
662
  */
528
663
  rb_define_method(cSedna, "autocommit=", cSedna_autocommit_set, 1);
529
664
  rb_define_method(cSedna, "autocommit", cSedna_autocommit_get, 0);
530
- rb_define_method(cSedna, "transaction", cSedna_transaction, 0);
531
665
 
532
666
  /*
533
667
  * The result set of a database query.
data/test/sedna_test.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # encoding: utf-8
2
3
 
3
4
  # Copyright 2008 Voormedia B.V.
4
5
  #
@@ -27,6 +28,23 @@ require 'sedna'
27
28
  require 'socket'
28
29
 
29
30
  class SednaTest < Test::Unit::TestCase
31
+ # Support declarative specification of test methods.
32
+ def self.test name, &block
33
+ test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
34
+ defined = instance_method(test_name) rescue false
35
+ raise "#{test_name} is already defined in #{self}" if defined
36
+ if block_given?
37
+ define_method test_name, &block
38
+ else
39
+ define_method test_name do
40
+ flunk "No implementation provided for #{test_name}"
41
+ end
42
+ end
43
+ end
44
+
45
+ alias :__method__ :method_name if method_defined? :method_name
46
+
47
+ # Setup.
30
48
  def setup
31
49
  @connection = {
32
50
  :database => "test",
@@ -35,13 +53,10 @@ class SednaTest < Test::Unit::TestCase
35
53
  :password => "MANAGER",
36
54
  }
37
55
  end
38
-
39
- def teardown
40
- end
41
56
 
42
57
  # Faux test that just checks if we can connect, otherwise the test
43
58
  # suite is aborted.
44
- def test_aaa_connection
59
+ test "aaa connection" do
45
60
  port = 5050
46
61
  begin
47
62
  s = TCPSocket.new @connection[:host], port
@@ -54,26 +69,46 @@ class SednaTest < Test::Unit::TestCase
54
69
  s.close
55
70
  end
56
71
 
72
+ # Test Sedna.version.
73
+ test "version should return 3 0" do
74
+ assert_equal "3.0", Sedna.version
75
+ end
76
+
77
+ # Test Sedna.blocking?
78
+ test "blocking should return true if ruby 18 and false if ruby 19" do
79
+ if RUBY_VERSION < "1.9"
80
+ assert Sedna.blocking?
81
+ else
82
+ assert !Sedna.blocking?
83
+ end
84
+ end
85
+
57
86
  # Test Sedna.connect.
58
- def test_connect_should_return_sedna_object
87
+ test "connect should return Sedna object" do
59
88
  sedna = Sedna.connect @connection
60
89
  assert_kind_of Sedna, sedna
61
90
  sedna.close
62
91
  end
92
+
93
+ test "connect should raise TypeError if argument is not a Hash" do
94
+ assert_raises TypeError do
95
+ sedna = Sedna.connect Object.new
96
+ end
97
+ end
63
98
 
64
- def test_connect_should_raise_exception_when_host_not_found
99
+ test "connect should raise exception when host not found" do
65
100
  assert_raises Sedna::ConnectionError do
66
101
  Sedna.connect @connection.merge(:host => "non-existent-host")
67
102
  end
68
103
  end
69
104
 
70
- def test_connect_should_raise_exception_when_credentials_are_incorrect
105
+ test "connect should raise exception when credentials are incorrect" do
71
106
  assert_raises Sedna::AuthenticationError do
72
107
  Sedna.connect @connection.merge(:username => "non-existent-user")
73
108
  end
74
109
  end
75
110
 
76
- def test_connect_should_return_nil_on_error
111
+ test "connect should return nil on error" do
77
112
  begin
78
113
  sedna = Sedna.connect @connection.merge(:username => "non-existent-user")
79
114
  rescue
@@ -81,12 +116,12 @@ class SednaTest < Test::Unit::TestCase
81
116
  assert_nil sedna
82
117
  end
83
118
 
84
- def test_connect_should_return_nil_if_block_given
119
+ test "connect should return nil if block given" do
85
120
  sedna = Sedna.connect @connection do |s| end
86
121
  assert_nil sedna
87
122
  end
88
123
 
89
- def test_connect_should_close_connection_after_block
124
+ test "connect should close connection after block" do
90
125
  sedna = nil
91
126
  Sedna.connect @connection do |s|
92
127
  sedna = s
@@ -96,7 +131,7 @@ class SednaTest < Test::Unit::TestCase
96
131
  end
97
132
  end
98
133
 
99
- def test_connect_should_close_connection_if_exception_is_raised_inside_block
134
+ test "connect should close connection if exception is raised inside block" do
100
135
  sedna = nil
101
136
  begin
102
137
  Sedna.connect @connection do |s|
@@ -110,7 +145,7 @@ class SednaTest < Test::Unit::TestCase
110
145
  end
111
146
  end
112
147
 
113
- def test_connect_should_close_connection_if_something_is_thrown_inside_block
148
+ test "connect should close connection if something is thrown inside block" do
114
149
  sedna = nil
115
150
  catch :ball do
116
151
  Sedna.connect @connection do |s|
@@ -123,7 +158,7 @@ class SednaTest < Test::Unit::TestCase
123
158
  end
124
159
  end
125
160
 
126
- def test_connect_should_reraise_exceptions_from_inside_block
161
+ test "connect should re-raise exceptions from inside block" do
127
162
  assert_raises Exception do
128
163
  Sedna.connect @connection do
129
164
  raise Exception
@@ -132,12 +167,12 @@ class SednaTest < Test::Unit::TestCase
132
167
  end
133
168
 
134
169
  # Test sedna.close.
135
- def test_close_should_return_nil
170
+ test "close should return nil" do
136
171
  sedna = Sedna.connect @connection
137
172
  assert_nil sedna.close
138
173
  end
139
174
 
140
- def test_close_should_fail_silently_if_connection_is_already_closed
175
+ test "close should fail silently if connection is already closed" do
141
176
  sedna = Sedna.connect @connection
142
177
  assert_nothing_raised do
143
178
  sedna.close
@@ -146,34 +181,47 @@ class SednaTest < Test::Unit::TestCase
146
181
  end
147
182
 
148
183
  # Test sedna.execute / sedna.query.
149
- def test_execute_should_return_nil_for_data_structure_query
184
+ test "execute should raise TypeError if argument cannot be converted to String" do
185
+ Sedna.connect @connection do |sedna|
186
+ assert_raises TypeError do
187
+ sedna.execute Object.new
188
+ end
189
+ end
190
+ end
191
+
192
+ test "execute should return nil for data structure query" do
150
193
  Sedna.connect @connection do |sedna|
151
- name = "test_execute_should_return_nil_for_create_document_query"
152
- sedna.execute("drop document '#{name}'") rescue Sedna::Exception
153
- assert_nil sedna.execute("create document '#{name}'")
154
- sedna.execute("drop document '#{name}'") rescue Sedna::Exception
194
+ sedna.execute("drop document '#{__method__}'") rescue Sedna::Exception
195
+ assert_nil sedna.execute("create document '#{__method__}'")
196
+ sedna.execute("drop document '#{__method__}'") rescue Sedna::Exception
155
197
  end
156
198
  end
157
199
 
158
- def test_execute_should_return_array_for_select_query
200
+ test "execute should return array for select query" do
159
201
  Sedna.connect @connection do |sedna|
160
202
  assert_kind_of Array, sedna.execute("<test/>")
161
203
  end
162
204
  end
163
205
 
164
- def test_execute_should_return_array_with_single_string_for_single_select_query
206
+ test "execute should return array with single string for single select query" do
165
207
  Sedna.connect @connection do |sedna|
166
208
  assert_equal ["<test/>"], sedna.execute("<test/>")
167
209
  end
168
210
  end
169
211
 
170
- def test_execute_should_return_array_with_strings_for_select_query
212
+ test "execute should return array with strings for select query" do
171
213
  Sedna.connect @connection do |sedna|
172
214
  assert_equal ["<test/>", "<test/>", "<test/>"], sedna.execute("<test/>, <test/>, <test/>")
173
215
  end
174
216
  end
217
+
218
+ test "execute should return tainted strings" do
219
+ Sedna.connect @connection do |sedna|
220
+ assert sedna.execute("<test/>").first.tainted?
221
+ end
222
+ end
175
223
 
176
- def test_execute_should_fail_if_autocommit_is_false
224
+ test "execute should fail if autocommit is false" do
177
225
  Sedna.connect @connection do |sedna|
178
226
  sedna.autocommit = false
179
227
  assert_raises Sedna::Exception do
@@ -182,7 +230,7 @@ class SednaTest < Test::Unit::TestCase
182
230
  end
183
231
  end
184
232
 
185
- def test_execute_should_fail_with_sedna_exception_for_invalid_statments
233
+ test "execute should fail with Sedna::Exception for invalid statements" do
186
234
  Sedna.connect @connection do |sedna|
187
235
  assert_raises Sedna::Exception do
188
236
  sedna.execute "INVALID"
@@ -190,7 +238,7 @@ class SednaTest < Test::Unit::TestCase
190
238
  end
191
239
  end
192
240
 
193
- def test_execute_should_fail_with_sedna_connection_error_if_connection_is_closed
241
+ test "execute should fail with Sedna::ConnectionError if connection is closed" do
194
242
  Sedna.connect @connection do |sedna|
195
243
  sedna.close
196
244
  assert_raises Sedna::ConnectionError do
@@ -199,73 +247,165 @@ class SednaTest < Test::Unit::TestCase
199
247
  end
200
248
  end
201
249
 
202
- def test_execute_should_strip_first_newline_of_all_but_first_results
250
+ test "execute should strip first newline of all but first results" do
251
+ Sedna.connect @connection do |sedna|
252
+ sedna.execute("drop document '#{__method__}'") rescue Sedna::Exception
253
+ sedna.execute("create document '#{__method__}'")
254
+ sedna.execute("update insert <test><a>\n\nt</a><a>\n\nt</a><a>\n\nt</a></test> into doc('#{__method__}')")
255
+ assert_equal ["\n\nt", "\n\nt", "\n\nt"], sedna.execute("doc('#{__method__}')/test/a/text()")
256
+ sedna.execute("drop document '#{__method__}'") rescue Sedna::Exception
257
+ end
258
+ end
259
+
260
+ test "execute should block other threads for ruby 18 and not block for ruby 19" do
261
+ n = 5 # Amount of threads to be run. Increase for more accuracy.
262
+ i = 10000 # Times to loop in query. Increase for more accuracy.
263
+ threads = []
264
+ start_times = {}
265
+ end_times = {}
266
+ n.times do |number|
267
+ threads << Thread.new do
268
+ Sedna.connect @connection do |sedna|
269
+ start_times[number] = Time.now
270
+ sedna.execute "for $x in 1 to #{i} where $x = 1 return <node/>"
271
+ end_times[number] = Time.now
272
+ end
273
+ end
274
+ end
275
+ threads.each do |thread| thread.join end
276
+ # Count the amount of time that is overlapped between threads. If the execute
277
+ # method blocks, there should be hardly any overlap.
278
+ time_diff = 0
279
+ (n - 1).times do |number|
280
+ time_diff += start_times[number + 1] - end_times[number]
281
+ end
282
+ if RUBY_VERSION < "1.9"
283
+ # Blocking behaviour. The start/end times of two threads should not overlap.
284
+ assert time_diff > 0
285
+ else
286
+ # We have concurrency, the execute method in the threads should have been
287
+ # run in parallel and there should be considerable overlap in the start/end
288
+ # times of the executed threads.
289
+ assert time_diff < 0
290
+ end
291
+ end
292
+
293
+ test "execute should be run in serially if called from different threads on same connection" do
294
+ Sedna.connect @connection do |sedna|
295
+ i = 1000
296
+ threads = []
297
+ exceptions = []
298
+ Thread.abort_on_exception = true
299
+ 5.times do
300
+ threads << Thread.new do
301
+ begin
302
+ sedna.execute "for $x in #{i} where $x = 1 return <node/>"
303
+ rescue StandardError => e
304
+ exceptions << e
305
+ end
306
+ end
307
+ end
308
+ threads.each do |thread| thread.join end
309
+ assert_equal [], exceptions
310
+ end
311
+ end
312
+
313
+ test "execute should quit if exception is raised in it by another thread in ruby 19" do
203
314
  Sedna.connect @connection do |sedna|
204
- name = "test_execute_should_strip_first_newline_of_all_but_first_results"
205
- sedna.execute("drop document '#{name}'") rescue Sedna::Exception
206
- sedna.execute("create document '#{name}'")
207
- sedna.execute("update insert <test><a>\n\nt</a><a>\n\nt</a><a>\n\nt</a></test> into doc('#{name}')")
208
- assert_equal ["\n\nt", "\n\nt", "\n\nt"], sedna.execute("doc('#{name}')/test/a/text()")
209
- sedna.execute("drop document '#{name}'") rescue Sedna::Exception
315
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
316
+ begin
317
+ thread = Thread.new do
318
+ sedna.execute "create document '#{__method__}'"
319
+ end
320
+ thread.raise
321
+ thread.join
322
+ rescue
323
+ end
324
+ count = sedna.execute("count(doc('$documents')//*[@name='#{__method__}'])").first.to_i
325
+ if RUBY_VERSION < "1.9"
326
+ assert_equal 1, count
327
+ else
328
+ assert_equal 0, count
329
+ end
210
330
  end
211
331
  end
212
332
 
213
- def test_query_should_be_alias_of_execute
333
+ test "query should be alias of execute" do
214
334
  Sedna.connect @connection do |sedna|
215
335
  assert_equal ["<test/>"], sedna.query("<test/>")
216
336
  end
217
337
  end
218
338
 
219
339
  # Test sedna.load_document.
220
- def test_load_document_should_create_document_in_given_collection
340
+ test "load_document should raise TypeError if document argument cannot be converted to String" do
341
+ Sedna.connect @connection do |sedna|
342
+ assert_raises TypeError do
343
+ sedna.load_document Object.new, __method__.to_s
344
+ end
345
+ end
346
+ end
347
+
348
+ test "load_document should raise TypeError if doc_name argument cannot be converted to String" do
349
+ Sedna.connect @connection do |sedna|
350
+ assert_raises TypeError do
351
+ sedna.load_document "<doc/>", Object.new
352
+ end
353
+ end
354
+ end
355
+
356
+ test "load_document should raise TypeError if col_name argument cannot be converted to String" do
357
+ Sedna.connect @connection do |sedna|
358
+ assert_raises TypeError do
359
+ sedna.load_document "<doc/>", __method__.to_s, Object.new
360
+ end
361
+ end
362
+ end
363
+
364
+ test "load_document should create document in given collection" do
221
365
  Sedna.connect @connection do |sedna|
222
- name = "test_load_document_should_create_document_in_given_collection"
223
366
  col = "test_collection"
224
367
  doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>\n <node/>\n</document>"
225
368
 
226
369
  sedna.execute "create collection '#{col}'" rescue Sedna::Exception
227
- sedna.execute "drop document '#{name}' in collection '#{col}'" rescue Sedna::Exception
228
- sedna.load_document doc, name, col
229
- assert_equal doc, sedna.execute("doc('#{name}', '#{col}')").first
230
- sedna.execute "drop document '#{name}' in collection '#{col}'" rescue Sedna::Exception
370
+ sedna.execute "drop document '#{__method__}' in collection '#{col}'" rescue Sedna::Exception
371
+ sedna.load_document doc, __method__.to_s, col
372
+ assert_equal doc, sedna.execute("doc('#{__method__}', '#{col}')").first
373
+ sedna.execute "drop document '#{__method__}' in collection '#{col}'" rescue Sedna::Exception
231
374
  sedna.execute "drop collection '#{col}'" rescue Sedna::Exception
232
375
  end
233
376
  end
234
377
 
235
- def test_load_document_should_create_standalone_document_if_collection_is_unspecified
378
+ test "load_document should create standalone document if collection is unspecified" do
236
379
  Sedna.connect @connection do |sedna|
237
- name = "test_load_document_should_create_standalone_document_if_collection_is_unspecified"
238
380
  doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>\n <node/>\n</document>"
239
381
 
240
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
241
- sedna.load_document doc, name
242
- assert_equal doc, sedna.execute("doc('#{name}')").first
243
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
382
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
383
+ sedna.load_document doc, __method__.to_s
384
+ assert_equal doc, sedna.execute("doc('#{__method__}')").first
385
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
244
386
  end
245
387
  end
246
388
 
247
- def test_load_document_should_create_standalone_document_if_collection_is_nil
389
+ test "load_document should create standalone document if collection is nil" do
248
390
  Sedna.connect @connection do |sedna|
249
- name = "test_load_document_should_create_standalone_document_if_collection_is_unspecified"
250
391
  doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>\n <node/>\n</document>"
251
392
 
252
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
253
- sedna.load_document doc, name, nil
254
- assert_equal doc, sedna.execute("doc('#{name}')").first
255
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
393
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
394
+ sedna.load_document doc, __method__.to_s, nil
395
+ assert_equal doc, sedna.execute("doc('#{__method__}')").first
396
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
256
397
  end
257
398
  end
258
399
 
259
- def test_load_document_should_return_nil_if_standalone_document_loaded_successfully
400
+ test "load_document should return nil if standalone document loaded successfully" do
260
401
  Sedna.connect @connection do |sedna|
261
- name = "test_load_document_should_return_nil_if_document_loaded_successfully"
262
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
263
- assert_nil sedna.load_document("<document><node/></document>", name)
264
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
402
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
403
+ assert_nil sedna.load_document("<document><node/></document>", __method__.to_s)
404
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
265
405
  end
266
406
  end
267
407
 
268
- def test_load_document_should_fail_if_autocommit_is_false
408
+ test "load_document should fail if autocommit is false" do
269
409
  Sedna.connect @connection do |sedna|
270
410
  sedna.autocommit = false
271
411
  assert_raises Sedna::Exception do
@@ -274,7 +414,7 @@ class SednaTest < Test::Unit::TestCase
274
414
  end
275
415
  end
276
416
 
277
- def test_load_document_should_fail_with_sedna_exception_for_invalid_documents
417
+ test "load_document should fail with Sedna::Exception for invalid documents" do
278
418
  Sedna.connect @connection do |sedna|
279
419
  assert_raises Sedna::Exception do
280
420
  sedna.load_document "<doc/> this is an invalid document", "some_doc"
@@ -282,7 +422,7 @@ class SednaTest < Test::Unit::TestCase
282
422
  end
283
423
  end
284
424
 
285
- def test_load_document_should_raise_exception_with_complete_details_for_invalid_documents
425
+ test "load_document should raise exception with complete details for invalid documents" do
286
426
  Sedna.connect @connection do |sedna|
287
427
  e = nil
288
428
  begin
@@ -293,7 +433,7 @@ class SednaTest < Test::Unit::TestCase
293
433
  end
294
434
  end
295
435
 
296
- def test_load_document_should_fail_with_sedna_connection_error_if_connection_is_closed
436
+ test "load_document should fail with Sedna::ConnectionError if connection is closed" do
297
437
  Sedna.connect @connection do |sedna|
298
438
  sedna.close
299
439
  assert_raises Sedna::ConnectionError do
@@ -302,74 +442,71 @@ class SednaTest < Test::Unit::TestCase
302
442
  end
303
443
  end
304
444
 
305
- def test_load_document_should_create_document_if_given_document_is_io_object
445
+ test "load_document should create document if given document is IO object" do
306
446
  Sedna.connect @connection do |sedna|
307
- name = "test_load_document_should_create_document_if_given_document_is_io_object"
308
447
  doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>" << ("\n <some_very_often_repeated_node/>" * 800) << "\n</document>"
309
448
  p_out, p_in = IO.pipe
310
449
  p_in.write doc
311
450
  p_in.close
312
451
 
313
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
314
- sedna.load_document p_out, name, nil
315
- assert_equal doc.length, sedna.execute("doc('#{name}')").first.length
316
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
452
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
453
+ sedna.load_document p_out, __method__.to_s, nil
454
+ assert_equal doc.length, sedna.execute("doc('#{__method__}')").first.length
455
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
317
456
  end
318
457
  end
319
458
 
320
- def test_load_document_should_raise_sedna_exception_if_given_document_is_empty_io_object
459
+ test "load_document should raise Sedna::Exception if given document is empty IO object" do
321
460
  Sedna.connect @connection do |sedna|
322
- name = "test_load_document_should_raise_sedna_exception_if_given_document_is_empty_io_object"
323
461
  p_out, p_in = IO.pipe
324
462
  p_in.close
325
463
 
326
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
464
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
327
465
  e = nil
328
466
  begin
329
- sedna.load_document p_out, name, nil
467
+ sedna.load_document p_out, __method__.to_s, nil
330
468
  rescue Sedna::Exception => e
331
469
  end
332
470
  assert_equal "Document is empty.", e.message
333
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
471
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
334
472
  end
335
473
  end
336
474
 
337
- def test_load_document_should_raise_sedna_exception_if_given_document_is_empty_string
475
+ test "load_document should raise Sedna::Exception if given document is empty string" do
338
476
  Sedna.connect @connection do |sedna|
339
- name = "test_load_document_should_raise_sedna_exception_if_given_document_is_empty_string"
340
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
477
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
341
478
  e = nil
342
479
  begin
343
- sedna.load_document "", name, nil
480
+ sedna.load_document "", __method__.to_s, nil
344
481
  rescue Sedna::Exception => e
345
482
  end
346
483
  assert_equal "Document is empty.", e.message
347
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
484
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
348
485
  end
349
486
  end
350
487
 
351
488
  # Test sedna.autocommit= / sedna.autocommit.
352
- def test_autocommit_should_return_true_by_default
489
+ test "autocommit should return true by default" do
353
490
  Sedna.connect @connection do |sedna|
354
491
  assert_equal true, sedna.autocommit
355
492
  end
356
493
  end
357
494
 
358
- def test_autocommit_should_return_true_if_set_to_true
495
+ test "autocommit should return true if set to true" do
359
496
  Sedna.connect @connection do |sedna|
360
497
  sedna.autocommit = true
361
498
  assert_equal true, sedna.autocommit
362
499
  end
363
500
  end
364
501
 
365
- def test_autocommit_should_return_false_if_set_to_false
502
+ test "autocommit should return false if set to false" do
366
503
  Sedna.connect @connection do |sedna|
367
504
  sedna.autocommit = false
368
505
  assert_equal false, sedna.autocommit
369
506
  end
370
507
  end
371
508
 
372
- def test_autocommit_should_return_true_if_set_to_true_after_being_set_to_false
509
+ test "autocommit should return true if set to true after being set to false" do
373
510
  Sedna.connect @connection do |sedna|
374
511
  sedna.autocommit = false
375
512
  sedna.autocommit = true
@@ -377,21 +514,21 @@ class SednaTest < Test::Unit::TestCase
377
514
  end
378
515
  end
379
516
 
380
- def test_autocommit_should_return_true_if_argument_evaluates_to_true
517
+ test "autocommit should return true if argument evaluates to true" do
381
518
  Sedna.connect @connection do |sedna|
382
519
  sedna.autocommit = "string evaluates to true"
383
520
  assert_equal true, sedna.autocommit
384
521
  end
385
522
  end
386
523
 
387
- def test_autocommit_should_return_false_if_argument_evaluates_to_false
524
+ test "autocommit should return false if argument evaluates to false" do
388
525
  Sedna.connect @connection do |sedna|
389
526
  sedna.autocommit = nil
390
527
  assert_equal false, sedna.autocommit
391
528
  end
392
529
  end
393
530
 
394
- def test_autocommit_should_be_reenabled_after_transactions
531
+ test "autocommit should be re-enabled after transactions" do
395
532
  Sedna.connect @connection do |sedna|
396
533
  sedna.autocommit = true
397
534
  sedna.transaction do end
@@ -402,13 +539,13 @@ class SednaTest < Test::Unit::TestCase
402
539
  end
403
540
 
404
541
  # Test sedna.transaction.
405
- def test_transaction_should_return_nil_if_committed
542
+ test "transaction should return nil if committed" do
406
543
  Sedna.connect @connection do |sedna|
407
544
  assert_nil sedna.transaction(){}
408
545
  end
409
546
  end
410
547
 
411
- def test_transaction_should_raise_localjumperror_if_no_block_is_given
548
+ test "transaction should raise LocalJumpError if no block is given" do
412
549
  assert_raises LocalJumpError do
413
550
  Sedna.connect @connection do |sedna|
414
551
  sedna.transaction
@@ -416,7 +553,7 @@ class SednaTest < Test::Unit::TestCase
416
553
  end
417
554
  end
418
555
 
419
- def test_transaction_should_be_possible_with_autocommit
556
+ test "transaction should be possible with autocommit" do
420
557
  Sedna.connect @connection do |sedna|
421
558
  sedna.autocommit = true
422
559
  assert_nothing_raised do
@@ -425,9 +562,9 @@ class SednaTest < Test::Unit::TestCase
425
562
  end
426
563
  end
427
564
 
428
- def test_transaction_should_fail_with_transaction_error_if_another_transaction_is_started_inside_it
429
- assert_raises Sedna::TransactionError do
430
- Sedna.connect @connection do |sedna|
565
+ test "transaction should fail with Sedna::TransactionError if another transaction is started inside it" do
566
+ Sedna.connect @connection do |sedna|
567
+ assert_raises Sedna::TransactionError do
431
568
  sedna.transaction do
432
569
  sedna.transaction do end
433
570
  end
@@ -435,52 +572,52 @@ class SednaTest < Test::Unit::TestCase
435
572
  end
436
573
  end
437
574
 
438
- def test_transaction_should_commit_if_block_given
575
+ test "transaction should commit if block given" do
439
576
  Sedna.connect @connection do |sedna|
440
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
441
- sedna.execute "create document '#{name}'"
577
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
578
+ sedna.execute "create document '#{__method__}'"
442
579
  sedna.transaction do
443
- sedna.execute "update insert <test>test</test> into doc('#{name}')"
580
+ sedna.execute "update insert <test>test</test> into doc('#{__method__}')"
444
581
  end
445
- assert_equal 1, sedna.execute("count(doc('#{name}')/test)").first.to_i
446
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
582
+ assert_equal 1, sedna.execute("count(doc('#{__method__}')/test)").first.to_i
583
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
447
584
  end
448
585
  end
449
586
 
450
- def test_transaction_should_rollback_if_exception_is_raised_inside_block
587
+ test "transaction should rollback if exception is raised inside block" do
451
588
  Sedna.connect @connection do |sedna|
452
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
453
- sedna.execute "create document '#{name}'"
589
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
590
+ sedna.execute "create document '#{__method__}'"
454
591
  begin
455
592
  sedna.transaction do
456
- sedna.execute "update insert <test>test</test> into doc('#{name}')"
593
+ sedna.execute "update insert <test>test</test> into doc('#{__method__}')"
457
594
  raise Exception
458
595
  end
459
596
  rescue Exception
460
597
  end
461
- assert_equal 0, sedna.execute("count(doc('#{name}')/test)").first.to_i
462
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
598
+ assert_equal 0, sedna.execute("count(doc('#{__method__}')/test)").first.to_i
599
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
463
600
  end
464
601
  end
465
602
 
466
- def test_transaction_should_rollback_if_something_is_thrown_inside_block
603
+ test "transaction should rollback if something is thrown inside block" do
467
604
  Sedna.connect @connection do |sedna|
468
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
469
- sedna.execute "create document '#{name}'"
605
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
606
+ sedna.execute "create document '#{__method__}'"
470
607
  catch :ball do
471
608
  sedna.transaction do
472
- sedna.execute "update insert <test>test</test> into doc('#{name}')"
609
+ sedna.execute "update insert <test>test</test> into doc('#{__method__}')"
473
610
  throw :ball
474
611
  end
475
612
  end
476
- assert_equal 0, sedna.execute("count(doc('#{name}')/test)").first.to_i
477
- sedna.execute "drop document '#{name}'" rescue Sedna::Exception
613
+ assert_equal 0, sedna.execute("count(doc('#{__method__}')/test)").first.to_i
614
+ sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
478
615
  end
479
616
  end
480
617
 
481
- def test_transaction_should_raise_transaction_error_if_invalid_statement_caused_exception_but_it_was_rescued
482
- assert_raises Sedna::TransactionError do
483
- Sedna.connect @connection do |sedna|
618
+ test "transaction should raise Sedna::TransactionError if invalid statement caused exception but it was rescued" do
619
+ Sedna.connect @connection do |sedna|
620
+ assert_raises Sedna::TransactionError do
484
621
  sedna.transaction do
485
622
  sedna.execute "FAILS" rescue Sedna::Exception
486
623
  end
@@ -488,7 +625,7 @@ class SednaTest < Test::Unit::TestCase
488
625
  end
489
626
  end
490
627
 
491
- def test_transaction_should_reraise_exceptions_from_inside_block
628
+ test "transaction should re-raise exceptions from inside block" do
492
629
  Sedna.connect @connection do |sedna|
493
630
  assert_raises Exception do
494
631
  sedna.transaction do
@@ -498,7 +635,7 @@ class SednaTest < Test::Unit::TestCase
498
635
  end
499
636
  end
500
637
 
501
- def test_transaction_with_invalid_statements_should_cause_transaction_to_roll_back_once
638
+ test "transaction with invalid statements should cause transaction to roll back once" do
502
639
  exc = nil
503
640
  begin
504
641
  Sedna.connect @connection do |sedna|
@@ -510,4 +647,25 @@ class SednaTest < Test::Unit::TestCase
510
647
  end
511
648
  assert_equal "It is a dynamic error if evaluation of an expression relies on some part of the dynamic context that has not been assigned a value.", exc.message
512
649
  end
650
+
651
+ test "transaction should raise Sedna::TransactionError if called from different threads on same connection" do
652
+ Sedna.connect @connection do |sedna|
653
+ threads = []
654
+ exceptions = []
655
+ Thread.abort_on_exception = true
656
+ 5.times do
657
+ threads << Thread.new do
658
+ begin
659
+ sedna.transaction do
660
+ sleep 0.1
661
+ end
662
+ rescue StandardError => e
663
+ exceptions << e.class
664
+ end
665
+ end
666
+ end
667
+ threads.each do |thread| thread.join end
668
+ assert_equal [Sedna::TransactionError] * 4, exceptions
669
+ end
670
+ end
513
671
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sedna
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rolf Timmermans
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-12-15 00:00:00 +01:00
12
+ date: 2008-12-21 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -24,6 +24,7 @@ extra_rdoc_files:
24
24
  - README
25
25
  - ext/sedna.c
26
26
  files:
27
+ - Rakefile
27
28
  - ext/extconf.rb
28
29
  - ext/sedna.c
29
30
  - test/sedna_test.rb