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 Handle a SQL select statement.
777 my ($ident, $select, $condition, $attrs) = @_;
778 return $self->cursor->new($self, \@_, $attrs);
783 Performs a select, fetch and return of data - handles a single row
788 # Need to call finish() to work round broken DBDs
792 my ($rv, $sth, @bind) = $self->_select(@_);
793 my @row = $sth->fetchrow_array;
800 Returns a L<DBI> sth (statement handle) for the supplied SQL.
805 my ($self, $sql) = @_;
806 # 3 is the if_active parameter which avoids active sth re-use
807 return $self->dbh_do(sub { shift->prepare_cached($sql, {}, 3) });
810 =head2 columns_info_for
812 Returns database type info for a given table columns.
816 sub columns_info_for {
817 my ($self, $table) = @_;
822 if ($dbh->can('column_info')) {
825 my ($schema,$tab) = $table =~ /^(.+?)\.(.+)$/ ? ($1,$2) : (undef,$table);
826 my $sth = $dbh->column_info( undef,$schema, $tab, '%' );
828 while ( my $info = $sth->fetchrow_hashref() ){
830 $column_info{data_type} = $info->{TYPE_NAME};
831 $column_info{size} = $info->{COLUMN_SIZE};
832 $column_info{is_nullable} = $info->{NULLABLE} ? 1 : 0;
833 $column_info{default_value} = $info->{COLUMN_DEF};
834 my $col_name = $info->{COLUMN_NAME};
835 $col_name =~ s/^\"(.*)\"$/$1/;
837 $result{$col_name} = \%column_info;
840 return \%result if !$@;
844 my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0");
846 my @columns = @{$sth->{NAME_lc}};
847 for my $i ( 0 .. $#columns ){
849 my $type_num = $sth->{TYPE}->[$i];
851 if(defined $type_num && $dbh->can('type_info')) {
852 my $type_info = $dbh->type_info($type_num);
853 $type_name = $type_info->{TYPE_NAME} if $type_info;
855 $column_info{data_type} = $type_name ? $type_name : $type_num;
856 $column_info{size} = $sth->{PRECISION}->[$i];
857 $column_info{is_nullable} = $sth->{NULLABLE}->[$i] ? 1 : 0;
859 if ($column_info{data_type} =~ m/^(.*?)\((.*?)\)$/) {
860 $column_info{data_type} = $1;
861 $column_info{size} = $2;
864 $result{$columns[$i]} = \%column_info;
871 =head2 last_insert_id
873 Return the row id of the last insert.
878 my ($self, $row) = @_;
880 $self->dbh_do(sub { shift->func('last_insert_rowid') });
885 Returns the database driver name.
889 sub sqlt_type { shift->dbh_do(sub { shift->{Driver}->{Name} }) }
891 =head2 create_ddl_dir (EXPERIMENTAL)
895 =item Arguments: $schema \@databases, $version, $directory, $sqlt_args
899 Creates an SQL file based on the Schema, for each of the specified
900 database types, in the given directory.
902 Note that this feature is currently EXPERIMENTAL and may not work correctly
903 across all databases, or fully handle complex relationships.
909 my ($self, $schema, $databases, $version, $dir, $sqltargs) = @_;
911 if(!$dir || !-d $dir)
913 warn "No directory given, using ./\n";
916 $databases ||= ['MySQL', 'SQLite', 'PostgreSQL'];
917 $databases = [ $databases ] if(ref($databases) ne 'ARRAY');
918 $version ||= $schema->VERSION || '1.x';
919 $sqltargs = { ( add_drop_table => 1 ), %{$sqltargs || {}} };
921 eval "use SQL::Translator";
922 $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@;
924 my $sqlt = SQL::Translator->new($sqltargs);
925 foreach my $db (@$databases)
928 $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
929 # $sqlt->parser_args({'DBIx::Class' => $schema);
930 $sqlt->data($schema);
931 $sqlt->producer($db);
934 my $filename = $schema->ddl_filename($db, $dir, $version);
937 $self->throw_exception("$filename already exists, skipping $db");
940 open($file, ">$filename")
941 or $self->throw_exception("Can't open $filename for writing ($!)");
942 my $output = $sqlt->translate;
944 # print join(":", keys %{$schema->source_registrations});
945 # print Dumper($sqlt->schema);
948 $self->throw_exception("Failed to translate to $db. (" . $sqlt->error . ")");
957 =head2 deployment_statements
959 Create the statements for L</deploy> and
960 L<DBIx::Class::Schema/deploy>.
964 sub deployment_statements {
965 my ($self, $schema, $type, $version, $dir, $sqltargs) = @_;
966 # Need to be connected to get the correct sqlt_type
967 $self->ensure_connected() unless $type;
968 $type ||= $self->sqlt_type;
969 $version ||= $schema->VERSION || '1.x';
971 eval "use SQL::Translator";
974 eval "use SQL::Translator::Parser::DBIx::Class;";
975 $self->throw_exception($@) if $@;
976 eval "use SQL::Translator::Producer::${type};";
977 $self->throw_exception($@) if $@;
978 my $tr = SQL::Translator->new(%$sqltargs);
979 SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema );
980 return "SQL::Translator::Producer::${type}"->can('produce')->($tr);
983 my $filename = $schema->ddl_filename($type, $dir, $version);
986 # $schema->create_ddl_dir([ $type ], $version, $dir, $sqltargs);
987 $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy");
991 open($file, "<$filename")
992 or $self->throw_exception("Can't open $filename ($!)");
996 return join('', @rows);
1001 my ($self, $schema, $type, $sqltargs) = @_;
1002 foreach my $statement ( $self->deployment_statements($schema, $type, undef, undef, { no_comments => 1, %{ $sqltargs || {} } } ) ) {
1003 for ( split(";\n", $statement)) {
1004 next if($_ =~ /^--/);
1006 # next if($_ =~ /^DROP/m);
1007 next if($_ =~ /^BEGIN TRANSACTION/m);
1008 next if($_ =~ /^COMMIT/m);
1009 next if $_ =~ /^\s+$/; # skip whitespace only
1010 $self->debugobj->query_start($_) if $self->debug;
1011 $self->dbh->do($_) or warn "SQL was:\n $_"; # XXX exceptions?
1012 $self->debugobj->query_end($_) if $self->debug;
1017 =head2 datetime_parser
1019 Returns the datetime parser class
1023 sub datetime_parser {
1025 return $self->{datetime_parser} ||= $self->build_datetime_parser(@_);
1028 =head2 datetime_parser_type
1030 Defines (returns) the datetime parser class - currently hardwired to
1031 L<DateTime::Format::MySQL>
1035 sub datetime_parser_type { "DateTime::Format::MySQL"; }
1037 =head2 build_datetime_parser
1039 See L</datetime_parser>
1043 sub build_datetime_parser {
1045 my $type = $self->datetime_parser_type(@_);
1047 $self->throw_exception("Couldn't load ${type}: $@") if $@;
1053 return if !$self->_dbh;
1063 The module defines a set of methods within the DBIC::SQL::Abstract
1064 namespace. These build on L<SQL::Abstract::Limit> to provide the
1065 SQL query functions.
1067 The following methods are extended:-
1081 See L</connect_info> for details.
1082 For setting, this method is deprecated in favor of L</connect_info>.
1086 See L</connect_info> for details.
1087 For setting, this method is deprecated in favor of L</connect_info>.
1091 See L</connect_info> for details.
1092 For setting, this method is deprecated in favor of L</connect_info>.
1098 Matt S. Trout <mst@shadowcatsystems.co.uk>
1100 Andy Grundman <andy@hybridized.org>
1104 You may distribute this code under the same terms as Perl itself.