7 use English qw(-no_match_vars);
 
   9 use Rose::DBx::Cache::Anywhere;
 
  11 use base qw(Rose::DB);
 
  13 __PACKAGE__->db_cache_class('Rose::DBx::Cache::Anywhere');
 
  14 __PACKAGE__->use_private_registry;
 
  21   # runtime require to break circular include
 
  22   require SL::DBConnect;
 
  23   return SL::DBConnect->connect(@_);
 
  27   my $domain = shift || SL::DB->default_domain;
 
  28   my $type   = shift || SL::DB->default_type;
 
  30   ($domain, $type) = _register_db($domain, $type);
 
  32   my $db = __PACKAGE__->new_or_cached(domain => $domain, type => $type);
 
  38   create(undef, 'KIVITENDO');
 
  42   create(undef, 'KIVITENDO_AUTH');
 
  49   require SL::DBConnect;
 
  50   my %specific_connect_settings;
 
  51   my %common_connect_settings = (
 
  53     european_dates   => ((SL::DBConnect->get_datestyle || '') =~ m/european/i) ? 1 : 0,
 
  59   if (($type eq 'KIVITENDO_AUTH') && $::auth && $::auth->{DB_config} && $::auth->session_tables_present) {
 
  60     %specific_connect_settings = (
 
  61       database        => $::auth->{DB_config}->{db},
 
  62       host            => $::auth->{DB_config}->{host} || 'localhost',
 
  63       port            => $::auth->{DB_config}->{port} || 5432,
 
  64       username        => $::auth->{DB_config}->{user},
 
  65       password        => $::auth->{DB_config}->{password},
 
  68   } elsif ($::auth && $::auth->client) {
 
  69     my $client        = $::auth->client;
 
  70     %specific_connect_settings = (
 
  71       database        => $client->{dbname},
 
  72       host            => $client->{dbhost} || 'localhost',
 
  73       port            => $client->{dbport} || 5432,
 
  74       username        => $client->{dbuser},
 
  75       password        => $client->{dbpasswd},
 
  78   } elsif (%::myconfig && $::myconfig{dbname}) {
 
  79     %specific_connect_settings = (
 
  80       database        => $::myconfig{dbname},
 
  81       host            => $::myconfig{dbhost} || 'localhost',
 
  82       port            => $::myconfig{dbport} || 5432,
 
  83       username        => $::myconfig{dbuser},
 
  84       password        => $::myconfig{dbpasswd},
 
  88     $type = 'KIVITENDO_EMPTY';
 
  91   my %connect_settings   = (%common_connect_settings, %specific_connect_settings);
 
  92   my %flattened_settings = _flatten_settings(%connect_settings);
 
  94   $domain                = 'KIVITENDO' if $type =~ m/^KIVITENDO/;
 
  95   $type                 .= join($SUBSCRIPT_SEPARATOR, map { ($_, $flattened_settings{$_} || '') } sort grep { $_ ne 'dbpasswd' } keys %flattened_settings);
 
  96   my $idx                = "${domain}::${type}";
 
  98   if (!$_db_registered{$idx}) {
 
  99     $_db_registered{$idx} = 1;
 
 101     __PACKAGE__->register_db(domain => $domain,
 
 107   return ($domain, $type);
 
 110 sub _flatten_settings {
 
 114   while (my ($key, $value) = each %settings) {
 
 115     if ('HASH' eq ref $value) {
 
 116       %flattened = ( %flattened, _flatten_settings(%{ $value }) );
 
 118       $flattened{$key} = $value;
 
 125 sub with_transaction {
 
 126   my ($self, $code, @args) = @_;
 
 128   return $code->(@args) if $self->in_transaction;
 
 130   my (@result, $result);
 
 137       ? $self->do_transaction(sub { @result = $code->(@args) })
 
 138       : $self->do_transaction(sub { $result = $code->(@args) });
 
 140     my $error = $self->error;
 
 142       if ($error->isa('SL::X::DBError')) {
 
 143         # gobble the exception
 
 152   return wantarray ? @result : $result;
 
 164 SL::DB - Database access class for all RDB objects
 
 170 =item C<create $domain, $type>
 
 172 Registers the database information with Rose, creates a cached
 
 173 connection and executes initial SQL statements. Those can include
 
 174 setting the time & date format to the user's preferences.
 
 176 =item C<dbi_connect $dsn, $login, $password, $options>
 
 178 Forwards the call to L<SL::DBConnect/connect> which connects to the
 
 179 database. This indirection allows L<SL::DBConnect/connect> to route
 
 180 the calls through L<DBIx::Log4Perl> if this is enabled in the
 
 183 =item C<with_transaction $code_ref, @args>
 
 185 Executes C<$code_ref> with parameters C<@args> within a transaction,
 
 186 starting one only if none is currently active. Example:
 
 188   return $self->db->with_transaction(sub {
 
 189     # do stuff with $self
 
 192 This is a wrapper around L<Rose::DB/do_transaction> that does a few additional
 
 193 things, and should always be used in favour of the other:
 
 197 =item Composition of transactions
 
 199 When C<with_transaction> is called without a running transaction, a new one is
 
 200 created. If it is called within a running transaction, it performs no
 
 201 additional handling. This means that C<with_transaction> can be safely used
 
 202 within another C<with_transaction>, whereas L<Rose::DB/do_transaction> can not.
 
 206 C<with_transaction> adopts the behaviour of C<eval> in that it returns the
 
 207 result of the inner block, and C<undef> if an error occured. This way you can
 
 208 use the same pattern you would normally use with C<eval> for
 
 211   SL::DB->client->with_transaction(sub {
 
 213      # and return nominal true value
 
 216     # transaction error handling
 
 217     my $error = SL::DB->client->error;
 
 220 or you can use it to safely calulate things.
 
 224 The original L<Rose::DB/do_transaction> gobbles up all execptions and expects
 
 225 the caller to manually check return value and error, and then to process all
 
 226 exceptions as strings. This is very fragile and generally a step backwards from
 
 227 proper exception handling.
 
 229 C<with_transaction> only gobbles up exception that are used to signal an
 
 230 error in the transaction, and returns undef on those. All other exceptions
 
 231 bubble out of the transaction like normal, so that it is transparent to typoes,
 
 232 runtime exceptions and other generally wanted things.
 
 234 If you just use the snippet above, your code will catch everything related to
 
 235 the transaction aborting, but will not catch other errors that might have been
 
 236 thrown. The transaction will be rollbacked in both cases.
 
 238 If you want to play nice in case your transaction is embedded in another
 
 239 transaction, just rethrow the error:
 
 241   $db->with_transaction(sub {
 
 242     # code deep in the engine
 
 244   }) or die $db->error;
 
 256 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>