Provided by: libalzabo-perl_0.92-3_all bug

NAME

       Alzabo::Design - Documentation on Alzabo's design

DESCRIPTION

       This document describes some of the Alzabo's design.

ARCHITECTURE

       There are objects representing the schema, which contains table objects.  Table objects
       contain column, foreign key, and index objects.  Column objects contain column definition
       objects.  A single column definition may be shared by multiple columns, but has only one
       owner.

       This is a diagram of these inheritance relationships:

         Alzabo::* (::Schema, ::Table, ::Column, ::ColumnDefinition, ::ForeignKey, ::Index)
                          /   \
                       is parent to
                        /       \
        Alzabo::Create::*   Alzabo::Runtime::*

       This a diagram of how objects contain other objects:

                             Schema - makes--Alzabo::SQLMaker subclass object (many)
                            /      \
                     contains       contains--Alzabo::Driver subclass object (1)
                         |                 \
                      Table (0 or more)     Alzabo::RDBMSRules subclass object (1)
                       /  \                  (* Alzabo::Create::Schema only)
                      /    \
                     contains--------------------
                    /        \                   \
                   /          \                   \
            ForeignKey      Column (0 or more)    Index (0 or more)
            (0 or more)       |
                           contains
                              |
                         ColumnDefinition (1)

       Note that more than one column may share a single definition object (this is explained in
       the "Alzabo::Create::ColumnDefinition" documentation).  This is only relevant if you are
       writing a schema creation interface.

   Other classes
       •   "Alzabo::Driver"

           These objects handle all the actual communication with the database, using a thin
           wrapper over DBI.  The subclasses are used to implement functionality that must be
           handled uniquely for a given RDBMS, such as creating new values for sequenced columns.

       •   "Alzabo::SQLMaker"

           These objects handle the generation of all SQL for runtime operations.  The subclasses
           are used to implement functionality that varies between RDBMS's, such as outer joins.

       •   "Alzabo::RDBMSRules"

           These objects perform several funtions.  First, they validate things such as schema or
           table names, column type and length, etc.

           Second they are used to generate SQL for creating and updating the database and its
           tables.

           And finally, they also handle the reverse engineering of an existing database.

       •   "Alzabo::Runtime::Row" and "Alzabo::Runtime::RowState::*"

           The "Alzabo::Runtime::Row" class represents a single row.  These objects are created
           by "Alzabo::Runtime::Table", "Alzabo::Runtime::RowCursor", and
           "Alzabo::Runtime::JoinCursor" objects.  It is the sole interface by which actual data
           is retrieved, updated, or deleted in a table.

           The various "RowState" classes are used in order to change a row's behavior depending
           on whether it is live, live and cached, potential, or deleted.

       •   "Alzabo::Runtime::JoinCursor" and "Alzabo::Runtime::RowCursor"

           These objects are cursor that returns row objects.  Using a cursor saves a lot of
           memory for big selects.

       •   "Alzabo::Runtime::UniqueRowCache"

           Loading this class turns on Alzabo's simple row caching mechanism.

       •   "Alzabo::Config"

           This class is generated by Makefile.PL during installation and contains information
           such as what directory contains saved schemas and other configuration information.

       •   "Alzabo::ChangeTracker"

           This object provides a method for an object to register a series to backout from
           multiple changes.  This is done by providing the ChangeTracker object with a callback
           after a change is succesfully made to an object or objects.  If a future change in a
           set of operations fail, the tracker can be told to back the changes out. This is used
           primarily in "Alzabo::Create::Schema".

       •   "Alzabo::MethodMaker"

           This module can auto-generate useful methods for you schema, table, and row objects
           based on the structure of your schema.

       •   "Alzabo::Exceptions"

           This object creates the exception subclasses used by Alzabo.

WHY THE SUBDIVISION BETWEEN Alzabo::*, Alzabo::Create::*, and Alzabo::Runtime::*?

       There are several reasons for doing this:

       •   In some environments (mod_perl) we would like to optimize for memory.  For an
           application that uses an existing schema, all we need is to be able read object
           information, rather than needing to change the schema's definition.  This means there
           is no reason to have the overhead of compiling all the methods used when creating and
           modifying objects.

       •   In other environments (for example, when running as a separately spawned CGI process)
           compile time is important.

       •   Many people using Alzabo will use the schema creation GUI and then write an
           application using that schema.  At the simplest level, they would only need to learn
           how to instantiate "Alzabo::Runtime::Row" objects and how that class's methods work.
           For more sophisticated users, they can still avoid having to ever look at
           documentation on methods that alter the schema and its contained objects.

RATIONALE FOR CURSORS

       Using cursors is definitely more complicated.  However, there are two excellent reasons
       for using them: speed and memory savings.  As an example, I did a test with the old code
       (which returned all its objects at once) against a table with about 8,000 rows using the
       "Alzabo::Runtime::Table->all_rows" method.  Under the old implementation, it took
       significantly longer to return the first row.  Even more importantly than that, the old
       implementation used up about 10MB of memory versus about 4MB!  Now imagine that with a
       1,000,000 row table.

       Thus Alzabo uses cursors so it can scale better.  This is a particularly big win in the
       case where you are working through a long list of rows and may stop before the end is
       reached.  With cursors, Alzabo creates only as many rows as you need.  Plus the start up
       time on your loop is much, much quicker.  In the end, your program is quicker and less of
       a memory hog.  This is good.

AUTHOR

       Dave Rolsky, <autarch@urth.org>