Class MUES::ObjectStore::DBIBackend
In: lib/mues/os-extensions/dbibackend.rb  (CVS)
Parent: Backend

RDBMS ObjectStore backend via DBI.

Methods

[]   addIndexes   clear   close   drop   exists?   hasIndex?   indexKeys   lookup   new   nitems   open?   retrieve   retrieve_all   retrieve_by_index   store  

Included Modules

MUES::TypeCheckFunctions

Classes and Modules

Class MUES::ObjectStore::DBIBackend::Adapter

Constants

SVNRev = %q$Rev: 1206 $   SVN Revision
SVNId = %q$Id: dbibackend.rb 1206 2004-05-09 21:25:11Z deveiant $   SVN Id
SVNURL = %q$URL: svn+ssh://deveiate.org/usr/local/svn/MUES/trunk/lib/mues/os-extensions/dbibackend.rb $   SVN URL
DefaultConfig = { :username => 'mues', :password => 'mues', :preconnect => true, }   The default settings to use when connecting.

Public Class methods

Create a new DBIBackend object, where the specified name is the DSN of the database to use. If the database does not already exist, it will be created. The config should contain the username and the password to use when connecting (:username and :password keys) and any other settings used by the adapter for the DBD specified by the DSN.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 91
        def initialize( name, indexes=[], config=DefaultConfig )
            checkType( indexes, ::Array )
            checkType( config, ::Hash )

            @dsn = name.to_s
            @indexes = indexes
            @config = config

            @username = config[:username] || DefaultConfig[:username]
            @password = config[:password] || DefaultConfig[:password]

            @adapter = nil
            @tablesUpToDate = false
            @indexesUpToDate = false

            self.log.debug "Connecting to '%s' as '%s'..." %
                [ @dsn, @username ]
            @adapter = self.getAdapter( @dsn, @username, @password )

            return true
        end

Public Instance methods

[]( *ids )

Alias for retrieve

Add the specified indexes, which are Strings or Symbols that represent methods to call on stored objects.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 317
        def addIndexes( *indexes )
            checkOpened()

            indexes.each {|idx|
                indexName = idx.to_s
                idx = indexName.intern

                self.log.debug {"Adding index '#{indexName}'"}

                # Open a secondary handle and associate it with the first,
                # along with a proc for making the index value from a key =>
                # value pair.
                @indexes[idx] =
                    @env.open_db( BDB::Hash, indexName + "_i", nil, BDB::CREATE,
                                  :set_flags => BDB::DUP|BDB::DUPSORT, :marshal => ::Marshal )

                @db.associate( @indexes[idx], BDB::CREATE ) {|db,key,value|
                    # :TODO: This should be taken out of production code
                    unless value.is_a? MUES::StorableObject
                        $stderr.puts "Ack! Value in indexer proc is not a StorableObject," +
                            "but a #{value.class.name}!"
                    end

                    if value.respond_to?( idx )
                        value.send( idx )
                    else
                        false
                    end
                }
            }
        end

Clear

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 309
        def clear
            checkOpened()
            @db.clear
        end

Close the backend

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 278
        def close
            checkOpened()
            @open = false
            @indexes.each_value {|idx| idx.close}
            @db.close
            @env.close
        end

Drop the backing store for this backend

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 125
        def drop
            self.close if self.open?
            self.log.notice( "Dropping backing store for '#@name'" )
            dropAllTables()
        end

Returns true if an object with the specified id exists in the store.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 289
        def exists?( id )
            checkOpened()
            @db.include?( id )
        end

Returns true if the backing store has the specified index, which can be either a String or a Symbol.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 359
        def hasIndex?( index )
            @indexes.key?( index.to_s.intern )
        end

Return an Array of keys for the specified index.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 351
        def indexKeys( index )
            raise MUES::IndexError, "No such index #{index}" unless self.hasIndex?( index )
            @indexes[index.to_s.intern].keys
        end

