Alzabo

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

- With Postgres 7.4, the DBI tables method always includes system
  tables, so we have to filter these out in the
  Alzabo::Driver::PostgreSQL->tables method.  Patch from Josh Jore.

- Make the is_date & is_datetime method consistent across various
  databases.  For Postgres, is_date was only returning true for the
  DATE type, not TIMESTAMP.

- Make is_datetime return true for Postgres' TIMESTAMPTZ column type.

- Turning on SQL debugging could cause Alzabo to alter bound values
  that were null to the string "NULL" before performing a query.

- If a table name was changed and an index, column, or foreign key
  dropped from that table, then the generated "diff" SQL could refer
  to the old table name in the various DROP statements that were
  generated.

- Workaround a bug in MySQL that reports a "Sub_part" of 1 for
  fulltext indexes.

Changes  view on Meta::CPAN


0.70  November 21, 2002

ENHANCEMENTS:

- The exception thrown when you attempt to set a non-nullable column
  to NULL is now an Alzabo::Exception::NotNullable exception, instead
  of an Alzabo::Exception::Params exception.  In the interests of
  backwards compatibility, the former is a subclass of the latter.

- Improved debugging options.  See the new Alzabo::Debug module for
  details.

BUG FIXES:

- Fixed Alzabo::Table->primary_key, which would die when no primary
  key existed and it was called in a scalar context.  In an array
  context, all the columns in the table were returned.  Reported by
  Eric Prestemon.

- Alzabo::ObjectCache::Sync::RDBMS created a table that it would later

Changes  view on Meta::CPAN


- The schema creator now requires HTTP::BrowserDetect.

- Fix what was arguably a bug in the caching/syncing code.
  Previously, one process could update a row and another process could
  then update that same row.  Now the second process will throw an
  exception.

BUG FIXES:

- Accidentally left debugging turned on in Alzabo::Exceptions.

- The schema creator did not allow you to remove a length or precision
  setting for a column once it had been made.

- Require a length for CHAR and VARCHAR columns with MySQL.

- Add error on setting precision for any column that doesn't allow
  them with MySQL.

- The interaction of caching rows and Alzabo::MethodMaker was not

MANIFEST  view on Meta::CPAN

t/03-runtime.t
t/04-rev-engineer.t
t/05a-rules-mysql.t
t/05b-rules-pg.t
t/07-methodmaker.t
t/09-storable.t
t/12-rev-engineer-pg-fk.t
t/14-unique-row-cache.t
t/15-alias-ref.t
t/17-insert-handle.t
t/18-debug-null-bug.t
t/19-schema-name.t
t/20-rev-engineer-pg-now.t
t/21-row_by_pk-exception.t
t/98-schema-diff.t
t/99-cleanup.t
t/99-pod.t
t/lib/Alzabo/Test/Utils.pm
TODO
SIGNATURE    Added here by Module::Build

README  view on Meta::CPAN


CREDITS

Ilya Martynov deserves special thanks for providing a _lot_ of patches
as well as many useful suggestions on new features, bug fixing, etc.

Ken Williams contributed a number of patches, many of which focused on
improving Postgres support.

Bob Gustafson has provided extensive assistance in testing and
debugging.

Thanks very much to Randal Schwartz for spending time with me on IRC
helping me find problems with the install process way back in the
early releases.

Some features in Alzabo have been borrowed or influenced by Class:DBI
(Michael Schwern and Tony Bowden) and Tangram (Jean-Louis Leroy).

Various bug reports and assistance from:

SIGNATURE  view on Meta::CPAN

SHA1 7eec7b4cd0bdccecf807ae241def67a1970f2a20 t/03-runtime.t
SHA1 0a68944ae8d4231f82b9873bc34436e41f7ec909 t/04-rev-engineer.t
SHA1 d22a8f365a9cc9853d7f690a62660c0892940b6e t/05a-rules-mysql.t
SHA1 cf1c5704e531920b7235a403e1c079c177b40803 t/05b-rules-pg.t
SHA1 da1bcec912461842b2b72ae8bfe312dcb32f6bd2 t/07-methodmaker.t
SHA1 2ba2912b5d3c857990d78cb44ef3ff1ece770beb t/09-storable.t
SHA1 faaa357dfd9c03a46f92142c6f26071558d0205c t/12-rev-engineer-pg-fk.t
SHA1 cc2b350f26a568a9e9d3832000430663b5906df2 t/14-unique-row-cache.t
SHA1 f1f7c23556f4a628ef91271d028aa42dc309f743 t/15-alias-ref.t
SHA1 c7398df9824312b432e5fc6da8f254291c893089 t/17-insert-handle.t
SHA1 cbf87932e76567c47447fa46536968d4e99de1ac t/18-debug-null-bug.t
SHA1 252b07115c2aecd2002f06bec0a1805c35495d43 t/19-schema-name.t
SHA1 b0783a16eecc2b30cb8f7171bb2db1e66301d7a0 t/20-rev-engineer-pg-now.t
SHA1 a2777dfc7a5375769a115bcd501bc5e3f22c6f5b t/21-row_by_pk-exception.t
SHA1 3e795c888d385b0ab405a8e89558eed680f8f66e t/98-schema-diff.t
SHA1 5926b115ebdc4830bcb8f8485c8f4186e2a90cec t/99-cleanup.t
SHA1 77232b92127c5f3069f8b6d349aa5ef49ca1b9d4 t/99-pod.t
SHA1 efd947c4a054b2b32473dac7ca7c66a580e6245f t/lib/Alzabo/Test/Utils.pm
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

