The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

BingoX::Carbon - An object oriented database abstraction superclass

SYNOPSIS

use BingoX::Carbon ( [ ':cache_all', ] [ ':no_dynmeth' ] );

  # $BR - Blessed Reference
  # $SV - Scalar Value
  # @AV - Array Value
  # $HR - Hash Ref
  # $AR - Array Ref
  # $SR - Stream Ref

  # $proto - BingoX::Carbon object OR sub-class
  # $object - BingoX::Carbon object

CONSTRUCTORS

  $BR = $proto->new( $dbh, \%data );
  $BR = $proto->get( $dbh, $ID );
  $BR = $object->duplicate;

STREAM CONSTRUCTOR METHODS

  $SR = $proto->stream_obj( $dbh, \%params, [\@fields,] [\@sort] );
  $SR = $proto->stream_hash( $dbh, \%params, [\@fields,] [\@sort] );
  $SR = $proto->stream_array( $dbh, \%params, [\@fields,] [\@sort] );

LIST CONSTRUCTOR METHODS

  $AR = $proto->list_obj( $dbh, \%params, [\@fields,] [\@sort] );
  $AR = $proto->list_hash( $dbh, \%params, [\@fields,] [\@sort] );
  $AR = $proto->list_array( $dbh, \%params, [\@fields,] [\@sort] );

RELATION METHODS

  $SV = $object->relate( $fobject | \@fobjects | (\@fids, $fclass, $fcolumn) );
  $SV = $object->unrelate( $fobject | \@fobjects | (\@fids, $fclass, $fcolumn) );
  $SV = $object->isrelated( $fobject );
  $AR = $object->list_related( $fclass [, \@fields] [, \@sort] );
  $SR = $object->stream_related( $fclass [, \@fields] [, \@sort] );
  $SV = $proto->unrelate_all( $fclass )

DATABASE MANIUPLATION METHODS

  $BR = $object->modify( \%new_data );
  $SV = $proto->modify( $dbh, \%params, \%new_data );
  $SV = $object->rm;
  $SV = $proto->rm( $dbh, \%params );

CLASS DATA ACCESSOR METHODS

  $SV = $proto->table( $dbh );
  $SV = $proto->identity( $dbh );
  $AR = $proto->primary_keys( $dbh );
  $HR = $proto->def_fields( $dbh );
  $AR = $proto->field_order( $dbh );
  $SV = $proto->identity( $dbh );
  $SV = $proto->sequence( $dbh );
  $SV = $proto->seqcol( $dbh );

DATABASE/SQL METHODS

  $SR = $proto->sql_select( $dbh );
  @AV = $proto->format_select( $dbh, \%params, \@fields, \@sort [,$alias] );
  @AV = $proto->format_conditions( $dbh, \%params [,$alias] );

REQUIRES

DBI, Carp, strict, Date::Parse, BingoX::Time

EXPORTS

Nothing

DESCRIPTION

BingoX::Carbon provides a database abstraction in which each row of data is represented by an object blessed into a class representing the table. Each Carbon Class represents a table or view in the database. The database and classes are described by variables in said classes. After Carbon classes have been set up to represent tables in a database, creating, retrieving and modifying data is simply a matter of method calls against Carbon objects and classes. For a tutorial on setting up a Carbon project, see carbontut packaged with BingoX.

DATABASE SUPPORT

Currently, Carbon has been tested and is known to work with Sybase, Oracle, and PostgreSQL. Carbon should with minimal change be able to support many other databases.

Due to Carbon's extensive use of transactions, it will not currently work with database engines that do not support transactions - most notably MySQL.

We tried to keep Carbon/BingoX as database agnostic as possible, but had to put some database specific code to support such things as Sybase's Indentity columns, Oracles Sequences, the different date formats, and Oracles use of all caps.

If you would like to help Carbon support MySQL or other database engines, feel free to send patches or contact us for help.

CLASS VARIABLES

REQUIRED

Classes that inherit from BingoX::Carbon must have the following class variables:

  • $table

    A scalar that contains the table name the class represents.

  • @primary_keys

    An array containing the primary key colulmn names.

  • @field_order

    An array containing all the table's column names in perferred order of use. Was formerly used for order of display, but has since been replaced by the BingoX Display namespace modules.

OPTIONAL

  • $identity

    A scalar that contains the name of the column which is a Sybase IDENTITY column. There can be only one identity column per table.

  • $seqcol

    A scalar that contains the name of the column which should be populated by a sequence.

  • $sequence

    A scalar that contains the name of the sequence object in the database which new() will poll to determine what to populate $seqcol with.

  • %relations

    A hash containing relational data. The keys to the hash are class names of foreign objects. The values are class names that represents the referential class for any object blessed in the class of the key.

    e.g. :

      %relations = ( ClassOne => 'ClassOne_ClassTwo' );
  • %foreign_keys

    A hash containing relational data present in referential classes. The keys to the hash are class names representing the classes referred to. The values are the names of the columns which contain the foreign key to the referred table.

    e.g. :

     %foreign_keys = ( ClassOne => 'one', ClassTwo => 'two' );
  • $title_field

    The column which should be used to display the object in a list.

  • %content_fields

    This hash lets Carbon classes have virtual columns to hold large amounts of content data. The keys of this hash are virtual colum names whose values are Carbon classnames that handle those content columns.

    e.g. :

     %content_fields = ( content    => 'Class::Data::Content' );
  • %date_fields

    The keys of this hash are column names from the fieldorder array that have a database type of date. Putting those columns in here make sure that you will get Unix time when asking for those columns.

    e.g. :

     %date_fields = ( showdate      => 1 );

