NAME

TBI - abstract table interface


SYNOPSIS

   use TBI;
   $t = TBI->open($name);
   @tables = TBI->tables();
   $rc = TBI->exists($name);
   TBI->register($name, $package, \%params);
   $name = $t->name;
   @names = $t->fields;
   @names = $t->keyfields;
   $rc = $t->exists($key);
   %fields = $t->fetch($key);
   @fields = $t->get($key, @fieldnames);
   $field = $t->getfield($key, $fieldname);
   @keys = $t->keys;
   @keys = $t->select(%equations);
   $t->add($key, %entry);
   $t->delete($key);
   $t->modify($key, %changes);
   $t->close;


DESCRIPTION

TBI provides a convenient abstract interface for various concrete table implementations including all DBI drivers. Each table is defined by an identifying name, a set of named fields, and a subset thereof that constitutes the primary key. Field values are scalar string values.

Class Methods

TBI maintains a global name space for tables. Any implementation or initialization procedure is free to register more tables but it should be taken care of possible name clashes -- usually the first entry should remain undisturbed.

open

if the given table name has been successfully opened before, its pointer is returned. Otherwise, the table is newly opened. Upon success, a valid pointer is returned, otherwise undef.

tables

returns all table names known so far.

exists

checks whether a table with the given name is present.

register

registers a table for the given name. Multiple registrations for the same name are permitted, causing the older registration to be lost. Usually this method is called by implementors or initialization procedures that set up your data dictionary (see below).

Methods of Table Objects

Following operations are supported:

name

return the identifying name of the table as string.

fields

return the names of all fields as a list of strings.

keyfields

return the names of all primary key fields as a list of strings.

fetch($key)

return the referenced table entry as hash table where the field names serve as keys. For example,

   %fields = $members->fetch("borchert");

is expected to return all fields of the selected table entry. In case of multiple key fields, a pointer to an associative array has to be given:

   %fields = $documents->fetch({group => '1', shortname => '13'});
exists($key)

return true if an entry with the given key exists in the table.

get($key, @fieldnames)

return the specified fields of the selected table entry as list of scalars.

getfield($key, $fieldname)

return the specified field of the selected table entry.

keys

return all keys of the table as list. This allows to iterate through all table entries:

   foreach $key ($members->keys) {
      # operate on $members->fetch($key)
   }
select(%equations)

return a list of keys of table entries for which the given equations hold. Following example retrieves all documents of an author who belongs to a given group:

   @keys = $authors->select(login => $login, groupname => $groupname);
add($key, %entry)

add an entry to the table with the given key. Note that the key must not have been used previously.

delete($key)

delete the entry with the given key.

modify($key, %entry)

modify the given components of the already existing key. Note that %entry does not need to give all component-value pairs. For example,

   $accounts->modify("borchert", name => 'Andreas Franz Borchert');

modifies just the name of borchert but not the other components.

close

closes the given table. Note that this method is seldomly called directly because TBI closes tables implicitly if they are subject to the garbage collector. Special care has to be taken in case of multiple openings of the same table name because they all share the same table object. The first close would make the table inaccessible by the other parties using that table object.

Guide for implementors

Implementations of the TBI abstraction (sometimes also called drivers) are conventionally put under the TBD hierarchy. This is similar to the structure of the DBI interface where drivers are put under the DBD hierarchy.

TBI provides the constructor new that is called by the open method with the parameters supplied previously by register. new starts a two-phase initialization procedure using the methods initialize1 and initialize2. Together with close, all these methods should be executed through-out the whole @ISA-chain using following patterns:

   sub initialize1 {
      my ($self, %attributes) = @_;
      $self->SUPER::initialize1(%attributes);
      # early initialization code
   }
   sub initialize2 {
      my ($self) = @_;
      # late initialization code
      $self->SUPER::initialize2();
   }
   sub close {
      my ($self) = @_;
      # clean up code
      $self->SUPER::close();
   }

Due to the two-phase initialization it is possible to embed the initialization code of the more derived classes into the initialization of the more general packages. Note that close may be invoked explicitly or implicitly by the DESTROY method of TBI.

Some of the methods have default implementations in TBI: name, fields, keyfields, and get.

Table objects are represented by hashes whose keys are used like field names. Following field names of table objects are predefined or used by TBI:

closed

is set to 1 by the close method of TBI to avoid repeated executions of cleanup code. Otherwise cleanup might occur twice, i.e. once on an explicit close and once due to DESTROY.

tablename

is expected be set by the implementation to hold the name of the table.

fields

must be set by the implementation as a pointer to an ordered list of field names.

keyfields

is expected to be a reference to an ordered list of field names belonging to the primary key.

Registration

During its initialization, TBI tries to load a module named TBI_Scanner and invokes the class method scan of it. This hook allows to register a base set of tables of your application. If TBI_Scanner is not found, TBI starts with an empty table set.

Implementations are free to add more tables (e.g. all other tables of a database if the first one gets opened) if and only if the global variable $TBI::AUTO_REG is true. Applications should set this variable to 0 if they want to keep full control over the name space of TBI.


DIAGNOSTICS

Some of the operations may croak in case of fatal errors, for example on invalid field names, or on trying to add tuples with an already existing primary key.


AUTHOR

Andreas Borchert