sedna 0.1.0 → 0.2.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 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 the December 8th, 2008.
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.1.0. This version is a <b>development
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 BUF_LEN 8192
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
- if((p = strstr(details, "\n")) != NULL) strncpy(p, "\0", 1);
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[BUF_LEN];
118
+ char buffer[RESULT_BUF_LEN];
118
119
  VALUE str = rb_str_buf_new(0);
119
120
  do {
120
- bytes_read = SEgetData(conn, buffer, BUF_LEN - 1);
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
- verify_res(SEDNA_SESSION_OPEN, res, conn);
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
- * Execute the given +query+ against a \Sedna database. Returns an array if the
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 { ... } -> true
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 +true+ if the \transaction is successfully committed
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('mycol')/items)" #=> 0
385
- * sedna.execute "update insert <total>#{count}</total> into doc('mydoc')"
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('mycol')/items)" #=> 0
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 Qtrue;
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 '../ext/sedna'
26
+ require 'sedna'
25
27
  require 'socket'
26
28
 
27
- class TestSedna < Test::Unit::TestCase
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-existant-host")
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-existant-user")
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-existant-user")
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 test_transaction_should_return_true_if_committed
405
+ def test_transaction_should_return_nil_if_committed
280
406
  Sedna.connect @connection do |sedna|
281
- assert_equal true, sedna.transaction(){}
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.1.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-08 00:00:00 +01:00
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/test_sedna.rb
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/test_sedna.rb
62
+ - test/sedna_test.rb