Class: Sedna

Inherits:
Object
  • Object
show all
Defined in:
ext/sedna/sedna.c

Defined Under Namespace

Classes: AuthenticationError, ConnectionError, Exception, TransactionError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Object

:nodoc:

Initialize a new instance of Sedna. Undocumented, because Sedna.connect should be used instead.



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'ext/sedna/sedna.c', line 402

static VALUE cSedna_initialize(VALUE self, VALUE options)
{
	VALUE host_k, db_k, user_k, pw_k,
	      host_v, db_v, user_v, pw_v;
	char *host, *db, *user, *pw;

	// Ensure the argument is a Hash.
	Check_Type(options, T_HASH);
	
	// Store the symbols of the valid hash keys.
	host_k = ID2SYM(rb_intern("host"));
	db_k   = ID2SYM(rb_intern("database"));
	user_k = ID2SYM(rb_intern("username"));
	pw_k   = ID2SYM(rb_intern("password"));

	// Get the connection details or set them to the default values if not given.
	if(NIL_P(host_v = rb_hash_aref(options, host_k))) host = strdup(DEFAULT_HOST); else host = StringValuePtr(host_v);
	if(NIL_P(db_v   = rb_hash_aref(options, db_k  ))) db   = strdup(DEFAULT_DB);   else db =   StringValuePtr(db_v);
	if(NIL_P(user_v = rb_hash_aref(options, user_k))) user = strdup(DEFAULT_USER); else user = StringValuePtr(user_v);
	if(NIL_P(pw_v   = rb_hash_aref(options, pw_k  ))) pw   = strdup(DEFAULT_PW);   else pw =   StringValuePtr(pw_v);
	
	// Save all connection details to instance variables.
	rb_iv_set(self, IV_HOST, rb_str_new2(host));
	rb_iv_set(self, IV_DB,   rb_str_new2(db));
	rb_iv_set(self, IV_USER, rb_str_new2(user));
	rb_iv_set(self, IV_PW,   rb_str_new2(pw));

#ifdef NON_BLOCKING
	// Create a mutex if this build supports non-blocking queries.
	rb_iv_set(self, IV_MUTEX, rb_mutex_new());
#endif

	// Connect to the database.
	SCA c = { sedna_struct(self), host, db, user, pw };
	sedna_connect(self, &c);

	// Initialize @autocommit to true.
	rb_iv_set(self, IV_AUTOCOMMIT, Qtrue);

	return self;
}

Instance Attribute Details

#autocommitObject

When autocommit is set to true (default), database queries can be run without explicitly wrapping them in a transaction. Each query that is not part of a transaction is automatically committed to the database. Explicit transactions in auto-commit mode will still be committed atomically.

When autocommit is set to false, queries can only be run inside an explicit transaction. Queries run outside transactions will fail with a Sedna::Exception.



775
776
777
778
# File 'ext/sedna/sedna.c', line 775

static VALUE cSedna_autocommit_get(VALUE self)
{
	return rb_iv_get(self, IV_AUTOCOMMIT);
}

Class Method Details

.blocking?Boolean

Returns true if connecting with Sedna.connect or querying the database with Sedna#execute will block other threads. Returns false if multiple queries can be run or multiple connections can be made simultaneously in different threads. Sedna will not block other threads (this method returns false) when compiled against Ruby 1.9.1+.

Returns:

  • (Boolean)


589
590
591
592
# File 'ext/sedna/sedna.c', line 589

static VALUE cSedna_s_blocking(VALUE klass)
{
	return SEDNA_BLOCKING;
}

.connect(details) ⇒ Sedna instance .connect(details) {|sedna| ... } ⇒ nil

Establishes a new connection to a Sedna XML database. Accepts a hash that describes which database to connect to.

If a block is given, the block is executed if a connection was successfully established. The connection is closed at the end of the block or if the stack is unwinded (if an exception is raised, for example). If called without a block, a Sedna object that represents the connection is returned. The connection should be closed by calling Sedna#close.

If a connection cannot be initiated, a Sedna::ConnectionError is raised. If the authentication fails, a Sedna::AuthenticationError is raised.

This method does not block other threads in Ruby 1.9.1+ – connections that are initiated in different threads will be created concurrently. You can use Sedna.blocking? to verify if the extension supports non-blocking behaviour.

Valid connection details keys

  • :host - Host name or IP address to which to connect to (defaults to localhost).

  • :database - Name of the database to connect to (defaults to test).

  • :username - User name to authenticate with (defaults to SYSTEM).

  • :password - Password to authenticate with (defaults to MANAGER).

