more dbh_do/txn_do improvements
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI.pm
CommitLineData
8b445e33 1package DBIx::Class::Storage::DBI;
e673f011 2# -*- mode: cperl; cperl-indent-level: 2 -*-
8b445e33 3
a62cf8d4 4use base 'DBIx::Class::Storage';
5
20a2c954 6use strict;
7use warnings;
8b445e33 8use DBI;
aeaf3ce2 9use SQL::Abstract::Limit;
28927b50 10use DBIx::Class::Storage::DBI::Cursor;
4c248161 11use DBIx::Class::Storage::Statistics;
92b858c9 12use IO::File;
701da8c4 13use Carp::Clan qw/DBIx::Class/;
046ad905 14
15__PACKAGE__->mk_group_accessors(
16 'simple' =>
17 qw/_connect_info _dbh _sql_maker _sql_maker_opts _conn_pid _conn_tid
18 cursor on_connect_do transaction_depth/
19);
20
bd7efd39 21BEGIN {
22
cb5f2eea 23package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :(
bd7efd39 24
25use base qw/SQL::Abstract::Limit/;
26
2cc3a7be 27# This prevents the caching of $dbh in S::A::L, I believe
28sub new {
29 my $self = shift->SUPER::new(@_);
30
31 # If limit_dialect is a ref (like a $dbh), go ahead and replace
32 # it with what it resolves to:
33 $self->{limit_dialect} = $self->_find_syntax($self->{limit_dialect})
34 if ref $self->{limit_dialect};
35
36 $self;
37}
38
39# While we're at it, this should make LIMIT queries more efficient,
40# without digging into things too deeply
41sub _find_syntax {
42 my ($self, $syntax) = @_;
43 $self->{_cached_syntax} ||= $self->SUPER::_find_syntax($syntax);
44}
45
54540863 46sub select {
47 my ($self, $table, $fields, $where, $order, @rest) = @_;
6346a152 48 $table = $self->_quote($table) unless ref($table);
eac29141 49 local $self->{rownum_hack_count} = 1
50 if (defined $rest[0] && $self->{limit_dialect} eq 'RowNum');
54540863 51 @rest = (-1) unless defined $rest[0];
0823196c 52 die "LIMIT 0 Does Not Compute" if $rest[0] == 0;
53 # and anyway, SQL::Abstract::Limit will cause a barf if we don't first
8839560b 54 local $self->{having_bind} = [];
bc0c9800 55 my ($sql, @ret) = $self->SUPER::select(
56 $table, $self->_recurse_fields($fields), $where, $order, @rest
57 );
8839560b 58 return wantarray ? ($sql, @ret, @{$self->{having_bind}}) : $sql;
54540863 59}
60
6346a152 61sub insert {
62 my $self = shift;
63 my $table = shift;
64 $table = $self->_quote($table) unless ref($table);
65 $self->SUPER::insert($table, @_);
66}
67
68sub update {
69 my $self = shift;
70 my $table = shift;
71 $table = $self->_quote($table) unless ref($table);
72 $self->SUPER::update($table, @_);
73}
74
75sub delete {
76 my $self = shift;
77 my $table = shift;
78 $table = $self->_quote($table) unless ref($table);
79 $self->SUPER::delete($table, @_);
80}
81
54540863 82sub _emulate_limit {
83 my $self = shift;
84 if ($_[3] == -1) {
85 return $_[1].$self->_order_by($_[2]);
86 } else {
87 return $self->SUPER::_emulate_limit(@_);
88 }
89}
90
91sub _recurse_fields {
92 my ($self, $fields) = @_;
93 my $ref = ref $fields;
94 return $self->_quote($fields) unless $ref;
95 return $$fields if $ref eq 'SCALAR';
96
97 if ($ref eq 'ARRAY') {
eac29141 98 return join(', ', map {
99 $self->_recurse_fields($_)
100 .(exists $self->{rownum_hack_count}
101 ? ' AS col'.$self->{rownum_hack_count}++
102 : '')
103 } @$fields);
54540863 104 } elsif ($ref eq 'HASH') {
105 foreach my $func (keys %$fields) {
106 return $self->_sqlcase($func)
107 .'( '.$self->_recurse_fields($fields->{$func}).' )';
108 }
109 }
110}
111
112sub _order_by {
113 my $self = shift;
114 my $ret = '';
8839560b 115 my @extra;
54540863 116 if (ref $_[0] eq 'HASH') {
117 if (defined $_[0]->{group_by}) {
118 $ret = $self->_sqlcase(' group by ')
119 .$self->_recurse_fields($_[0]->{group_by});
120 }
8839560b 121 if (defined $_[0]->{having}) {
122 my $frag;
123 ($frag, @extra) = $self->_recurse_where($_[0]->{having});
124 push(@{$self->{having_bind}}, @extra);
125 $ret .= $self->_sqlcase(' having ').$frag;
126 }
54540863 127 if (defined $_[0]->{order_by}) {
7ce5cbe7 128 $ret .= $self->_order_by($_[0]->{order_by});
54540863 129 }
d09c569a 130 } elsif (ref $_[0] eq 'SCALAR') {
e535069e 131 $ret = $self->_sqlcase(' order by ').${ $_[0] };
d09c569a 132 } elsif (ref $_[0] eq 'ARRAY' && @{$_[0]}) {
133 my @order = @{+shift};
134 $ret = $self->_sqlcase(' order by ')
135 .join(', ', map {
136 my $r = $self->_order_by($_, @_);
137 $r =~ s/^ ?ORDER BY //i;
138 $r;
139 } @order);
54540863 140 } else {
141 $ret = $self->SUPER::_order_by(@_);
142 }
143 return $ret;
144}
145
f48dd03f 146sub _order_directions {
147 my ($self, $order) = @_;
148 $order = $order->{order_by} if ref $order eq 'HASH';
149 return $self->SUPER::_order_directions($order);
150}
151
2a816814 152sub _table {
bd7efd39 153 my ($self, $from) = @_;
154 if (ref $from eq 'ARRAY') {
155 return $self->_recurse_from(@$from);
156 } elsif (ref $from eq 'HASH') {
157 return $self->_make_as($from);
158 } else {
6346a152 159 return $from; # would love to quote here but _table ends up getting called
160 # twice during an ->select without a limit clause due to
161 # the way S::A::Limit->select works. should maybe consider
162 # bypassing this and doing S::A::select($self, ...) in
163 # our select method above. meantime, quoting shims have
164 # been added to select/insert/update/delete here
bd7efd39 165 }
166}
167
168sub _recurse_from {
169 my ($self, $from, @join) = @_;
170 my @sqlf;
171 push(@sqlf, $self->_make_as($from));
172 foreach my $j (@join) {
173 my ($to, $on) = @$j;
73856587 174
54540863 175 # check whether a join type exists
176 my $join_clause = '';
ca7b9fdf 177 my $to_jt = ref($to) eq 'ARRAY' ? $to->[0] : $to;
178 if (ref($to_jt) eq 'HASH' and exists($to_jt->{-join_type})) {
179 $join_clause = ' '.uc($to_jt->{-join_type}).' JOIN ';
54540863 180 } else {
181 $join_clause = ' JOIN ';
182 }
73856587 183 push(@sqlf, $join_clause);
184
bd7efd39 185 if (ref $to eq 'ARRAY') {
186 push(@sqlf, '(', $self->_recurse_from(@$to), ')');
187 } else {
96cdbbab 188 push(@sqlf, $self->_make_as($to));
bd7efd39 189 }
190 push(@sqlf, ' ON ', $self->_join_condition($on));
191 }
192 return join('', @sqlf);
193}
194
195sub _make_as {
196 my ($self, $from) = @_;
54540863 197 return join(' ', map { (ref $_ eq 'SCALAR' ? $$_ : $self->_quote($_)) }
bc0c9800 198 reverse each %{$self->_skip_options($from)});
73856587 199}
200
201sub _skip_options {
54540863 202 my ($self, $hash) = @_;
203 my $clean_hash = {};
204 $clean_hash->{$_} = $hash->{$_}
205 for grep {!/^-/} keys %$hash;
206 return $clean_hash;
bd7efd39 207}
208
209sub _join_condition {
210 my ($self, $cond) = @_;
5efe4c79 211 if (ref $cond eq 'HASH') {
212 my %j;
bc0c9800 213 for (keys %$cond) {
214 my $x = '= '.$self->_quote($cond->{$_}); $j{$_} = \$x;
215 };
5efe4c79 216 return $self->_recurse_where(\%j);
217 } elsif (ref $cond eq 'ARRAY') {
218 return join(' OR ', map { $self->_join_condition($_) } @$cond);
219 } else {
220 die "Can't handle this yet!";
221 }
bd7efd39 222}
223
2a816814 224sub _quote {
225 my ($self, $label) = @_;
226 return '' unless defined $label;
3b24f6ea 227 return "*" if $label eq '*';
41728a6e 228 return $label unless $self->{quote_char};
3b24f6ea 229 if(ref $self->{quote_char} eq "ARRAY"){
230 return $self->{quote_char}->[0] . $label . $self->{quote_char}->[1]
231 if !defined $self->{name_sep};
232 my $sep = $self->{name_sep};
233 return join($self->{name_sep},
234 map { $self->{quote_char}->[0] . $_ . $self->{quote_char}->[1] }
235 split(/\Q$sep\E/,$label));
236 }
2a816814 237 return $self->SUPER::_quote($label);
238}
239
7be93b07 240sub limit_dialect {
241 my $self = shift;
242 $self->{limit_dialect} = shift if @_;
243 return $self->{limit_dialect};
244}
245
2437a1e3 246sub quote_char {
247 my $self = shift;
248 $self->{quote_char} = shift if @_;
249 return $self->{quote_char};
250}
251
252sub name_sep {
253 my $self = shift;
254 $self->{name_sep} = shift if @_;
255 return $self->{name_sep};
256}
257
bd7efd39 258} # End of BEGIN block
259
b327f988 260=head1 NAME
261
262DBIx::Class::Storage::DBI - DBI storage handler
263
264=head1 SYNOPSIS
265
266=head1 DESCRIPTION
267
046ad905 268This class represents the connection to an RDBMS via L<DBI>. See
269L<DBIx::Class::Storage> for general information. This pod only
270documents DBI-specific methods and behaviors.
b327f988 271
272=head1 METHODS
273
9b83fccd 274=cut
275
8b445e33 276sub new {
046ad905 277 my $new = shift->next::method(@_);
82cc0386 278
28927b50 279 $new->cursor("DBIx::Class::Storage::DBI::Cursor");
d79f59b9 280 $new->transaction_depth(0);
2cc3a7be 281 $new->_sql_maker_opts({});
82cc0386 282
046ad905 283 $new;
1c339d71 284}
285
1b45b01e 286=head2 connect_info
287
bb4f246d 288The arguments of C<connect_info> are always a single array reference.
1b45b01e 289
bb4f246d 290This is normally accessed via L<DBIx::Class::Schema/connection>, which
291encapsulates its argument list in an arrayref before calling
292C<connect_info> here.
1b45b01e 293
bb4f246d 294The arrayref can either contain the same set of arguments one would
295normally pass to L<DBI/connect>, or a lone code reference which returns
296a connected database handle.
d7c4c15c 297
2cc3a7be 298In either case, if the final argument in your connect_info happens
299to be a hashref, C<connect_info> will look there for several
300connection-specific options:
301
302=over 4
303
304=item on_connect_do
305
306This can be set to an arrayref of literal sql statements, which will
307be executed immediately after making the connection to the database
308every time we [re-]connect.
309
310=item limit_dialect
311
312Sets the limit dialect. This is useful for JDBC-bridge among others
313where the remote SQL-dialect cannot be determined by the name of the
314driver alone.
315
316=item quote_char
d7c4c15c 317
2cc3a7be 318Specifies what characters to use to quote table and column names. If
319you use this you will want to specify L<name_sep> as well.
320
321quote_char expects either a single character, in which case is it is placed
322on either side of the table/column, or an arrayref of length 2 in which case the
323table/column name is placed between the elements.
324
325For example under MySQL you'd use C<quote_char =E<gt> '`'>, and user SQL Server you'd
326use C<quote_char =E<gt> [qw/[ ]/]>.
327
328=item name_sep
329
330This only needs to be used in conjunction with L<quote_char>, and is used to
331specify the charecter that seperates elements (schemas, tables, columns) from
332each other. In most cases this is simply a C<.>.
333
334=back
335
336These options can be mixed in with your other L<DBI> connection attributes,
337or placed in a seperate hashref after all other normal L<DBI> connection
338arguments.
339
340Every time C<connect_info> is invoked, any previous settings for
341these options will be cleared before setting the new ones, regardless of
342whether any options are specified in the new C<connect_info>.
343
f5de3933 344Important note: DBIC expects the returned database handle provided by
345a subref argument to have RaiseError set on it. If it doesn't, things
346might not work very well, YMMV. If you don't use a subref, DBIC will
347force this setting for you anyways. Setting HandleError to anything
348other than simple exception object wrapper might cause problems too.
349
2cc3a7be 350Examples:
351
352 # Simple SQLite connection
bb4f246d 353 ->connect_info([ 'dbi:SQLite:./foo.db' ]);
6789ebe3 354
2cc3a7be 355 # Connect via subref
bb4f246d 356 ->connect_info([ sub { DBI->connect(...) } ]);
6789ebe3 357
2cc3a7be 358 # A bit more complicated
bb4f246d 359 ->connect_info(
360 [
361 'dbi:Pg:dbname=foo',
362 'postgres',
363 'my_pg_password',
364 { AutoCommit => 0 },
2cc3a7be 365 { quote_char => q{"}, name_sep => q{.} },
366 ]
367 );
368
369 # Equivalent to the previous example
370 ->connect_info(
371 [
372 'dbi:Pg:dbname=foo',
373 'postgres',
374 'my_pg_password',
375 { AutoCommit => 0, quote_char => q{"}, name_sep => q{.} },
bb4f246d 376 ]
377 );
6789ebe3 378
2cc3a7be 379 # Subref + DBIC-specific connection options
bb4f246d 380 ->connect_info(
381 [
382 sub { DBI->connect(...) },
2cc3a7be 383 {
384 quote_char => q{`},
385 name_sep => q{@},
386 on_connect_do => ['SET search_path TO myschema,otherschema,public'],
387 },
bb4f246d 388 ]
389 );
6789ebe3 390
004d31fb 391=cut
392
046ad905 393sub connect_info {
394 my ($self, $info_arg) = @_;
4c248161 395
046ad905 396 return $self->_connect_info if !$info_arg;
4c248161 397
046ad905 398 # Kill sql_maker/_sql_maker_opts, so we get a fresh one with only
399 # the new set of options
400 $self->_sql_maker(undef);
401 $self->_sql_maker_opts({});
486ad69b 402
046ad905 403 my $info = [ @$info_arg ]; # copy because we can alter it
404 my $last_info = $info->[-1];
405 if(ref $last_info eq 'HASH') {
406 if(my $on_connect_do = delete $last_info->{on_connect_do}) {
407 $self->on_connect_do($on_connect_do);
408 }
409 for my $sql_maker_opt (qw/limit_dialect quote_char name_sep/) {
410 if(my $opt_val = delete $last_info->{$sql_maker_opt}) {
411 $self->_sql_maker_opts->{$sql_maker_opt} = $opt_val;
412 }
413 }
486ad69b 414
046ad905 415 # Get rid of any trailing empty hashref
416 pop(@$info) if !keys %$last_info;
417 }
d7c4c15c 418
046ad905 419 $self->_connect_info($info);
420}
004d31fb 421
046ad905 422=head2 on_connect_do
4c248161 423
046ad905 424This method is deprecated in favor of setting via L</connect_info>.
486ad69b 425
f11383c2 426=head2 dbh_do
427
046ad905 428Arguments: $subref, @extra_coderef_args?
429
56769f7c 430Execute the given subref with the underlying database handle as its
431first argument, using the new exception-based connection management.
046ad905 432
433Any additional arguments will be passed verbatim to the called subref
434as arguments 2 and onwards.
435
56769f7c 436Example:
f11383c2 437
56769f7c 438 my @stuff = $schema->storage->dbh_do(
439 sub {
046ad905 440 my $dbh = shift;
441 my $cols = join(q{, }, @_);
442 shift->selectrow_array("SELECT $cols FROM foo")
443 },
444 @column_list
56769f7c 445 );
f11383c2 446
447=cut
448
449sub dbh_do {
046ad905 450 my $self = shift;
aa27edf7 451 my $coderef = shift;
452
453 return $coderef->($self->_dbh, @_) if $self->{_in_txn_do};
454
455 ref $coderef eq 'CODE' or $self->throw_exception
456 ('$coderef must be a CODE reference');
f11383c2 457
458 my @result;
459 my $want_array = wantarray;
460
461 eval {
56769f7c 462 $self->_verify_pid if $self->_dbh;
f11383c2 463 $self->_populate_dbh if !$self->_dbh;
f11383c2 464 if($want_array) {
aa27edf7 465 @result = $coderef->($self->_dbh, @_);
f11383c2 466 }
56769f7c 467 elsif(defined $want_array) {
aa27edf7 468 $result[0] = $coderef->($self->_dbh, @_);
f11383c2 469 }
56769f7c 470 else {
aa27edf7 471 $coderef->($self->_dbh, @_);
56769f7c 472 }
f11383c2 473 };
56769f7c 474
aa27edf7 475 my $exception = $@;
476 if(!$exception) { return $want_array ? @result : $result[0] }
477
478 $self->throw_exception($exception) if $self->connected;
479
480 # We were not connected - reconnect and retry, but let any
481 # exception fall right through this time
482 $self->_populate_dbh;
483 $coderef->($self->_dbh, @_);
484}
485
486# This is basically a blend of dbh_do above and DBIx::Class::Storage::txn_do.
487# It also informs dbh_do to bypass itself while under the direction of txn_do,
488# via $self->{_in_txn_do} (this saves some redundant eval and errorcheck, etc)
489sub txn_do {
490 my $self = shift;
491 my $coderef = shift;
492
493 ref $coderef eq 'CODE' or $self->throw_exception
494 ('$coderef must be a CODE reference');
495
496 local $self->{_in_txn_do} = 1;
f11383c2 497
aa27edf7 498 my $tried = 0;
499
500 my @result;
501 my $want_array = wantarray;
502
503 START_TXN: eval {
504 $self->_verify_pid if $self->_dbh;
505 $self->_populate_dbh if !$self->_dbh;
506
507 $self->txn_begin;
508 if($want_array) {
509 @result = $coderef->(@_);
510 }
511 elsif(defined $want_array) {
512 $result[0] = $coderef->(@_);
513 }
514 else {
515 $coderef->(@_);
516 }
517 $self->txn_commit;
518 };
519
520 my $exception = $@;
521 if(!$exception) { return $want_array ? @result : $result[0] }
522
523 if($tried++ > 0 || $self->connected) {
524 eval { $self->txn_rollback };
525 my $rollback_exception = $@;
526 if($rollback_exception) {
527 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
528 $self->throw_exception($exception) # propagate nested rollback
529 if $rollback_exception =~ /$exception_class/;
530
531 $self->throw_exception(
532 "Transaction aborted: ${exception}. "
533 . "Rollback failed: ${rollback_exception}"
534 );
535 }
536 $self->throw_exception($exception)
f11383c2 537 }
56769f7c 538
aa27edf7 539 # We were not connected, and was first try - reconnect and retry
540 # XXX I know, gotos are evil. If you can find a better way
541 # to write this that doesn't duplicate a lot of code/structure,
542 # and behaves identically, feel free...
543
544 $self->_populate_dbh;
545 goto START_TXN;
f11383c2 546}
547
9b83fccd 548=head2 disconnect
549
046ad905 550Our C<disconnect> method also performs a rollback first if the
9b83fccd 551database is not in C<AutoCommit> mode.
552
553=cut
554
412db1f4 555sub disconnect {
556 my ($self) = @_;
557
92925617 558 if( $self->connected ) {
559 $self->_dbh->rollback unless $self->_dbh->{AutoCommit};
560 $self->_dbh->disconnect;
561 $self->_dbh(undef);
562 }
412db1f4 563}
564
f11383c2 565sub connected {
566 my ($self) = @_;
412db1f4 567
1346e22d 568 if(my $dbh = $self->_dbh) {
569 if(defined $self->_conn_tid && $self->_conn_tid != threads->tid) {
1346e22d 570 return $self->_dbh(undef);
571 }
56769f7c 572 else {
573 $self->_verify_pid;
574 }
1346e22d 575 return ($dbh->FETCH('Active') && $dbh->ping);
576 }
577
578 return 0;
412db1f4 579}
580
f11383c2 581# handle pid changes correctly
56769f7c 582# NOTE: assumes $self->_dbh is a valid $dbh
f11383c2 583sub _verify_pid {
584 my ($self) = @_;
585
56769f7c 586 return if $self->_conn_pid == $$;
f11383c2 587
f11383c2 588 $self->_dbh->{InactiveDestroy} = 1;
d3abf3fe 589 $self->_dbh(undef);
f11383c2 590
591 return;
592}
593
412db1f4 594sub ensure_connected {
595 my ($self) = @_;
596
597 unless ($self->connected) {
8b445e33 598 $self->_populate_dbh;
599 }
412db1f4 600}
601
c235bbae 602=head2 dbh
603
604Returns the dbh - a data base handle of class L<DBI>.
605
606=cut
607
412db1f4 608sub dbh {
609 my ($self) = @_;
610
611 $self->ensure_connected;
8b445e33 612 return $self->_dbh;
613}
614
f1f56aad 615sub _sql_maker_args {
616 my ($self) = @_;
617
2cc3a7be 618 return ( limit_dialect => $self->dbh, %{$self->_sql_maker_opts} );
f1f56aad 619}
620
48c69e7c 621sub sql_maker {
622 my ($self) = @_;
fdc1c3d0 623 unless ($self->_sql_maker) {
f1f56aad 624 $self->_sql_maker(new DBIC::SQL::Abstract( $self->_sql_maker_args ));
48c69e7c 625 }
626 return $self->_sql_maker;
627}
628
8b445e33 629sub _populate_dbh {
630 my ($self) = @_;
1b45b01e 631 my @info = @{$self->_connect_info || []};
8b445e33 632 $self->_dbh($self->_connect(@info));
2fd24e78 633
634 if(ref $self eq 'DBIx::Class::Storage::DBI') {
635 my $driver = $self->_dbh->{Driver}->{Name};
efe6365b 636 if ($self->load_optional_class("DBIx::Class::Storage::DBI::${driver}")) {
2fd24e78 637 bless $self, "DBIx::Class::Storage::DBI::${driver}";
638 $self->_rebless() if $self->can('_rebless');
639 }
843f8ecd 640 }
2fd24e78 641
d7c4c15c 642 # if on-connect sql statements are given execute them
643 foreach my $sql_statement (@{$self->on_connect_do || []}) {
4c248161 644 $self->debugobj->query_start($sql_statement) if $self->debug();
d7c4c15c 645 $self->_dbh->do($sql_statement);
4c248161 646 $self->debugobj->query_end($sql_statement) if $self->debug();
d7c4c15c 647 }
5ef3e508 648
1346e22d 649 $self->_conn_pid($$);
650 $self->_conn_tid(threads->tid) if $INC{'threads.pm'};
8b445e33 651}
652
653sub _connect {
654 my ($self, @info) = @_;
5ef3e508 655
9d31f7dc 656 $self->throw_exception("You failed to provide any connection info")
657 if !@info;
658
90ec6cad 659 my ($old_connect_via, $dbh);
660
5ef3e508 661 if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
90ec6cad 662 $old_connect_via = $DBI::connect_via;
5ef3e508 663 $DBI::connect_via = 'connect';
5ef3e508 664 }
665
75db246c 666 eval {
f5de3933 667 if(ref $info[0] eq 'CODE') {
668 $dbh = &{$info[0]}
669 }
670 else {
671 $dbh = DBI->connect(@info);
672 $dbh->{RaiseError} = 1;
673 $dbh->{PrintError} = 0;
674 }
75db246c 675 };
90ec6cad 676
677 $DBI::connect_via = $old_connect_via if $old_connect_via;
678
75db246c 679 if (!$dbh || $@) {
680 $self->throw_exception("DBI Connection failed: " . ($@ || $DBI::errstr));
681 }
90ec6cad 682
e571e823 683 $dbh;
8b445e33 684}
685
d32d82f9 686sub __txn_begin {
687 my ($dbh, $self) = @_;
688 if ($dbh->{AutoCommit}) {
689 $self->debugobj->txn_begin()
690 if ($self->debug);
691 $dbh->begin_work;
692 }
693}
694
8091aa91 695sub txn_begin {
d79f59b9 696 my $self = shift;
d32d82f9 697 $self->dbh_do(\&__txn_begin, $self)
698 if $self->{transaction_depth}++ == 0;
699}
700
701sub __txn_commit {
702 my ($dbh, $self) = @_;
703 if ($self->{transaction_depth} == 0) {
704 unless ($dbh->{AutoCommit}) {
705 $self->debugobj->txn_commit()
706 if ($self->debug);
707 $dbh->commit;
708 }
709 }
710 else {
711 if (--$self->{transaction_depth} == 0) {
712 $self->debugobj->txn_commit()
713 if ($self->debug);
714 $dbh->commit;
715 }
986e4fca 716 }
8091aa91 717}
8b445e33 718
8091aa91 719sub txn_commit {
d79f59b9 720 my $self = shift;
d32d82f9 721 $self->dbh_do(\&__txn_commit, $self);
722}
723
724sub __txn_rollback {
725 my ($dbh, $self) = @_;
726 if ($self->{transaction_depth} == 0) {
727 unless ($dbh->{AutoCommit}) {
728 $self->debugobj->txn_rollback()
729 if ($self->debug);
730 $dbh->rollback;
731 }
732 }
733 else {
734 if (--$self->{transaction_depth} == 0) {
735 $self->debugobj->txn_rollback()
736 if ($self->debug);
737 $dbh->rollback;
986e4fca 738 }
f11383c2 739 else {
d32d82f9 740 die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new;
986e4fca 741 }
d32d82f9 742 }
8091aa91 743}
744
8091aa91 745sub txn_rollback {
d79f59b9 746 my $self = shift;
d32d82f9 747 eval { $self->dbh_do(\&__txn_rollback, $self) };
a62cf8d4 748 if ($@) {
749 my $error = $@;
750 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
751 $error =~ /$exception_class/ and $self->throw_exception($error);
752 $self->{transaction_depth} = 0; # ensure that a failed rollback
753 $self->throw_exception($error); # resets the transaction depth
8091aa91 754 }
755}
8b445e33 756
223b8fe3 757sub _execute {
758 my ($self, $op, $extra_bind, $ident, @args) = @_;
759 my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
944f30bf 760 unshift(@bind, @$extra_bind) if $extra_bind;
f59ffc79 761 if ($self->debug) {
e673f011 762 my @debug_bind = map { defined $_ ? qq{'$_'} : q{'NULL'} } @bind;
4c248161 763 $self->debugobj->query_start($sql, @debug_bind);
f59ffc79 764 }
75db246c 765 my $sth = eval { $self->sth($sql,$op) };
766
767 if (!$sth || $@) {
ec0ff6f6 768 $self->throw_exception(
769 'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"
770 );
75db246c 771 }
438adc0e 772 @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
701da8c4 773 my $rv;
75d07914 774 if ($sth) {
4c248161 775 my $time = time();
95dad7e2 776 $rv = eval { $sth->execute(@bind) };
777
778 if ($@ || !$rv) {
779 $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr));
780 }
75d07914 781 } else {
1c339d71 782 $self->throw_exception("'$sql' did not generate a statement.");
701da8c4 783 }
4c248161 784 if ($self->debug) {
785 my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind;
786 $self->debugobj->query_end($sql, @debug_bind);
787 }
223b8fe3 788 return (wantarray ? ($rv, $sth, @bind) : $rv);
789}
790
8b445e33 791sub insert {
792 my ($self, $ident, $to_insert) = @_;
bc0c9800 793 $self->throw_exception(
794 "Couldn't insert ".join(', ',
795 map "$_ => $to_insert->{$_}", keys %$to_insert
796 )." into ${ident}"
797 ) unless ($self->_execute('insert' => [], $ident, $to_insert));
8b445e33 798 return $to_insert;
799}
800
801sub update {
223b8fe3 802 return shift->_execute('update' => [], @_);
8b445e33 803}
804
805sub delete {
223b8fe3 806 return shift->_execute('delete' => [], @_);
8b445e33 807}
808
de705b51 809sub _select {
8b445e33 810 my ($self, $ident, $select, $condition, $attrs) = @_;
223b8fe3 811 my $order = $attrs->{order_by};
812 if (ref $condition eq 'SCALAR') {
813 $order = $1 if $$condition =~ s/ORDER BY (.*)$//i;
814 }
8839560b 815 if (exists $attrs->{group_by} || $attrs->{having}) {
bc0c9800 816 $order = {
817 group_by => $attrs->{group_by},
818 having => $attrs->{having},
819 ($order ? (order_by => $order) : ())
820 };
54540863 821 }
5c91499f 822 my @args = ('select', $attrs->{bind}, $ident, $select, $condition, $order);
9229f20a 823 if ($attrs->{software_limit} ||
824 $self->sql_maker->_default_limit_syntax eq "GenericSubQ") {
825 $attrs->{software_limit} = 1;
5c91499f 826 } else {
0823196c 827 $self->throw_exception("rows attribute must be positive if present")
828 if (defined($attrs->{rows}) && !($attrs->{rows} > 0));
5c91499f 829 push @args, $attrs->{rows}, $attrs->{offset};
830 }
de705b51 831 return $self->_execute(@args);
832}
833
834sub select {
835 my $self = shift;
836 my ($ident, $select, $condition, $attrs) = @_;
cb5f2eea 837 return $self->cursor->new($self, \@_, $attrs);
8b445e33 838}
839
1a14aa3f 840sub select_single {
de705b51 841 my $self = shift;
842 my ($rv, $sth, @bind) = $self->_select(@_);
6157db4f 843 my @row = $sth->fetchrow_array;
a3eaff0e 844 # Need to call finish() to work round broken DBDs
6157db4f 845 $sth->finish();
846 return @row;
1a14aa3f 847}
848
9b83fccd 849=head2 sth
850
851Returns a L<DBI> sth (statement handle) for the supplied SQL.
852
853=cut
854
d32d82f9 855sub __sth {
856 my ($dbh, $sql) = @_;
857 # 3 is the if_active parameter which avoids active sth re-use
858 $dbh->prepare_cached($sql, {}, 3);
859}
860
8b445e33 861sub sth {
cb5f2eea 862 my ($self, $sql) = @_;
d32d82f9 863 $self->dbh_do(\&__sth, $sql);
8b445e33 864}
865
bfe10d87 866
d32d82f9 867sub __columns_info_for {
868 my ($dbh, $self, $table) = @_;
a32e8402 869
d32d82f9 870 if ($dbh->can('column_info')) {
a953d8d9 871 my %result;
d32d82f9 872 eval {
873 my ($schema,$tab) = $table =~ /^(.+?)\.(.+)$/ ? ($1,$2) : (undef,$table);
874 my $sth = $dbh->column_info( undef,$schema, $tab, '%' );
875 $sth->execute();
876 while ( my $info = $sth->fetchrow_hashref() ){
877 my %column_info;
878 $column_info{data_type} = $info->{TYPE_NAME};
879 $column_info{size} = $info->{COLUMN_SIZE};
880 $column_info{is_nullable} = $info->{NULLABLE} ? 1 : 0;
881 $column_info{default_value} = $info->{COLUMN_DEF};
882 my $col_name = $info->{COLUMN_NAME};
883 $col_name =~ s/^\"(.*)\"$/$1/;
884
885 $result{$col_name} = \%column_info;
0d67fe74 886 }
d32d82f9 887 };
888 return \%result if !$@;
889 }
0d67fe74 890
d32d82f9 891 my %result;
892 my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0");
893 $sth->execute;
894 my @columns = @{$sth->{NAME_lc}};
895 for my $i ( 0 .. $#columns ){
896 my %column_info;
897 my $type_num = $sth->{TYPE}->[$i];
898 my $type_name;
899 if(defined $type_num && $dbh->can('type_info')) {
900 my $type_info = $dbh->type_info($type_num);
901 $type_name = $type_info->{TYPE_NAME} if $type_info;
902 }
903 $column_info{data_type} = $type_name ? $type_name : $type_num;
904 $column_info{size} = $sth->{PRECISION}->[$i];
905 $column_info{is_nullable} = $sth->{NULLABLE}->[$i] ? 1 : 0;
0d67fe74 906
d32d82f9 907 if ($column_info{data_type} =~ m/^(.*?)\((.*?)\)$/) {
908 $column_info{data_type} = $1;
909 $column_info{size} = $2;
0d67fe74 910 }
911
d32d82f9 912 $result{$columns[$i]} = \%column_info;
913 }
914
915 return \%result;
916}
917
918sub columns_info_for {
919 my ($self, $table) = @_;
920 $self->dbh_do(\&__columns_info_for, $self, $table);
a953d8d9 921}
922
9b83fccd 923=head2 last_insert_id
924
925Return the row id of the last insert.
926
927=cut
928
843f8ecd 929sub last_insert_id {
930 my ($self, $row) = @_;
931
f11383c2 932 $self->dbh_do(sub { shift->func('last_insert_rowid') });
843f8ecd 933}
934
9b83fccd 935=head2 sqlt_type
936
937Returns the database driver name.
938
939=cut
940
f11383c2 941sub sqlt_type { shift->dbh_do(sub { shift->{Driver}->{Name} }) }
1c339d71 942
9b83fccd 943=head2 create_ddl_dir (EXPERIMENTAL)
944
945=over 4
946
947=item Arguments: $schema \@databases, $version, $directory, $sqlt_args
948
949=back
950
951Creates an SQL file based on the Schema, for each of the specified
952database types, in the given directory.
953
954Note that this feature is currently EXPERIMENTAL and may not work correctly
955across all databases, or fully handle complex relationships.
956
957=cut
958
e673f011 959sub create_ddl_dir
960{
961 my ($self, $schema, $databases, $version, $dir, $sqltargs) = @_;
962
963 if(!$dir || !-d $dir)
964 {
965 warn "No directory given, using ./\n";
966 $dir = "./";
967 }
968 $databases ||= ['MySQL', 'SQLite', 'PostgreSQL'];
969 $databases = [ $databases ] if(ref($databases) ne 'ARRAY');
970 $version ||= $schema->VERSION || '1.x';
9e7b9292 971 $sqltargs = { ( add_drop_table => 1 ), %{$sqltargs || {}} };
e673f011 972
1c339d71 973 eval "use SQL::Translator";
974 $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@;
e673f011 975
9e7b9292 976 my $sqlt = SQL::Translator->new($sqltargs);
e673f011 977 foreach my $db (@$databases)
978 {
979 $sqlt->reset();
980 $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
981# $sqlt->parser_args({'DBIx::Class' => $schema);
982 $sqlt->data($schema);
983 $sqlt->producer($db);
984
985 my $file;
986 my $filename = $schema->ddl_filename($db, $dir, $version);
987 if(-e $filename)
988 {
989 $self->throw_exception("$filename already exists, skipping $db");
990 next;
991 }
992 open($file, ">$filename")
993 or $self->throw_exception("Can't open $filename for writing ($!)");
994 my $output = $sqlt->translate;
995#use Data::Dumper;
996# print join(":", keys %{$schema->source_registrations});
997# print Dumper($sqlt->schema);
998 if(!$output)
999 {
1000 $self->throw_exception("Failed to translate to $db. (" . $sqlt->error . ")");
1001 next;
1002 }
1003 print $file $output;
1004 close($file);
1005 }
1006
1007}
1008
9b83fccd 1009=head2 deployment_statements
1010
1011Create the statements for L</deploy> and
1012L<DBIx::Class::Schema/deploy>.
1013
1014=cut
1015
e673f011 1016sub deployment_statements {
1017 my ($self, $schema, $type, $version, $dir, $sqltargs) = @_;
915919c5 1018 # Need to be connected to get the correct sqlt_type
c377d939 1019 $self->ensure_connected() unless $type;
e673f011 1020 $type ||= $self->sqlt_type;
1021 $version ||= $schema->VERSION || '1.x';
1022 $dir ||= './';
0382d607 1023 eval "use SQL::Translator";
1024 if(!$@)
1025 {
1026 eval "use SQL::Translator::Parser::DBIx::Class;";
1027 $self->throw_exception($@) if $@;
1028 eval "use SQL::Translator::Producer::${type};";
1029 $self->throw_exception($@) if $@;
1030 my $tr = SQL::Translator->new(%$sqltargs);
1031 SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema );
1032 return "SQL::Translator::Producer::${type}"->can('produce')->($tr);
1033 }
e673f011 1034
1035 my $filename = $schema->ddl_filename($type, $dir, $version);
1036 if(!-f $filename)
1037 {
0382d607 1038# $schema->create_ddl_dir([ $type ], $version, $dir, $sqltargs);
1039 $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy");
1040 return;
e673f011 1041 }
1042 my $file;
1043 open($file, "<$filename")
1044 or $self->throw_exception("Can't open $filename ($!)");
1045 my @rows = <$file>;
1046 close($file);
1047
1048 return join('', @rows);
1049
1c339d71 1050}
843f8ecd 1051
1c339d71 1052sub deploy {
cb561d1a 1053 my ($self, $schema, $type, $sqltargs) = @_;
9e7b9292 1054 foreach my $statement ( $self->deployment_statements($schema, $type, undef, undef, { no_comments => 1, %{ $sqltargs || {} } } ) ) {
e4fe9ba3 1055 for ( split(";\n", $statement)) {
e673f011 1056 next if($_ =~ /^--/);
1057 next if(!$_);
1058# next if($_ =~ /^DROP/m);
1059 next if($_ =~ /^BEGIN TRANSACTION/m);
1060 next if($_ =~ /^COMMIT/m);
b489f68a 1061 next if $_ =~ /^\s+$/; # skip whitespace only
bdea30e3 1062 $self->debugobj->query_start($_) if $self->debug;
f11383c2 1063 $self->dbh->do($_) or warn "SQL was:\n $_"; # XXX exceptions?
4c248161 1064 $self->debugobj->query_end($_) if $self->debug;
e4fe9ba3 1065 }
75d07914 1066 }
1c339d71 1067}
843f8ecd 1068
9b83fccd 1069=head2 datetime_parser
1070
1071Returns the datetime parser class
1072
1073=cut
1074
f86fcf0d 1075sub datetime_parser {
1076 my $self = shift;
1077 return $self->{datetime_parser} ||= $self->build_datetime_parser(@_);
1078}
1079
9b83fccd 1080=head2 datetime_parser_type
1081
1082Defines (returns) the datetime parser class - currently hardwired to
1083L<DateTime::Format::MySQL>
1084
1085=cut
1086
f86fcf0d 1087sub datetime_parser_type { "DateTime::Format::MySQL"; }
1088
9b83fccd 1089=head2 build_datetime_parser
1090
1091See L</datetime_parser>
1092
1093=cut
1094
f86fcf0d 1095sub build_datetime_parser {
1096 my $self = shift;
1097 my $type = $self->datetime_parser_type(@_);
1098 eval "use ${type}";
1099 $self->throw_exception("Couldn't load ${type}: $@") if $@;
1100 return $type;
1101}
1102
c756145c 1103sub DESTROY {
1104 my $self = shift;
f5de3933 1105 return if !$self->_dbh;
c756145c 1106
1107 $self->_verify_pid;
1108 $self->_dbh(undef);
1109}
92925617 1110
8b445e33 11111;
1112
9b83fccd 1113=head1 SQL METHODS
1114
1115The module defines a set of methods within the DBIC::SQL::Abstract
1116namespace. These build on L<SQL::Abstract::Limit> to provide the
1117SQL query functions.
1118
1119The following methods are extended:-
1120
1121=over 4
1122
1123=item delete
1124
1125=item insert
1126
1127=item select
1128
1129=item update
1130
1131=item limit_dialect
1132
2cc3a7be 1133See L</connect_info> for details.
1134For setting, this method is deprecated in favor of L</connect_info>.
bb4f246d 1135
9b83fccd 1136=item quote_char
1137
2cc3a7be 1138See L</connect_info> for details.
1139For setting, this method is deprecated in favor of L</connect_info>.
bb4f246d 1140
9b83fccd 1141=item name_sep
1142
2cc3a7be 1143See L</connect_info> for details.
1144For setting, this method is deprecated in favor of L</connect_info>.
bb4f246d 1145
9b83fccd 1146=back
1147
8b445e33 1148=head1 AUTHORS
1149
daec44b8 1150Matt S. Trout <mst@shadowcatsystems.co.uk>
8b445e33 1151
9f19b1d6 1152Andy Grundman <andy@hybridized.org>
1153
8b445e33 1154=head1 LICENSE
1155
1156You may distribute this code under the same terms as Perl itself.
1157
1158=cut
1159