lib/Alzabo.pm  view on Meta::CPAN


L<The Alzabo::MethodMaker docs|Alzabo::MethodMaker> - One of the most
useful parts of Alzabo.  This module can be used to auto-generate
methods based on the structure of your schema.

L<The Alzabo::Runtime::UniqueRowCache
docs|Alzabo::Runtime::UniqueRowCache> - This describes the simple
caching system included with Alzabo.

L<The Alzabo::Debug docs|Alzabo::Debug> - How to turn on various kinds
of debugging output.

L<The Alzabo::Exceptions docs|Alzabo::Exceptions> - Describes the
nature of all the exceptions used in Alzabo.

L<The FAQ|Alzabo::FAQ>.

L<The quick reference|Alzabo::QuickRef> - A quick reference for the
various methods of the Alzabo objects.

=head1 SCRIPTS

lib/Alzabo/Debug.pm  view on Meta::CPAN

{
    my %constants =
        ( SQL => 0,
          TRACE => 0,
          METHODMAKER => 0,
          REVERSE_ENGINEER => 0,
        );

    if ( $ENV{ALZABO_DEBUG} )
    {
        my %debug = map { uc $_ => 1 } split /\|/, $ENV{ALZABO_DEBUG};

        if ( $debug{ALL} )
        {
            @constants{ keys %constants } = (1) x keys %constants;
        }
        else
        {
            foreach ( grep { exists $constants{$_} } keys %debug )
            {
                $constants{$_} = $debug{$_} ? 1 : 0;
            }
        }
    }

    while ( my ($k, $v) = each %constants )
    {
        eval "use constant $k => $v";
        die $@ if $@;
    }
}


1;

__END__

=head1 NAME

Alzabo::Debug - Creates constants used to turn on debugging

=head1 SYNOPSIS

  export ALZABO_DEBUG='SQL|TRACE'

  ... load and run code using Alzabo ...

  export ALZABO_DEBUG=METHODMAKER

  ... load and run code using Alzabo ...

=head1 DESCRIPTION

This module creates constants used by other modules in order to
determine what debugging output should be generated.

The interface is currently experimental.

=head1 USAGE

Currently, the only way to turn on debugging is by setting the
C<ALZABO_DEBUG> environment variable.  This variable can contain
various flags, each separated by a pipe char (|).  Each flag turns on
different types of debugging output.

These flags B<must be set before Alzabo is loaded>, as debugging is
turned on or off through the use of constants.

The current flags are:

=over 4

=item * SQL

Generated SQL and its associated bound variables.

lib/Alzabo/Debug.pm  view on Meta::CPAN


The modules involved in reverse-engineering will generate output
describing what it finds during reverse-engineering.

=item * ALL

Turn on all flags.

=back

For now, all debugging output is sent to C<STDERR>.

=cut

lib/Alzabo/MethodMaker.pm  view on Meta::CPAN

                           foreign_key_2 => { optional => 1 },
                           column => { optional => 1 },
                           table => { optional => 1 },
                           parent => { optional => 1 },
                           plural => { optional => 1 },
                         };

    my $name = $self->{opts}{name_maker}->( %p )
        or return;

    my ($code_name, $debug_name) = ("$p{class}::$name",
                                    "$p{class}\->$name");

    if ( $p{class}->can($name) )
    {
        warn "MethodMaker: Creating $p{type} method $debug_name will override"
             . " the method of the same name in the parent class\n";
    }

    no strict 'refs';  # We use symbolic references here
    if ( defined &$code_name )
    {
        # This should probably always be shown to the user, not just
        # when debugging mode is turned on, because name clashes can
        # cause confusion - whichever subroutine happens first will
        # arbitrarily win.

        warn "MethodMaker: skipping $p{type} method $debug_name, subroutine already exists\n";
        return;
    }

    if (Alzabo::Debug::METHODMAKER)
    {
        my $message = "Making $p{type} method $debug_name";
        $message .= ": returns $p{returns}" if $p{returns};
        print STDERR "$message\n";
    }

    *$code_name = $p{code};
    return $name;
}

