sedna 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +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
|