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