METHODS

CONSTRUCTORS AND STATIC METHODS

import ( )

Called from 'use BingoX::Carbon;'

Possible arguments are as follows:

  • :cache_all

    flag will put all data into the object at time of construction. The default action is to store only primary_keys in the object until data is needed, but this approach proved very inefficient in certain situations.

  • :no_dynmeth

    flag will stop the creation of methods in calling class for all the elements of the class' fieldorder.

  • :full_dynmeth

    flag will avoid using 'forwarder' methods and instead use full dynamic methods - each with it's own code resembling AUTOLOAD. By default, forwarder methods are used and all call _old_school_autoload

    Any other element, not starting with the ':' character will be interpreted as a method name with which to create an access method in the calling class.

new ( [ $dbh, ] \%data )

Constructs a new object with the data in the hashref passed to it. You must supply a value for all primary key columns, unless the database provides a method for autogenerating this value. If an ID isn't specified the ID will be the next number in the database sequence :

Sybase

Class data $identity contains name of column which is a Sybase IDENTITY. The IDENTITY field will be omitted from the insert statement so Sybase can autogenerate it. New will then select @@identity to retreive the result of triggering IDENTITY. Because it does this within a single statement, it will get the correct value.

Oracle, Postgres

Class data $sequence contains name of Sequence object in database, and class data $seqcol contains name of column where BingoX::Carbon will insert the value returned by triggering $sequence.

get ( [ $dbh, ] \%params )

Constructs an object from the database using the parameters passed. NOTE: Passing one scalar variable as an ID to get has been deprecated! get will assume the parameters { $self->primary_keys->[0] => shift() } if a scalar is passed in place of \%params. Future version of BingoX::Carbon will not support this!

For instructions on the format of \%params, see "item_format_conditions".

modify ( \%new_data )

Object method:

Modify instance data with \%new_data and sync with database. Returns $self if success, undef if failure.

modify ( $dbh, \%params, \%new_data )

Static method:

Modify all rows satisfied by conditions in \%params with \%new_data Returns true if success, undef if failure.

N.B. - You CAN NOT modify non-columnar information (i.e., relationships or extra-tabular "content" fields) for multiple rows simultaneously.

For instructions on the format of \%params, see "item_format_conditions".

duplicate ( [ $dbh, ] [ $object ] )

Returns a duplicate object, differing only by the primary key.

rm ( )

Object method:

Deletes the object and all database records associated with it.

rm ( $dbh, \%params )

Static method:

Deletes all database rows satisfied by the conditions in \%params.

For instructions on the format of \%params, see "item_format_conditions".

DATA LIST CONSTRUCTOR METHODS

list_obj ( [ $dbh, ] \%params, \@fields, \@sort )

Returns an arrayref of objects meeting all specifications in \%params.

For instructions on the format of \%params, see "item_format_conditions".

list_hash ( [ $dbh, ] \%params, \@fields, \@sort )

Returns an arrayref of hashrefs meeting all specifications in \%params

For instructions on the format of \%params, see "item_format_conditions".

list_array ( [ $dbh, ] \%params, \@fields, \@sort )

Returns an arrayref of arrayrefs meeting all specifications in \%params. Data is sorted in the order of $class->deffields.

For instructions on the format of \%params, see "item_format_conditions".

DATA STREAM CONSTRUCTOR METHODS

stream_obj ( [ $dbh, ] \%params, \@fields, \@sort )

Returns an code reference which, when dereferenced, will continue to return objects meeting all specifications in \%params until all matching rows have been returned, then it will return undef. Will optionally limit itself to the fields specified in \@fields, and optionally sorted in the order of \@sort.

For instructions on the format of \%params, see "item_format_conditions".

stream_hash ( [ $dbh, ] \%params, \@fields, \@sort )

Returns an code reference which, when dereferenced, will continue to return a hashref meeting all specifications in \%params until all matching rows have been returned, then it will return undef. Will optionally limit itself to the fields specified in \@fields, and optionally sorted in the order of \@sort.

For instructions on the format of \%params, see "item_format_conditions".

stream_array ( [ $dbh, ] \%params, \@fields, \@sort )

Returns an code reference which, when dereferenced, will continue to return an arrayref meeting all specifications in \%params until all matching rows have been returned, then it will return undef. Will optionally limit itself to the fields specified in \@fields, and optionally sorted in the order of \@sort.

For instructions on the format of \%params, see "item_format_conditions".

RELATIONAL METHODS

relate ( $OBJ )
relate ( \@OBJECTS )
relate ( \@IDS, $ID_CLASS, $ID_COLUMN )

Relates object to $OBJ, all objects in \@OBJECTS, or objects represented by \@IDS from the $ID_CLASS class in the $ID_COLUMN column of the database. Returns the number of objects passed that have been related to the object.

unrelate ( $OBJ )
unrelate ( \@OBJECTS )
unrelate ( \@IDS, $ID_CLASS, $ID_COLUMN )

Unrelates object from $OBJ, all objects in \@OBJECTS, or objects represented by \@IDS from the $ID_CLASS class in the $ID_COLUMN column of the database. Returns the number of objects that no longer are related to the object.

unrelate_all ( $fclass [ , $unary_rev ] )

Unrelates all related objects in class $fclass from current object. Optional $unary_rev does reverse operation for unary relationships

i.e.:

 unrelates "parent" objects from current
