Consolidate various $storage state resets in $storage->disconnect()
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage.pm
CommitLineData
4012acd8 1package DBIx::Class::Storage;
a62cf8d4 2
3use strict;
4use warnings;
5
046ad905 6use base qw/DBIx::Class/;
2ad62d97 7use mro 'c3';
046ad905 8
90d7422f 9{
10 package # Hide from PAUSE
11 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION;
12 use base 'DBIx::Class::Exception';
13}
14
15use DBIx::Class::Carp;
9345b14c 16use DBIx::Class::Storage::BlockRunner;
90d7422f 17use Scalar::Util qw/blessed weaken/;
1bc193ac 18use DBIx::Class::Storage::TxnScopeGuard;
f43ea814 19use Try::Tiny;
fd323bf1 20use namespace::clean;
046ad905 21
90d7422f 22__PACKAGE__->mk_group_accessors(simple => qw/debug schema transaction_depth auto_savepoint savepoints/);
23__PACKAGE__->mk_group_accessors(component_class => 'cursor_class');
e4eb8ee1 24
25__PACKAGE__->cursor_class('DBIx::Class::Cursor');
26
27sub cursor { shift->cursor_class(@_); }
046ad905 28
046ad905 29=head1 NAME
30
31DBIx::Class::Storage - Generic Storage Handler
32
33=head1 DESCRIPTION
34
35A base implementation of common Storage methods. For specific
36information about L<DBI>-based storage, see L<DBIx::Class::Storage::DBI>.
37
38=head1 METHODS
39
40=head2 new
41
42Arguments: $schema
43
44Instantiates the Storage object.
45
46=cut
47
48sub new {
49 my ($self, $schema) = @_;
50
51 $self = ref $self if ref $self;
52
90d7422f 53 my $new = bless( {
90d7422f 54 savepoints => [],
55 }, $self);
046ad905 56
57 $new->set_schema($schema);
4d753fb8 58 $new->debug(1)
59 if $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} || $ENV{DBIC_TRACE};
046ad905 60
61 $new;
62}
63
64=head2 set_schema
65
66Used to reset the schema class or object which owns this
67storage object, such as during L<DBIx::Class::Schema/clone>.
68
69=cut
70
71sub set_schema {
72 my ($self, $schema) = @_;
73 $self->schema($schema);
6298a324 74 weaken $self->{schema} if ref $self->{schema};
046ad905 75}
76
77=head2 connected
78
79Returns true if we have an open storage connection, false
80if it is not (yet) open.
81
82=cut
83
a62cf8d4 84sub connected { die "Virtual method!" }
046ad905 85
86=head2 disconnect
87
88Closes any open storage connection unconditionally.
89
90=cut
91
92sub disconnect { die "Virtual method!" }
93
94=head2 ensure_connected
95
96Initiate a connection to the storage if one isn't already open.
97
98=cut
99
a62cf8d4 100sub ensure_connected { die "Virtual method!" }
046ad905 101
102=head2 throw_exception
103
104Throws an exception - croaks.
105
106=cut
107
108sub throw_exception {
109 my $self = shift;
110
2a2a7b23 111 if (ref $self and $self->schema) {
1a58752c 112 $self->schema->throw_exception(@_);
113 }
114 else {
115 DBIx::Class::Exception->throw(@_);
116 }
046ad905 117}
a62cf8d4 118
4012acd8 119=head2 txn_do
a62cf8d4 120
4012acd8 121=over 4
a62cf8d4 122
4012acd8 123=item Arguments: C<$coderef>, @coderef_args?
a62cf8d4 124
4012acd8 125=item Return Value: The return value of $coderef
126
127=back
128
129Executes C<$coderef> with (optional) arguments C<@coderef_args> atomically,
130returning its result (if any). If an exception is caught, a rollback is issued
131and the exception is rethrown. If the rollback fails, (i.e. throws an
132exception) an exception is thrown that includes a "Rollback failed" message.
133
134For example,
135
136 my $author_rs = $schema->resultset('Author')->find(1);
137 my @titles = qw/Night Day It/;
138
139 my $coderef = sub {
140 # If any one of these fails, the entire transaction fails
141 $author_rs->create_related('books', {
142 title => $_
143 }) foreach (@titles);
144
145 return $author->books;
146 };
147
148 my $rs;
20674fcd 149 try {
4012acd8 150 $rs = $schema->txn_do($coderef);
20674fcd 151 } catch {
6b89ee0b 152 my $error = shift;
20674fcd 153 # Transaction failed
90d7422f 154 die "something terrible has happened!"
6b89ee0b 155 if ($error =~ /Rollback failed/); # Rollback failed
4012acd8 156
157 deal_with_failed_transaction();
20674fcd 158 };
4012acd8 159
160In a nested transaction (calling txn_do() from within a txn_do() coderef) only
161the outermost transaction will issue a L</txn_commit>, and txn_do() can be
162called in void, scalar and list context and it will behave as expected.
163
05075aee 164Please note that all of the code in your coderef, including non-DBIx::Class
165code, is part of a transaction. This transaction may fail out halfway, or
166it may get partially double-executed (in the case that our DB connection
167failed halfway through the transaction, in which case we reconnect and
168restart the txn). Therefore it is best that any side-effects in your coderef
169are idempotent (that is, can be re-executed multiple times and get the
170same result), and that you check up on your side-effects in the case of
171transaction failure.
6500d50f 172
4012acd8 173=cut
174
175sub txn_do {
38ed54cd 176 my $self = shift;
4012acd8 177
9345b14c 178 DBIx::Class::Storage::BlockRunner->new(
179 storage => $self,
9345b14c 180 wrap_txn => 1,
7d534e68 181 retry_handler => sub {
182 $_[0]->failed_attempt_count == 1
183 and
184 ! $_[0]->storage->connected
185 },
186 )->run(@_);
a62cf8d4 187}
188
046ad905 189=head2 txn_begin
190
191Starts a transaction.
192
193See the preferred L</txn_do> method, which allows for
194an entire code block to be executed transactionally.
195
196=cut
197
90d7422f 198sub txn_begin {
199 my $self = shift;
200
201 if($self->transaction_depth == 0) {
202 $self->debugobj->txn_begin()
203 if $self->debug;
204 $self->_exec_txn_begin;
205 }
206 elsif ($self->auto_savepoint) {
207 $self->svp_begin;
208 }
209 $self->{transaction_depth}++;
210
211}
046ad905 212
213=head2 txn_commit
214
215Issues a commit of the current transaction.
216
be01f1be 217It does I<not> perform an actual storage commit unless there's a DBIx::Class
218transaction currently in effect (i.e. you called L</txn_begin>).
219
046ad905 220=cut
221
90d7422f 222sub txn_commit {
223 my $self = shift;
224
225 if ($self->transaction_depth == 1) {
226 $self->debugobj->txn_commit() if $self->debug;
227 $self->_exec_txn_commit;
228 $self->{transaction_depth}--;
398215b1 229 $self->savepoints([]);
90d7422f 230 }
231 elsif($self->transaction_depth > 1) {
232 $self->{transaction_depth}--;
233 $self->svp_release if $self->auto_savepoint;
234 }
235 else {
236 $self->throw_exception( 'Refusing to commit without a started transaction' );
237 }
238}
046ad905 239
240=head2 txn_rollback
241
242Issues a rollback of the current transaction. A nested rollback will
243throw a L<DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION> exception,
244which allows the rollback to propagate to the outermost transaction.
245
246=cut
247
90d7422f 248sub txn_rollback {
249 my $self = shift;
250
251 if ($self->transaction_depth == 1) {
252 $self->debugobj->txn_rollback() if $self->debug;
253 $self->_exec_txn_rollback;
254 $self->{transaction_depth}--;
398215b1 255 $self->savepoints([]);
90d7422f 256 }
257 elsif ($self->transaction_depth > 1) {
258 $self->{transaction_depth}--;
259
260 if ($self->auto_savepoint) {
261 $self->svp_rollback;
262 $self->svp_release;
263 }
264 else {
265 DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->throw(
266 "A txn_rollback in nested transaction is ineffective! (depth $self->{transaction_depth})"
267 );
268 }
269 }
270 else {
271 $self->throw_exception( 'Refusing to roll back without a started transaction' );
272 }
273}
046ad905 274
adb3554a 275=head2 svp_begin
276
360dc8a5 277Arguments: $savepoint_name?
adb3554a 278
360dc8a5 279Created a new savepoint using the name provided as argument. If no name
280is provided, a random name will be used.
adb3554a 281
282=cut
283
90d7422f 284sub svp_begin {
285 my ($self, $name) = @_;
286
287 $self->throw_exception ("You can't use savepoints outside a transaction")
288 unless $self->transaction_depth;
289
290 my $exec = $self->can('_exec_svp_begin')
291 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
292
293 $name = $self->_svp_generate_name
294 unless defined $name;
295
296 push @{ $self->{savepoints} }, $name;
297
298 $self->debugobj->svp_begin($name) if $self->debug;
299
300 $exec->($self, $name);
301}
302
303sub _svp_generate_name {
304 my ($self) = @_;
305 return 'savepoint_'.scalar(@{ $self->{'savepoints'} });
306}
307
adb3554a 308
309=head2 svp_release
310
360dc8a5 311Arguments: $savepoint_name?
adb3554a 312
360dc8a5 313Release the savepoint provided as argument. If none is provided,
314release the savepoint created most recently. This will implicitly
315release all savepoints created after the one explicitly released as well.
adb3554a 316
317=cut
318
90d7422f 319sub svp_release {
320 my ($self, $name) = @_;
321
322 $self->throw_exception ("You can't use savepoints outside a transaction")
323 unless $self->transaction_depth;
324
325 my $exec = $self->can('_exec_svp_release')
326 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
327
328 if (defined $name) {
329 my @stack = @{ $self->savepoints };
330 my $svp;
331
332 do { $svp = pop @stack } until $svp eq $name;
333
334 $self->throw_exception ("Savepoint '$name' does not exist")
335 unless $svp;
336
337 $self->savepoints(\@stack); # put back what's left
338 }
339 else {
340 $name = pop @{ $self->savepoints }
341 or $self->throw_exception('No savepoints to release');;
342 }
343
344 $self->debugobj->svp_release($name) if $self->debug;
345
346 $exec->($self, $name);
347}
adb3554a 348
349=head2 svp_rollback
350
360dc8a5 351Arguments: $savepoint_name?
adb3554a 352
360dc8a5 353Rollback to the savepoint provided as argument. If none is provided,
354rollback to the savepoint created most recently. This will implicitly
355release all savepoints created after the savepoint we rollback to.
adb3554a 356
357=cut
358
90d7422f 359sub svp_rollback {
360 my ($self, $name) = @_;
361
362 $self->throw_exception ("You can't use savepoints outside a transaction")
363 unless $self->transaction_depth;
364
365 my $exec = $self->can('_exec_svp_rollback')
366 or $self->throw_exception ("Your Storage implementation doesn't support savepoints");
367
368 if (defined $name) {
369 my @stack = @{ $self->savepoints };
370 my $svp;
371
372 # a rollback doesn't remove the named savepoint,
373 # only everything after it
374 while (@stack and $stack[-1] ne $name) {
375 pop @stack
376 };
377
378 $self->throw_exception ("Savepoint '$name' does not exist")
379 unless @stack;
380
381 $self->savepoints(\@stack); # put back what's left
382 }
383 else {
384 $name = $self->savepoints->[-1]
385 or $self->throw_exception('No savepoints to rollback');;
386 }
387
388 $self->debugobj->svp_rollback($name) if $self->debug;
389
390 $exec->($self, $name);
391}
adb3554a 392
11544e1d 393=head2 txn_scope_guard
3b7f3eac 394
11544e1d 395An alternative way of transaction handling based on
396L<DBIx::Class::Storage::TxnScopeGuard>:
1bc193ac 397
11544e1d 398 my $txn_guard = $storage->txn_scope_guard;
89028f42 399
47d7b769 400 $result->col1("val1");
401 $result->update;
89028f42 402
11544e1d 403 $txn_guard->commit;
89028f42 404
11544e1d 405If an exception occurs, or the guard object otherwise leaves the scope
406before C<< $txn_guard->commit >> is called, the transaction will be rolled
407back by an explicit L</txn_rollback> call. In essence this is akin to
408using a L</txn_begin>/L</txn_commit> pair, without having to worry
409about calling L</txn_rollback> at the right places. Note that since there
410is no defined code closure, there will be no retries and other magic upon
411database disconnection. If you need such functionality see L</txn_do>.
1bc193ac 412
413=cut
414
415sub txn_scope_guard {
416 return DBIx::Class::Storage::TxnScopeGuard->new($_[0]);
417}
418
046ad905 419=head2 sql_maker
420
421Returns a C<sql_maker> object - normally an object of class
d5dedbd6 422C<DBIx::Class::SQLMaker>.
046ad905 423
424=cut
425
426sub sql_maker { die "Virtual method!" }
427
428=head2 debug
429
f92a9d79 430Causes trace information to be emitted on the L</debugobj> object.
431(or C<STDERR> if L</debugobj> has not specifically been set).
046ad905 432
433This is the equivalent to setting L</DBIC_TRACE> in your
434shell environment.
435
436=head2 debugfh
437
4d93345c 438An opportunistic proxy to L<< ->debugobj->debugfh(@_)
439|DBIx::Class::Storage::Statistics/debugfh >>
440If the currently set L</debugobj> does not have a L</debugfh> method, caling
441this is a no-op.
046ad905 442
443=cut
444
445sub debugfh {
446 my $self = shift;
447
448 if ($self->debugobj->can('debugfh')) {
449 return $self->debugobj->debugfh(@_);
450 }
451}
452
453=head2 debugobj
454
455Sets or retrieves the object used for metric collection. Defaults to an instance
456of L<DBIx::Class::Storage::Statistics> that is compatible with the original
457method of using a coderef as a callback. See the aforementioned Statistics
458class for more information.
459
4d753fb8 460=cut
461
462sub debugobj {
463 my $self = shift;
464
465 if (@_) {
466 return $self->{debugobj} = $_[0];
467 }
468
469 $self->{debugobj} ||= do {
470 if (my $profile = $ENV{DBIC_TRACE_PROFILE}) {
471 require DBIx::Class::Storage::Debug::PrettyPrint;
6e102c8f 472 my @pp_args;
473
4d753fb8 474 if ($profile =~ /^\.?\//) {
475 require Config::Any;
476
477 my $cfg = try {
478 Config::Any->load_files({ files => [$profile], use_ext => 1 });
479 } catch {
480 # sanitize the error message a bit
481 $_ =~ s/at \s+ .+ Storage\.pm \s line \s \d+ $//x;
482 $self->throw_exception("Failure processing \$ENV{DBIC_TRACE_PROFILE}: $_");
483 };
484
6e102c8f 485 @pp_args = values %{$cfg->[0]};
4d753fb8 486 }
487 else {
6e102c8f 488 @pp_args = { profile => $profile };
489 }
490
491 # FIXME - FRAGILE
492 # Hash::Merge is a sorry piece of shit and tramples all over $@
493 # *without* throwing an exception
494 # This is a rather serious problem in the debug codepath
495 # Insulate the condition here with a try{} until a review of
496 # DBIx::Class::Storage::Debug::PrettyPrint takes place
497 # we do rethrow the error unconditionally, the only reason
498 # to try{} is to preserve the precise state of $@ (down
499 # to the scalar (if there is one) address level)
500 #
501 # Yes I am aware this is fragile and TxnScopeGuard needs
502 # a better fix. This is another yak to shave... :(
503 try {
504 DBIx::Class::Storage::Debug::PrettyPrint->new(@pp_args);
505 } catch {
506 $self->throw_exception($_);
4d753fb8 507 }
508 }
509 else {
510 require DBIx::Class::Storage::Statistics;
511 DBIx::Class::Storage::Statistics->new
512 }
513 };
514}
515
046ad905 516=head2 debugcb
517
518Sets a callback to be executed each time a statement is run; takes a sub
519reference. Callback is executed as $sub->($op, $info) where $op is
520SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed.
521
f92a9d79 522See L</debugobj> for a better way.
046ad905 523
524=cut
525
526sub debugcb {
527 my $self = shift;
528
529 if ($self->debugobj->can('callback')) {
530 return $self->debugobj->callback(@_);
531 }
532}
533
e4eb8ee1 534=head2 cursor_class
046ad905 535
536The cursor class for this Storage object.
537
538=cut
539
046ad905 540=head2 deploy
541
542Deploy the tables to storage (CREATE TABLE and friends in a SQL-based
543Storage class). This would normally be called through
544L<DBIx::Class::Schema/deploy>.
545
546=cut
547
548sub deploy { die "Virtual method!" }
549
a3eaff0e 550=head2 connect_info
551
552The arguments of C<connect_info> are always a single array reference,
553and are Storage-handler specific.
554
555This is normally accessed via L<DBIx::Class::Schema/connection>, which
556encapsulates its argument list in an arrayref before calling
557C<connect_info> here.
558
559=cut
560
046ad905 561sub connect_info { die "Virtual method!" }
a3eaff0e 562
563=head2 select
564
565Handle a select statement.
566
567=cut
568
569sub select { die "Virtual method!" }
570
571=head2 insert
572
573Handle an insert statement.
574
575=cut
576
046ad905 577sub insert { die "Virtual method!" }
a3eaff0e 578
579=head2 update
580
581Handle an update statement.
582
583=cut
584
046ad905 585sub update { die "Virtual method!" }
a3eaff0e 586
587=head2 delete
588
589Handle a delete statement.
590
591=cut
592
046ad905 593sub delete { die "Virtual method!" }
a3eaff0e 594
595=head2 select_single
596
597Performs a select, fetch and return of data - handles a single row
598only.
599
600=cut
601
046ad905 602sub select_single { die "Virtual method!" }
a3eaff0e 603
604=head2 columns_info_for
605
c22c7625 606Returns metadata for the given source's columns. This
607is *deprecated*, and will be removed before 1.0. You should
608be specifying the metadata yourself if you need it.
a3eaff0e 609
610=cut
611
046ad905 612sub columns_info_for { die "Virtual method!" }
613
614=head1 ENVIRONMENT VARIABLES
615
616=head2 DBIC_TRACE
617
618If C<DBIC_TRACE> is set then trace information
f92a9d79 619is produced (as when the L</debug> method is set).
046ad905 620
621If the value is of the form C<1=/path/name> then the trace output is
622written to the file C</path/name>.
623
624This environment variable is checked when the storage object is first
fd323bf1 625created (when you call connect on your schema). So, run-time changes
626to this environment variable will not take effect unless you also
046ad905 627re-connect on your schema.
628
b6cd6478 629=head2 DBIC_TRACE_PROFILE
630
2514a73f 631If C<DBIC_TRACE_PROFILE> is set, L<DBIx::Class::Storage::Debug::PrettyPrint>
b6cd6478 632will be used to format the output from C<DBIC_TRACE>. The value it
633is set to is the C<profile> that it will be used. If the value is a
634filename the file is read with L<Config::Any> and the results are
635used as the configuration for tracing. See L<SQL::Abstract::Tree/new>
636for what that structure should look like.
637
046ad905 638=head2 DBIX_CLASS_STORAGE_DBI_DEBUG
639
640Old name for DBIC_TRACE
641
ace385bd 642=head1 SEE ALSO
643
2f0790c4 644L<DBIx::Class::Storage::DBI> - reference storage implementation using
645SQL::Abstract and DBI.
ace385bd 646
a2bd3796 647=head1 FURTHER QUESTIONS?
046ad905 648
a2bd3796 649Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
046ad905 650
a2bd3796 651=head1 COPYRIGHT AND LICENSE
046ad905 652
a2bd3796 653This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
654by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
655redistribute it and/or modify it under the same terms as the
656L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
046ad905 657
658=cut
659
a62cf8d4 6601;