An Overview of the rhdb-admin backend abstraction layer
=======================================================

Neil Padgett <npadgett@redhat.com>
Original Version: March 20, 2002
Rev. 1 (Last updated March 20, 2002)


About Handles
=============

The backend abstraction layer is based on the notion of handles. The
abstraction layer provides the programmer with handles with which to
manipulate database objects. Handles have the following properties:

1) They manipulate objects in the database by name
   -> implication: If a database object is DROPed and the another is
      CREATEd with the same name underneath you, the handle will

2) There is no guarantee that an object is attached to the handle. That is,
   you can have a handle to which no object corresponds. This can be useful
   if you are creating an object. You can check if, at a given instant
   there is a database object behind a handle, using the validate method of 
   a handle.
   -> implication: An operation on a handle can fail because the object 
      associated with the handle doesn't exist.

If one thinks about it, it is soon apparent this is as good of a
guarantee as any command line tool, which allows you to manipulate the
database using SQL, can provide. 

 
Acquiring Handles
=================

In order to acquire a database handle you must use the appropriate
object factory. There is only one factory available from which to
acquire database handles at program start -- an instance of
ClusterCollection. It is created at program start-up and has the
user's saved cluster settings data streamed in from disk.

From a ClusterCollection (from here on in a <class name> means an
instance of that class) a Cluster (a cluster handle) can be
obtained. (This isn't called a ClusterHandle as, unlike all other
handles, Cluster doesn't inherit from class Handle.) From a
ClusterHandle,  a DatabaseHandle can be obtained, and so on and so
forth. In other words, objects in the database act as factories for
handles that can be used to manipulate their children.

Here is the hierarchy for Handles:

                           ClusterCollection
                                  |
                                Cluster
                                  |
                        +---------+---------+
                        |         |         |
                  UserHandle GroupHandle DatabaseHandle
                                            |    
                                            |
               +------------+---------------+-----------+------------+
               |            |               |           |            |
            TableHandle DataTypeHandle FunctionHandle ViewHandle SequenceHandle
               |
               +
    +----------+-----------+-------------+
    |          |           |             |
IndexHandle RuleHandle ColumnHandle TriggerHandle

In order to facilitate handle acquisition, parents can also provide a
list of child objects for which backend instances currently exist. For
example, a list of tables can be obtained from a DatabaseHandle via
the getTableList and getSystemTableList methods. These
get<ChildType>List methods are standard.


Behind the Scenes: SQLInteractor, SQLResult, and the ConnectionPool
===================================================================

Behind the scenes, SQL code is, of course, sent by the backend. This
is done inside of the handle -- inside of the handle's methods
appropriate queries are constructed and sent to the backend. (Almost
every method on a handle causes a query to be executed in the backend,
as the handles maintain no state information, aside from information
about the name of their target object.)

Inside the abstraction later, queries are executed as follows:

An SQLInteractor object is obtained -- SQLInteractors can be obtained
from DatabaseHandles via the getSQLInteractor method. There is also
a call available from any DatabaseHandle or Cluster,
getSQLInteractorSystemDB, which gives an SQLInteractor for connecting
to template1. This should be used for any system table
queries. (However all of these should be abstracted by the handles, so
you shouldn't have to use getSQLInteractorSystemDB -- it is mainly
there for use by the handles.)

Connections do not need to be managed explicitly when using an
SQLInteractor. Instead, when using an SQLInteractor, one simply
passes in a query string. Then, and SQLResult object is returned. (In
fact a name of an SQLResult object is returned -- this name is valid
in the method caller's scope.) This object can be used for working
with the result set. (And in fact wraps the result "object" of the pgtcl
library.) 

Internally, connections are made by the SQLInteractor as
needed. Connections are made by requesting a connection from the
DatabaseConnectionPool, which maintains a pool of database
connections. The DatabaseConnectionPool then works with the underlying
pgtcl library to open connections. (Once opened, connections are not
released until program termination, unless explicitly released to
permit a DROP or someother contentious operation.)

Note, that any SQLInteractor / SQLResult objects obtained should be
explicitly deleted when you are done with them. Otherwise, resources
from the pgtcl library (connections, results) will be leaked. This is
especially a problem with results, as they are statically allocated. If
sufficient results are not available when making a query, the query
will fail violently. Also, if connections are leaked, huge numbers of
backends could be consumed on the server.