isrelated ( $OBJ )

Returns a true value if $OBJ is related to the object, false otherwise.

Returns an arrayref of objects of $class related to the object.

Returns an code reference which, when dereferenced, will continue to return objects of $class related to the object until all matching rows have been returned, then it will return undef.

get_relation_info ( ('I' | 'S'), $OBJ )
get_relation_info ( ('I' | 'S'), \@OBJECTS )
get_relation_info ( ('I' | 'S'), \@IDS, $ID_CLASS, $ID_COLUMN )

Returns as the first element the relational class on which the second data element is associated. If the first argument is an 'I' (INSERT), the second element is a hashref suitable to pass to new or modify. If the first argument is an 'S' (SELECT), returns a hashref suitable to pass to get, list_*, stream_*, etc. This method is primarily used by methods relate, unrelate, isrelated, and related.

CLASS DATA ACCESSOR METHODS

table ( [ $dbh ] )

Returns class defined table name

identity ( [ $dbh ] )

Returns class defined identity column as a string.

sequence ( [ $dbh ] )

Returns class defined sequence used by this table as a string.

seqcol ( [ $dbh ] )

Returns class defined seqcol used by this table as a string.

def_fields ( )

Returns class defined fields as a hash reference

field_order ( )

Returns class defined field order as an anonymous array

relations ( )

Returns class defined relations as a hash reference

foreign_keys ( )

Returns class defined foreign_keys as a hash reference

cpkey ( )

Returns a string representing a single composite primary key joined by $self->pkd.

OPTIMIZE

cpkey_params ($cpkey)

Returns a params hash from the cpkey string passed.

title_field ( )

Returns class defined title_field as a string.

title_size ( )

Returns class defined title_size as a string.

content_fields ( )

Returns class defined content fields order as an hashref

date_fields ( )

Returns class defined date fields order as an hashref

primary_keys ( [ $dbh ] )

Returns class defined primary keys as an array ref. NOTE: The use of the class variable $primary_key has been deprecated! primary_keys will construct an array ref with $primary_key if it is used. Future verson of BingoX::Carbon will not support this!

dbh ( )

Returns class database handler object. Keeps a global which gets removed with a $r->register_cleanup.

cached_dbh ( [ $dbh ] )

Returns the cached database handle if it has been set. Optionally sets the cached database handle to $dbh.

purge_dbh ( )

Clears the database handle cache variable.

error_handler ( )

Carps @_ if debugging is activated prints stack backtrace if $debug > 2 always returns undef (if you choose to overload this method, please make sure you return undef)

errors ( )

Returns $self->{'_errors'} if called as an object method. Returns $errstr of the called class otherwise.

data_class ( )

Returns the data class

data_class_name ( )

Returns the rightmost part of the data_class_name name (thats the text right of the ::)

connectdb ( )

*** NEEDS ERROR HANDLER ****

Called as a static method, it just returns the DBH Object.

pkd ( )

Returns a constant for the primary key delimiter.

date_format ( )

Returns a constant, the strftime template for what date strings are to be displayed as.

str2time ( $string )

This is method is used to parse your default date format into a format that str2time understands, such as:

  Date: 961221               (yymmdd)
  Date: 12-21-96             (mm-dd-yy)    ( '-', '.' or '/' )
  Date: 12-June-96           (dd-month-yy) ( '-', '.' or '/' )
  Date: June 12 96 00:00PM   (month dd yy hh:mmPM)
  Date: June 12 96 00::00:00 (month dd yy hh:mm::ss)

If time is not passed then time defaults to 00:00:00.

SQL GENERATION METHODS

sql_select ( [ $dbh, ] $SELECT [, \@bindings] )

Excutes the prepared select statement and returns an arrayref of objects. The @bindings array contains values of SQL placeholders if used. Returns a code reference that will return a hashref when dereferenced.

format_select ( [ $dbh, ] \%params, \@fields, \@sort [, $alias ] )

Returns formatted SQL 'SELECT' statement, selecting @fields that meet all specifications in %params, sorted by @sort. Also returns bindings of all dynamic placeholders in SQL statement. Optionally, $alias may specify the table alias to be used in the statment (" WHERE alias.name = 'myname'"). Sub-values should be surrounded by ##, ie "name LIKE ##me##", for conditions involving: ( LIKE, NOT, <, >, SELECT ) Finally, and sort terms may be reversed (made descending) by prepending them with a '-'. (e.g., to sort by 'col1' ascending and 'col2' descending, pass 'qw(col1 -col2)'.)

For instructions on the format of \%params, see "item_format_conditions".

format_conditions ( [ $dbh, ] \%params [, $alias ] )