Given the indexValuePairs Hash, which contains index => lookup values pairs, return objects which match the equality natural join of the pairs.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 221
        def lookup( indexValuePairs )
            checkOpened()
            objs = []

            indexValuePairs.keys.each {|idx|
                raise MUES::ObjectStoreError, "No such index #{idx.inspect}" unless
                    @indexes.has_key? idx
            }

            begin
                cursors = []
                # @env.begin( 0, @db ) {|txn, db|
                    indexValuePairs.each {|idx,vals|
                        self.log.debug { "Looking up values '#{vals.inspect}' for idx '#{idx.inspect}'" }
                        vals.to_a.each {|val|
                            self.log.debug { "Fetching cursor for '#{val}'" }
                            #cursor = txn.associate( @indexes[idx] ).cursor
                            cursor = @indexes[idx].cursor
                            rval = cursor.set( val )
                            self.log.debug { "Got a cursor with #{cursor.count} values." }

                            cursors << cursor
                        }
                    }

                    self.log.debug {"Preparing to do a join with #{cursors.length} cursors."}

                    @db.join(cursors) {|key,val|
                        self.log.debug {"Adding a '%s' object (%s) for join." % [val.class.name, val.muesid]}

                        # Have to do this despite the source saying not to
                        # use this method because the delegator it returns
                        # doesn't delegate inherited methods...
                        objs << val.to_orig
                        val = nil
                    }

                    #self.log.debug "Closing join transaction..."
                    #txn.close
                #}
            rescue => err
                self.log.error "Transaction failed while fetching values: %s: %s" % [
                    err.message, err.backtrace.join("\n\t") ]
                self.log.notice "Attempting recovery"
                @env.recover {|txn, id|
                    self.log.error "Discarding txn #{id}"
                    txn.discard
                }

            end

            self.log.debug {"Join returned '%d' ids" % objs.length}
            return objs
        end

Returns the number of objects stored in the database/

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 302
        def nitems
            checkOpened()
            @db.length
        end

Returns true if the backend‘s datastore is open.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 296
        def open?
            @open
        end

Retrieve the objects that have the specified ids from the database and return them.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 163
        def retrieve( *ids )
            checkOpened()
            objs = []

            begin
                objs.replace ids.collect {|id| @db[ id ].to_orig}
            rescue => err
                raise MUES::ObjectStoreError,
                    "Transaction failed while fetching: #{err.message}",
                    err.backtrace
            end

            return objs
        end

Fetch and return every object stored in the ObjectStore.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 202
        def retrieve_all
            checkOpened()
            objs = nil

            begin
                objs.replace( @db.values.collect {|o| o.to_orig} )
            rescue => err
                raise MUES::ObjectStoreError,
                    "Transaction failed while fetching values: #{err.message}",
                    err.backtrace
            end

            return objs
        end

Retrieve the object/s specified by the given key and value. The specified key may be a Symbol or a String, and must be a valid index.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 183
        def retrieve_by_index( key, val )
            checkOpened()
            raise ArgumentError, "Invalid index #{key.inspect}" unless
                @indexes.has_key?[ key.to_s ]

            objs = nil
            begin
                objs.replace( @indexes[key.to_s.intern].duplicates(val, false) )
            rescue => err
                raise MUES::ObjectStoreError,
                    "Transaction failed while fetching: #{err.message}",
                    err.backtrace
            end

            return objs
        end

Store the specified objects in the database.

[Source]

# File lib/mues/os-extensions/dbibackend.rb, line 133
        def store( *objects )
            objects.flatten!
            checkOpened()
            checkEachType( objects, MUES::StorableObject )

            self.log.debug { "Storing %d objects" % objects.length }

            begin
                # Start a transaction and store each object. Txn
                # auto-commits at the end of the block.
                self.log.debug { "   Beginning transaction..." }
                # @env.begin( BDB::TXN_COMMIT, @db ) do |txn, db|
                    objects.each {|obj|
                        id = obj.objectStoreId

                        self.log.debug { "      Storing object '#{obj.objectStoreId}'..." }
                        @db[ id ] = obj
                    }
                #end
                self.log.debug { "   Done with transaction." }
            rescue => err
                raise MUES::ObjectStoreError,
                    "Transaction failed while storing: #{err.message}",
                    err.backtrace
            end
        end

[Validate]