sedna 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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