DBIx-DataModel

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

  - 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



( run in 2.078 seconds using v1.01-cache-2.11-cpan-9b1e4054eb1 )