sub _make_fk_method
{

lib/Alzabo/Runtime/InsertHandle.pm  view on Meta::CPAN

    my %id;

    $schema->begin_work if @fk;
    eval
    {
        foreach my $fk (@fk)
        {
            $fk->register_insert( map { $_->name => $vals->{ $_->name } } $fk->columns_from );
        }

        $self->{sql}->debug(\*STDERR) if Alzabo::Debug::SQL;
        print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

        $self->{handle}->execute_no_result
            ( map { exists $vals->{$_} ? $vals->{$_} : undef } @val_order );

        foreach my $pk (@pk)
        {
            $id{ $pk->name } = ( defined $vals->{ $pk->name } ?
                                 $vals->{ $pk->name } :
                                 $driver->get_last_id($self) );

lib/Alzabo/Runtime/RowState/Live.pm  view on Meta::CPAN


    unless ( keys %{ $row->{data} } > keys %{ $row->{pk} } )
    {
        # Need to try to fetch something to confirm that this row exists!
        my $sql = ( $row->schema->sqlmaker->
                    select( ($row->table->primary_key)[0] )->
                    from( $row->table ) );

        $class->_where($row, $sql);

        $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
        print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

        return
            unless defined $row->schema->driver->one_row( sql => $sql->sql,
                                                          bind => $sql->bind );
    }

    return 1;
}

lib/Alzabo/Runtime/RowState/Live.pm  view on Meta::CPAN

        }
    }

    return %data unless @select;

    my $sql = ( $row->schema->sqlmaker->
                select( $row->table->columns(@select) )->
                from( $row->table ) );
    $class->_where($row, $sql);

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    my %d;
    @d{@select} =
        $row->schema->driver->one_row( sql  => $sql->sql,
                                       bind => $sql->bind )
            or $row->_no_such_row_error;

    while ( my( $k, $v ) = each %d )
    {

lib/Alzabo/Runtime/RowState/Live.pm  view on Meta::CPAN

    # If we have foreign keys we'd like all the fiddling to be atomic.
    $schema->begin_work if @fk;

    eval
    {
        foreach my $fk (@fk)
        {
            $fk->register_update( map { $_->name => $data{ $_->name } } $fk->columns_from );
        }

        $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
        print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

        $schema->driver->do( sql  => $sql->sql,
                             bind => $sql->bind );

        $schema->commit if @fk;
    };

    if (my $e = $@)
    {

lib/Alzabo/Runtime/RowState/Live.pm  view on Meta::CPAN

    $class->_where($row, $sql);

    $schema->begin_work if @fk;
    eval
    {
        foreach my $fk (@fk)
        {
            $fk->register_delete($row);
        }

        $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
        print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

        $schema->driver->do( sql => $sql->sql,
                             bind => $sql->bind );

        $schema->commit if @fk;
    };

    if (my $e = $@)
    {

lib/Alzabo/Runtime/Schema.pm  view on Meta::CPAN

    $self->_join_all_tables( sql => $sql,
                             join => $p{join} );

    Alzabo::Runtime::process_where_clause( $sql, $p{where} ) if exists $p{where};

    Alzabo::Runtime::process_order_by_clause( $sql, $p{order_by} )
        if $p{order_by};

    $sql->limit( ref $p{limit} ? @{ $p{limit} } : $p{limit} ) if $p{limit};

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    my $statement = $self->driver->statement( sql => $sql->sql,
                                              bind => $sql->bind );

    if (@select_tables == 1)
    {
        return Alzabo::Runtime::RowCursor->new
                   ( statement => $statement,
                     table => $select_tables[0]->real_table,

lib/Alzabo/Runtime/Schema.pm  view on Meta::CPAN

sub function
{
    my $self = shift;
    my %p = @_;

    my $sql = $self->_select_sql(%p);

    my $method =
        Alzabo::Utils::is_arrayref( $p{select} ) && @{ $p{select} } > 1 ? 'rows' : 'column';

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->driver->$method( sql => $sql->sql,
                                   bind => $sql->bind );
}

sub select
{
    my $self = shift;

    my $sql = $self->_select_sql(@_);

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->driver->statement( sql => $sql->sql,
                                     bind => $sql->bind );
}

use constant _SELECT_SQL_SPEC => { join => { type => ARRAYREF | OBJECT,
                                             optional => 1 },
                                   tables => { type => ARRAYREF | OBJECT,
                                               optional => 1 },

lib/Alzabo/Runtime/Table.pm  view on Meta::CPAN

    my %id;

    $schema->begin_work if @fk;
    eval
    {
        foreach my $fk (@fk)
        {
            $fk->register_insert( map { $_->name => $vals->{ $_->name } } $fk->columns_from );
        }

        $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
        print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

        $self->schema->driver->do( sql => $sql->sql,
                                   bind => $sql->bind );

        foreach my $pk (@pk)
        {
            $id{ $pk->name } = ( defined $vals->{ $pk->name } ?
                                 $vals->{ $pk->name } :
                                 $schema->driver->get_last_id($self) );

lib/Alzabo/Runtime/Table.pm  view on Meta::CPAN


sub rows_where
{
    my $self = shift;
    my %p = @_;

    my $sql = $self->_make_sql(%p);

    Alzabo::Runtime::process_where_clause( $sql, $p{where} ) if exists $p{where};

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->_cursor_by_sql( %p, sql => $sql );
}

sub one_row
{
    my $self = shift;
    my %p = @_;

lib/Alzabo/Runtime/Table.pm  view on Meta::CPAN


    Alzabo::Runtime::process_where_clause( $sql, $p{where} ) if exists $p{where};

    Alzabo::Runtime::process_order_by_clause( $sql, $p{order_by} ) if exists $p{order_by};

    if ( exists $p{limit} )
    {
        $sql->limit( ref $p{limit} ? @{ $p{limit} } : $p{limit} );
    }

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    my @return = $self->schema->driver->one_row( sql => $sql->sql,
                                                 bind => $sql->bind )
        or return;

    my @pk = $self->primary_key;

    my (%pk, %prefetch);

lib/Alzabo/Runtime/Table.pm  view on Meta::CPAN

                             prefetch => \%prefetch,
                           );
}

sub all_rows
{
    my $self = shift;

    my $sql = $self->_make_sql;

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->_cursor_by_sql( @_, sql => $sql );
}

sub _make_sql
{
    my $self = shift;
    my %p = @_;

lib/Alzabo/Runtime/Table.pm  view on Meta::CPAN

sub function
{
    my $self = shift;
    my %p = @_;

    my $sql = $self->_select_sql(%p);

    my $method =
        Alzabo::Utils::is_arrayref( $p{select} ) && @{ $p{select} } > 1 ? 'rows' : 'column';

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->schema->driver->$method( sql => $sql->sql,
                                           bind => $sql->bind );
}

sub select
{
    my $self = shift;

    my $sql = $self->_select_sql(@_);

    $sql->debug(\*STDERR) if Alzabo::Debug::SQL;
    print STDERR Devel::StackTrace->new if Alzabo::Debug::TRACE;

    return $self->schema->driver->statement( sql => $sql->sql,
                                             bind => $sql->bind );
}

use constant
    _SELECT_SQL_SPEC => { select => { type => SCALAR | ARRAYREF | OBJECT },
                          where  => { type => ARRAYREF | OBJECT,
                                      optional => 1 },

lib/Alzabo/SQLMaker.pm  view on Meta::CPAN

sub _virtual
{
    my $self = shift;

    my $sub = (caller(1))[3];
    $sub =~ s/.*::(.*?)$/$1/;
    Alzabo::Exception::VirtualMethod->throw( error =>
					     "$sub is a virtual method and must be subclassed in " . ref $self );
}

sub debug
{
    my $self = shift;
    my $fh = shift;

    print $fh '-' x 75 . "\n";
    print $fh "SQL\n - " . $self->sql . "\n";
    print $fh "Bound values\n";

    foreach my $b ( @{ $self->bind } )
    {

t/18-debug-null-bug.t  view on Meta::CPAN

#!/usr/bin/perl -w

#
# There was a bug which occurred when SQL debugging was on, which
# caused bound parameters that were explicitly set to undef to be
# converted to the string 'NULL'.
#

use strict;

use File::Spec;

use lib '.', File::Spec->catdir( File::Spec->curdir, 't', 'lib' );

t/18-debug-null-bug.t  view on Meta::CPAN

my $rdbms = $rdbms_names[0];

Alzabo::Test::Utils->make_schema($rdbms);

my $config = Alzabo::Test::Utils->test_config_for($rdbms);

my $s = Alzabo::Runtime::Schema->load_from_file( name => $config->{schema_name} );

$s->connect( Alzabo::Test::Utils->connect_params_for($rdbms)  );

Test::More::diag( 'This test will produce a lot of debugging output.  Please ignore it' );

my $dep = $s->table('department')->insert( values => { name => 'department' } );

my $emp;
eval_ok ( sub { $emp = $s->table('employee')->insert
                    ( values => { name   => 'Bubba',
                                  cash   => undef,
                                  dep_id => $dep->select('department_id'),
                                } ) },
          'insert with explicit cash => undef while debugging is on' );

is( $emp->select('cash'), undef, 'cash is undef' );



( run in 1.170 second using v1.01-cache-2.11-cpan-49f99fa48dc )