NAME
    DBIx::Sequence - A simple SQL92 ID generator

SYNOPSIS
      use DBIx::Sequence;

      my $sequence = new DBIx::Sequence({ dbh => $dbh });
      my $next_id = $sequence->Next('dataset');

DESCRIPTION
    This module is intended to give easier portability to Perl database
    application by providing a database independant unique ID generator.
    This way, an application developer is not bound to use his database's
    SEQUENCE or auto_increment thus making his application portable on
    multiple database environnements.

    This module implements a simple Spin Locker mechanism and is garanteed
    to return a unique value every time it is called, even with concurrent
    processes. It uses your database for its state storage with ANSI SQL92
    compliant SQL. All SQL queries inside DBIx::Sequence are pre cached and
    very efficient especially under mod_perl.

INSTALLATION
            perl Makefile.PL
            make
            make test
            make install

    Note:

    If you decide to run extended tests for the module, you will have to
    provide the make test with a DSN (connect string) to your database
    (dbi:Driver:db;host=hostname) and a valid username/password combination
    for a privileged user.

    DBIx::Sequence uses 2 tables for its operation, namely the
    dbix_sequence_state and the dbix_sequence_release tables. Those tables
    will be created if you run extended tests, if not you will need to
    create them yourself.

            dbix_sequence_state:
            | dataset  | varchar(50) |      
            | state_id | int(11)     |    

            dbix_sequence_release:
            | dataset     | varchar(50) |     
            | released_id | int(11)     |     

    Those table names are overloadable at your convenience, see the
    OVERLOADING section for details.