Examples

Call without a block and close the connection afterwards.

sedna = Sedna.connect :database => "my_db", :host => "my_host"
# Query the database and close afterwards.
sedna.close

Call with a block. The connection is closed automatically.

Sedna.connect :database => "my_db", :host => "my_host" do |sedna|
  # Query the database.
  # The connection is closed automatically.
end

Overloads:

  • .connect(details) ⇒ Sedna instance

    Returns:

  • .connect(details) {|sedna| ... } ⇒ nil

    Yields:

    • (sedna)

    Returns:

    • (nil)


543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
# File 'ext/sedna/sedna.c', line 543

static VALUE cSedna_s_connect(VALUE klass, VALUE options)
{
	int status;
	
	// Create a new instance.
	VALUE obj = rb_funcall(klass, rb_intern("new"), 1, options);

	if(rb_block_given_p()) {
		// If a block is given, yield the instance, and make sure we always return...
		rb_protect(rb_yield, obj, &status);
		
		// ...to ensure that the connection is closed afterwards.
		cSedna_close(obj);
		
		// Re-raise any exception.
		if(status != 0) rb_jump_tag(status);

		// Always return nil if successful.
		return Qnil;
	} else {
		// If no block is given, simply return the instance.
		return obj;
	}
}

.versionString

Returns the current version of the Sedna client protocol.

Returns:

  • (String)


574
575
576
577
# File 'ext/sedna/sedna.c', line 574

static VALUE cSedna_s_version(VALUE klass)
{
	return rb_str_new2(PROTOCOL_VERSION);
}

Instance Method Details

#closenil

Closes an open Sedna connection. If the connection is already closed when this method is called, nothing happens. A Sedna::ConnectionError is raised if the connection was open but could not be closed.

Returns:

  • (nil)


452
453
454
455
456
457
458
459
460
461
# File 'ext/sedna/sedna.c', line 452

static VALUE cSedna_close(VALUE self)
{
	SC *conn = sedna_struct(self);
	
	// Ensure the connection is closed.
	sedna_close(conn);
	
	// Always return nil if successful.
	return Qnil;
}

#commitnil

Commits a currently active transaction. Only use this method if you are specifying a transaction declaratively. Invoking Sedna#transaction with a block will automatically commit the transaction if the block finishes successfully.

This method will raise a Sedna::TransactionError if no transaction is in progress when it is called.

Returns:

  • (nil)


901
902
903
904
905
906
907
908
909
910
# File 'ext/sedna/sedna.c', line 901

static VALUE cSedna_commit(VALUE self)
{
	SC *conn = sedna_struct(self);

	// Attempt to commit.
	sedna_commit(conn, self);

	// Always return nil if successful.
	return Qnil;
}

#connected?Boolean

Returns true if the connection is connected and functioning properly. Returns false if the connection has been closed.

Returns:

  • (Boolean)


601
602
603
604
605
606
607
608
# File 'ext/sedna/sedna.c', line 601

static VALUE cSedna_connected(VALUE self)
{
	SC *conn = sedna_struct(self);
	
	// Return true if the connection status is OK. This only indicates that the
	// client still thinks it is connected.
	return (SEconnectionStatus(conn) == SEDNA_CONNECTION_OK) ? Qtrue : Qfalse;
}

#execute(query) ⇒ Array? #query(query) ⇒ Array?

Executes the given query against a Sedna database. Returns an array if the given query is a select query. The elements of the array are strings that correspond to each result in the result set. If the query is an update query or a (bulk) load query, nil is returned. When attempting to execute a query on a closed connection, a Sedna::ConnectionError will be raised. A Sedna::Exception is raised if the query fails or is invalid.

This method does not block other threads in Ruby 1.9.1+ – database queries that are run in different threads with different connections will run concurrently. You can use Sedna.blocking? to verify if the extension supports non-blocking behaviour. Database queries run from different threads, but on the same connection will still block and be executed serially.

Examples

Create a new document.

sedna.execute "create document 'mydoc'"
  #=> nil

Update the newly created document with a root node.

sedna.execute "update insert <message>Hello world!</message> into doc('mydoc')"
  #=> nil

Select a node in a document using XPath.

sedna.execute "doc('mydoc')/message/text()"
  #=> ["Hello world!"]

Further reading

For more information about Sedna’s database query syntax and support, see the Database language section of the official documentation of the Sedna project at modis.ispras.ru/sedna/progguide/ProgGuidese2.html

Overloads:

  • #execute(query) ⇒ Array?

    Returns:

    • (Array, nil)
  • #query(query) ⇒ Array?

    Returns:

    • (Array, nil)


