view release on metacpan or search on metacpan
- replace Acme::Damn by Data::Structure::Util
- documentation and tests for the set operators -union, -intersect, etc.
- -result_as => 'hashref' or 'categorize' can take a subroutine argument
- kwalitee improvements (Filippo Biondi++)
v3.0 23.02.2018
- bump to next major version
- complete revision of documentation
v2.47_06-08 06.02.2018
- after_commit callbacks are executed outside of the transaction
- new schema parameter 'resultAs_classes'
- suppress ConnectedSource -- now handled directly within source classes
- new parameter 'join_with_USING', both at statement and at schema level
- simplification and cleanup of all v2 tests
v2.47_05 08.01.2018
- extending -result_as through subclasses in DBIDM::Schema::ResultAs namespace
- new result kinds : xlsx, tsv, file_tabular, count, table, etc.
- methods db_schema() and with_db_schema to prefix tables by DB schema name
- new method $schema->do_after_commit()
lib/DBIx/DataModel/Doc/Cookbook.pod view on Meta::CPAN
=head2 Timestamp validation
Suppose we want to sure that the record was not touched between the time
it was presented to the user in a display form and the time
the user wants to update or delete that record.
In order to do this, we will suppose that every record in every
table has a timestamp field C<TS_MODIF>, updated automatically by
a trigger within the database. When defining the schema, we
register an I<auto_update> callback on that column; such callbacks
are called automatically both on C<update()> and C<insert()> calls :
DBIx::DataModel->define_schema(
class => 'My::Schema',
auto_update_columns => {TS_MODIF => \&_check_time_stamp},
);
The body of the callback looks like this :
sub _check_time_stamp {
lib/DBIx/DataModel/Doc/Design.pod view on Meta::CPAN
=head1 TO DO
Here is a list of points to improve in future versions C<DBIx::DataModel> :
- 'has_invalid_columns' : should be called automatically before insert/update ?
- 'validate' record handler (at record level, not only column handlers)
- walk through WHERE queries and apply 'to_DB' handler (not obvious!)
- add PKEYS keyword in -columns, to be automatically replaced by
names of primary key columns of the touched tables
- pre/post callbacks: support arrays of handlers, refine(..) should add
to the array
- refine(-order_by => ..) should add to the ordering
- update with subtrees (insert/update on dependent records. Quid: delete?)
- auto-unjoin (API for partioning columns into subobjects).
- support DISTINCT ON ...
- support find_or_create, update_or_create
- copy idea from DBIC:Storage:DBI:MultiColumnIn
- Storable
- hooks for YAML ?
- think about STORABLE_attach
lib/DBIx/DataModel/Doc/Reference.pod view on Meta::CPAN
$statement->sqlize(%args)->prepare;
=head3 execute()
$statement->execute(@bindings);
Calls the L</bind()> method, calls L<DBI/execute> on the internal C<$sth>,
and applies the C<-pre_exec> and C<-post_exec> callbacks
if necessary. The state switches to C<EXECUTED>.
Arguments are optional, and are just a shortcut for
$statement->bind(@bindings)->execute;
An executed statement can be executed again, possibly with some
different bindings. When this happens, the internal result set
is reset, and fresh data rows can be retrieved again through
the L</next> or L</all> methods.
lib/DBIx/DataModel/Schema.pm view on Meta::CPAN
sub do_after_commit {
my ($self, $coderef) = @_;
ref $self or $self = $self->singleton;
$self->{transaction_dbhs}
or croak "do_after_commit() called outside of a transaction";
push @{$self->{after_commit_callbacks}}, $coderef;
}
sub do_transaction {
my ($self, $coderef, @new_dbh) = @_;
ref $self or $self = $self->singleton;
does($coderef, 'CODE')
or croak 'first arg to $schema->do_transaction(...) should be a coderef';
lib/DBIx/DataModel/Schema.pm view on Meta::CPAN
$self->exc_conn_trans_fatal->throw; # .. or no hope (and no rollback)
}
# otherwise, for regular SQL errors, try to rollback and then throw
my @rollback_errs;
foreach my $dbh (reverse @$transaction_dbhs) {
try {$dbh->rollback}
catch {push @rollback_errs, $_};
}
delete $self->{transaction_dbhs};
delete $self->{after_commit_callbacks};
DBIx::DataModel::Schema::_Exception->throw($err, @rollback_errs);
};
}
}
# execute the after_commit callbacks
my $callbacks = delete $self->{after_commit_callbacks} || [];
$_->() foreach @$callbacks;
return $in_context->{return}->();
}
sub unbless {
my $class = shift;
Data::Structure::Util::unbless($_) foreach @_;
return wantarray ? @_ : $_[0];
lib/DBIx/DataModel/Schema/ResultAs/Sql.pm view on Meta::CPAN
#----------------------------------------------------------------------
use warnings;
use strict;
use DBIx::DataModel::Statement;
use parent 'DBIx::DataModel::Schema::ResultAs';
sub get_result {
my ($self, $statement) = @_;
$statement->_forbid_callbacks(__PACKAGE__);
$statement->sqlize if $statement->status < DBIx::DataModel::Statement::SQLIZED;
return $statement->sql;
}
1;
__END__
=head1 NAME
lib/DBIx/DataModel/Schema/ResultAs/Subquery.pm view on Meta::CPAN
my $self = {alias => $alias};
return bless $self, $class;
}
sub get_result {
my ($self, $statement) = @_;
$statement->_forbid_callbacks(__PACKAGE__);
my @sqlize_args = $self->{alias} ? (-as => $self->{alias}) : ();
$statement->sqlize(@sqlize_args) if $statement->status < DBIx::DataModel::Statement::SQLIZED;
my ($sql, @bind) = $statement->sql;
# make sure the $sql is in parenthesis
$sql = "($sql)" if $sql !~ /^\(/;
return \ [$sql, @bind]; # ref to an arrayref with SQL and bind values
lib/DBIx/DataModel/Statement.pm view on Meta::CPAN
}
#----------------------------------------------------------------------
# PRIVATE METHODS IN RELATION WITH select()
#----------------------------------------------------------------------
sub _forbid_callbacks {
my ($self, $subclass) = @_;
my $callbacks = CORE::join ", ", grep {$self->arg($_)}
qw/-pre_exec -post_exec -post_bless/;
if ($callbacks) {
$subclass =~ s/^.*:://;
croak "$callbacks incompatible with -result_as=>'$subclass'";
}
}
sub _next_and_finish {
my $self = shift;
my $row_or_rows = $self->next( @_ ); # pass original parameters
$self->finish;
return $row_or_rows;
t/v1_DBIx-DataModel.t view on Meta::CPAN
$emp = HR::Employee->blessFromDB({emp_id => 999, spouse_id => 888});
my $emp_spouse = $emp->spouse;
sqlLike('SELECT * ' .
'FROM T_Employee ' .
"WHERE ( emp_id = ? )", [888], 'spouse self-ref assoc.');
# testing -preExec / -postExec
my %check_callbacks;
HR::Employee->select(-where => {foo=>'bar'},
-preExec => sub {$check_callbacks{pre} = "was called"},
-postExec => sub {$check_callbacks{post} = "was called"},);
is_deeply(\%check_callbacks, {pre =>"was called",
post => "was called" }, 'select, pre/post callbacks');
%check_callbacks = ();
HR::Employee->fetch(1234, {-preExec => sub {$check_callbacks{pre} = "was called"},
-postExec => sub {$check_callbacks{post} = "was called"}});
is_deeply(\%check_callbacks, {pre =>"was called",
post => "was called" }, 'fetch, pre/post callbacks');
# testing transactions
my $ok_trans = sub { return "scalar transaction OK" };
my $ok_trans_array = sub { return qw/array transaction OK/ };
my $fail_trans = sub { die "failed transaction" };
my $nested_1 = sub { HR->doTransaction($ok_trans) };
my $nested_many = sub {
my $r1 = HR->doTransaction($nested_1);
t/v2_Dbix-DataModel.t view on Meta::CPAN
$statement->prepare;
my $row = $statement->execute($emp)->next;
sqlLike('SELECT * ' .
'FROM T_Activity ' .
'INNER JOIN T_Department ' .
'ON T_Activity.dpt_id=T_Department.dpt_id ' .
'WHERE (emp_id = ? AND gender = ? AND gender != ?)', [999, 'F', 'M'],
'statement prepare/execute');
# -pre_exec / -post_exec callbacks
my %check_callbacks;
HR::Employee->select(-where => {foo=>'bar'},
-pre_exec => sub {$check_callbacks{pre} = "was called"},
-post_exec => sub {$check_callbacks{post} = "was called"},);
is_deeply(\%check_callbacks, {pre =>"was called",
post => "was called" }, 'select, pre/post callbacks');
%check_callbacks = ();
HR::Employee->fetch(1234, {-pre_exec => sub {$check_callbacks{pre} = "was called"},
-post_exec => sub {$check_callbacks{post} = "was called"}});
is_deeply(\%check_callbacks, {pre =>"was called",
post => "was called" }, 'fetch, pre/post callbacks');
# nb_fetched_rows
HR->dbh->{mock_add_resultset} = [[qw/foo bar/], ([1, 2]) x 23];
$statement = HR->table('Employee')->select(-result_as => 'statement');
$statement->all; # throw away the result -- this call is just to make sure the statement is finished
is $statement->nb_fetched_rows, 23, "nb_fetched_rows";
# page boundaries