BASIC USAGE
    The basic usage of this module is to generate a unique ID to replace the
    use of your database's SEQUENCE of auto_increment field.

  INIT

    First, you need to create the sequence object:

            use DBIx::Sequence;
            my $sequence = new DBIx::Sequence({
                                                    db_user => 'scott',
                                                    db_pw => 'tiger',
                                                    db_dsn => 'dbi:mysql:scottdb',
                                                    });

    DBIx::Sequence can be used to manage multiple sets of ID's (perhaps you
    could have one dataset per table, or one and only one dataset). This
    permits you to handle multiple applications with the same sequence
    class. The dataset is normally simply a token string that represents
    your ID set. If the dataset does not exists, DBIx::Sequence will create
    automagically for you. No special steps are involved in the creation of
    a dataset.

    The arguments contains the database informations, db_user, db_pw and
    db_dsn and are stored in a hash reference.

    At this point, the object has pre cached all of the SQL that will be
    used to generate the spin locker race. It is normally a good idea to
    have a shared sequence object (especially) under mod_perl to save the
    prepare overhead.

  GETTING THE NEXT ID

    To get the next id, you simpy have to use the Next() method of your
    sequence while specifying the dataset you are getting the next id for.

            my $next_id = $sequence->Next($dataset);

  RELEASING ID'S.

    Generated ID's can be _explicitly_ released in your application. When an
    ID is released, the sequence will be able to give this id back to you
    throught the Next() method.

    This is how it is done:

            $sequence->Release($dataset, $id);

    Note:

    You must use release only when you are _CERTAIN_ that your ID is not
    used anymore and that you want it to be recycled. The Spin Locking
    mechanism will also take place on released id's to ensure that no two
    processes can get the same ID.

  PERMANENTLY REMOVING A DATASET

    To make DBIx::Sequence forget about an existing dataset, you need to use
    the Delete_Dataset() method.

            $sequence->Delete_Dataset($dataset);

    This will clear all state and existence for this dataset and will also
    clear it's released id's. Note that if your application still uses this
    dataset, it will be automatically recreated blank.

  BOOTSTRAPPING A DATASET FROM EXISTING DATA

    It is possible to sync the state of a DBIx::Sequence dataset by using
    the Bootstrap() method.

            $sequence->Bootstrap('my_dataset','my_bootstrap_table','my_primary_field');

    Bootstrap() takes 3 arguments.

    * The dataset to bootstrap
    * The table from wich you will bootstrap
    * The field in the bootstrap table that will be used to bootstrap the
    dataset.
    Bootstrap will then sync up the DBIx::Sequence's state with the maximum
    id of the 'my_primary_field' in 'my_bootstrap_table'. The bootstrap
    field must be a numeric field as you can suspect. The SQL function MAX()
    will be called on it during the bootstrap process.

    Note: The bootstrap method _can_ be used at runtime since it will
    initiate a race for updating the value thus following the same
    algorithm. It is recommended though that you use Bootstrap() when no
    other concurrent processes are requesting id's.

  OVERLOADING

    It is possible to create an overloaded class of DBIx::Sequence. This
    permits you to create a DBIx::Sequence that has different properties
    than the orignal one. The only thing you really have to overload to
    modify the behaviour of DBIx::Sequence are some constants:

    * STATE_TABLE : Defines the table used by DBIx::Sequence to store
    dataset's states.
    * RELEASE_TABLE : Defines the table used by DBIx::Sequence to store
    released id's.
    * COLUMN_PREFIX : A string to be prepended to every column in the
    internal SQL statements.
    * DEFAULT_INIT_VALUE : Value used to initialize a dataset when it is
    first created.
    * ALLOW_ID_REUSE : When set to true, will allow the use of Release().
    * DEBUG_LEVEL : When set to true, will enable debugging to STDERR.
    So it is very easy to specify the behaviour of DBIx::Sequence that you
    wish to use by creating an overloaded class.

    Also, a very important method to overload is the getDbh() method. This
    is the function that returns the database handle to the DBIx::Sequence.
    Your overloaded class should redefine the getDbh method.

    Overloading getDbh will make your sequence class integrate more cleanly
    with your application.

    i.e.

            package MySequence;

            use DBI;
            use DBIx::Sequence;

            use vars qw(@ISA);
            @ISA = qw(DBIx::Sequence);

            use constant STATE_TABLE => 'my_state_table';
            use constant RELEASE_TABLE => 'my_release_table';
            use constant COLUMN_PREFIX => '';
            use constant DEFAULT_INIT_VALUE => '100';
            use constant ALLOW_ID_REUSE => 1;
            use constant DEBUG_LEVEL => 0;

            sub getDbh
            {
                    my $self = shift;

                    return MyApplication::MyDBModule::getDbh();
            }

            1;

    Then, your code can use this class for its sequencing. Notice that since
    we overloaded getDbh(), we don't need to pass a second parameter to
    new().

            use MySequence;

            my $sequence = new MySequence();
            my $next_id = $sequence->Next($dataset);

SPECIAL NOTE ON DATABASE HANDLE OPTIONS
    DBIx::Sequence requires that the dbh object you passe to it has the
    AutoCommit flag set to 1. The main reason for this is that if AutoCommit
    is off, DBIx::Sequence will have to do an implicit commit() call, wich
    in most cases is a bad idea, especially when the dbh passed to the
    sequence object already has transactions prelogged in it.

CVS AND BLEEDING VERSIONS
    For the latest development information, CVS access and Changelog, please
    visit:

    http://labs.turbulent.ca

    If you use this module in a project, please let me know!

    Your comments and rants are more than welcomed!

    Commercial support for this module is available, please contact me for
    info!

TODO
    * Implement multiple locking mechanism (semaphore, spin, db locker)
    * Implement pluggable locking module support
AUTHOR
    Benoit Beausejour, <bbeausej@pobox.com>

NOTES
    This code was made possible by the help of individuals:

    Philippe "Gozer" M. Chiasson <gozer@cpan.org>

    Thanks to Uri Guttman for documentation checks ;)

SEE ALSO
    perl(1).

COPYRIGHT
    Copyright (c) 2000 Benoit Beausejour <bbeausej@pobox.com> All rights
    reserved. This program is free software, you can redistribute it and/or
    modify it under the same terms as Perl itself.

