view release on metacpan or search on metacpan
- 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.
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
- 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
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
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:
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' );