sedna 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +9 -0
- data/README +86 -0
- data/ext/extconf.rb +71 -0
- data/ext/sedna.c +515 -0
- data/test/test_sedna.rb +387 -0
- metadata +62 -0
data/CHANGES
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
=== 0.1.0
|
2
|
+
|
3
|
+
* Released on the December 8th, 2008.
|
4
|
+
* Development preview (alpha) release.
|
5
|
+
* Simple, high-level API.
|
6
|
+
* Tested with Ruby 1.8.7 and Ruby 1.9.0.
|
7
|
+
* Support for version 3.1 of the \Sedna XML DBMS.
|
8
|
+
* Support for regular queries and transactions.
|
9
|
+
* Support for auto-commit mode.
|
data/README
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
= \Sedna XML database client library
|
2
|
+
|
3
|
+
This library provides a Ruby client for <i>Sedna</i>, an open-source, native XML
|
4
|
+
database system. The client is a Ruby extension that uses the official C
|
5
|
+
driver that is shipped as part of the S<i></i>edna distribution.
|
6
|
+
|
7
|
+
\Sedna provides a full range of core database services -- persistent storage,
|
8
|
+
ACID transactions, security, indices, hot backup. Flexible XML processing
|
9
|
+
facilities include W3C XQuery implementation, tight integration of XQuery with
|
10
|
+
full-text search facilities and a node-level update language.
|
11
|
+
|
12
|
+
For more information about the \Sedna XML database system, see the project page
|
13
|
+
at http://modis.ispras.ru/sedna
|
14
|
+
|
15
|
+
=== About the client library
|
16
|
+
|
17
|
+
Author: Rolf Timmermans (r.timmermans <i>at</i> voormedia.com)
|
18
|
+
|
19
|
+
Copyright 2008 Voormedia B.V.
|
20
|
+
|
21
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
22
|
+
you may not use this file except in compliance with the License.
|
23
|
+
You may obtain a copy of the License at
|
24
|
+
|
25
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
26
|
+
|
27
|
+
Unless required by applicable law or agreed to in writing, software
|
28
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
29
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
30
|
+
See the License for the specific language governing permissions and
|
31
|
+
limitations under the License.
|
32
|
+
|
33
|
+
=== Current version
|
34
|
+
|
35
|
+
The current version of this library is 0.1.0. This version is a <b>development
|
36
|
+
preview</b>. The API may change significantly between minor versions.
|
37
|
+
For a complete overview all recent and previous changes, see CHANGES.
|
38
|
+
|
39
|
+
=== Requirements
|
40
|
+
|
41
|
+
Before installing the \Sedna client library, make sure you have the library and
|
42
|
+
header files of the C driver. They are shipped and installed as part of the binary
|
43
|
+
distribution of \Sedna. When installing, choose either <tt>/usr/local/sedna</tt> or
|
44
|
+
<tt>/opt/sedna</tt> as target locations.
|
45
|
+
|
46
|
+
=== Installation
|
47
|
+
|
48
|
+
After installing the \Sedna C driver (see above), simply install the Ruby client
|
49
|
+
library as a rubygem.
|
50
|
+
|
51
|
+
% gem install sedna
|
52
|
+
|
53
|
+
If the library or header files of the S<i></i>edna C driver are located at non-default
|
54
|
+
locations, you can specify these locations by adding the <tt>--with-sedna-lib</tt>
|
55
|
+
or <tt>--with-sedna-include</tt> options when installing.
|
56
|
+
|
57
|
+
% gem install sedna -- --with-sedna-lib=/path/to/sedna/lib --with-sedna-include=/path/to/sedna/include
|
58
|
+
|
59
|
+
=== Usage
|
60
|
+
|
61
|
+
After installation of the gem, +require+ the sedna library in order to start
|
62
|
+
using it.
|
63
|
+
|
64
|
+
require 'rubygems'
|
65
|
+
require 'sedna'
|
66
|
+
|
67
|
+
To start querying a database, create a new connection with the Sedna.connect
|
68
|
+
method. When a block is given, the Sedna connection object will be returned
|
69
|
+
which can be used to execute database statements.
|
70
|
+
|
71
|
+
connection_details = {
|
72
|
+
:database => "my_db",
|
73
|
+
:host => "localhost",
|
74
|
+
:username => "SYSTEM",
|
75
|
+
:password => "MANAGER",
|
76
|
+
}
|
77
|
+
Sedna.connect(connection_details) do |sedna|
|
78
|
+
# Start querying the database.
|
79
|
+
sedna.execute("create document 'mydoc'") #=> nil
|
80
|
+
sedna.execute("update insert <msg>Hello world!</msg> into doc('mydoc')") #=> nil
|
81
|
+
sedna.execute("doc('mydoc')/msg/text()") #=> ["Hello world!"]
|
82
|
+
# The connection is closed automatically for us.
|
83
|
+
end
|
84
|
+
|
85
|
+
See the documentation of the Sedna class -- the methods Sedna.connect and
|
86
|
+
Sedna#execute in particular -- for more details.
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,71 @@
|
|
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 will generate a Makefile that can be used to build this extension.
|
19
|
+
|
20
|
+
require "mkmf"
|
21
|
+
|
22
|
+
if RUBY_VERSION < "1.8"
|
23
|
+
puts "This library requires ruby 1.8."
|
24
|
+
exit 1
|
25
|
+
end
|
26
|
+
|
27
|
+
driver = "/driver/c"
|
28
|
+
default_driver_dirs = ["/usr/local/sedna#{driver}", "/opt/sedna#{driver}"]
|
29
|
+
default_include_dirs = ["/usr/include", "/usr/include/sedna"] + default_driver_dirs
|
30
|
+
default_lib_dirs = ["/usr/lib", "/usr/lib/sedna"] + default_driver_dirs
|
31
|
+
idir, ldir = dir_config "sedna", nil, nil
|
32
|
+
idir ||= default_include_dirs
|
33
|
+
ldir ||= default_lib_dirs
|
34
|
+
|
35
|
+
if not find_library "sedna", "SEconnect", *ldir
|
36
|
+
$stderr.write %{
|
37
|
+
==============================================================================
|
38
|
+
Could not find libsedna.
|
39
|
+
* Did you install Sedna somewhere else than in /usr/local/sedna or /opt/sedna?
|
40
|
+
Call extconf.rb with --with-sedna-lib=/path to override default location.
|
41
|
+
* Did you install a version of Sedna not compiled for your architecture?
|
42
|
+
==============================================================================
|
43
|
+
}
|
44
|
+
exit 2
|
45
|
+
end
|
46
|
+
|
47
|
+
if not find_header "libsedna.h", *idir or not find_header "sp_defs.h", *idir
|
48
|
+
$stderr.write %{
|
49
|
+
==============================================================================
|
50
|
+
Could not find header file(s) for libsedna.
|
51
|
+
* Did you install Sedna somewhere else than in /usr/local/sedna or /opt/sedna?
|
52
|
+
Call extconf.rb with --with-sedna-include=/path to override default location.
|
53
|
+
==============================================================================
|
54
|
+
}
|
55
|
+
exit 3
|
56
|
+
end
|
57
|
+
|
58
|
+
if CONFIG["arch"] =~ /x86_64/i and File.exist?(f = $LIBPATH.first + File::Separator + "libsedna.a")
|
59
|
+
if system "/usr/bin/objdump --reloc \"#{f}\" 2>/dev/null | grep R_X86_64_32S >/dev/null && echo"
|
60
|
+
$stderr.write %{==============================================================================
|
61
|
+
Library libsedna.a was statically compiled for a 64-bit platform as position-
|
62
|
+
dependent code. It will not be possible to create a Ruby shared library with
|
63
|
+
this Sedna library. Recompile the library as position-independent code by
|
64
|
+
passing the -fPIC option to gcc.
|
65
|
+
==============================================================================
|
66
|
+
}
|
67
|
+
exit 4
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
create_makefile "sedna"
|
data/ext/sedna.c
ADDED
@@ -0,0 +1,515 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2008 Voormedia B.V.
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*
|
16
|
+
* Ruby extension library providing a client API to the Sedna native XML
|
17
|
+
* database management system, based on the official Sedna C driver.
|
18
|
+
*
|
19
|
+
* This file contains the Ruby C-extension.
|
20
|
+
*/
|
21
|
+
|
22
|
+
#include <string.h>
|
23
|
+
#include "ruby.h"
|
24
|
+
#include "libsedna.h"
|
25
|
+
|
26
|
+
// Size of the read buffer.
|
27
|
+
#define BUF_LEN 8192
|
28
|
+
|
29
|
+
// Use macros to fool RDoc and hide some of our methods/aliases.
|
30
|
+
#define rb_define_undocumented_alias(kl, new, old) rb_define_alias(kl, new, old)
|
31
|
+
|
32
|
+
// Macro for verification of return values of the Sedna API + error handling.
|
33
|
+
#define verify_res(expected, real, conn) if(real != expected) sedna_err(conn, real)
|
34
|
+
|
35
|
+
// Macro for setting autocommit values on connection conn.
|
36
|
+
#define sedna_autocommit_on(conn) sedna_autocommit(conn, SEDNA_AUTOCOMMIT_ON)
|
37
|
+
#define sedna_autocommit_off(conn) sedna_autocommit(conn, SEDNA_AUTOCOMMIT_OFF)
|
38
|
+
#define switch_sedna_autocommit(conn, val) if(val) sedna_autocommit_on(conn); else sedna_autocommit_off(conn)
|
39
|
+
|
40
|
+
|
41
|
+
// Our classes.
|
42
|
+
static VALUE cSedna;
|
43
|
+
//static VALUE cSednaSet; //Unused so far.
|
44
|
+
static VALUE cSednaException;
|
45
|
+
static VALUE cSednaAuthError;
|
46
|
+
static VALUE cSednaConnError;
|
47
|
+
static VALUE cSednaTrnError;
|
48
|
+
|
49
|
+
// Define a shorthand for the common SednaConnection structure.
|
50
|
+
typedef struct SednaConnection SC;
|
51
|
+
|
52
|
+
|
53
|
+
// Test the last error message for conn, and raise an exception if there is one.
|
54
|
+
// The type of the exception is based on the result of the function that was
|
55
|
+
// called that generated the error and should be passed as res.
|
56
|
+
static void sedna_err(SC *conn, int res)
|
57
|
+
{
|
58
|
+
VALUE exception;
|
59
|
+
const char *msg;
|
60
|
+
char *err, *details, *p;
|
61
|
+
switch(res) {
|
62
|
+
case SEDNA_AUTHENTICATION_FAILED:
|
63
|
+
exception = cSednaAuthError; break;
|
64
|
+
case SEDNA_OPEN_SESSION_FAILED:
|
65
|
+
case SEDNA_CLOSE_SESSION_FAILED:
|
66
|
+
exception = cSednaConnError; break;
|
67
|
+
case SEDNA_BEGIN_TRANSACTION_FAILED:
|
68
|
+
case SEDNA_ROLLBACK_TRANSACTION_FAILED:
|
69
|
+
case SEDNA_COMMIT_TRANSACTION_FAILED:
|
70
|
+
exception = cSednaTrnError; break;
|
71
|
+
case SEDNA_ERROR:
|
72
|
+
default:
|
73
|
+
exception = cSednaException;
|
74
|
+
}
|
75
|
+
msg = SEgetLastErrorMsg(conn);
|
76
|
+
err = strstr(msg, "\n");
|
77
|
+
details = strstr(err, "\nDetails: ");
|
78
|
+
if(err != NULL) {
|
79
|
+
err++;
|
80
|
+
if((p = strstr(err, "\n")) != NULL) strncpy(p, "\0", 1);
|
81
|
+
} else {
|
82
|
+
err = "Unknown error.";
|
83
|
+
}
|
84
|
+
if(details != NULL) {
|
85
|
+
details += 10;
|
86
|
+
if((p = strstr(details, "\n")) != NULL) strncpy(p, "\0", 1);
|
87
|
+
rb_raise(exception, "%s (%s)", err, details);
|
88
|
+
} else {
|
89
|
+
rb_raise(exception, "%s", err);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
// Retrieve the SednaConnection struct from the Ruby Sedna object obj.
|
94
|
+
static SC* sedna_struct(VALUE obj)
|
95
|
+
{
|
96
|
+
SC *conn;
|
97
|
+
Data_Get_Struct(obj, SC, conn);
|
98
|
+
return conn;
|
99
|
+
}
|
100
|
+
|
101
|
+
// Close the Sedna connection and free memory of the SednaConnection struct.
|
102
|
+
// Called at GC.
|
103
|
+
static void sedna_free(SC *conn)
|
104
|
+
{
|
105
|
+
if(SEconnectionStatus(conn) != SEDNA_CONNECTION_CLOSED) SEclose(conn);
|
106
|
+
free(conn);
|
107
|
+
}
|
108
|
+
|
109
|
+
// Mark any references to other objects for Ruby GC (if any).
|
110
|
+
static void sedna_mark(SC *conn)
|
111
|
+
{ /* Unused. */ }
|
112
|
+
|
113
|
+
// Read one record completely and return it as a Ruby String object.
|
114
|
+
static VALUE sedna_read(SC *conn, int strip_n)
|
115
|
+
{
|
116
|
+
int bytes_read = 0;
|
117
|
+
char buffer[BUF_LEN];
|
118
|
+
VALUE str = rb_str_buf_new(0);
|
119
|
+
do {
|
120
|
+
bytes_read = SEgetData(conn, buffer, BUF_LEN - 1);
|
121
|
+
if(bytes_read == SEDNA_ERROR) {
|
122
|
+
sedna_err(conn, SEDNA_ERROR);
|
123
|
+
} else {
|
124
|
+
if(bytes_read > 0) {
|
125
|
+
if(strip_n) {
|
126
|
+
// Strange bug adds newlines to beginning of every result
|
127
|
+
// except the first. Strip them! This a known issue in the
|
128
|
+
// network protocol and serialization mechanism.
|
129
|
+
// See: http://sourceforge.net/mailarchive/forum.php?thread_name=3034886f0812030132v3bbd8e2erd86480d3dc640664%40mail.gmail.com&forum_name=sedna-discussion
|
130
|
+
rb_str_buf_cat(str, buffer + 1, bytes_read - 1);
|
131
|
+
// Do not strip newlines from subsequent buffer reads.
|
132
|
+
strip_n = 0;
|
133
|
+
} else {
|
134
|
+
rb_str_buf_cat(str, buffer, bytes_read);
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
} while(bytes_read > 0);
|
139
|
+
return str;
|
140
|
+
}
|
141
|
+
|
142
|
+
// Iterate over all records and add them to a Ruby Array.
|
143
|
+
static VALUE sedna_get_results(SC *conn)
|
144
|
+
{
|
145
|
+
int res, strip_n = 0;
|
146
|
+
VALUE set = rb_ary_new();
|
147
|
+
while((res = SEnext(conn)) != SEDNA_RESULT_END) {
|
148
|
+
if(res == SEDNA_ERROR) sedna_err(conn, res);
|
149
|
+
// Set strip_n to 1 for all results except the first. This will cause
|
150
|
+
// sedna_read() an incorrect newline that is prepended to these results.
|
151
|
+
rb_ary_push(set, sedna_read(conn, strip_n));
|
152
|
+
if(!strip_n) strip_n = 1;
|
153
|
+
};
|
154
|
+
return set;
|
155
|
+
}
|
156
|
+
|
157
|
+
// Enable or disable autocommit.
|
158
|
+
static void sedna_autocommit(SC *conn, int value)
|
159
|
+
{
|
160
|
+
int res = SEsetConnectionAttr(conn, SEDNA_ATTR_AUTOCOMMIT, (void *)&value, sizeof(int));
|
161
|
+
verify_res(SEDNA_SET_ATTRIBUTE_SUCCEEDED, res, conn);
|
162
|
+
}
|
163
|
+
|
164
|
+
// Begin a transaction.
|
165
|
+
static void sedna_tr_begin(SC *conn)
|
166
|
+
{
|
167
|
+
int res = SEbegin(conn);
|
168
|
+
verify_res(SEDNA_BEGIN_TRANSACTION_SUCCEEDED, res, conn);
|
169
|
+
}
|
170
|
+
|
171
|
+
// Commit a transaction.
|
172
|
+
static void sedna_tr_commit(SC *conn)
|
173
|
+
{
|
174
|
+
int res = SEcommit(conn);
|
175
|
+
verify_res(SEDNA_COMMIT_TRANSACTION_SUCCEEDED, res, conn);
|
176
|
+
}
|
177
|
+
|
178
|
+
// Rollback a transaction.
|
179
|
+
static void sedna_tr_rollback(SC *conn)
|
180
|
+
{
|
181
|
+
int res = SErollback(conn);
|
182
|
+
verify_res(SEDNA_ROLLBACK_TRANSACTION_SUCCEEDED, res, conn);
|
183
|
+
}
|
184
|
+
|
185
|
+
// Alocates memory for a SednaConnection struct.
|
186
|
+
static VALUE cSedna_s_new(VALUE klass)
|
187
|
+
{
|
188
|
+
SC *conn, init = SEDNA_CONNECTION_INITIALIZER;
|
189
|
+
conn = (SC *)malloc(sizeof(SC));
|
190
|
+
if(conn == NULL) rb_raise(rb_eNoMemError, "Could not allocate memory.");
|
191
|
+
memcpy(conn, &init, sizeof(init));
|
192
|
+
return Data_Wrap_Struct(klass, sedna_mark, sedna_free, conn);
|
193
|
+
}
|
194
|
+
|
195
|
+
/* :nodoc:
|
196
|
+
*
|
197
|
+
* Initialize a new instance of Sedna.
|
198
|
+
*/
|
199
|
+
static VALUE cSedna_initialize(VALUE self, VALUE options)
|
200
|
+
{
|
201
|
+
int res;
|
202
|
+
VALUE host_k, db_k, user_k, pw_k, host_v, db_v, user_v, pw_v;
|
203
|
+
char *host, *db, *user, *pw;
|
204
|
+
SC *conn = sedna_struct(self);
|
205
|
+
|
206
|
+
Check_Type(options, T_HASH);
|
207
|
+
host_k = ID2SYM(rb_intern("host"));
|
208
|
+
db_k = ID2SYM(rb_intern("database"));
|
209
|
+
user_k = ID2SYM(rb_intern("username"));
|
210
|
+
pw_k = ID2SYM(rb_intern("password"));
|
211
|
+
|
212
|
+
if(NIL_P(host_v = rb_hash_aref(options, host_k))) host = "localhost"; else host = STR2CSTR(host_v);
|
213
|
+
if(NIL_P(db_v = rb_hash_aref(options, db_k))) db = "test"; else db = STR2CSTR(db_v);
|
214
|
+
if(NIL_P(user_v = rb_hash_aref(options, user_k))) user = "SYSTEM"; else user = STR2CSTR(user_v);
|
215
|
+
if(NIL_P(pw_v = rb_hash_aref(options, pw_k))) pw = "MANAGER"; else pw = STR2CSTR(pw_v);
|
216
|
+
|
217
|
+
res = SEconnect(conn, host, db, user, pw);
|
218
|
+
verify_res(SEDNA_SESSION_OPEN, res, conn);
|
219
|
+
|
220
|
+
// Initialize @autocommit to true (default argument).
|
221
|
+
rb_iv_set(self, "@autocommit", Qtrue);
|
222
|
+
|
223
|
+
return self;
|
224
|
+
}
|
225
|
+
|
226
|
+
/*
|
227
|
+
* call-seq:
|
228
|
+
* sedna.close -> nil
|
229
|
+
*
|
230
|
+
* Closes an open Sedna connection. If the connection was already closed when
|
231
|
+
* this method is called, nothing happens. A Sedna::ConnectionError is raised
|
232
|
+
* if the connection was open but could not be closed.
|
233
|
+
*/
|
234
|
+
static VALUE cSedna_close(VALUE self)
|
235
|
+
{
|
236
|
+
int res;
|
237
|
+
SC *conn = sedna_struct(self);
|
238
|
+
if(SEconnectionStatus(conn) != SEDNA_CONNECTION_CLOSED) {
|
239
|
+
res = SEclose(conn);
|
240
|
+
verify_res(SEDNA_SESSION_CLOSED, res, conn);
|
241
|
+
}
|
242
|
+
return Qnil;
|
243
|
+
}
|
244
|
+
|
245
|
+
/*
|
246
|
+
* call-seq:
|
247
|
+
* Sedna.connect(details) -> Sedna instance
|
248
|
+
* Sedna.connect(details) {|sedna| ... } -> nil
|
249
|
+
*
|
250
|
+
* Establishes a new connection to a \Sedna XML database. Accepts a hash that
|
251
|
+
* describes which database to \connect to.
|
252
|
+
*
|
253
|
+
* If a block is given, the block is executed if a connection was successfully
|
254
|
+
* established. The connection is closed at the end of the block. If called
|
255
|
+
* without a block, a Sedna object that represents the connection is returned.
|
256
|
+
* The connection should be closed by calling Sedna#close.
|
257
|
+
*
|
258
|
+
* If a connection cannot be initiated, a Sedna::ConnectionError is raised.
|
259
|
+
* If the authentication fails, a Sedna::AuthenticationError is raised.
|
260
|
+
*
|
261
|
+
* ==== Valid connection details keys
|
262
|
+
*
|
263
|
+
* * <tt>:host</tt> - Host name or IP address to which to \connect to (defaults to +localhost+).
|
264
|
+
* * <tt>:database</tt> - Name of the database to \connect to (defaults to +test+).
|
265
|
+
* * <tt>:username</tt> - User name to authenticate with (defaults to +SYSTEM+).
|
266
|
+
* * <tt>:password</tt> - Password to authenticate with (defaults to +MANAGER+).
|
267
|
+
*
|
268
|
+
* ==== Examples
|
269
|
+
*
|
270
|
+
* Call without a block and close the connection afterwards.
|
271
|
+
* sedna = Sedna.connect(:database => "my_db", :host => "my_host")
|
272
|
+
* # Query the database and close afterwards.
|
273
|
+
* sedna.close
|
274
|
+
*
|
275
|
+
* Call with a block. The connection is closed automatically.
|
276
|
+
* Sedna.connect(:database => "my_db", :host => "my_host") do |sedna|
|
277
|
+
* # Query the database.
|
278
|
+
* # The connection is closed automatically.
|
279
|
+
* end
|
280
|
+
*/
|
281
|
+
static VALUE cSedna_s_connect(VALUE klass, VALUE options)
|
282
|
+
{
|
283
|
+
int status;
|
284
|
+
VALUE obj = rb_funcall(klass, rb_intern("new"), 1, options);
|
285
|
+
if(rb_block_given_p()) {
|
286
|
+
rb_protect(rb_yield, obj, &status);
|
287
|
+
cSedna_close(obj);
|
288
|
+
if(status != 0) rb_jump_tag(status); // Re-raise any exception.
|
289
|
+
return Qnil;
|
290
|
+
} else {
|
291
|
+
return obj;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
/*
|
296
|
+
* call-seq:
|
297
|
+
* sedna.execute(query) -> array or nil
|
298
|
+
* sedna.query(query) -> array or nil
|
299
|
+
*
|
300
|
+
* Execute the given +query+ against a \Sedna database. Returns an array if the
|
301
|
+
* given query is a select query. The elements of the array are strings that
|
302
|
+
* correspond to each result in the result set. If the query is an update query
|
303
|
+
* or a (bulk) load query, +nil+ is returned. When attempting to \execute a
|
304
|
+
* query on a closed connection, a Sedna::ConnectionError will be raised. A
|
305
|
+
* Sedna::Exception is raised if the query fails or is invalid.
|
306
|
+
*
|
307
|
+
* ==== Examples
|
308
|
+
*
|
309
|
+
* Create a new document.
|
310
|
+
* sedna.execute "create document 'mydoc'"
|
311
|
+
* #=> nil
|
312
|
+
* Update the newly created document with a root node.
|
313
|
+
* sedna.execute "update insert <message>Hello world!</message> into doc('mydoc')"
|
314
|
+
* #=> nil
|
315
|
+
* Select a node in a document using XPath.
|
316
|
+
* sedna.execute "doc('mydoc')/message/text()"
|
317
|
+
* #=> ["Hello world!"]
|
318
|
+
*
|
319
|
+
* ==== Further reading
|
320
|
+
*
|
321
|
+
* For more information about \Sedna's database query syntax and support, see the
|
322
|
+
* <i>Database language</i> section of the official documentation of the
|
323
|
+
* \Sedna project at http://modis.ispras.ru/sedna/progguide/ProgGuidese2.html
|
324
|
+
*/
|
325
|
+
static VALUE cSedna_execute(VALUE self, VALUE query)
|
326
|
+
{
|
327
|
+
int res;
|
328
|
+
SC *conn = sedna_struct(self);
|
329
|
+
if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
|
330
|
+
res = SEexecute(conn, STR2CSTR(query));
|
331
|
+
switch(res) {
|
332
|
+
case SEDNA_QUERY_SUCCEEDED:
|
333
|
+
return sedna_get_results(conn);
|
334
|
+
case SEDNA_UPDATE_SUCCEEDED:
|
335
|
+
case SEDNA_BULK_LOAD_SUCCEEDED:
|
336
|
+
return Qnil;
|
337
|
+
default:
|
338
|
+
sedna_err(conn, res);
|
339
|
+
return Qnil;
|
340
|
+
}
|
341
|
+
}
|
342
|
+
|
343
|
+
/* :nodoc:
|
344
|
+
*
|
345
|
+
* Turn autocommit on or off.
|
346
|
+
*/
|
347
|
+
static VALUE cSedna_autocommit_set(VALUE self, VALUE auto_commit)
|
348
|
+
{
|
349
|
+
int val = (RTEST(auto_commit) ? Qtrue : Qfalse);
|
350
|
+
SC *conn = sedna_struct(self);
|
351
|
+
switch_sedna_autocommit(conn, val);
|
352
|
+
rb_iv_set(self, "@autocommit", val);
|
353
|
+
return Qnil;
|
354
|
+
}
|
355
|
+
|
356
|
+
/* :nodoc:
|
357
|
+
*
|
358
|
+
* Get the current autocommit value.
|
359
|
+
*/
|
360
|
+
static VALUE cSedna_autocommit_get(VALUE self)
|
361
|
+
{
|
362
|
+
return rb_iv_get(self, "@autocommit");
|
363
|
+
}
|
364
|
+
|
365
|
+
/*
|
366
|
+
* call-seq:
|
367
|
+
* sedna.transaction { ... } -> true
|
368
|
+
*
|
369
|
+
* Wraps the given block in a \transaction. If the block runs
|
370
|
+
* completely, the \transaction is committed. If the stack is unwinded
|
371
|
+
* prematurely, the \transaction is rolled back. This typically happens
|
372
|
+
* when an \Exception is raised by calling +raise+ or a Symbol is thrown by
|
373
|
+
* invoking +throw+. Note that Exceptions will not be rescued -- they will be
|
374
|
+
* re-raised after rolling back the \transaction.
|
375
|
+
*
|
376
|
+
* This method returns +true+ if the \transaction is successfully committed
|
377
|
+
* to the database. If the given block completes successfully, but the
|
378
|
+
* \transaction fails to be committed, a Sedna::TransactionError will
|
379
|
+
* be raised.
|
380
|
+
*
|
381
|
+
* ==== Examples
|
382
|
+
*
|
383
|
+
* sedna.transaction do
|
384
|
+
* count = sedna.execute "count(collection('mycol')/items)" #=> 0
|
385
|
+
* sedna.execute "update insert <total>#{count}</total> into doc('mydoc')"
|
386
|
+
* # ...
|
387
|
+
* end
|
388
|
+
* # Transaction is committed.
|
389
|
+
*
|
390
|
+
* sedna.transaction do
|
391
|
+
* count = sedna.execute "count(collection('mycol')/items)" #=> 0
|
392
|
+
* throw :no_items if count == 0
|
393
|
+
* # ... never get here
|
394
|
+
* end
|
395
|
+
* # Transaction is rolled back.
|
396
|
+
*/
|
397
|
+
static VALUE cSedna_transaction(VALUE self)
|
398
|
+
{
|
399
|
+
int status;
|
400
|
+
SC *conn = sedna_struct(self);
|
401
|
+
sedna_autocommit_off(conn);
|
402
|
+
sedna_tr_begin(conn);
|
403
|
+
rb_protect(rb_yield, Qnil, &status);
|
404
|
+
if(status == 0) {
|
405
|
+
// Attempt to commit.
|
406
|
+
if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) sedna_tr_commit(conn);
|
407
|
+
else rb_raise(cSednaTrnError, "The transaction was prematurely ended, but no error was encountered. Did you rescue an exception inside the transaction?");
|
408
|
+
switch_sedna_autocommit(conn, rb_iv_get(self, "@autocommit"));
|
409
|
+
} else {
|
410
|
+
// Stack has unwinded, attempt to roll back.
|
411
|
+
if(SEtransactionStatus(conn) == SEDNA_TRANSACTION_ACTIVE) sedna_tr_rollback(conn);
|
412
|
+
switch_sedna_autocommit(conn, rb_iv_get(self, "@autocommit"));
|
413
|
+
rb_jump_tag(status); // Re-raise exception.
|
414
|
+
}
|
415
|
+
return Qtrue;
|
416
|
+
}
|
417
|
+
|
418
|
+
void Init_sedna()
|
419
|
+
{
|
420
|
+
/*
|
421
|
+
* Objects of class Sedna represent a connection to a \Sedna XML
|
422
|
+
* database. Establish a new connection by invoking the Sedna.connect
|
423
|
+
* class method.
|
424
|
+
*
|
425
|
+
* connection_details = {
|
426
|
+
* :database => "my_db",
|
427
|
+
* :host => "localhost",
|
428
|
+
* :username => "SYSTEM",
|
429
|
+
* :password => "MANAGER",
|
430
|
+
* }
|
431
|
+
* Sedna.connect(connection_details) do |sedna|
|
432
|
+
* # Query the database.
|
433
|
+
* # The connection is closed automatically.
|
434
|
+
* end
|
435
|
+
*
|
436
|
+
* See the README for a high-level overview of how to use this library.
|
437
|
+
*/
|
438
|
+
cSedna = rb_define_class("Sedna", rb_cObject);
|
439
|
+
rb_define_alloc_func(cSedna, cSedna_s_new);
|
440
|
+
rb_define_singleton_method(cSedna, "connect", cSedna_s_connect, 1);
|
441
|
+
rb_define_method(cSedna, "initialize", cSedna_initialize, 1);
|
442
|
+
rb_define_method(cSedna, "execute", cSedna_execute, 1);
|
443
|
+
rb_define_undocumented_alias(cSedna, "query", "execute");
|
444
|
+
rb_define_method(cSedna, "close", cSedna_close, 0);
|
445
|
+
|
446
|
+
/*
|
447
|
+
* Document-attr: autocommit
|
448
|
+
*
|
449
|
+
* When autocommit is set to +true+ (default), database queries can be run
|
450
|
+
* without explicitly wrapping them in a transaction. Each query that is not
|
451
|
+
* part of a \transaction is automatically committed to the database.
|
452
|
+
* Explicit transactions in auto-commit mode will still be committed
|
453
|
+
* atomically.
|
454
|
+
*
|
455
|
+
* When autocommit is set to +false+, queries can only be run inside an
|
456
|
+
* explicit transaction. Queries run outside transactions will fail with a
|
457
|
+
* Sedna::Exception.
|
458
|
+
*/
|
459
|
+
/* Trick RDoc into thinking this is a regular attribute. We documented the
|
460
|
+
* attribute above.
|
461
|
+
rb_define_attr(cSedna, "autocommit", 1, 1);
|
462
|
+
*/
|
463
|
+
rb_define_method(cSedna, "autocommit=", cSedna_autocommit_set, 1);
|
464
|
+
rb_define_method(cSedna, "autocommit", cSedna_autocommit_get, 0);
|
465
|
+
rb_define_method(cSedna, "transaction", cSedna_transaction, 0);
|
466
|
+
|
467
|
+
/*
|
468
|
+
* The result set of a database query.
|
469
|
+
*/
|
470
|
+
// Unused so far...
|
471
|
+
//cSednaSet = rb_define_class_under(cSedna, "Set", rb_cArray);
|
472
|
+
|
473
|
+
/*
|
474
|
+
* Generic exception class for errors. All errors raised by the \Sedna
|
475
|
+
* client library are of type Sedna::Exception.
|
476
|
+
*
|
477
|
+
* === Subclasses
|
478
|
+
*
|
479
|
+
* For some specific errors, an exception of a particular subtype is raised.
|
480
|
+
*
|
481
|
+
* [Sedna::AuthenticationError]
|
482
|
+
* Raised when a database connection was successfully established, but
|
483
|
+
* the supplied credentials were incorrect. Can only be raised when
|
484
|
+
* invoking Sedna.connect.
|
485
|
+
* [Sedna::ConnectionError]
|
486
|
+
* Raised when a connection to a database could not be established or when
|
487
|
+
* a connection could not be closed. Can be raised when invoking Sedna.connect
|
488
|
+
* or Sedna#close.
|
489
|
+
* [Sedna::TransactionError]
|
490
|
+
* Raised when a transaction could not be committed.
|
491
|
+
*/
|
492
|
+
cSednaException = rb_define_class_under(cSedna, "Exception", rb_eStandardError);
|
493
|
+
|
494
|
+
/*
|
495
|
+
* Sedna::AuthenticationError is a subclass of Sedna::Exception, and is
|
496
|
+
* raised when a database connection was successfully established, but the
|
497
|
+
* supplied credentials were incorrect. Can only be raised when invoking
|
498
|
+
* Sedna.connect.
|
499
|
+
*/
|
500
|
+
cSednaAuthError = rb_define_class_under(cSedna, "AuthenticationError", cSednaException);
|
501
|
+
|
502
|
+
/*
|
503
|
+
* Sedna::ConnectionError is a subclass of Sedna::Exception, and is
|
504
|
+
* raised when a connection to a database could not be established or when
|
505
|
+
* a connection could not be closed. Can be raised when invoking Sedna.connect
|
506
|
+
* or Sedna#close.
|
507
|
+
*/
|
508
|
+
cSednaConnError = rb_define_class_under(cSedna, "ConnectionError", cSednaException);
|
509
|
+
|
510
|
+
/*
|
511
|
+
* Sedna::TransactionError is a subclass of Sedna::Exception, and is
|
512
|
+
* raised when a transaction could not be committed.
|
513
|
+
*/
|
514
|
+
cSednaTrnError = rb_define_class_under(cSedna, "TransactionError", cSednaException);
|
515
|
+
}
|
data/test/test_sedna.rb
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2008 Voormedia B.V.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
# Ruby extension library providing a client API to the Sedna native XML
|
18
|
+
# database management system, based on the official Sedna C driver.
|
19
|
+
|
20
|
+
# This file contains the test suite to verify the client library is working
|
21
|
+
# correctly.
|
22
|
+
|
23
|
+
require 'test/unit'
|
24
|
+
require '../ext/sedna'
|
25
|
+
require 'socket'
|
26
|
+
|
27
|
+
class TestSedna < Test::Unit::TestCase
|
28
|
+
def setup
|
29
|
+
@connection = {
|
30
|
+
:database => "test",
|
31
|
+
:host => "localhost",
|
32
|
+
:username => "SYSTEM",
|
33
|
+
:password => "MANAGER",
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def teardown
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_aaa_connection
|
41
|
+
port = 5050
|
42
|
+
begin
|
43
|
+
s = TCPSocket.new @connection[:host], port
|
44
|
+
rescue Errno::ECONNREFUSED, SocketError
|
45
|
+
# No DB appears to be running; fail fatally. Do not run the other tests and just exit.
|
46
|
+
puts "Connection to port #{port} on #{@connection[:host]} could not be established. Check if the Sedna XML database is running before running this test suite."
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
assert s
|
50
|
+
s.close
|
51
|
+
end
|
52
|
+
|
53
|
+
# Test Sedna.connect.
|
54
|
+
def test_connect_should_return_sedna_object
|
55
|
+
sedna = Sedna.connect @connection
|
56
|
+
assert_kind_of Sedna, sedna
|
57
|
+
sedna.close
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_connect_should_raise_exception_when_host_not_found
|
61
|
+
assert_raises Sedna::ConnectionError do
|
62
|
+
Sedna.connect @connection.merge(:host => "non-existant-host")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_connect_should_raise_exception_when_credentials_are_incorrect
|
67
|
+
assert_raises Sedna::AuthenticationError do
|
68
|
+
Sedna.connect @connection.merge(:username => "non-existant-user")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_connect_should_return_nil_on_error
|
73
|
+
begin
|
74
|
+
sedna = Sedna.connect @connection.merge(:username => "non-existant-user")
|
75
|
+
rescue
|
76
|
+
end
|
77
|
+
assert_nil sedna
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_connect_should_return_nil_if_block_given
|
81
|
+
sedna = Sedna.connect @connection do |s| end
|
82
|
+
assert_nil sedna
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_connect_should_close_connection_after_block
|
86
|
+
sedna = nil
|
87
|
+
Sedna.connect @connection do |s|
|
88
|
+
sedna = s
|
89
|
+
end
|
90
|
+
assert_raises Sedna::ConnectionError do
|
91
|
+
sedna.execute "<test/>"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_connect_should_close_connection_if_exception_is_raised_inside_block
|
96
|
+
sedna = nil
|
97
|
+
begin
|
98
|
+
Sedna.connect @connection do |s|
|
99
|
+
sedna = s
|
100
|
+
raise Exception
|
101
|
+
end
|
102
|
+
rescue Exception
|
103
|
+
end
|
104
|
+
assert_raises Sedna::ConnectionError do
|
105
|
+
sedna.execute "<test/>"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_connect_should_close_connection_if_something_is_thrown_inside_block
|
110
|
+
sedna = nil
|
111
|
+
catch :ball do
|
112
|
+
Sedna.connect @connection do |s|
|
113
|
+
sedna = s
|
114
|
+
throw :ball
|
115
|
+
end
|
116
|
+
end
|
117
|
+
assert_raises Sedna::ConnectionError do
|
118
|
+
sedna.execute "<test/>"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_connect_should_reraise_exceptions_from_inside_block
|
123
|
+
assert_raises Exception do
|
124
|
+
Sedna.connect @connection do
|
125
|
+
raise Exception
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# TODO: Fix the following strangely-failing test case.
|
131
|
+
#def test_zzz_connect_should_not_fail_to_close_connection_when_require_called_inside_block
|
132
|
+
# # Squash a strange bug -- only appears to work if this test is run last and nothing else fails.
|
133
|
+
# assert_nothing_raised do
|
134
|
+
# Sedna.connect @connection do |sedna|
|
135
|
+
# require 'pp'
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
#end
|
139
|
+
|
140
|
+
# Test sedna.close.
|
141
|
+
def test_close_should_return_nil
|
142
|
+
sedna = Sedna.connect @connection
|
143
|
+
assert_nil sedna.close
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_close_should_fail_silently_if_connection_is_already_closed
|
147
|
+
sedna = Sedna.connect @connection
|
148
|
+
assert_nothing_raised do
|
149
|
+
sedna.close
|
150
|
+
sedna.close
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Test sedna.execute / sedna.query.
|
155
|
+
def test_execute_should_return_nil_for_data_structure_query
|
156
|
+
Sedna.connect @connection do |sedna|
|
157
|
+
name = "test_execute_should_return_nil_for_create_document_query"
|
158
|
+
sedna.execute("drop document '#{name}'") rescue Sedna::Exception
|
159
|
+
assert_nil sedna.execute("create document '#{name}'")
|
160
|
+
sedna.execute("drop document '#{name}'") rescue Sedna::Exception
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def test_execute_should_return_array_for_select_query
|
165
|
+
Sedna.connect @connection do |sedna|
|
166
|
+
assert_kind_of Array, sedna.execute("<test/>")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_execute_should_return_array_with_single_string_for_single_select_query
|
171
|
+
Sedna.connect @connection do |sedna|
|
172
|
+
assert_equal ["<test/>"], sedna.execute("<test/>")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_execute_should_return_array_with_strings_for_select_query
|
177
|
+
Sedna.connect @connection do |sedna|
|
178
|
+
assert_equal ["<test/>", "<test/>", "<test/>"], sedna.execute("<test/>, <test/>, <test/>")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def test_execute_should_fail_if_autocommit_is_false
|
183
|
+
Sedna.connect @connection do |sedna|
|
184
|
+
sedna.autocommit = false
|
185
|
+
assert_raises Sedna::Exception do
|
186
|
+
sedna.execute "<test/>"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_execute_should_fail_with_sedna_exception_for_invalid_statments
|
192
|
+
Sedna.connect @connection do |sedna|
|
193
|
+
assert_raises Sedna::Exception do
|
194
|
+
sedna.execute "INVALID"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_execute_should_fail_with_sedna_connection_error_if_connection_is_closed
|
200
|
+
Sedna.connect @connection do |sedna|
|
201
|
+
sedna.close
|
202
|
+
assert_raises Sedna::ConnectionError do
|
203
|
+
sedna.execute "<test/>"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_execute_should_strip_first_newline_of_all_but_first_results
|
209
|
+
Sedna.connect @connection do |sedna|
|
210
|
+
name = "test_execute_should_strip_first_newline_of_all_but_first_results"
|
211
|
+
sedna.execute("drop document '#{name}'") rescue Sedna::Exception
|
212
|
+
sedna.execute("create document '#{name}'")
|
213
|
+
sedna.execute("update insert <test><a>\n\nt</a><a>\n\nt</a><a>\n\nt</a></test> into doc('#{name}')")
|
214
|
+
assert_equal ["\n\nt", "\n\nt", "\n\nt"], sedna.execute("doc('#{name}')/test/a/text()")
|
215
|
+
sedna.execute("drop document '#{name}'") rescue Sedna::Exception
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_query_should_be_alias_of_execute
|
220
|
+
Sedna.connect @connection do |sedna|
|
221
|
+
assert_equal ["<test/>"], sedna.query("<test/>")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Test sedna.autocommit= / sedna.autocommit.
|
226
|
+
def test_autocommit_should_return_true_by_default
|
227
|
+
Sedna.connect @connection do |sedna|
|
228
|
+
assert_equal true, sedna.autocommit
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_autocommit_should_return_true_if_set_to_true
|
233
|
+
Sedna.connect @connection do |sedna|
|
234
|
+
sedna.autocommit = true
|
235
|
+
assert_equal true, sedna.autocommit
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def test_autocommit_should_return_false_if_set_to_false
|
240
|
+
Sedna.connect @connection do |sedna|
|
241
|
+
sedna.autocommit = false
|
242
|
+
assert_equal false, sedna.autocommit
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_autocommit_should_return_true_if_set_to_true_after_being_set_to_false
|
247
|
+
Sedna.connect @connection do |sedna|
|
248
|
+
sedna.autocommit = false
|
249
|
+
sedna.autocommit = true
|
250
|
+
assert_equal true, sedna.autocommit
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def test_autocommit_should_return_true_if_argument_evaluates_to_true
|
255
|
+
Sedna.connect @connection do |sedna|
|
256
|
+
sedna.autocommit = "string evaluates to true"
|
257
|
+
assert_equal true, sedna.autocommit
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_autocommit_should_return_false_if_argument_evaluates_to_false
|
262
|
+
Sedna.connect @connection do |sedna|
|
263
|
+
sedna.autocommit = nil
|
264
|
+
assert_equal false, sedna.autocommit
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_autocommit_should_be_reenabled_after_transactions
|
269
|
+
Sedna.connect @connection do |sedna|
|
270
|
+
sedna.autocommit = true
|
271
|
+
sedna.transaction do end
|
272
|
+
assert_nothing_raised do
|
273
|
+
sedna.execute "<test/>"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
# Test sedna.transaction.
|
279
|
+
def test_transaction_should_return_true_if_committed
|
280
|
+
Sedna.connect @connection do |sedna|
|
281
|
+
assert_equal true, sedna.transaction(){}
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_transaction_should_raise_localjumperror_if_no_block_is_given
|
286
|
+
assert_raises LocalJumpError do
|
287
|
+
Sedna.connect @connection do |sedna|
|
288
|
+
sedna.transaction
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_transaction_should_be_possible_with_autocommit
|
294
|
+
Sedna.connect @connection do |sedna|
|
295
|
+
sedna.autocommit = true
|
296
|
+
assert_nothing_raised do
|
297
|
+
sedna.transaction do end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_transaction_should_fail_with_transaction_error_if_another_transaction_is_started_inside_it
|
303
|
+
assert_raises Sedna::TransactionError do
|
304
|
+
Sedna.connect @connection do |sedna|
|
305
|
+
sedna.transaction do
|
306
|
+
sedna.transaction do end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def test_transaction_should_commit_if_block_given
|
313
|
+
Sedna.connect @connection do |sedna|
|
314
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
315
|
+
sedna.execute "create document '#{name}'"
|
316
|
+
sedna.transaction do
|
317
|
+
sedna.execute "update insert <test>test</test> into doc('#{name}')"
|
318
|
+
end
|
319
|
+
assert_equal 1, sedna.execute("count(doc('#{name}')/test)").first.to_i
|
320
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_transaction_should_rollback_if_exception_is_raised_inside_block
|
325
|
+
Sedna.connect @connection do |sedna|
|
326
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
327
|
+
sedna.execute "create document '#{name}'"
|
328
|
+
begin
|
329
|
+
sedna.transaction do
|
330
|
+
sedna.execute "update insert <test>test</test> into doc('#{name}')"
|
331
|
+
raise Exception
|
332
|
+
end
|
333
|
+
rescue Exception
|
334
|
+
end
|
335
|
+
assert_equal 0, sedna.execute("count(doc('#{name}')/test)").first.to_i
|
336
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_transaction_should_rollback_if_something_is_thrown_inside_block
|
341
|
+
Sedna.connect @connection do |sedna|
|
342
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
343
|
+
sedna.execute "create document '#{name}'"
|
344
|
+
catch :ball do
|
345
|
+
sedna.transaction do
|
346
|
+
sedna.execute "update insert <test>test</test> into doc('#{name}')"
|
347
|
+
throw :ball
|
348
|
+
end
|
349
|
+
end
|
350
|
+
assert_equal 0, sedna.execute("count(doc('#{name}')/test)").first.to_i
|
351
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_transaction_should_raise_transaction_error_if_invalid_statement_caused_exception_but_it_was_rescued
|
356
|
+
assert_raises Sedna::TransactionError do
|
357
|
+
Sedna.connect @connection do |sedna|
|
358
|
+
sedna.transaction do
|
359
|
+
sedna.execute "FAILS" rescue Sedna::Exception
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_transaction_should_reraise_exceptions_from_inside_block
|
366
|
+
Sedna.connect @connection do |sedna|
|
367
|
+
assert_raises Exception do
|
368
|
+
sedna.transaction do
|
369
|
+
raise Exception
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def test_transaction_with_invalid_statements_should_cause_transaction_to_roll_back_once
|
376
|
+
exc = nil
|
377
|
+
begin
|
378
|
+
Sedna.connect @connection do |sedna|
|
379
|
+
sedna.transaction do
|
380
|
+
sedna.execute "FAILS"
|
381
|
+
end
|
382
|
+
end
|
383
|
+
rescue Sedna::Exception => exc
|
384
|
+
end
|
385
|
+
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
|
386
|
+
end
|
387
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sedna
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rolf Timmermans
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-08 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Ruby extension that provides a client library for the Sedna XML DBMS, making use of the official C driver of the Sedna project.
|
17
|
+
email: r.timmermans@voormedia.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/extconf.rb
|
22
|
+
extra_rdoc_files:
|
23
|
+
- CHANGES
|
24
|
+
- README
|
25
|
+
- ext/sedna.c
|
26
|
+
files:
|
27
|
+
- ext/extconf.rb
|
28
|
+
- ext/sedna.c
|
29
|
+
- test/test_sedna.rb
|
30
|
+
- CHANGES
|
31
|
+
- README
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://sedna.rubyforge.org/
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- --title
|
37
|
+
- Sedna XML DBMS client library for Ruby
|
38
|
+
- --main
|
39
|
+
- README
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: "0"
|
47
|
+
version:
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
requirements:
|
55
|
+
- Sedna XML DBMS C driver (library and headers).
|
56
|
+
rubyforge_project: sedna
|
57
|
+
rubygems_version: 1.3.1
|
58
|
+
signing_key:
|
59
|
+
specification_version: 2
|
60
|
+
summary: Sedna XML DBMS client library.
|
61
|
+
test_files:
|
62
|
+
- test/test_sedna.rb
|