651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
# File 'ext/sedna/sedna.c', line 651

static VALUE cSedna_execute(VALUE self, VALUE query)
{
	SC *conn = sedna_struct(self);

	// Prepare query arguments.
	SQ q = { conn, StringValuePtr(query) };

	// Verify that the connection is OK.
	if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");
	
	// Execute query.
	int res = SEDNA_EXECUTE(self, &q);

	switch(res) {
		case SEDNA_QUERY_SUCCEEDED:
			// Return the results if this was a query.
			return sedna_get_results(conn);
		case SEDNA_UPDATE_SUCCEEDED:
		case SEDNA_BULK_LOAD_SUCCEEDED:
			// Return nil if this was an update or bulk load.
			return Qnil;
		default:
			// Raise an exception if something else happened.
			sedna_err(conn, res);
			return Qnil;
	}
}

#load_document(document, doc_name, col_name = nil) ⇒ nil

Creates a new document named doc_name in collection col_name, or as a stand-alone document if col_name is nil. The string document is subsequently loaded into the newly created document. As an alternative, the argument document may be an IO object (or any descendant, such as a File object).

If the document was successfully loaded, this method returns nil. If an error occurs, a Sedna::Exception is raised.

Examples

Create a new document and retrieve its contents.

sedna.load_document "<my_document>Hello world!</my_document>", "my_doc"
  #=> nil
sedna.execute "doc('my_doc')"
  #=> ["<?xml version=\"1.0\" standalone=\"yes\"?><my_document>Hello world!</my_document>"]

Open a file and import its contents into a new document in an existing collection.

File.open "document.xml" do |file|
  sedna.load_document file, "my_doc", "my_col"
end

Returns:

  • (nil)


707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
# File 'ext/sedna/sedna.c', line 707

static VALUE cSedna_load_document(int argc, VALUE *argv, VALUE self)
{
	int res = 0;
	SC *conn = sedna_struct(self);
	VALUE document, doc_name, col_name, buf;
	char *doc_name_c, *col_name_c;

	// Verify that the connection is OK.
	if(SEconnectionStatus(conn) != SEDNA_CONNECTION_OK) rb_raise(cSednaConnError, "Connection is closed.");

	// 2 mandatory arguments, 1 optional.
	rb_scan_args(argc, argv, "21", &document, &doc_name, &col_name);
	doc_name_c = StringValuePtr(doc_name);
	col_name_c = NIL_P(col_name) ? NULL : StringValuePtr(col_name);

	if(TYPE(document) == T_FILE) {
		// If the document is an IO object...
		while(!NIL_P(buf = rb_funcall(document, rb_intern("read"), 1, INT2NUM(LOAD_BUF_LEN)))) {
			// ...read from it until we reach EOF and load the data.
			res = SEloadData(conn, StringValuePtr(buf), RSTRING_LEN(buf), doc_name_c, col_name_c);
			VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
		}

		// If there is no data, raise an exception.
		if(res == 0) rb_raise(cSednaException, "Document is empty.");
	} else {
		// If the document is not an IO object, verify it is a string instead.
		Check_Type(document, T_STRING);
		
		// If there is no data, raise an exception.
		if(RSTRING_LEN(document) == 0) rb_raise(cSednaException, "Document is empty.");

		// Load the data.
		res = SEloadData(conn, StringValuePtr(document), RSTRING_LEN(document), doc_name_c, col_name_c);
		VERIFY_RES(SEDNA_DATA_CHUNK_LOADED, res, conn);
	}

	// Signal that we're finished.
	res = SEendLoadData(conn);
	VERIFY_RES(SEDNA_BULK_LOAD_SUCCEEDED, res, conn);

	// Always return nil if successful.
	return Qnil;
}

#resetnil

Closes an open Sedna connection and reconnects. If the connection is already closed when this method is called, the connection is just reestablished. When reconnecting, the same connection details are used that were given when initially connecting with the connect method.

If the connection could not be closed or reopened, a Sedna::ConnectionError is raised. If the authentication fails when reconnecting, a Sedna::AuthenticationError is raised.

Returns:

  • (nil)


476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'ext/sedna/sedna.c', line 476

