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 my ($dbh, $self) = @_;
625 if ($dbh->{AutoCommit}) {
626 $self->debugobj->txn_begin()
634 $self->dbh_do(\&__txn_begin, $self)
635 if $self->{transaction_depth}++ == 0;
639 my ($dbh, $self) = @_;
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()
658 $self->dbh_do(\&__txn_commit, $self);
662 my ($dbh, $self) = @_;
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;
684 eval { $self->dbh_do(\&__txn_rollback, $self) };
687 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
688 $error =~ /$exception_class/ and $self->throw_exception($error);
689 $self->{transaction_depth} = 0; # ensure that a failed rollback
690 $self->throw_exception($error); # resets the transaction depth
695 my ($self, $op, $extra_bind, $ident, @args) = @_;
696 my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
697 unshift(@bind, @$extra_bind) if $extra_bind;
699 my @debug_bind = map { defined $_ ? qq{'$_'} : q{'NULL'} } @bind;
700 $self->debugobj->query_start($sql, @debug_bind);
702 my $sth = eval { $self->sth($sql,$op) };
705 $self->throw_exception(
706 'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"
709 @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
713 $rv = eval { $sth->execute(@bind) };
716 $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr));
719 $self->throw_exception("'$sql' did not generate a statement.");
722 my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind;
723 $self->debugobj->query_end($sql, @debug_bind);
725 return (wantarray ? ($rv, $sth, @bind) : $rv);
729 my ($self, $ident, $to_insert) = @_;
730 $self->throw_exception(
731 "Couldn't insert ".join(', ',
732 map "$_ => $to_insert->{$_}", keys %$to_insert
734 ) unless ($self->_execute('insert' => [], $ident, $to_insert));
739 return shift->_execute('update' => [], @_);
743 return shift->_execute('delete' => [], @_);
747 my ($self, $ident, $select, $condition, $attrs) = @_;
748 my $order = $attrs->{order_by};
749 if (ref $condition eq 'SCALAR') {
750 $order = $1 if $$condition =~ s/ORDER BY (.*)$//i;
752 if (exists $attrs->{group_by} || $attrs->{having}) {
754 group_by => $attrs->{group_by},
755 having => $attrs->{having},
756 ($order ? (order_by => $order) : ())
759 my @args = ('select', $attrs->{bind}, $ident, $select, $condition, $order);
760 if ($attrs->{software_limit} ||
761 $self->sql_maker->_default_limit_syntax eq "GenericSubQ") {
762 $attrs->{software_limit} = 1;
764 $self->throw_exception("rows attribute must be positive if present")
765 if (defined($attrs->{rows}) && !($attrs->{rows} > 0));
766 push @args, $attrs->{rows}, $attrs->{offset};
768 return $self->_execute(@args);
773 my ($ident, $select, $condition, $attrs) = @_;
774 return $self->cursor->new($self, \@_, $attrs);
779 my ($rv, $sth, @bind) = $self->_select(@_);
780 my @row = $sth->fetchrow_array;
781 # Need to call finish() to work round broken DBDs
788 Returns a L<DBI> sth (statement handle) for the supplied SQL.
793 my ($dbh, $sql) = @_;
794 # 3 is the if_active parameter which avoids active sth re-use
795 $dbh->prepare_cached($sql, {}, 3);
799 my ($self, $sql) = @_;
800 $self->dbh_do(\&__sth, $sql);
804 sub __columns_info_for {
805 my ($dbh, $self, $table) = @_;
807 if ($dbh->can('column_info')) {
810 my ($schema,$tab) = $table =~ /^(.+?)\.(.+)$/ ? ($1,$2) : (undef,$table);
811 my $sth = $dbh->column_info( undef,$schema, $tab, '%' );
813 while ( my $info = $sth->fetchrow_hashref() ){
815 $column_info{data_type} = $info->{TYPE_NAME};
816 $column_info{size} = $info->{COLUMN_SIZE};
817 $column_info{is_nullable} = $info->{NULLABLE} ? 1 : 0;
818 $column_info{default_value} = $info->{COLUMN_DEF};
819 my $col_name = $info->{COLUMN_NAME};
820 $col_name =~ s/^\"(.*)\"$/$1/;
822 $result{$col_name} = \%column_info;
825 return \%result if !$@;
829 my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0");
831 my @columns = @{$sth->{NAME_lc}};
832 for my $i ( 0 .. $#columns ){
834 my $type_num = $sth->{TYPE}->[$i];
836 if(defined $type_num && $dbh->can('type_info')) {
837 my $type_info = $dbh->type_info($type_num);
838 $type_name = $type_info->{TYPE_NAME} if $type_info;
840 $column_info{data_type} = $type_name ? $type_name : $type_num;
841 $column_info{size} = $sth->{PRECISION}->[$i];
842 $column_info{is_nullable} = $sth->{NULLABLE}->[$i] ? 1 : 0;
844 if ($column_info{data_type} =~ m/^(.*?)\((.*?)\)$/) {
845 $column_info{data_type} = $1;
846 $column_info{size} = $2;
849 $result{$columns[$i]} = \%column_info;
855 sub columns_info_for {
856 my ($self, $table) = @_;
857 $self->dbh_do(\&__columns_info_for, $self, $table);
860 =head2 last_insert_id
862 Return the row id of the last insert.
867 my ($self, $row) = @_;
869 $self->dbh_do(sub { shift->func('last_insert_rowid') });
874 Returns the database driver name.
878 sub sqlt_type { shift->dbh_do(sub { shift->{Driver}->{Name} }) }
880 =head2 create_ddl_dir (EXPERIMENTAL)
884 =item Arguments: $schema \@databases, $version, $directory, $sqlt_args
888 Creates an SQL file based on the Schema, for each of the specified
889 database types, in the given directory.
891 Note that this feature is currently EXPERIMENTAL and may not work correctly
892 across all databases, or fully handle complex relationships.
898 my ($self, $schema, $databases, $version, $dir, $sqltargs) = @_;
900 if(!$dir || !-d $dir)
902 warn "No directory given, using ./\n";
905 $databases ||= ['MySQL', 'SQLite', 'PostgreSQL'];
906 $databases = [ $databases ] if(ref($databases) ne 'ARRAY');
907 $version ||= $schema->VERSION || '1.x';
908 $sqltargs = { ( add_drop_table => 1 ), %{$sqltargs || {}} };
910 eval "use SQL::Translator";
911 $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@;
913 my $sqlt = SQL::Translator->new($sqltargs);
914 foreach my $db (@$databases)
917 $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
918 # $sqlt->parser_args({'DBIx::Class' => $schema);
919 $sqlt->data($schema);
920 $sqlt->producer($db);
923 my $filename = $schema->ddl_filename($db, $dir, $version);
926 $self->throw_exception("$filename already exists, skipping $db");
929 open($file, ">$filename")
930 or $self->throw_exception("Can't open $filename for writing ($!)");
931 my $output = $sqlt->translate;
933 # print join(":", keys %{$schema->source_registrations});
934 # print Dumper($sqlt->schema);
937 $self->throw_exception("Failed to translate to $db. (" . $sqlt->error . ")");
946 =head2 deployment_statements
948 Create the statements for L</deploy> and
949 L<DBIx::Class::Schema/deploy>.
953 sub deployment_statements {
954 my ($self, $schema, $type, $version, $dir, $sqltargs) = @_;
955 # Need to be connected to get the correct sqlt_type
956 $self->ensure_connected() unless $type;
957 $type ||= $self->sqlt_type;
958 $version ||= $schema->VERSION || '1.x';
960 eval "use SQL::Translator";
963 eval "use SQL::Translator::Parser::DBIx::Class;";
964 $self->throw_exception($@) if $@;
965 eval "use SQL::Translator::Producer::${type};";
966 $self->throw_exception($@) if $@;
967 my $tr = SQL::Translator->new(%$sqltargs);
968 SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema );
969 return "SQL::Translator::Producer::${type}"->can('produce')->($tr);
972 my $filename = $schema->ddl_filename($type, $dir, $version);
975 # $schema->create_ddl_dir([ $type ], $version, $dir, $sqltargs);
976 $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy");
980 open($file, "<$filename")
981 or $self->throw_exception("Can't open $filename ($!)");
985 return join('', @rows);
990 my ($self, $schema, $type, $sqltargs) = @_;
991 foreach my $statement ( $self->deployment_statements($schema, $type, undef, undef, { no_comments => 1, %{ $sqltargs || {} } } ) ) {
992 for ( split(";\n", $statement)) {
993 next if($_ =~ /^--/);
995 # next if($_ =~ /^DROP/m);
996 next if($_ =~ /^BEGIN TRANSACTION/m);
997 next if($_ =~ /^COMMIT/m);
998 next if $_ =~ /^\s+$/; # skip whitespace only
999 $self->debugobj->query_start($_) if $self->debug;
1000 $self->dbh->do($_) or warn "SQL was:\n $_"; # XXX exceptions?
1001 $self->debugobj->query_end($_) if $self->debug;
1006 =head2 datetime_parser
1008 Returns the datetime parser class
1012 sub datetime_parser {
1014 return $self->{datetime_parser} ||= $self->build_datetime_parser(@_);
1017 =head2 datetime_parser_type
1019 Defines (returns) the datetime parser class - currently hardwired to
1020 L<DateTime::Format::MySQL>
1024 sub datetime_parser_type { "DateTime::Format::MySQL"; }
1026 =head2 build_datetime_parser
1028 See L</datetime_parser>
1032 sub build_datetime_parser {
1034 my $type = $self->datetime_parser_type(@_);
1036 $self->throw_exception("Couldn't load ${type}: $@") if $@;
1042 return if !$self->_dbh;
1052 The module defines a set of methods within the DBIC::SQL::Abstract
1053 namespace. These build on L<SQL::Abstract::Limit> to provide the
1054 SQL query functions.
1056 The following methods are extended:-
1070 See L</connect_info> for details.
1071 For setting, this method is deprecated in favor of L</connect_info>.
1075 See L</connect_info> for details.
1076 For setting, this method is deprecated in favor of L</connect_info>.
1080 See L</connect_info> for details.
1081 For setting, this method is deprecated in favor of L</connect_info>.
1087 Matt S. Trout <mst@shadowcatsystems.co.uk>
1089 Andy Grundman <andy@hybridized.org>
1093 You may distribute this code under the same terms as Perl itself.