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 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