static VALUE cSedna_reset(VALUE self)
{
	VALUE host_v, db_v, user_v, pw_v;
	SC *conn = sedna_struct(self);
	
	// First ensure the current connection is closed.
	sedna_close(conn);

	// Retrieve stored connection details.
	host_v = rb_iv_get(self, IV_HOST);
	db_v   = rb_iv_get(self, IV_DB);
	user_v = rb_iv_get(self, IV_USER);
	pw_v   = rb_iv_get(self, IV_PW);

	SCA c = { conn, StringValuePtr(host_v), StringValuePtr(db_v), StringValuePtr(user_v), StringValuePtr(pw_v) };
	
	// Connect to the database.
	sedna_connect(self, &c);

	// Always return nil if successful.
	return Qnil;
}

#rollbacknil

Rolls back a currently active transaction. Only use this method if you are specifying a transaction declaratively. Invoking Sedna#transaction with a block will automatically roll back the transaction if an exception is raised or if the stack is unwinded for whatever reason.

This method will do nothing if no transaction is in progress when it is called.

Returns:

  • (nil)


924
925
926
927
928
929
930
931
932
933
# File 'ext/sedna/sedna.c', line 924

static VALUE cSedna_rollback(VALUE self)
{
	SC *conn = sedna_struct(self);

	// Attempt to roll back.
	sedna_rollback(conn, self);

	// Always return nil if successful.
	return Qnil;
}

#transaction { ... } ⇒ nil #transactionnil

Wraps the given block in a transaction. If the block runs completely, the transaction is committed. If the stack is unwinded prematurely, the transaction is rolled back. This typically happens when an exception is raised by calling raise or a Symbol is thrown by invoking throw. Note that exceptions will not be rescued – they will be re-raised after rolling back the transaction.

This method returns nil if the transaction is successfully committed to the database. If the given block completes successfully, but the transaction fails to be committed, a Sedna::TransactionError will be raised.

Transactions cannot be nested or executed simultaneously with the same connection. Calling this method inside a block that is passed to another transaction, or with the same connection in two concurrent threads will raise a Sedna::TransactionError on the second invocation.

If no block is given, this method only signals the beginning of a new transaction. A subsequent call to Sedna#commit or Sedna#rollback is required to end the transaction. Note that invoking this method with a block is the preferred way of executing transactions, because any exceptions that may be raised will automatically trigger a proper transaction rollback. Only call commit and rollback directly if you cannot use a block to wrap your transaction in.

Examples

Transactions are committed after the given block ends.

sedna.transaction do
  amount = 100
  sedna.execute "update replace $balance in doc('my_account')/balance
                 with <balance>{$balance - #{amount}}</balance>"
  sedna.execute "update replace $balance in doc('your_account')/balance
                 with <balance>{$balance + #{amount}}</balance>"
  # ...
end
# Transaction is committed.

Transactions are rolled back if something is thrown from inside the block.

sedna.transaction do
  articles = sedna.execute "for $a in collection('articles')
                            where $a/article/author = 'me' return $a"
  throw :no_articles if articles.empty?
  # ... never get here
end
# Transaction is rolled back.

Transactions are also rolled back if an exception is raised inside the block.

sedna.transaction do
  amount = 100
  sedna.execute "update replace $balance in doc('my_account')/balance
                 with <balance>{$balance - #{amount}}</balance>"
  new_balance = sedna.execute "doc('my_account')/balance"
  raise "Insufficient funds" if new_balance.to_i < 0
  # ... never get here
end
# Transaction is rolled back.

If you really have to, you can also use transactions declaratively. Make sure to roll back the transaction if something goes wrong!

sedna.transaction
begin
  amount = 100
  sedna.execute "update replace $balance in doc('my_account')/balance
                 with <balance>{$balance - #{amount}}</balance>"
  sedna.execute "update replace $balance in doc('your_account')/balance
                 with <balance>{$balance + #{amount}}</balance>"
rescue Exception
  sedna.rollback
else
  sedna.commit
end

Overloads:

  • #transaction { ... } ⇒ nil

    Yields:

    Returns:

    • (nil)
  • #transactionnil

    Returns:

    • (nil)


861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
# File 'ext/sedna/sedna.c', line 861

static VALUE cSedna_transaction(VALUE self)
{
	int status;
	SC *conn = sedna_struct(self);

	// Begin the transaction.
	sedna_begin(conn);

	if(rb_block_given_p()) {
		// Yield to the given block and protect it so we can always commit or rollback.
		rb_protect(rb_yield, Qnil, &status);

		if(status == 0) {
			// Attempt to commit if block completed successfully.
			sedna_commit(conn, self);
		} else {
			// Stack has unwinded, attempt to roll back!
			sedna_rollback(conn, self);

			// Re-raise any exception or re-throw whatever was thrown.
			rb_jump_tag(status);
		}
	}

	// Always return nil if successful.
	return Qnil;
}