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/;
16 package DBIC::SQL::Abstract; # Would merge upstream, but nate doesn't reply :(
18 use base qw/SQL::Abstract::Limit/;
21 my ($self, $table, $fields, $where, $order, @rest) = @_;
22 $table = $self->_quote($table) unless ref($table);
23 @rest = (-1) unless defined $rest[0];
24 die "LIMIT 0 Does Not Compute" if $rest[0] == 0;
25 # and anyway, SQL::Abstract::Limit will cause a barf if we don't first
26 local $self->{having_bind} = [];
27 my ($sql, @ret) = $self->SUPER::select(
28 $table, $self->_recurse_fields($fields), $where, $order, @rest
30 return wantarray ? ($sql, @ret, @{$self->{having_bind}}) : $sql;
36 $table = $self->_quote($table) unless ref($table);
37 $self->SUPER::insert($table, @_);
43 $table = $self->_quote($table) unless ref($table);
44 $self->SUPER::update($table, @_);
50 $table = $self->_quote($table) unless ref($table);
51 $self->SUPER::delete($table, @_);
57 return $_[1].$self->_order_by($_[2]);
59 return $self->SUPER::_emulate_limit(@_);
64 my ($self, $fields) = @_;
65 my $ref = ref $fields;
66 return $self->_quote($fields) unless $ref;
67 return $$fields if $ref eq 'SCALAR';
69 if ($ref eq 'ARRAY') {
70 return join(', ', map { $self->_recurse_fields($_) } @$fields);
71 } elsif ($ref eq 'HASH') {
72 foreach my $func (keys %$fields) {
73 return $self->_sqlcase($func)
74 .'( '.$self->_recurse_fields($fields->{$func}).' )';
83 if (ref $_[0] eq 'HASH') {
84 if (defined $_[0]->{group_by}) {
85 $ret = $self->_sqlcase(' group by ')
86 .$self->_recurse_fields($_[0]->{group_by});
88 if (defined $_[0]->{having}) {
90 ($frag, @extra) = $self->_recurse_where($_[0]->{having});
91 push(@{$self->{having_bind}}, @extra);
92 $ret .= $self->_sqlcase(' having ').$frag;
94 if (defined $_[0]->{order_by}) {
95 $ret .= $self->SUPER::_order_by($_[0]->{order_by});
97 } elsif(ref $_[0] eq 'SCALAR') {
98 $ret = $self->_sqlcase(' order by ').${ $_[0] };
100 $ret = $self->SUPER::_order_by(@_);
105 sub _order_directions {
106 my ($self, $order) = @_;
107 $order = $order->{order_by} if ref $order eq 'HASH';
108 return $self->SUPER::_order_directions($order);
112 my ($self, $from) = @_;
113 if (ref $from eq 'ARRAY') {
114 return $self->_recurse_from(@$from);
115 } elsif (ref $from eq 'HASH') {
116 return $self->_make_as($from);
118 return $from; # would love to quote here but _table ends up getting called
119 # twice during an ->select without a limit clause due to
120 # the way S::A::Limit->select works. should maybe consider
121 # bypassing this and doing S::A::select($self, ...) in
122 # our select method above. meantime, quoting shims have
123 # been added to select/insert/update/delete here
128 my ($self, $from, @join) = @_;
130 push(@sqlf, $self->_make_as($from));
131 foreach my $j (@join) {
134 # check whether a join type exists
135 my $join_clause = '';
136 if (ref($to) eq 'HASH' and exists($to->{-join_type})) {
137 $join_clause = ' '.uc($to->{-join_type}).' JOIN ';
139 $join_clause = ' JOIN ';
141 push(@sqlf, $join_clause);
143 if (ref $to eq 'ARRAY') {
144 push(@sqlf, '(', $self->_recurse_from(@$to), ')');
146 push(@sqlf, $self->_make_as($to));
148 push(@sqlf, ' ON ', $self->_join_condition($on));
150 return join('', @sqlf);
154 my ($self, $from) = @_;
155 return join(' ', map { (ref $_ eq 'SCALAR' ? $$_ : $self->_quote($_)) }
156 reverse each %{$self->_skip_options($from)});
160 my ($self, $hash) = @_;
162 $clean_hash->{$_} = $hash->{$_}
163 for grep {!/^-/} keys %$hash;
167 sub _join_condition {
168 my ($self, $cond) = @_;
169 if (ref $cond eq 'HASH') {
172 my $x = '= '.$self->_quote($cond->{$_}); $j{$_} = \$x;
174 return $self->_recurse_where(\%j);
175 } elsif (ref $cond eq 'ARRAY') {
176 return join(' OR ', map { $self->_join_condition($_) } @$cond);
178 die "Can't handle this yet!";
183 my ($self, $label) = @_;
184 return '' unless defined $label;
185 return "*" if $label eq '*';
186 return $label unless $self->{quote_char};
187 if(ref $self->{quote_char} eq "ARRAY"){
188 return $self->{quote_char}->[0] . $label . $self->{quote_char}->[1]
189 if !defined $self->{name_sep};
190 my $sep = $self->{name_sep};
191 return join($self->{name_sep},
192 map { $self->{quote_char}->[0] . $_ . $self->{quote_char}->[1] }
193 split(/\Q$sep\E/,$label));
195 return $self->SUPER::_quote($label);
201 $_[0] =~ s/SELECT (.*?) FROM/
202 'SELECT '.join(', ', map { $_.' AS col'.++$c } split(', ', $1)).' FROM'/e;
203 $self->SUPER::_RowNum(@_);
208 $self->{limit_dialect} = shift if @_;
209 return $self->{limit_dialect};
214 $self->{quote_char} = shift if @_;
215 return $self->{quote_char};
220 $self->{name_sep} = shift if @_;
221 return $self->{name_sep};
224 } # End of BEGIN block
226 use base qw/DBIx::Class/;
228 __PACKAGE__->load_components(qw/AccessorGroup/);
230 __PACKAGE__->mk_group_accessors('simple' =>
231 qw/_connect_info _dbh _sql_maker _conn_pid _conn_tid debug debugobj
232 cursor on_connect_do transaction_depth/);
239 my $new = bless({}, ref $_[0] || $_[0]);
240 $new->cursor("DBIx::Class::Storage::DBI::Cursor");
241 $new->transaction_depth(0);
243 $new->debugobj(new DBIx::Class::Storage::Statistics());
246 if (defined($ENV{DBIX_CLASS_STORAGE_DBI_DEBUG}) &&
247 ($ENV{DBIX_CLASS_STORAGE_DBI_DEBUG} =~ /=(.+)$/)) {
248 $fh = IO::File->new($1, 'w')
249 or $new->throw_exception("Cannot open trace file $1");
251 $fh = IO::File->new('>&STDERR');
254 $new->debug(1) if $ENV{DBIX_CLASS_STORAGE_DBI_DEBUG};
258 =head2 throw_exception
260 Throws an exception - croaks.
264 sub throw_exception {
265 my ($self, $msg) = @_;
271 DBIx::Class::Storage::DBI - DBI storage handler
277 This class represents the connection to the database
285 The arguments of C<connect_info> are always a single array reference.
287 This is normally accessed via L<DBIx::Class::Schema/connection>, which
288 encapsulates its argument list in an arrayref before calling
289 C<connect_info> here.
291 The arrayref can either contain the same set of arguments one would
292 normally pass to L<DBI/connect>, or a lone code reference which returns
293 a connected database handle.
295 In either case, there is an optional final element within the arrayref
296 which can hold a hashref of connection-specific Storage::DBI options.
297 These include C<on_connect_do>, and the sql_maker options
298 C<limit_dialect>, C<quote_char>, and C<name_sep>. Examples:
300 ->connect_info([ 'dbi:SQLite:./foo.db' ]);
302 ->connect_info([ sub { DBI->connect(...) } ]);
310 { quote_char => q{`}, name_sep => q{@} },
316 sub { DBI->connect(...) },
317 { quote_char => q{`}, name_sep => q{@} },
323 Executes the sql statements given as a listref on every db connect.
325 This option can also be set via L</connect_info>.
329 Causes SQL trace information to be emitted on the C<debugobj> object.
330 (or C<STDERR> if C<debugobj> has not specifically been set).
334 Set or retrieve the filehandle used for trace/debug output. This should be
335 an IO::Handle compatible ojbect (only the C<print> method is used. Initially
336 set to be STDERR - although see information on the
337 L<DBIX_CLASS_STORAGE_DBI_DEBUG> environment variable.
344 if ($self->debugobj->can('debugfh')) {
345 return $self->debugobj->debugfh(@_);
351 Sets or retrieves the object used for metric collection. Defaults to an instance
352 of L<DBIx::Class::Storage::Statistics> that is campatible with the original
353 method of using a coderef as a callback. See the aforementioned Statistics
354 class for more information.
358 Sets a callback to be executed each time a statement is run; takes a sub
359 reference. Callback is executed as $sub->($op, $info) where $op is
360 SELECT/INSERT/UPDATE/DELETE and $info is what would normally be printed.
362 See L<debugobj> for a better way.
369 if ($self->debugobj->can('callback')) {
370 return $self->debugobj->callback(@_);
376 Disconnect the L<DBI> handle, performing a rollback first if the
377 database is not in C<AutoCommit> mode.
384 if( $self->connected ) {
385 $self->_dbh->rollback unless $self->_dbh->{AutoCommit};
386 $self->_dbh->disconnect;
393 Check if the L<DBI> handle is connected. Returns true if the handle
398 sub connected { my ($self) = @_;
400 if(my $dbh = $self->_dbh) {
401 if(defined $self->_conn_tid && $self->_conn_tid != threads->tid) {
402 $self->_sql_maker(undef);
403 return $self->_dbh(undef);
405 elsif($self->_conn_pid != $$) {
406 $self->_dbh->{InactiveDestroy} = 1;
407 $self->_sql_maker(undef);
408 return $self->_dbh(undef)
410 return ($dbh->FETCH('Active') && $dbh->ping);
416 =head2 ensure_connected
418 Check whether the database handle is connected - if not then make a
423 sub ensure_connected {
426 unless ($self->connected) {
427 $self->_populate_dbh;
433 Returns the dbh - a data base handle of class L<DBI>.
440 $self->ensure_connected;
444 sub _sql_maker_args {
447 return ( limit_dialect => $self->dbh );
452 Returns a C<sql_maker> object - normally an object of class
453 C<DBIC::SQL::Abstract>.
459 unless ($self->_sql_maker) {
460 $self->_sql_maker(new DBIC::SQL::Abstract( $self->_sql_maker_args ));
462 return $self->_sql_maker;
466 my ($self, $info_arg) = @_;
469 my $info = [ @$info_arg ]; # copy because we can alter it
470 my $last_info = $info->[-1];
471 if(ref $last_info eq 'HASH') {
473 if(my $on_connect_do = $last_info->{on_connect_do}) {
475 $self->on_connect_do($on_connect_do);
477 for my $sql_maker_opt (qw/limit_dialect quote_char name_sep/) {
478 if(my $opt_val = $last_info->{$sql_maker_opt}) {
480 $self->sql_maker->$sql_maker_opt($opt_val);
484 # remove our options hashref if it was there, to avoid confusing
485 # DBI in the case the user didn't use all 4 DBI options, as in:
486 # [ 'dbi:SQLite:foo.db', { quote_char => q{`} } ]
487 pop(@$info) if $used;
490 $self->_connect_info($info);
493 $self->_connect_info;
498 my @info = @{$self->_connect_info || []};
499 $self->_dbh($self->_connect(@info));
500 my $driver = $self->_dbh->{Driver}->{Name};
501 eval "require DBIx::Class::Storage::DBI::${driver}";
503 bless $self, "DBIx::Class::Storage::DBI::${driver}";
504 $self->_rebless() if $self->can('_rebless');
506 # if on-connect sql statements are given execute them
507 foreach my $sql_statement (@{$self->on_connect_do || []}) {
508 $self->debugobj->query_start($sql_statement) if $self->debug();
509 $self->_dbh->do($sql_statement);
510 $self->debugobj->query_end($sql_statement) if $self->debug();
513 $self->_conn_pid($$);
514 $self->_conn_tid(threads->tid) if $INC{'threads.pm'};
518 my ($self, @info) = @_;
520 $self->throw_exception("You failed to provide any connection info")
523 my ($old_connect_via, $dbh);
525 if ($INC{'Apache/DBI.pm'} && $ENV{MOD_PERL}) {
526 $old_connect_via = $DBI::connect_via;
527 $DBI::connect_via = 'connect';
531 $dbh = ref $info[0] eq 'CODE'
533 : DBI->connect(@info);
536 $DBI::connect_via = $old_connect_via if $old_connect_via;
539 $self->throw_exception("DBI Connection failed: " . ($@ || $DBI::errstr));
547 Calls begin_work on the current dbh.
549 See L<DBIx::Class::Schema> for the txn_do() method, which allows for
550 an entire code block to be executed transactionally.
556 if ($self->{transaction_depth}++ == 0) {
557 my $dbh = $self->dbh;
558 if ($dbh->{AutoCommit}) {
559 $self->debugobj->txn_begin()
568 Issues a commit against the current dbh.
574 my $dbh = $self->dbh;
575 if ($self->{transaction_depth} == 0) {
576 unless ($dbh->{AutoCommit}) {
577 $self->debugobj->txn_commit()
583 if (--$self->{transaction_depth} == 0) {
584 $self->debugobj->txn_commit()
593 Issues a rollback against the current dbh. A nested rollback will
594 throw a L<DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION> exception,
595 which allows the rollback to propagate to the outermost transaction.
603 my $dbh = $self->dbh;
604 if ($self->{transaction_depth} == 0) {
605 unless ($dbh->{AutoCommit}) {
606 $self->debugobj->txn_rollback()
612 if (--$self->{transaction_depth} == 0) {
613 $self->debugobj->txn_rollback()
618 die DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION->new;
625 my $exception_class = "DBIx::Class::Storage::NESTED_ROLLBACK_EXCEPTION";
626 $error =~ /$exception_class/ and $self->throw_exception($error);
627 $self->{transaction_depth} = 0; # ensure that a failed rollback
628 $self->throw_exception($error); # resets the transaction depth
633 my ($self, $op, $extra_bind, $ident, @args) = @_;
634 my ($sql, @bind) = $self->sql_maker->$op($ident, @args);
635 unshift(@bind, @$extra_bind) if $extra_bind;
637 my @debug_bind = map { defined $_ ? qq{'$_'} : q{'NULL'} } @bind;
638 $self->debugobj->query_start($sql, @debug_bind);
640 my $sth = eval { $self->sth($sql,$op) };
643 $self->throw_exception(
644 'no sth generated via sql (' . ($@ || $self->_dbh->errstr) . "): $sql"
647 @bind = map { ref $_ ? ''.$_ : $_ } @bind; # stringify args
651 $rv = eval { $sth->execute(@bind) };
654 $self->throw_exception("Error executing '$sql': ".($@ || $sth->errstr));
657 $self->throw_exception("'$sql' did not generate a statement.");
660 my @debug_bind = map { defined $_ ? qq{`$_'} : q{`NULL'} } @bind;
661 $self->debugobj->query_end($sql, @debug_bind);
663 return (wantarray ? ($rv, $sth, @bind) : $rv);
667 my ($self, $ident, $to_insert) = @_;
668 $self->throw_exception(
669 "Couldn't insert ".join(', ',
670 map "$_ => $to_insert->{$_}", keys %$to_insert
672 ) unless ($self->_execute('insert' => [], $ident, $to_insert));
677 return shift->_execute('update' => [], @_);
681 return shift->_execute('delete' => [], @_);
685 my ($self, $ident, $select, $condition, $attrs) = @_;
686 my $order = $attrs->{order_by};
687 if (ref $condition eq 'SCALAR') {
688 $order = $1 if $$condition =~ s/ORDER BY (.*)$//i;
690 if (exists $attrs->{group_by} || $attrs->{having}) {
692 group_by => $attrs->{group_by},
693 having => $attrs->{having},
694 ($order ? (order_by => $order) : ())
697 my @args = ('select', $attrs->{bind}, $ident, $select, $condition, $order);
698 if ($attrs->{software_limit} ||
699 $self->sql_maker->_default_limit_syntax eq "GenericSubQ") {
700 $attrs->{software_limit} = 1;
702 $self->throw_exception("rows attribute must be positive if present")
703 if (defined($attrs->{rows}) && !($attrs->{rows} > 0));
704 push @args, $attrs->{rows}, $attrs->{offset};
706 return $self->_execute(@args);
711 Handle a SQL select statement.
717 my ($ident, $select, $condition, $attrs) = @_;
718 return $self->cursor->new($self, \@_, $attrs);
723 Performs a select, fetch and return of data - handles a single row
728 # Need to call finish() to work round broken DBDs
732 my ($rv, $sth, @bind) = $self->_select(@_);
733 my @row = $sth->fetchrow_array;
740 Returns a L<DBI> sth (statement handle) for the supplied SQL.
745 my ($self, $sql) = @_;
746 # 3 is the if_active parameter which avoids active sth re-use
747 return $self->dbh->prepare_cached($sql, {}, 3);
750 =head2 columns_info_for
752 Returns database type info for a given table columns.
756 sub columns_info_for {
757 my ($self, $table) = @_;
759 my $dbh = $self->dbh;
761 if ($dbh->can('column_info')) {
763 my $old_raise_err = $dbh->{RaiseError};
764 my $old_print_err = $dbh->{PrintError};
765 $dbh->{RaiseError} = 1;
766 $dbh->{PrintError} = 0;
768 my ($schema,$tab) = $table =~ /^(.+?)\.(.+)$/ ? ($1,$2) : (undef,$table);
769 my $sth = $dbh->column_info( undef,$schema, $tab, '%' );
771 while ( my $info = $sth->fetchrow_hashref() ){
773 $column_info{data_type} = $info->{TYPE_NAME};
774 $column_info{size} = $info->{COLUMN_SIZE};
775 $column_info{is_nullable} = $info->{NULLABLE} ? 1 : 0;
776 $column_info{default_value} = $info->{COLUMN_DEF};
777 my $col_name = $info->{COLUMN_NAME};
778 $col_name =~ s/^\"(.*)\"$/$1/;
780 $result{$col_name} = \%column_info;
783 $dbh->{RaiseError} = $old_raise_err;
784 $dbh->{PrintError} = $old_print_err;
785 return \%result if !$@;
789 my $sth = $dbh->prepare("SELECT * FROM $table WHERE 1=0");
791 my @columns = @{$sth->{NAME_lc}};
792 for my $i ( 0 .. $#columns ){
794 my $type_num = $sth->{TYPE}->[$i];
796 if(defined $type_num && $dbh->can('type_info')) {
797 my $type_info = $dbh->type_info($type_num);
798 $type_name = $type_info->{TYPE_NAME} if $type_info;
800 $column_info{data_type} = $type_name ? $type_name : $type_num;
801 $column_info{size} = $sth->{PRECISION}->[$i];
802 $column_info{is_nullable} = $sth->{NULLABLE}->[$i] ? 1 : 0;
804 if ($column_info{data_type} =~ m/^(.*?)\((.*?)\)$/) {
805 $column_info{data_type} = $1;
806 $column_info{size} = $2;
809 $result{$columns[$i]} = \%column_info;
815 =head2 last_insert_id
817 Return the row id of the last insert.
822 my ($self, $row) = @_;
824 return $self->dbh->func('last_insert_rowid');
830 Returns the database driver name.
834 sub sqlt_type { shift->dbh->{Driver}->{Name} }
836 =head2 create_ddl_dir (EXPERIMENTAL)
840 =item Arguments: $schema \@databases, $version, $directory, $sqlt_args
844 Creates an SQL file based on the Schema, for each of the specified
845 database types, in the given directory.
847 Note that this feature is currently EXPERIMENTAL and may not work correctly
848 across all databases, or fully handle complex relationships.
854 my ($self, $schema, $databases, $version, $dir, $sqltargs) = @_;
856 if(!$dir || !-d $dir)
858 warn "No directory given, using ./\n";
861 $databases ||= ['MySQL', 'SQLite', 'PostgreSQL'];
862 $databases = [ $databases ] if(ref($databases) ne 'ARRAY');
863 $version ||= $schema->VERSION || '1.x';
865 eval "use SQL::Translator";
866 $self->throw_exception("Can't deploy without SQL::Translator: $@") if $@;
868 my $sqlt = SQL::Translator->new({
872 foreach my $db (@$databases)
875 $sqlt->parser('SQL::Translator::Parser::DBIx::Class');
876 # $sqlt->parser_args({'DBIx::Class' => $schema);
877 $sqlt->data($schema);
878 $sqlt->producer($db);
881 my $filename = $schema->ddl_filename($db, $dir, $version);
884 $self->throw_exception("$filename already exists, skipping $db");
887 open($file, ">$filename")
888 or $self->throw_exception("Can't open $filename for writing ($!)");
889 my $output = $sqlt->translate;
891 # print join(":", keys %{$schema->source_registrations});
892 # print Dumper($sqlt->schema);
895 $self->throw_exception("Failed to translate to $db. (" . $sqlt->error . ")");
904 =head2 deployment_statements
906 Create the statements for L</deploy> and
907 L<DBIx::Class::Schema/deploy>.
911 sub deployment_statements {
912 my ($self, $schema, $type, $version, $dir, $sqltargs) = @_;
913 $type ||= $self->sqlt_type;
914 $version ||= $schema->VERSION || '1.x';
916 eval "use SQL::Translator";
919 eval "use SQL::Translator::Parser::DBIx::Class;";
920 $self->throw_exception($@) if $@;
921 eval "use SQL::Translator::Producer::${type};";
922 $self->throw_exception($@) if $@;
923 my $tr = SQL::Translator->new(%$sqltargs);
924 SQL::Translator::Parser::DBIx::Class::parse( $tr, $schema );
925 return "SQL::Translator::Producer::${type}"->can('produce')->($tr);
928 my $filename = $schema->ddl_filename($type, $dir, $version);
931 # $schema->create_ddl_dir([ $type ], $version, $dir, $sqltargs);
932 $self->throw_exception("No SQL::Translator, and no Schema file found, aborting deploy");
936 open($file, "<$filename")
937 or $self->throw_exception("Can't open $filename ($!)");
941 return join('', @rows);
947 Sends the appropriate statements to create or modify tables to the
948 db. This would normally be called through
949 L<DBIx::Class::Schema/deploy>.
954 my ($self, $schema, $type, $sqltargs) = @_;
955 foreach my $statement ( $self->deployment_statements($schema, $type, undef, undef, $sqltargs) ) {
956 for ( split(";\n", $statement)) {
957 next if($_ =~ /^--/);
959 # next if($_ =~ /^DROP/m);
960 next if($_ =~ /^BEGIN TRANSACTION/m);
961 next if($_ =~ /^COMMIT/m);
962 $self->debugobj->query_begin($_) if $self->debug;
963 $self->dbh->do($_) or warn "SQL was:\n $_";
964 $self->debugobj->query_end($_) if $self->debug;
969 =head2 datetime_parser
971 Returns the datetime parser class
975 sub datetime_parser {
977 return $self->{datetime_parser} ||= $self->build_datetime_parser(@_);
980 =head2 datetime_parser_type
982 Defines (returns) the datetime parser class - currently hardwired to
983 L<DateTime::Format::MySQL>
987 sub datetime_parser_type { "DateTime::Format::MySQL"; }
989 =head2 build_datetime_parser
991 See L</datetime_parser>
995 sub build_datetime_parser {
997 my $type = $self->datetime_parser_type(@_);
999 $self->throw_exception("Couldn't load ${type}: $@") if $@;
1003 sub DESTROY { shift->disconnect }
1009 The module defines a set of methods within the DBIC::SQL::Abstract
1010 namespace. These build on L<SQL::Abstract::Limit> to provide the
1011 SQL query functions.
1013 The following methods are extended:-
1027 Accessor for setting limit dialect. This is useful
1028 for JDBC-bridge among others where the remote SQL-dialect cannot
1029 be determined by the name of the driver alone.
1031 This option can also be set via L</connect_info>.
1035 Specifies what characters to use to quote table and column names. If
1036 you use this you will want to specify L<name_sep> as well.
1038 quote_char expectes either a single character, in which case is it is placed
1039 on either side of the table/column, or an arrayref of length 2 in which case the
1040 table/column name is placed between the elements.
1042 For example under MySQL you'd use C<quote_char('`')>, and user SQL Server you'd
1043 use C<quote_char(qw/[ ]/)>.
1045 This option can also be set via L</connect_info>.
1049 This only needs to be used in conjunction with L<quote_char>, and is used to
1050 specify the charecter that seperates elements (schemas, tables, columns) from
1051 each other. In most cases this is simply a C<.>.
1053 This option can also be set via L</connect_info>.
1057 =head1 ENVIRONMENT VARIABLES
1059 =head2 DBIX_CLASS_STORAGE_DBI_DEBUG
1061 If C<DBIX_CLASS_STORAGE_DBI_DEBUG> is set then SQL trace information
1062 is produced (as when the L<debug> method is set).
1064 If the value is of the form C<1=/path/name> then the trace output is
1065 written to the file C</path/name>.
1067 This environment variable is checked when the storage object is first
1068 created (when you call connect on your schema). So, run-time changes
1069 to this environment variable will not take effect unless you also
1070 re-connect on your schema.
1074 Matt S. Trout <mst@shadowcatsystems.co.uk>
1076 Andy Grundman <andy@hybridized.org>
1080 You may distribute this code under the same terms as Perl itself.