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.
- data/CHANGES +11 -0
- data/README +6 -3
- data/Rakefile +91 -0
- data/ext/extconf.rb +9 -0
- data/ext/sedna.c +162 -28
- data/test/sedna_test.rb +271 -113
- 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.
|
36
|
-
|
37
|
-
|
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
|
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
|
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
|
38
|
-
#define
|
39
|
-
#define
|
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
|
-
//
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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 =
|
214
|
-
if(NIL_P(db_v = rb_hash_aref(options, db_k))) db =
|
215
|
-
if(NIL_P(user_v = rb_hash_aref(options, user_k))) user =
|
216
|
-
if(NIL_P(pw_v = rb_hash_aref(options, pw_k))) pw =
|
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
|
-
|
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
|
-
|
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
|
-
|
510
|
+
VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
|
401
511
|
}
|
512
|
+
|
402
513
|
res = SEendLoadData(conn);
|
403
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
152
|
-
sedna.execute("
|
153
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
-
|
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
|
-
|
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 '#{
|
228
|
-
sedna.load_document doc,
|
229
|
-
assert_equal doc, sedna.execute("doc('#{
|
230
|
-
sedna.execute "drop document '#{
|
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
|
-
|
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 '#{
|
241
|
-
sedna.load_document doc,
|
242
|
-
assert_equal doc, sedna.execute("doc('#{
|
243
|
-
sedna.execute "drop document '#{
|
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
|
-
|
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 '#{
|
253
|
-
sedna.load_document doc,
|
254
|
-
assert_equal doc, sedna.execute("doc('#{
|
255
|
-
sedna.execute "drop document '#{
|
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
|
-
|
400
|
+
test "load_document should return nil if standalone document loaded successfully" do
|
260
401
|
Sedna.connect @connection do |sedna|
|
261
|
-
|
262
|
-
sedna.
|
263
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 '#{
|
314
|
-
sedna.load_document p_out,
|
315
|
-
assert_equal doc.length, sedna.execute("doc('#{
|
316
|
-
sedna.execute "drop document '#{
|
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
|
-
|
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 '#{
|
464
|
+
sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
|
327
465
|
e = nil
|
328
466
|
begin
|
329
|
-
sedna.load_document p_out,
|
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 '#{
|
471
|
+
sedna.execute "drop document '#{__method__}'" rescue Sedna::Exception
|
334
472
|
end
|
335
473
|
end
|
336
474
|
|
337
|
-
|
475
|
+
test "load_document should raise Sedna::Exception if given document is empty string" do
|
338
476
|
Sedna.connect @connection do |sedna|
|
339
|
-
|
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 "",
|
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 '#{
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
429
|
-
|
430
|
-
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
|
-
|
575
|
+
test "transaction should commit if block given" do
|
439
576
|
Sedna.connect @connection do |sedna|
|
440
|
-
sedna.execute "drop document '#{
|
441
|
-
sedna.execute "create document '#{
|
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('#{
|
580
|
+
sedna.execute "update insert <test>test</test> into doc('#{__method__}')"
|
444
581
|
end
|
445
|
-
assert_equal 1, sedna.execute("count(doc('#{
|
446
|
-
sedna.execute "drop document '#{
|
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
|
-
|
587
|
+
test "transaction should rollback if exception is raised inside block" do
|
451
588
|
Sedna.connect @connection do |sedna|
|
452
|
-
sedna.execute "drop document '#{
|
453
|
-
sedna.execute "create document '#{
|
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('#{
|
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('#{
|
462
|
-
sedna.execute "drop document '#{
|
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
|
-
|
603
|
+
test "transaction should rollback if something is thrown inside block" do
|
467
604
|
Sedna.connect @connection do |sedna|
|
468
|
-
sedna.execute "drop document '#{
|
469
|
-
sedna.execute "create document '#{
|
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('#{
|
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('#{
|
477
|
-
sedna.execute "drop document '#{
|
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
|
-
|
482
|
-
|
483
|
-
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
|
-
|
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
|
-
|
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.
|
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-
|
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
|