Returns formatted sql 'WHERE' block, and an array of values to be bound (passed to DBI->execute().) Optionally, $alias may specify the table alias to be used in the statment (" WHERE alias.name = 'myname'"). The keys of %params are the fields you are trying to match; the values of %params are specified as follows :

  • equality condition

    To test one equality, simply pass the value you wish to match as the value.

    e.g. :

      { $field => 12 }
      { $field => 'twelve' }
  • inequalities or multiple equalities (LIKE, NOT, <, >, IN, BETWEEN)

    Sub-values should be surrounded by ##, ie "name LIKE ##me##", for conditions involving: ( LIKE, NOT, <, > ).

    To use IN or BETWEEN (with or without NOT), params should contain something similar to ( $field => [ $type, @LIST ] ) where $type is '[NOT ]IN' or '[NOT ]BETWEEN' and @LIST is the set of values for the statement.

  • NULL/NOT NULL conditions

    To test for a NULL condition or NOT NULL condition, pass 'IS NULL' or 'IS NOT NULL' (respectively) as the value.

    e.g. :

      { $field => 'IS NOT NULL' }
  • subqueries

    When using a SELECT statement in an IN block, the argument immediately following the select statement should be the arrayref containing any bindings for that statement. This means that you may use the output of format_conditions as the input for a subquery :

     $conditions = $obj_class1->format_conditions(
            $dbh,
            {
               $field => [
                            'IN',
                            $obj_class2->format_conditions( $dbh, $field => $value )
                         ]
            }
     );
  • multiple values

    To match multiple conditions, pass an arrayref (that does not begin with 'IN' or 'BETWEEN'!) containing a list of conditions (any of the conditions above.) You MUST delimit each condition with either '&&' (for AND) or '||' (for OR), and you may also use '(' or ')' to separate logical conditions.

    e.g. :

      { $field => [ 1, '||', '(', 2, '&&', 3, ')' ] }

AUTOLOAD

AUTOLOAD

AUTOLOAD method - returns requested field data from object. If that requested data has not been cached, it is retrieved from the database, cached, and returned. If data is passed then field value is modified in the database and instance data, and value is returned.

int_to_bitmap ( $int )

Returns a 32-bit bit-string which corresponds to the integer $int.

bitmap_to_int ( $bitmap )

Returns an integer that corresponds to the (at most) 32-bit bit-string $bitmap.

get_list_hash ( )
get_list_hash ( [ $dbh ] [, \%params ] )

If used as a static method then you must pass $dbh.

Returns a hash ref of all the objects in the class it was called against. The hash is built from the cpkey, and the title_field, substr()'d to the title_size or by default 80 chars.

OPTIMIZE

