1 package DBIx::Class::Storage::DBI;
2 # -*- mode: cperl; cperl-indent-level: 2 -*-
4 use base 'DBIx::Class::Storage';
9 use SQL::Abstract::Limit;
10 use DBIx::Class::Storage::DBI::Cursor;
11 use DBIx::Class::Storage::Statistics;
13 use Carp::Clan qw/DBIx::Class/;
15 __PACKAGE__->mk_group_accessors(
17 qw/_connect_info _dbh _sql_maker _sql_maker_opts _conn_pid _conn_tid
18 cursor on_connect_do transaction_depth/
23 package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :(
25 use base qw/SQL::Abstract::Limit/;
27 # This prevents the caching of $dbh in S::A::L, I believe
29 my $self = shift->SUPER::new(@_);
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};
39 # While we're at it, this should make LIMIT queries more efficient,
40 # without digging into things too deeply
42 my ($self, $syntax) = @_;
43 $self->{_cached_syntax} ||= $self->SUPER::_find_syntax($syntax);
47 my ($self, $table, $fields, $where, $order, @rest) = @_;
48 $table = $self->_quote($table) unless ref($table);
49 local $self->{rownum_hack_count} = 1
50 if (defined $rest[0] && $self->{limit_dialect} eq 'RowNum');
51 @rest = (-1) unless defined $rest[0];
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
54 local $self->{having_bind} = [];
55 my ($sql, @ret) = $self->SUPER::select(
56 $table, $self->_recurse_fields($fields), $where, $order, @rest
58 return wantarray ? ($sql, @ret, @{$self->{having_bind}}) : $sql;
64 $table = $self->_quote($table) unless ref($table);
65 $self->SUPER::insert($table, @_);
71 $table = $self->_quote($table) unless ref($table);
72 $self->SUPER::update($table, @_);
78 $table = $self->_quote($table) unless ref($table);
79 $self->SUPER::delete($table, @_);
85 return $_[1].$self->_order_by($_[2]);
87 return $self->SUPER::_emulate_limit(@_);
92 my ($self, $fields) = @_;
93 my $ref = ref $fields;
94 return $self->_quote($fields) unless $ref;
95 return $$fields if $ref eq 'SCALAR';
97 if ($ref eq 'ARRAY') {
98 return join(', ', map {
99 $self->_recurse_fields($_)
100 .(exists $self->{rownum_hack_count}
101 ? ' AS col'.$self->{rownum_hack_count}++
104 } elsif ($ref eq 'HASH') {
105 foreach my $func (keys %$fields) {
106 return $self->_sqlcase($func)
107 .'( '.$self->_recurse_fields($fields->{$func}).' )';
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});
121 if (defined $_[0]->{having}) {
123 ($frag, @extra) = $self->_recurse_where($_[0]->{having});
124 push(@{$self->{having_bind}}, @extra);
125 $ret .= $self->_sqlcase(' having ').$frag;
127 if (defined $_[0]->{order_by}) {
128 $ret .= $self->_order_by($_[0]->{order_by});
130 } elsif (ref $_[0] eq 'SCALAR') {
131 $ret = $self->_sqlcase(' order by ').${ $_[0] };
132 } elsif (ref $_[0] eq 'ARRAY' && @{$_[0]}) {
133 my @order = @{+shift};
134 $ret = $self->_sqlcase(' order by ')
136 my $r = $self->_order_by($_, @_);
137 $r =~ s/^ ?ORDER BY //i;
141 $ret = $self->SUPER::_order_by(@_);
146 sub _order_directions {
147 my ($self, $order) = @_;
148 $order = $order->{order_by} if ref $order eq 'HASH';
149 return $self->SUPER::_order_directions($order);
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);
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
169 my ($self, $from, @join) = @_;
171 push(@sqlf, $self->_make_as($from));
172 foreach my $j (@join) {
175 # check whether a join type exists
176 my $join_clause = '';
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 ';
181 $join_clause = ' JOIN ';
183 push(@sqlf, $join_clause);
185 if (ref $to eq 'ARRAY') {
186 push(@sqlf, '(', $self->_recurse_from(@$to), ')');
188 push(@sqlf, $self->_make_as($to));
190 push(@sqlf, ' ON ', $self->_join_condition($on));
192 return join('', @sqlf);
196 my ($self, $from) = @_;
197 return join(' ', map { (ref $_ eq 'SCALAR' ? $$_ : $self->_quote($_)) }
198 reverse each %{$self->_skip_options($from)});
202 my ($self, $hash) = @_;
204 $clean_hash->{$_} = $hash->{$_}
205 for grep {!/^-/} keys %$hash;
209 sub _join_condition {
210 my ($self, $cond) = @_;
211 if (ref $cond eq 'HASH') {
214 my $x = '= '.$self->_quote($cond->{$_}); $j{$_} = \$x;
216 return $self->_recurse_where(\%j);
217 } elsif (ref $cond eq 'ARRAY') {
218 return join(' OR ', map { $self->_join_condition($_) } @$cond);
220 die "Can't handle this yet!";
225 my ($self, $label) = @_;
226 return '' unless defined $label;
227 return "*" if $label eq '*';
228 return $label unless $self->{quote_char};
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));
237 return $self->SUPER::_quote($label);
242 $self->{limit_dialect} = shift if @_;
243 return $self->{limit_dialect};
248 $self->{quote_char} = shift if @_;
249 return $self->{quote_char};
254 $self->{name_sep} = shift if @_;
255 return $self->{name_sep};
258 } # End of BEGIN block
262 DBIx::Class::Storage::DBI - DBI storage handler
268 This class represents the connection to an RDBMS via L<DBI>. See
269 L<DBIx::Class::Storage> for general information. This pod only
270 documents DBI-specific methods and behaviors.
277 my $new = shift->next::method(@_);
279 $new->cursor("DBIx::Class::Storage::DBI::Cursor");
280 $new->transaction_depth(0);
281 $new->_sql_maker_opts({});
288 The arguments of C<connect_info> are always a single array reference.
290 This is normally accessed via L<DBIx::Class::Schema/connection>, which
291 encapsulates its argument list in an arrayref before calling
292 C<connect_info> here.
294 The arrayref can either contain the same set of arguments one would
295 normally pass to L<DBI/connect>, or a lone code reference which returns
296 a connected database handle.
298 In either case, if the final argument in your connect_info happens
299 to be a hashref, C<connect_info> will look there for several
300 connection-specific options:
306 This can be set to an arrayref of literal sql statements, which will
307 be executed immediately after making the connection to the database
308 every time we [re-]connect.
312 Sets the limit dialect. This is useful for JDBC-bridge among others
313 where the remote SQL-dialect cannot be determined by the name of the
318 Specifies what characters to use to quote table and column names. If
319 you use this you will want to specify L<name_sep> as well.
321 quote_char expects either a single character, in which case is it is placed
322 on either side of the table/column, or an arrayref of length 2 in which case the
323 table/column name is placed between the elements.
325 For example under MySQL you'd use C<quote_char =E<gt> '`'>, and user SQL Server you'd
326 use C<quote_char =E<gt> [qw/[ ]/]>.
330 This only needs to be used in conjunction with L<quote_char>, and is used to
331 specify the charecter that seperates elements (schemas, tables, columns) from
332 each other. In most cases this is simply a C<.>.
336 These options can be mixed in with your other L<DBI> connection attributes,
337 or placed in a seperate hashref after all other normal L<DBI> connection
340 Every time C<connect_info> is invoked, any previous settings for
341 these options will be cleared before setting the new ones, regardless of
342 whether any options are specified in the new C<connect_info>.
344 Important note: DBIC expects the returned database handle provided by
345 a subref argument to have RaiseError set on it. If it doesn't, things
346 might not work very well, YMMV. If you don't use a subref, DBIC will
347 force this setting for you anyways. Setting HandleError to anything
348 other than simple exception object wrapper might cause problems too.
352 # Simple SQLite connection
353 ->connect_info([ 'dbi:SQLite:./foo.db' ]);
356 ->connect_info([ sub { DBI->connect(...) } ]);
358 # A bit more complicated
365 { quote_char => q{"}, name_sep => q{.} },
369 # Equivalent to the previous example
375 { AutoCommit => 0, quote_char => q{"}, name_sep => q{.} },
379 # Subref + DBIC-specific connection options
382 sub { DBI->connect(...) },
386 on_connect_do => ['SET search_path TO myschema,otherschema,public'],
394 my ($self, $info_arg) = @_;
396 return $self->_connect_info if !$info_arg;
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({});
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);
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;
415 # Get rid of any trailing empty hashref
416 pop(@$info) if !keys %$last_info;
419 $self->_connect_info($info);
424 This method is deprecated in favor of setting via L</connect_info>.
428 Arguments: $subref, @extra_coderef_args?
430 Execute the given subref with the underlying database handle as its
431 first argument, using the new exception-based connection management.
433 Any additional arguments will be passed verbatim to the called subref
434 as arguments 2 and onwards.
438 my @stuff = $schema->storage->dbh_do(
441 my $cols = join(q{, }, @_);
442 shift->selectrow_array("SELECT $cols FROM foo")
454 my $want_array = wantarray;
457 $self->_verify_pid if $self->_dbh;
458 $self->_populate_dbh if !$self->_dbh;
459 my $dbh = $self->_dbh;
461 @result = $todo->($dbh, @_);
463 elsif(defined $want_array) {
464 $result[0] = $todo->($dbh, @_);
474 ? $self->throw_exception($exception)
475 : $self->_populate_dbh;
477 my $dbh = $self->_dbh;
478 return $todo->($dbh, @_);
481 return $want_array ? @result : $result[0];
486 Our C<disconnect> method also performs a rollback first if the
487 database is not in C<AutoCommit> mode.
494 if( $self->connected ) {
495 $self->_dbh->rollback unless $self->_dbh->{AutoCommit};
496 $self->_dbh->disconnect;
504 if(my $dbh = $self->_dbh) {
505 if(defined $self->_conn_tid && $self->_conn_tid != threads->tid) {
506 return $self->_dbh(undef);
511 return ($dbh->FETCH('Active') && $dbh->ping);
517 # handle pid changes correctly
518 # NOTE: assumes $self->_dbh is a valid $dbh
522 return if $self->_conn_pid == $$;
524 $self->_dbh->{InactiveDestroy} = 1;
530 sub ensure_connected {
533 unless ($self->connected) {
534 $self->_populate_dbh;
540 Returns the dbh - a data base handle of class L<DBI>.
547 $self->ensure_connected;
551 sub _sql_maker_args {
554 return ( limit_dialect => $self->dbh, %{$self->_sql_maker_opts} );
559 unless ($self->_sql_maker) {
560 $self->_sql_maker(new DBIC::SQL::Abstract( $self->_sql_maker_args ));
562 return $self->_sql_maker;
567 my @info = @{$self->_connect_info || []};
568 $self->_dbh($self->_connect(@info));
570 if(ref $self eq 'DBIx::Class::Storage::DBI') {
571 my $driver = $self->_dbh->{Driver}->{Name};
572 if ($self->load_optional_class("DBIx::Class::Storage::DBI::${driver}")) {
573 bless $self, "DBIx::Class::Storage::DBI::${driver}";
574 $self->_rebless() if $self->can('_rebless');
578 # if on-connect sql statements are given execute them
579 foreach my $sql_statement (@{$self->on_connect_do || []}) {
580 $self->debugobj->query_start($sql_statement) if $self->debug();
581 $self->_dbh->do($sql_statement);
582 $self->debugobj->query_end($sql_statement) if $self->debug();
585 $self->_conn_pid($$);
586 $self->_conn_tid(threads->tid) if $INC{'threads.pm'};
590 my ($self, @info) = @_;
592 $self->throw_exception("You failed to provide any connection info")
595 my ($old_connect_via, $dbh);
597 if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
598 $old_connect_via = $DBI::connect_via;
599 $DBI::connect_via = 'connect';
603 if(ref $info[0] eq 'CODE') {
607 $dbh = DBI->connect(@info);
608 $dbh->{RaiseError} = 1;
609 $dbh->{PrintError} = 0;
613 $DBI::connect_via = $old_connect_via if $old_connect_via;
616 $self->throw_exception("DBI Connection failed: " . ($@ || $DBI::errstr));
624 if ($self->{transaction_depth}++ == 0) {
627 if ($dbh->{AutoCommit}) {
628 $self->debugobj->txn_begin()
640 if ($self->{transaction_depth} == 0) {
641 unless ($dbh->{AutoCommit}) {
642 $self->debugobj->txn_commit()
648 if (--$self->{transaction_depth} == 0) {
649 $self->debugobj->txn_commit()
663 if ($self->{transaction_depth} == 0) {
664 unless ($dbh->{AutoCommit}) {
665 $self->debugobj->txn_rollback()
671 if (--$self->{transaction_depth} == 0) {
672 $self->debugobj->txn_rollback()
677 die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new;
685 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
686 $error =~ /$exception_class/ and $self->throw_exception($error);
687 $self->{transaction_depth} = 0; # ensure that a failed rollback
688 $self->throw_exception($error); # resets the transaction depth
693 my ($self, $op, $extra_bind, $ident, @args) = @_;
694 my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
695 unshift(@bind, @$extra_bind) if $extra_bind;
697 my @debug_bind = map { defined $_ ? qq{'$_'} : q{'NULL'} } @bind;
698 $self->debugobj->query_start($sql, @debug_bind);
700 my $sth = eval { $self->sth($sql,$op) };
703 $self->throw_exception(
704 'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"
707 @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
711 $rv = eval { $sth->execute(@bind) };
714 $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr));
717 $self->throw_exception("'$sql' did not generate a statement.");
720 my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind;
721 $self->debugobj->query_end($sql, @debug_bind);
723 return (wantarray ? ($rv, $sth, @bind) : $rv);
727 my ($self, $ident, $to_insert) = @_;
728 $self->throw_exception(
729 "Couldn't insert ".join(', ',
730 map "$_ => $to_insert->{$_}", keys %$to_insert
732 ) unless ($self->_execute('insert' => [], $ident, $to_insert));
737 return shift->_execute('update' => [], @_);
741 return shift->_execute('delete' => [], @_);
745 my ($self, $ident, $select, $condition, $attrs) = @_;
746 my $order = $attrs->{order_by};
747 if (ref $condition eq 'SCALAR') {
748 $order = $1 if $$condition =~ s/ORDER BY (.*)$//i;
750 if (exists $attrs->{group_by} || $attrs->{having}) {
752 group_by => $attrs->{group_by},
753 having => $attrs->{having},
754 ($order ? (order_by => $order) : ())
757 my @args = ('select', $attrs->{bind}, $ident, $select, $condition, $order);
758 if ($attrs->{software_limit} ||
759 $self->sql_maker->_default_limit_syntax eq "GenericSubQ") {
760 $attrs->{software_limit} = 1;
762 $self->throw_exception("rows attribute must be positive if present")
763 if (defined($attrs->{rows}) && !($attrs->{rows} > 0));
764 push @args, $attrs->{rows}, $attrs->{offset};
766 return $self->_execute(@args);
771 my ($ident, $select, $condition, $attrs) = @_;
772 return $self->cursor->new($self, \@_, $attrs);
777 my ($rv, $sth, @bind) = $self->_select(@_);
778 my @row = $sth->fetchrow_array;
779 # Need to call finish() to work round broken DBDs
786 Returns a L<DBI> sth (statement handle) for the supplied SQL.
791 my ($self, $sql) = @_;
792 # 3 is the if_active parameter which avoids active sth re-use
793 return $self->dbh_do(sub { shift->prepare_cached($sql, {}, 3) });
796 sub columns_info_for {
797 my ($self, $table) = @_;
802 if ($dbh->can('column_info')) {
805 my ($schema,$tab) = $table =~ /^(.+?)\.(.+)$/ ? ($1,$2) : (undef,$table);
806 my $sth = $dbh->column_info( undef,$schema, $tab, '%' );
808 while ( my $info = $sth->fetchrow_hashref() ){
810 $column_info{data_type} = $info->{TYPE_NAME};
811 $column_info{size} = $info->{COLUMN_SIZE};
812 $column_info{is_nullable} = $info->{NULLABLE} ? 1 : 0;
813 $column_info{default_value} = $info->{COLUMN_DEF};
814 my $col_name = $info->{COLUMN_NAME};
815 $col_name =~ s/^\"(.*)\"$/$1/;
817 $result{$col_name} = \%column_info;
820 return \%result if !$@;
824 my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0");
826 my @columns = @{$sth->{NAME_lc}};
827 for my $i ( 0 .. $#columns ){
829 my $type_num = $sth->{TYPE}->[$i];
831 if(defined $type_num && $dbh->can('type_info')) {
832 my $type_info = $dbh->type_info($type_num);
833 $type_name = $type_info->{TYPE_NAME} if $type_info;
835 $column_info{data_type} = $type_name ? $type_name : $type_num;
836 $column_info{size} = $sth->{PRECISION}->[$i];
837 $column_info{is_nullable} = $sth->{NULLABLE}->[$i] ? 1 : 0;
839 if ($column_info{data_type} =~ m/^(.*?)\((.*?)\)$/) {
840 $column_info{data_type} = $1;
841 $column_info{size} = $2;
844 $result{$columns[$i]} = \%column_info;
851 =head2 last_insert_id
853 Return the row id of the last insert.
858 my ($self, $row) = @_;
860 $self->dbh_do(sub { shift->func('last_insert_rowid') });
865 Returns the database driver name.
869 sub sqlt_type { shift->dbh_do(sub { shift->{Driver}->{Name} }) }
871 =head2 create_ddl_dir (EXPERIMENTAL)
875 =item Arguments: $schema \@databases, $version, $directory, $sqlt_args
879 Creates an SQL file based on the Schema, for each of the specified
880 database types, in the given directory.
882 Note that this feature is currently EXPERIMENTAL and may not work correctly
883 across all databases, or fully handle complex relationships.
889 my ($self, $schema, $databases, $version, $dir, $sqltargs) = @_;
891 if(!$dir || !-d $dir)
893 warn "No directory given, using ./\n";
896 $databases ||= ['MySQL', 'SQLite', 'PostgreSQL'];
897 $databases = [ $databases ] if(ref($databases) ne 'ARRAY');
898 $version ||= $schema->VERSION || '1.x';
899 $sqltargs = { ( add_drop_table => 1 ), %{$sqltargs || {}} };
901 eval "use SQL::Translator";
902 $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@;
904 my $sqlt = SQL::Translator->new($sqltargs);
905 foreach my $db (@$databases)
908 $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
909 # $sqlt->parser_args({'DBIx::Class' => $schema);
910 $sqlt->data($schema);
911 $sqlt->producer($db);
914 my $filename = $schema->ddl_filename($db, $dir, $version);
917 $self->throw_exception("$filename already exists, skipping $db");
920 open($file, ">$filename")
921 or $self->throw_exception("Can't open $filename for writing ($!)");
922 my $output = $sqlt->translate;
924 # print join(":", keys %{$schema->source_registrations});
925 # print Dumper($sqlt->schema);
928 $self->throw_exception("Failed to translate to $db. (" . $sqlt->error . ")");
937 =head2 deployment_statements
939 Create the statements for L</deploy> and
940 L<DBIx::Class::Schema/deploy>.
944 sub deployment_statements {
945 my ($self, $schema, $type, $version, $dir, $sqltargs) = @_;
946 # Need to be connected to get the correct sqlt_type
947 $self->ensure_connected() unless $type;
948 $type ||= $self->sqlt_type;
949 $version ||= $schema->VERSION || '1.x';
951 eval "use SQL::Translator";
954 eval "use SQL::Translator::Parser::DBIx::Class;";
955 $self->throw_exception($@) if $@;
956 eval "use SQL::Translator::Producer::${type};";
957 $self->throw_exception($@) if $@;
958 my $tr = SQL::Translator->new(%$sqltargs);
959 SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema );
960 return "SQL::Translator::Producer::${type}"->can('produce')->($tr);
963 my $filename = $schema->ddl_filename($type, $dir, $version);
966 # $schema->create_ddl_dir([ $type ], $version, $dir, $sqltargs);
967 $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy");
971 open($file, "<$filename")
972 or $self->throw_exception("Can't open $filename ($!)");
976 return join('', @rows);
981 my ($self, $schema, $type, $sqltargs) = @_;
982 foreach my $statement ( $self->deployment_statements($schema, $type, undef, undef, { no_comments => 1, %{ $sqltargs || {} } } ) ) {
983 for ( split(";\n", $statement)) {
984 next if($_ =~ /^--/);
986 # next if($_ =~ /^DROP/m);
987 next if($_ =~ /^BEGIN TRANSACTION/m);
988 next if($_ =~ /^COMMIT/m);
989 next if $_ =~ /^\s+$/; # skip whitespace only
990 $self->debugobj->query_start($_) if $self->debug;
991 $self->dbh->do($_) or warn "SQL was:\n $_"; # XXX exceptions?
992 $self->debugobj->query_end($_) if $self->debug;
997 =head2 datetime_parser
999 Returns the datetime parser class
1003 sub datetime_parser {
1005 return $self->{datetime_parser} ||= $self->build_datetime_parser(@_);
1008 =head2 datetime_parser_type
1010 Defines (returns) the datetime parser class - currently hardwired to
1011 L<DateTime::Format::MySQL>
1015 sub datetime_parser_type { "DateTime::Format::MySQL"; }
1017 =head2 build_datetime_parser
1019 See L</datetime_parser>
1023 sub build_datetime_parser {
1025 my $type = $self->datetime_parser_type(@_);
1027 $self->throw_exception("Couldn't load ${type}: $@") if $@;
1033 return if !$self->_dbh;
1043 The module defines a set of methods within the DBIC::SQL::Abstract
1044 namespace. These build on L<SQL::Abstract::Limit> to provide the
1045 SQL query functions.
1047 The following methods are extended:-
1061 See L</connect_info> for details.
1062 For setting, this method is deprecated in favor of L</connect_info>.
1066 See L</connect_info> for details.
1067 For setting, this method is deprecated in favor of L</connect_info>.
1071 See L</connect_info> for details.
1072 For setting, this method is deprecated in favor of L</connect_info>.
1078 Matt S. Trout <mst@shadowcatsystems.co.uk>
1080 Andy Grundman <andy@hybridized.org>
1084 You may distribute this code under the same terms as Perl itself.