sedna 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +7 -1
- data/README +1 -1
- data/ext/sedna.c +77 -12
- data/test/{test_sedna.rb → sedna_test.rb} +144 -18
- metadata +4 -4
data/CHANGES
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
=== 0.2.0
|
2
|
+
|
3
|
+
* Released on December 15th, 2008.
|
4
|
+
* Added support for importing documents from a string or file (Sedna#load_document).
|
5
|
+
* Fixed garbage collection error for unsuccessful connections.
|
6
|
+
|
1
7
|
=== 0.1.0
|
2
8
|
|
3
|
-
* Released on
|
9
|
+
* Released on December 8th, 2008.
|
4
10
|
* Development preview (alpha) release.
|
5
11
|
* Simple, high-level API.
|
6
12
|
* Tested with Ruby 1.8.7 and Ruby 1.9.0.
|
data/README
CHANGED
@@ -32,7 +32,7 @@ limitations under the License.
|
|
32
32
|
|
33
33
|
=== Current version
|
34
34
|
|
35
|
-
The current version of this library is 0.
|
35
|
+
The current version of this library is 0.2.0. This version is a <b>development
|
36
36
|
preview</b>. The API may change significantly between minor versions.
|
37
37
|
For a complete overview all recent and previous changes, see CHANGES.
|
38
38
|
|
data/ext/sedna.c
CHANGED
@@ -24,7 +24,8 @@
|
|
24
24
|
#include "libsedna.h"
|
25
25
|
|
26
26
|
// Size of the read buffer.
|
27
|
-
#define
|
27
|
+
#define RESULT_BUF_LEN 8192
|
28
|
+
#define LOAD_BUF_LEN 8192
|
28
29
|
|
29
30
|
// Use macros to fool RDoc and hide some of our methods/aliases.
|
30
31
|
#define rb_define_undocumented_alias(kl, new, old) rb_define_alias(kl, new, old)
|
@@ -83,7 +84,7 @@ static void sedna_err(SC *conn, int res)
|
|
83
84
|
}
|
84
85
|
if(details != NULL) {
|
85
86
|
details += 10;
|
86
|
-
|
87
|
+
while((p = strstr(details, "\n")) != NULL) strncpy(p, " ", 1);
|
87
88
|
rb_raise(exception, "%s (%s)", err, details);
|
88
89
|
} else {
|
89
90
|
rb_raise(exception, "%s", err);
|
@@ -114,10 +115,10 @@ static void sedna_mark(SC *conn)
|
|
114
115
|
static VALUE sedna_read(SC *conn, int strip_n)
|
115
116
|
{
|
116
117
|
int bytes_read = 0;
|
117
|
-
char buffer[
|
118
|
+
char buffer[RESULT_BUF_LEN];
|
118
119
|
VALUE str = rb_str_buf_new(0);
|
119
120
|
do {
|
120
|
-
bytes_read = SEgetData(conn, buffer,
|
121
|
+
bytes_read = SEgetData(conn, buffer, RESULT_BUF_LEN - 1);
|
121
122
|
if(bytes_read == SEDNA_ERROR) {
|
122
123
|
sedna_err(conn, SEDNA_ERROR);
|
123
124
|
} else {
|
@@ -215,7 +216,16 @@ static VALUE cSedna_initialize(VALUE self, VALUE options)
|
|
215
216
|
if(NIL_P(pw_v = rb_hash_aref(options, pw_k))) pw = "MANAGER"; else pw = STR2CSTR(pw_v);
|
216
217
|
|
217
218
|
res = SEconnect(conn, host, db, user, pw);
|
218
|
-
|
219
|
+
if(res != SEDNA_SESSION_OPEN) {
|
220
|
+
// We have to set the connection status to closed explicitly here,
|
221
|
+
// because the GC routine sedna_free() will test for this status, but
|
222
|
+
// the socket is already closed by SEconnect(). If we do not change the
|
223
|
+
// status, sedna_free() will attempt to close the connection again by
|
224
|
+
// calling SEclose(), which will definitely lead to unpredictable
|
225
|
+
// results.
|
226
|
+
conn->isConnectionOk = SEDNA_CONNECTION_CLOSED;
|
227
|
+
sedna_err(conn, res);
|
228
|
+
}
|
219
229
|
|
220
230
|
// Initialize @autocommit to true (default argument).
|
221
231
|
rb_iv_set(self, "@autocommit", Qtrue);
|
@@ -297,7 +307,7 @@ static VALUE cSedna_s_connect(VALUE klass, VALUE options)
|
|
297
307
|
* sedna.execute(query) -> array or nil
|
298
308
|
* sedna.query(query) -> array or nil
|
299
309
|
*
|
300
|
-
*
|
310
|
+
* Executes the given +query+ against a \Sedna database. Returns an array if the
|
301
311
|
* given query is a select query. The elements of the array are strings that
|
302
312
|
* correspond to each result in the result set. If the query is an update query
|
303
313
|
* or a (bulk) load query, +nil+ is returned. When attempting to \execute a
|
@@ -340,6 +350,60 @@ static VALUE cSedna_execute(VALUE self, VALUE query)
|
|
340
350
|
}
|
341
351
|
}
|
342
352
|
|
353
|
+
/*
|
354
|
+
* call-seq:
|
355
|
+
* sedna.load_document(document, doc_name, col_name = nil) -> nil
|
356
|
+
*
|
357
|
+
* Creates a new document named +doc_name+ in collection +col_name+, or as a
|
358
|
+
* stand-alone document if +col_name+ is +nil+. The string +document+ is
|
359
|
+
* subsequently loaded into the newly created document. As an alternative, the
|
360
|
+
* argument +document+ may be an IO object (or any descendant, such as a File
|
361
|
+
* object).
|
362
|
+
*
|
363
|
+
* If the document was successfully loaded, this method returns +nil+. If an
|
364
|
+
* error occurs, a Sedna::Exception is raised.
|
365
|
+
*
|
366
|
+
* ==== Examples
|
367
|
+
*
|
368
|
+
* Create a new standalone document and retrieve it.
|
369
|
+
*
|
370
|
+
* sedna.load_document "<my_document>Hello world!</my_document>", "my_doc"
|
371
|
+
* #=> nil
|
372
|
+
* sedna.execute "doc('my_doc')"
|
373
|
+
* #=> ["<?xml version=\"1.0\" standalone=\"yes\"?><my_document>Hello world!</my_document>"]
|
374
|
+
*
|
375
|
+
* Open a file and import its contents into a new document in an existing collection.
|
376
|
+
*
|
377
|
+
* File.open "document.xml" do |file|
|
378
|
+
* sedna.load_document file, "my_doc", "my_col"
|
379
|
+
* end
|
380
|
+
*/
|
381
|
+
static VALUE cSedna_load_document(int argc, VALUE *argv, VALUE self)
|
382
|
+
{
|
383
|
+
int res = 0;
|
384
|
+
SC *conn = sedna_struct(self);
|
385
|
+
VALUE document, doc_name, col_name, buf;
|
386
|
+
char *doc_name_c, *col_name_c;
|
387
|
+
if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
|
388
|
+
rb_scan_args(argc, argv, "21", &document, &doc_name, &col_name); // 2 mandatory args, 1 optional.
|
389
|
+
doc_name_c = STR2CSTR(doc_name);
|
390
|
+
col_name_c = NIL_P(col_name) ? NULL : STR2CSTR(col_name);
|
391
|
+
if(TYPE(document) == T_FILE) {
|
392
|
+
while(!NIL_P(buf = rb_funcall(document, rb_intern("read"), 1, INT2NUM(LOAD_BUF_LEN)))) {
|
393
|
+
res = SEloadData(conn, STR2CSTR(buf), RSTRING_LEN(buf), doc_name_c, col_name_c);
|
394
|
+
verify_res(SEDNA_DATA_CHUNK_LOADED, res, conn);
|
395
|
+
}
|
396
|
+
if(res == 0) rb_raise(cSednaException, "Document is empty.");
|
397
|
+
} else {
|
398
|
+
if(RSTRING_LEN(document) == 0) rb_raise(cSednaException, "Document is empty.");
|
399
|
+
res = SEloadData(conn, STR2CSTR(document), RSTRING_LEN(document), doc_name_c, col_name_c);
|
400
|
+
verify_res(SEDNA_DATA_CHUNK_LOADED, res, conn);
|
401
|
+
}
|
402
|
+
res = SEendLoadData(conn);
|
403
|
+
verify_res(SEDNA_BULK_LOAD_SUCCEEDED, res, conn);
|
404
|
+
return Qnil;
|
405
|
+
}
|
406
|
+
|
343
407
|
/* :nodoc:
|
344
408
|
*
|
345
409
|
* Turn autocommit on or off.
|
@@ -364,7 +428,7 @@ static VALUE cSedna_autocommit_get(VALUE self)
|
|
364
428
|
|
365
429
|
/*
|
366
430
|
* call-seq:
|
367
|
-
* sedna.transaction { ... } ->
|
431
|
+
* sedna.transaction { ... } -> nil
|
368
432
|
*
|
369
433
|
* Wraps the given block in a \transaction. If the block runs
|
370
434
|
* completely, the \transaction is committed. If the stack is unwinded
|
@@ -373,7 +437,7 @@ static VALUE cSedna_autocommit_get(VALUE self)
|
|
373
437
|
* invoking +throw+. Note that Exceptions will not be rescued -- they will be
|
374
438
|
* re-raised after rolling back the \transaction.
|
375
439
|
*
|
376
|
-
* This method returns +
|
440
|
+
* This method returns +nil+ if the \transaction is successfully committed
|
377
441
|
* to the database. If the given block completes successfully, but the
|
378
442
|
* \transaction fails to be committed, a Sedna::TransactionError will
|
379
443
|
* be raised.
|
@@ -381,14 +445,14 @@ static VALUE cSedna_autocommit_get(VALUE self)
|
|
381
445
|
* ==== Examples
|
382
446
|
*
|
383
447
|
* sedna.transaction do
|
384
|
-
* count = sedna.execute "count(collection('
|
385
|
-
* sedna.execute "update insert <total>#{count}</total> into doc('
|
448
|
+
* count = sedna.execute "count(collection('my_col')/items)" #=> 0
|
449
|
+
* sedna.execute "update insert <total>#{count}</total> into doc('my_doc')"
|
386
450
|
* # ...
|
387
451
|
* end
|
388
452
|
* # Transaction is committed.
|
389
453
|
*
|
390
454
|
* sedna.transaction do
|
391
|
-
* count = sedna.execute "count(collection('
|
455
|
+
* count = sedna.execute "count(collection('my_col')/items)" #=> 0
|
392
456
|
* throw :no_items if count == 0
|
393
457
|
* # ... never get here
|
394
458
|
* end
|
@@ -412,7 +476,7 @@ static VALUE cSedna_transaction(VALUE self)
|
|
412
476
|
switch_sedna_autocommit(conn, rb_iv_get(self, "@autocommit"));
|
413
477
|
rb_jump_tag(status); // Re-raise exception.
|
414
478
|
}
|
415
|
-
return
|
479
|
+
return Qnil;
|
416
480
|
}
|
417
481
|
|
418
482
|
void Init_sedna()
|
@@ -440,6 +504,7 @@ void Init_sedna()
|
|
440
504
|
rb_define_singleton_method(cSedna, "connect", cSedna_s_connect, 1);
|
441
505
|
rb_define_method(cSedna, "initialize", cSedna_initialize, 1);
|
442
506
|
rb_define_method(cSedna, "execute", cSedna_execute, 1);
|
507
|
+
rb_define_method(cSedna, "load_document", cSedna_load_document, -1);
|
443
508
|
rb_define_undocumented_alias(cSedna, "query", "execute");
|
444
509
|
rb_define_method(cSedna, "close", cSedna_close, 0);
|
445
510
|
|
@@ -20,11 +20,13 @@
|
|
20
20
|
# This file contains the test suite to verify the client library is working
|
21
21
|
# correctly.
|
22
22
|
|
23
|
+
$:.unshift(File.dirname(__FILE__) + '/../ext')
|
24
|
+
|
23
25
|
require 'test/unit'
|
24
|
-
require '
|
26
|
+
require 'sedna'
|
25
27
|
require 'socket'
|
26
28
|
|
27
|
-
class
|
29
|
+
class SednaTest < Test::Unit::TestCase
|
28
30
|
def setup
|
29
31
|
@connection = {
|
30
32
|
:database => "test",
|
@@ -36,7 +38,9 @@ class TestSedna < Test::Unit::TestCase
|
|
36
38
|
|
37
39
|
def teardown
|
38
40
|
end
|
39
|
-
|
41
|
+
|
42
|
+
# Faux test that just checks if we can connect, otherwise the test
|
43
|
+
# suite is aborted.
|
40
44
|
def test_aaa_connection
|
41
45
|
port = 5050
|
42
46
|
begin
|
@@ -59,19 +63,19 @@ class TestSedna < Test::Unit::TestCase
|
|
59
63
|
|
60
64
|
def test_connect_should_raise_exception_when_host_not_found
|
61
65
|
assert_raises Sedna::ConnectionError do
|
62
|
-
Sedna.connect @connection.merge(:host => "non-
|
66
|
+
Sedna.connect @connection.merge(:host => "non-existent-host")
|
63
67
|
end
|
64
68
|
end
|
65
69
|
|
66
70
|
def test_connect_should_raise_exception_when_credentials_are_incorrect
|
67
71
|
assert_raises Sedna::AuthenticationError do
|
68
|
-
Sedna.connect @connection.merge(:username => "non-
|
72
|
+
Sedna.connect @connection.merge(:username => "non-existent-user")
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
72
76
|
def test_connect_should_return_nil_on_error
|
73
77
|
begin
|
74
|
-
sedna = Sedna.connect @connection.merge(:username => "non-
|
78
|
+
sedna = Sedna.connect @connection.merge(:username => "non-existent-user")
|
75
79
|
rescue
|
76
80
|
end
|
77
81
|
assert_nil sedna
|
@@ -126,16 +130,6 @@ class TestSedna < Test::Unit::TestCase
|
|
126
130
|
end
|
127
131
|
end
|
128
132
|
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
133
|
|
140
134
|
# Test sedna.close.
|
141
135
|
def test_close_should_return_nil
|
@@ -221,6 +215,138 @@ class TestSedna < Test::Unit::TestCase
|
|
221
215
|
assert_equal ["<test/>"], sedna.query("<test/>")
|
222
216
|
end
|
223
217
|
end
|
218
|
+
|
219
|
+
# Test sedna.load_document.
|
220
|
+
def test_load_document_should_create_document_in_given_collection
|
221
|
+
Sedna.connect @connection do |sedna|
|
222
|
+
name = "test_load_document_should_create_document_in_given_collection"
|
223
|
+
col = "test_collection"
|
224
|
+
doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>\n <node/>\n</document>"
|
225
|
+
|
226
|
+
sedna.execute "create collection '#{col}'" rescue Sedna::Exception
|
227
|
+
sedna.execute "drop document '#{name}' in collection '#{col}'" rescue Sedna::Exception
|
228
|
+
sedna.load_document doc, name, col
|
229
|
+
assert_equal doc, sedna.execute("doc('#{name}', '#{col}')").first
|
230
|
+
sedna.execute "drop document '#{name}' in collection '#{col}'" rescue Sedna::Exception
|
231
|
+
sedna.execute "drop collection '#{col}'" rescue Sedna::Exception
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_load_document_should_create_standalone_document_if_collection_is_unspecified
|
236
|
+
Sedna.connect @connection do |sedna|
|
237
|
+
name = "test_load_document_should_create_standalone_document_if_collection_is_unspecified"
|
238
|
+
doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>\n <node/>\n</document>"
|
239
|
+
|
240
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
241
|
+
sedna.load_document doc, name
|
242
|
+
assert_equal doc, sedna.execute("doc('#{name}')").first
|
243
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_load_document_should_create_standalone_document_if_collection_is_nil
|
248
|
+
Sedna.connect @connection do |sedna|
|
249
|
+
name = "test_load_document_should_create_standalone_document_if_collection_is_unspecified"
|
250
|
+
doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>\n <node/>\n</document>"
|
251
|
+
|
252
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
253
|
+
sedna.load_document doc, name, nil
|
254
|
+
assert_equal doc, sedna.execute("doc('#{name}')").first
|
255
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def test_load_document_should_return_nil_if_standalone_document_loaded_successfully
|
260
|
+
Sedna.connect @connection do |sedna|
|
261
|
+
name = "test_load_document_should_return_nil_if_document_loaded_successfully"
|
262
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
263
|
+
assert_nil sedna.load_document("<document><node/></document>", name)
|
264
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def test_load_document_should_fail_if_autocommit_is_false
|
269
|
+
Sedna.connect @connection do |sedna|
|
270
|
+
sedna.autocommit = false
|
271
|
+
assert_raises Sedna::Exception do
|
272
|
+
sedna.load_document "<test/>", "some_doc"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_load_document_should_fail_with_sedna_exception_for_invalid_documents
|
278
|
+
Sedna.connect @connection do |sedna|
|
279
|
+
assert_raises Sedna::Exception do
|
280
|
+
sedna.load_document "<doc/> this is an invalid document", "some_doc"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_load_document_should_raise_exception_with_complete_details_for_invalid_documents
|
286
|
+
Sedna.connect @connection do |sedna|
|
287
|
+
e = nil
|
288
|
+
begin
|
289
|
+
sedna.load_document "<doc/> junk here", "some_doc"
|
290
|
+
rescue Sedna::Exception => e
|
291
|
+
end
|
292
|
+
assert_match /junk after document element/, e.message
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_load_document_should_fail_with_sedna_connection_error_if_connection_is_closed
|
297
|
+
Sedna.connect @connection do |sedna|
|
298
|
+
sedna.close
|
299
|
+
assert_raises Sedna::ConnectionError do
|
300
|
+
sedna.load_document "<doc/>", "some_doc"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_load_document_should_create_document_if_given_document_is_io_object
|
306
|
+
Sedna.connect @connection do |sedna|
|
307
|
+
name = "test_load_document_should_create_document_if_given_document_is_io_object"
|
308
|
+
doc = "<?xml version=\"1.0\" standalone=\"yes\"?><document>" << ("\n <some_very_often_repeated_node/>" * 800) << "\n</document>"
|
309
|
+
p_out, p_in = IO.pipe
|
310
|
+
p_in.write doc
|
311
|
+
p_in.close
|
312
|
+
|
313
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
314
|
+
sedna.load_document p_out, name, nil
|
315
|
+
assert_equal doc.length, sedna.execute("doc('#{name}')").first.length
|
316
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_load_document_should_raise_sedna_exception_if_given_document_is_empty_io_object
|
321
|
+
Sedna.connect @connection do |sedna|
|
322
|
+
name = "test_load_document_should_raise_sedna_exception_if_given_document_is_empty_io_object"
|
323
|
+
p_out, p_in = IO.pipe
|
324
|
+
p_in.close
|
325
|
+
|
326
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
327
|
+
e = nil
|
328
|
+
begin
|
329
|
+
sedna.load_document p_out, name, nil
|
330
|
+
rescue Sedna::Exception => e
|
331
|
+
end
|
332
|
+
assert_equal "Document is empty.", e.message
|
333
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def test_load_document_should_raise_sedna_exception_if_given_document_is_empty_string
|
338
|
+
Sedna.connect @connection do |sedna|
|
339
|
+
name = "test_load_document_should_raise_sedna_exception_if_given_document_is_empty_string"
|
340
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
341
|
+
e = nil
|
342
|
+
begin
|
343
|
+
sedna.load_document "", name, nil
|
344
|
+
rescue Sedna::Exception => e
|
345
|
+
end
|
346
|
+
assert_equal "Document is empty.", e.message
|
347
|
+
sedna.execute "drop document '#{name}'" rescue Sedna::Exception
|
348
|
+
end
|
349
|
+
end
|
224
350
|
|
225
351
|
# Test sedna.autocommit= / sedna.autocommit.
|
226
352
|
def test_autocommit_should_return_true_by_default
|
@@ -276,9 +402,9 @@ class TestSedna < Test::Unit::TestCase
|
|
276
402
|
end
|
277
403
|
|
278
404
|
# Test sedna.transaction.
|
279
|
-
def
|
405
|
+
def test_transaction_should_return_nil_if_committed
|
280
406
|
Sedna.connect @connection do |sedna|
|
281
|
-
|
407
|
+
assert_nil sedna.transaction(){}
|
282
408
|
end
|
283
409
|
end
|
284
410
|
|
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.2.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-15 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -26,7 +26,7 @@ extra_rdoc_files:
|
|
26
26
|
files:
|
27
27
|
- ext/extconf.rb
|
28
28
|
- ext/sedna.c
|
29
|
-
- test/
|
29
|
+
- test/sedna_test.rb
|
30
30
|
- CHANGES
|
31
31
|
- README
|
32
32
|
has_rdoc: true
|
@@ -59,4 +59,4 @@ signing_key:
|
|
59
59
|
specification_version: 2
|
60
60
|
summary: Sedna XML DBMS client library.
|
61
61
|
test_files:
|
62
|
-
- test/
|
62
|
+
- test/sedna_test.rb
|