REVISION HISTORY

 $Log: Carbon.pm,v $
 Revision 2.36  2001/12/20 19:22:38  gefilte
 error() - fixes dumb mistake (see the diff and you'll understand :-)

 format_conditions()
        - changed some RE parens into non-capturing (for efficiency)
        - changed "Allow LIKE,NOT,IS" case to bind on "\s". This prevents values beginning with those letters from matching unless they are the "real deal".
        (Maybe we should add a '= $value' case in case we want to match something that actually does begin with said words.  For now, avoid them :-)

        "Do you think we should have cut this scene?"
                - Bad, nasty, evil, naughty Zoot of the Castle Anthrax

 Revision 2.35  2001/09/20 21:01:38  gefilte
 new() - fixed bug in %datefields processing code which caused insert to fail
                (bug introduced in rev 2.32)

 Revision 2.34  2000/12/12 19:05:00  gefilte
 new(), modify() - fixed incorrect usage of cpkey(), which caused relational code to fail.

 Revision 2.33  2000/12/12 18:49:36  useevil
  - removed $pkey from cpkey, because its not used
  - updated version for new release:  1.92

 Revision 2.32  2000/11/15 19:33:09  useevil
  - new(), modify(), and _date_meth_init() now calls time2str() instead
    of stringifying the object, and date_format() now works

 Revision 2.31  2000/09/26 21:17:38  zhobson
 Fixed bugs in connectdb() and dataclass() that conspired to prevent projects from
 specifying a connectarray in their own namespace (like @Foo::Data::connectarray)

 Revision 2.30  2000/09/20 20:48:27  dweimer
 Doh! fixed some typo's

 Revision 2.29  2000/09/20 20:46:14  dweimer
 Merged David's changes.
 His comments:
 Cleaned up some debugging code.  All stray carp() calls now call error_handler().  error_handler() itself cleaned up.

 Revision 2.28  2000/09/19 23:49:38  dweimer
 content() - no longer converts linebreaks into HTML break tags (must have seemed useful at the time...)
 Changed error_handler so that it clucks if debug is > 2.

 Revision 2.27  2000/09/19 23:42:42  dweimer
 Version update 1.91

 Revision 2.26  2000/09/13 18:16:00  david
 new(), modify()
        - now only open a transaction if necessary (multiple statements required)

 RATIONALE - This will allow you to use a new() or modify() call when you have an active stream open, so long as the new() or modify() call does not require changes made to a relationship or to a content_field.

 Revision 2.25  2000/09/13 02:21:32  david
 Some minor POD changes
 Now only requires Data::Dumper if $debug is on.
 new(), modify(), rm(), _content()
        - Made all database transaction code more conventional (in the perl/DBI sense.)
 modify()
        - static method mode now works again (broken by Carbonium code in rev. 2.1)
        - will now return an error if you attempt to use statically to change contentfields or relationships
        - no longer truncates trailing spaces (if you want to do that, do it yourself!)

 Revision 2.24  2000/09/08 03:08:15  adam
  - added cpkey_params method
  - changed cpkey (removed wantarray stuf)

 Revision 2.23  2000/09/07 20:00:58  thai
  - updated POD documentation to show all REQUIRED classes

 Revision 2.22  2000/09/07 19:57:29  thai
  - added data_class_name()
  - changed DateTime::Date usage to BingoX::Time

 Revision 2.21  2000/09/06 23:18:02  greg
 removed old POD, and make new POD more coherent and descriptive.

 Revision 2.20  2000/09/02 00:15:17  thai
  - fixed POD documentation for date_format()

 Revision 2.19  2000/09/01 23:26:30  thai
  - added:
     date_format() - the date format you want returned from the date object
        str2time() - used to parse date from the database

 Revision 2.18  2000/08/31 22:53:12  greg
 added COPYING and MANIFEST files

 Revision 2.17  2000/08/31 21:54:18  greg
 Added COPYRIGHT information.
 Added file COPYING (LGPL).
 Cleaned up POD.
 Moved into BingoX namespace.
 References to Bingo::XPP now point to Apache::XPP.

 "To the first approximation, syntactic sugar is trivial to implement.
  To the second approximation, the first approximation is totally bogus."
        -Larry Wall

 Revision 2.16  2000/08/10 21:08:41  thai
  - changed get_list_hash() to work a lot better
  - changed cpkey() to use wantarray and $cpkey for when you want only the
    primary keys
  - changed occurrances where you split the $cpkey to use $self->cpkey( $cpkey )

 Revision 2.15  2000/08/09 21:24:34  thai
  - added title_size()

 Revision 2.14  2000/08/07 23:08:41  thai
  - will return content fields with <BR> for (\r\n)|[\n\r]

 Revision 2.13  2000/08/07 17:58:30  thai
  - fixed bug in _content() where it tried to deference an undefined
    array ref

 Revision 2.12  2000/08/03 20:45:15  thai
  - addd pkd() method to handle the pkds

 Revision 2.11  2000/07/14 19:12:25  dougw
 Should fix the datefields and contentfields error. They were returning an arrayref and the meth_inits tried
 to dereference it as a hash. DOH!

 Revision 2.10  2000/07/13 22:12:44  thai
  - changed methods so they are more consistent
      deffields() is now def_fields()
         fieldorder() is now field_order()
         contentfields() is now content_fields()
         datefields() is now date_fields()
         dataclass() is now data_class()
    of course the other methods still work, but please use the new ones

 Revision 2.9  2000/07/12 19:29:02  thai
  - fixed POD, cleaned up code

 Revision 2.8  2000/07/11 01:18:40  zack
 removed explicit reference to Datatime::Date::Sybase from _date_meth_init()

 Revision 2.7  2000/07/11 00:05:39  zack
 in cached_dbh(), only use Apache->register_cleanup() if mod_perl
 is running

 Revision 2.6  2000/07/10 22:39:16  zack
 - added the ability to provide a selection to get_list_hash()

 Revision 2.5  2000/07/07 01:34:54  dougw
  - Took out Caching for _content methods until we decide on a method to 
    cache them other than just class name.

 Revision 2.4  2000/06/24 03:17:56  dougw
  - Modified _content to handle param passing when updating and retrieving 
    a content field. Made sure content_meth_init and date_meth_init check 
    to see if a method exists before mucking with other namespaces

 Revision 2.3  2000/05/31 02:39:20  greg
 changed use of s/.*:// to substr(...) in AUTOLOAD for efficiency.

 Revision 2.2  2000/05/24 20:45:11  thai
  - tried to weed out as much of the DateTime::Date::Sybase code as
    possible.  Still uses DateTime::Date, and only DateTime::Date::Sybase
    in _date_meth_init()

 Revision 2.1  2000/05/19 01:24:42  thai
  - cleaned up code
  - is now part of the Bingo user space
  - added code from Carbonium

 Revision 2.0  2000/05/02 00:54:33  thai
  - committed as 2.0

 Revision 1.58  2000/03/17 19:01:36  colin
 - changed format_conditions() so that it allows "IS " statements to work 
   (was formerly only matching "IS NOT" statements)

 Revision 1.57  2000/03/09 02:01:07  dougw
 Allows nulled numerics and values.
 Changed the push lines in &new and &modify to allow above.

 Revision 1.56  2000/03/08 23:24:36  zack
 Facilitated NULL updates

 Revision 1.55  2000/02/10 20:05:11  greg
 memory saver - now creates forwarder methods instead of full dynamic methods

 Revision 1.54  2000/02/10 08:56:40  greg
 lots of new TODO and TO BE INVESTIGATED items to think about or implement...

 Revision 1.53  2000/02/10 07:24:24  greg
 now doesn't pass empty string column names in sql to DBI...

 Revision 1.52  1999/12/30 04:40:18  thai
  - bug in format_conditions where if the param field was
    not in deffields, then it would "next" leaving behind
    the remnants of the joined condition "AND" or "OR"
    I pop'd the @query array to eliminate that

 Revision 1.51  1999/12/29 01:44:53  david
 More documentation improvements and additions.
 Fixed format_conditions() so BBEdit balances tags correctly. (loathe BBedit :-)

 Revision 1.50  1999/12/28 22:38:10  david
 new() now makes identity/sequence decisions based on actual driver type of $dbh.
 Better documentation of new().

 Revision 1.49  1999/11/21 02:19:24  adam
  - format_conditions() - you can now use 'is not' as well as 'not'

 Revision 1.48  1999/11/10 23:27:59  greg
 Cleaned up documentation and code. Documentation and debugging information
 now more accurately reflects what is actually happening in Carbon.

 Revision 1.47  1999/10/28 05:52:25  david
 AUTOLOAD(), _create_access_method(), _old_school_autoload()
        - revised accessor code for efficiency (made an 8% improvement!)
        - added private POD and lots of other useful comments

 I've wanted to do this for awhile...just never got around to it until now.

 Revision 1.46  1999/10/22 21:11:25  thai
  - fixed some typos in the warns and POD.

 Revision 1.45  1999/10/08 21:51:53  derek
 Fixed an error with over-sanitization in format_contditions.

 Revision 1.44  1999/10/08 21:01:48  david
 Merged 'oracle' branch (excluding r1.36.2.5-r1.36.2.7, merged previously 
 into r1.41), adding Oracle support to Carbon, as well as some other 
 improvements (listed below in revision history.)

 Oracle support in sql_select() still needs refining.

 Revision 1.36.2.8  1999/10/01 09:19:38  david
 Removed silly ($primary_keys >= 2) condition from new().
        (If you don't know what it is, you probably don't care.)

 format_conditions() - added means to specify order and logic of parameters
        (NEEDS DOCUMENTATION)

 _create_access_method() - changed warning of $AUTOLOAD to $name

 Revision 1.36.2.4  1999/09/21 21:45:05  colin
 Dave changed part of sql_select back so it works now. Huzzah!

 Revision 1.36.2.3  1999/09/21 19:46:30  colin
 changed format_select(): the actuall select statement use lowercase SQL 
 commands because uppercase ones don't work on views :P

 Revision 1.36.2.2  1999/09/21 18:49:57  david
 sql_select() - Minor fix to Oracleizing code -- now works for columns not in fieldorder.

 Revision 1.36.2.1  1999/09/10 07:46:51  david
 INITIAL ORACLE SUPPORT
        Added seqcol() and sequence() class variables and corresponding static methods
                $seqcol   - name of column which sequence applies to (like $identity)
                $sequence - name of sequence object in the database
        new() - will use nextval of $sequence to populate $seqcol (if no data supplied for that column)
        - removed $DATABASE::database kluge (Sybase seems to need it -- don't forget!)
        sql_select() - creates different closure for Oracle which does case matching
                (Oracle columns return uppercase and are case-insensitive, Carbon is case-sensitive.)
 Tested
        new() - seems to work as expected with sequences or without
        modify() - works correctly as object method, as well as through accessor methods
        rm() - works as object method
        accessor methods - work
 To do :
        test modify() and rm() in static form
        test relationships
        document changes to new() in POD

 I imagine most everything should work, since the change was made at sql_select() 
 -- the main point of database read access.  All methods which write to the database 
    should work normally, since Oracle is case-insensitive.  Still, more testing 
    should be done.

 Furthermore, the algorithm I used to make sql_select() work is slow, and can probably 
 be greatly improved.  Still, it's a start, and should preserve our API functionality.

 Revision 1.43  1999/10/08 01:13:29  david
 rm() (static) - sanity prevents you from removing entire table if params is empty
 Restructured stream/list_related :
        _related() broken into _related_conditions() and _related_subquery_conditions()
        stream_related() now calls sql_select() using result of related_conditions()
        list_related() now calls stream_related() (like list_obj() calls stream_obj()

 unrelate() - now uses list_obj() instead of stream_obj() to get objects to unrelate
        (this may bite us later, but for now it makes unrelate() "transaction-friendly")

 unrelate_all() - new method will unrelate all objects of a named class from an object
        (uses new _related_subquery_conditions() to form its parameters to call rm() with)

        "Think it'll work?"
        "It'll take a miracle..."

 Revision 1.42  1999/10/07 23:39:02  greg
 fixed sql BETWEEN syntax

 Revision 1.41  1999/09/29 20:52:03  david
 Merged in changes from r1.36.2.5 through r1.36.2.7 :
 Support for multiple conditions/field in SELECTs.

 Revision 1.36.2.7  1999/09/29 00:27:46  david
 Removed spurious warn left behind in last commit.

 "Do as I say, not as I do."

 Revision 1.36.2.6  1999/09/28 23:08:31  david
 format_conditions() - added support for multiple conditions/field

 Revision 1.36.2.5  1999/09/24 00:48:19  david
 Fixed bug in format_conditions() - it didn't let you have a condition value of 0.

 Revision 1.40  1999/09/28 00:39:25  greg
  - Added blesses, and class Carbon::Stream to support streams as objects - this isn't 
    heavily test yet.
  - Changed cmps (ref($stream) eq 'CODE') to (ref($stream)) to support new stream class.
  - Lots of other small bug fixes.

 Revision 1.39  1999/09/25 21:31:16  david
 table() - no longer prepends table names with the value of $DATABASE::database

 Revision 1.38  1999/09/25 20:14:39  david
 modify() - added [@wherevals] bind information to debugging output
 format_conditions() - bug fix : did not allow conditions with 0 values
 **************************************************************************
 sql_select() - REMOVED CALL TO DBI->TRACE.
                PEPPLER FIXED DBD::Sybase "Bind Placeholder" BUG
 **************************************************************************
 Just be nice to people, and they do nice things for you. :-)

 Revision 1.37  1999/09/10 07:41:38  david
 Bug fix :
        The first time you called an accessor method and AUTOLOAD() creates a dynamic 
        method, if you called it with content (i.e., to modify the object attribute) 
        that content would not be forwarded to the new accessor method by AUTOLOAD().  
        This is now fixed.

 (See the diff? Real tiny fix. :-)

 Revision 1.36  1999/09/10 03:22:22  david
 Bug Fixes
        new() - can now insert a NULL into a non-IDENTITY primary key column which has 
                a default (sort of kludgy -- basically it allows this if there are multiple 
                primary keys.
                    the SELECT MAX() thing should probably go the way of the Dodoe.)
        - changed default sort parameters (ORDER BY) to primary_keys() instead of fieldorder()
                (Many people have complained about this.  let me know if you disagree with it.)
 Miscellanea
        - Changed all calls for DBI::errstr to $dbh->errstr or $sth->errstr
        modify() - changed @update_values to @update_fields, and @values to @update_values
                (basically, this just uses the same naming as new(), for consistency.)
 debugging improvments
        - AUTOLOAD now reports what it was called as
        - new(), modify(), and rm() now display which class is calling them
 documentation
        - updated KNOWN BUGS section, added TO DO LIST section

    "We don't code bugs here!"

 Revision 1.35  1999/07/14 23:43:22  david
 Changed relations() method to not return %foreign_keys ref
 Added foreign_keys() method to replace functionality of relations() method
 Changed calls to relations() which should be calls to foreign_keys()

 Revision 1.34  1999/07/13 23:10:20  greg
 Added method _old_school_autoload so calling $obj->SUPER::method() works with
 dynamic methods. _create_access_method sets *Carbon::AUTOLOAD to
 \&_old_school_autoload when called with 'AUTOLOAD' (so as not to duplicate
 code).

 Revision 1.33  1999/07/02 03:36:27  david
 Changed a bunch of 'carp()' calls to $self->error_handler

 get() - fixed weird bug where extra arguments were forwarded to closure, signaling 
         termination of embedded $sth

 format_relations/conditions()
     - no longer require $dbh be first parameter (though it will still work)
     - commented out useless code to determine i f value is numeric
         (this was a throwback to our pre-placeholder-binding days)

 Unary relation fixes :
 _related()
     - renamed from related()
     - fixed unary relations for *_related methods()
     - added $unary_rev_flag to reverse unary operations (needs POD!)
 relate/unrelate()
     - fixed unary relations (reversed operands of grep operation, binaries don't care!)

 Unary relationships now work as undocumented.  That is, they work the way we 
 discussed they would work.  But they still need to be documented! ;=}

 Revision 1.32  1999/07/01 18:59:03  greg
 Made changes to modify to add sanity, and efficiency.

 Revision 1.31  1999/07/01 02:22:11  david
 mget() - extra 'my $params = shift;' caused wildly unpredictable results...

 BAD, NASTY, EVIL DARTH GREG BUG!

 Revision 1.30  1999/06/30 00:53:22  greg
 fixed bugs that were found when integrating with Conf with this version of Carbon.
 added workaround for sybase losing track of the current database - needs to be fixed.

 Revision 1.29  1999/06/25 22:23:42  david
 - Some minor POD corrections
 new()
     - Started changing new() to support inserting into identity fields (not finished)
     - Added minor debugging
 sanity - changed most verifications of (ref $params) to (ref $params eq 'HASH')
 get() - cleans up coderef before exiting
 modify() - added static method usage
 rm() - added static method usage
 - unanchored /##(.+?)##/ regular expression in modify() and format_conditions() 
   (so they will actually work)
 list/stream_obj() - adds primary_keys to \@fields if excluded

 Revision 1.28  1999/06/23 22:42:00  greg
 -import: Removed silliness; now you can pass options to classes that inherit
   from Carbon
 -format_contitions: now bindings in 'IN' are passes as an array and not an
   array ref
 -related: took advantage of new syntax for format_conditions
 -get_relation_into: added sanity and warnings

 Revision 1.27  1999/06/22 23:19:59  greg
 - submit: added sanity
 - rm: now removes hash data before undefining reference to object
 - format_conditions: added better support for 'IN' and 'BETWEEN'
 Rewrote relation methods relate and unrelate. Added list_related,
 stream_related, and isrelated based on the get_relation_info and
 related methods

 Revision 1.26  1999/06/15 18:41:31  greg
 import: changed to recognize all options starting with ':' as options,
        all others as methods. Added ':no_dynmeth' as import option to
        specify methods should not be generated dynamically, but left to
        AUTOLOAD
 Changed method AUTOLOAD, and added method _create_access_method to
        generate dynamic methods

 Revision 1.25  1999/06/11 19:29:09  greg
 Fixed cache_all option that broke with the introduction of the list_* and
 stream_* methods.

 Revision 1.24  1999/06/10 23:03:45  greg
 Added space to SQL statements using WHERE blocks returned by format_conditions.

 Revision 1.23  1999/06/10 21:13:13  greg
 Changed $Carbon::VERSION variable to contain the CVS revision number
 import - changed to use a class variable to control cache_all functionality
 new - Added sanity
 get/mget - changed to wrapper methods around the list_obj method
 _format_conditions - changed to public method format_conditions
 list - changed to wrapper method around list_obj and list_hash methods
 deffields - changed to construct a hash from the class fieldorder
 sql_select - Returns a coderef to stream hash data (no longer blesses objects)
 format_select - new method to construct and return a complete SELECT statement

 Added methods stream_obj, stream_hash, stream_array, list_obj, list_hash, list_array
 Removed scrolling_list, chop_blanks, escape
 Cleaned up code. Removed lots of warn statements.
 Updated POD to reflect new and changed methods.

 --
 "There is no conspiracy. Nobody is in charge. It's a headless blunder
 operating under the illusion of a master plan."    --Worth (Cube)

 Revision 1.22  1999/06/09 23:26:41  thai
  - moved the revision logs to the bottom.

 Revision 1.21  1999/05/08 20:16:23  adam
  - sql_select - added sanity to ensure that all retrieved objects have 
    primary keys.  Otherwise it creates bad objects.  It only does this 
    if there's no $nobless flag.  Added comments around it.
  - There may be a better way of checking.  Probably before the select.
  - identity - podified identity method.
  - AUTOLOAD - commented autoload sanity ensuring that the primary key 
    isn't autoloaded.  Maybe theres a better way to do this, like checking 
    right when you enter autoload. The object should always have a primary key.
  - new - updated pod to account for identity field.

 Revision 1.20  1999/05/07 22:25:46  greg
  - import - Added ability to specify caching of all data at object construction
             at time of require. To cache all data: use Carbon qw(cache_all);
             This will redefine the mget function from where objects are gotten
             (also from get, although get is just a convenient wrapper around mget)

 Revision 1.19  1999/05/07 16:47:37  greg
  - Changed names of variables set by return values of _format_conditions 
    to make more sense.
  - Added a bit of sanity to what _format_conditions does when something 
    isn't quite right.

 Revision 1.18  1999/05/06 22:30:13  adam
  - new() - Now properly handles identities.  It now puts the insert and 
    select @@identity in the same prepare to ensure it is getting the 
    correct identity value.

 Revision 1.17  1999/05/06 06:30:55  adam
  - Handles identity columns.
  - Changed new to look for identity information in class and use it 
    if it can.
  - Added identity method which looks for $identity in the class.
  - Split up insert statement in new to work with inserts of just the 
    identity field.

 Revision 1.16  1999/05/05 06:32:59  adam
  - Added if debug.

 Revision 1.15  1999/05/05 05:56:38  adam
  - Added if ($debug);

 Revision 1.14  1999/05/03 17:11:45  fred
  - added a line to chop_blancs to unescape single quotes.

 Revision 1.13  1999/04/30 00:56:49  thai
  - added more verbage to AUTOLOAD to explain how it returns $obj->name if
    its not already cached.
  - made a change in _format_conditions; ($value =~ /LIKE|NOT|IN|<|>/) to
    ($value =~ /^(LIKE|NOT|IN|<|>)/)

 Revision 1.12  1999/04/29 20:39:22  thai
  - removed $dbh = shift, because it doesn't need it.

 Revision 1.11  1999/04/15 19:44:56  greg
  - Silly me ;) New should return $self, not $dbh <G>

 Revision 1.10  1999/04/15 19:34:13  greg
  - Changed the instance data method dbh uses from 'dbh' to '_dbh'. Changed method
  - new to add dbh to the instance data.

 Revision 1.9  1999/04/13 17:04:37  thai
  - removed $fields->[1] from list(), so that it now returns only an array
    ref of hashes or objs.
  - removed $name from AUTOLOAD at (@{ $self->primary_keys }), causing infinite
    loops.

 Revision 1.8  1999/04/10 20:17:09  adam
  - AUTOLOAD - changed if (defined... to if (exists...

 Revision 1.7  1999/04/10 00:34:10  colin
  - new - changed the select max bit so that if there are no records, it makes it 1.

 Revision 1.6  1999/04/09 22:55:02  greg
  - Tiny work around in case primary_keys doesn't return an array ref.

 Revision 1.5  1999/04/09 22:41:58  greg
  - Added more POD. Removed duplicate primary_keys method.

 Revision 1.4  1999/04/09 20:21:29  greg
  - Removed some debugging information, and turned off all debugging.

 Revision 1.3  1999/04/09 20:10:54  greg
  - Ton of new changes. Multiple primary keys, multiple database support, relations.
  - This was rushed a little, so there may be bugs...

 Revision 1.2  1999/03/12 23:56:40  thai
  - added DBI->trace to modify().

 Revision 1.38  1999/03/12 22:16:36  thai
  - added cvs headers.

SEE ALSO

perl(1).

KNOWN BUGS

new() currently does not report failure if inserting with Sybase IDENTITY columns fails, because of an anomoly in the way DBI handles multiple result sets. You can tell if a new() has failed if the value of the IDENTITY column is < 1.

TODO

  • REWRITE RELATIONAL CODE!!!

  • ability to redirect debugging to a file instead of STDERR

  • write test suite testing ALL documented carbon features

  • document unary many-many relationships

    (works, presently undocumented)

  • one-many relationships

  • multirow inserts (multinew?)

TO BE INVESTIGATED

  • carbon profiling

    carbon could use it's own debugging information to profile projects, and generate statistics based on that information. it could optionally store the profiling data to a sepearte table for later perusing... fields to store would be global incremented unique number with PID, action (select, modify, etc), sql statement, timestamp.

  • join class

    dynamically create a class based on a join between carbon classes. class data from all joined classes would need to be stored to be able to modify data.

  • read only objects (useful in view objects)

  • view classes

    store class data in such a way that modifying through the view wouldn't break (one table at a time, or store info on how to modify the actual carbon class for the view tables).

  • virtual classes

    get rid of empty classes! carbon has worked to well, and there are far to many empty classes lying around. either generate classes at compile time, or pretending that the classes exist at run time (using some sort of Carbon object ($carbon->class->method). Using a carbon object would need to fill in class @ISA arrays so that calls to the class could be caught.

  • Change class data to be more flexible - containing meta data

COPYRIGHT

    Copyright (c) 2000, Cnation Inc. All Rights Reserved. This module is free
    software. It may be used, redistributed and/or modified under the terms
    of the GNU Lesser General Public License as published by the Free Software
    Foundation.

    You should have received a copy of the GNU Lesser General Public License
    along with this library; if not, write to the Free Software Foundation, Inc.,
    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

AUTHORS

 David Pisoni <david@cnation.com>
 Greg Williams <greg@cnation.com>
 Thai Nguyen <thai@cnation.com>