# default cursor class, overridable in connect_info attributes
__PACKAGE__->cursor_class('DBIx::Class::Storage::DBI::Cursor');
-__PACKAGE__->mk_group_accessors('inherited' => qw/sql_maker_class sql_limit_dialect/);
+__PACKAGE__->mk_group_accessors('inherited' => qw/
+ sql_maker_class sql_limit_dialect sql_quote_char sql_name_sep
+/);
+
+__PACKAGE__->sql_name_sep('.');
+
__PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker');
__PACKAGE__->mk_group_accessors('simple' => qw/
default L</sql_limit_dialect> setting of the storage (if any). For a list
of available limit dialects see L<DBIx::Class::SQLMaker::LimitDialects>.
+=item quote_names
+
+When true automatically sets L</quote_char> and L</name_sep> to the characters
+appropriate for your particular RDBMS. This option is preferred over specifying
+L</quote_char> directly.
+
=item quote_char
Specifies what characters to use to quote table and column names.
delete @attrs{@storage_opts} if @storage_opts;
my @sql_maker_opts = grep exists $attrs{$_},
- qw/limit_dialect quote_char name_sep/;
+ qw/limit_dialect quote_char name_sep quote_names/;
@{ $info{sql_maker_options} }{@sql_maker_opts} =
delete @attrs{@sql_maker_opts} if @sql_maker_opts;
"Your storage class ($s_class) does not set sql_limit_dialect and you "
. 'have not supplied an explicit limit_dialect in your connection_info. '
. 'DBIC will attempt to use the GenericSubQ dialect, which works on most '
- . 'databases but can be (and often is) painfully slow.'
+ . 'databases but can be (and often is) painfully slow. '
+ . "Please file an RT ticket against '$s_class' ."
);
'GenericSubQ';
}
;
+ my ($quote_char, $name_sep);
+
+ if ($opts{quote_names}) {
+ $quote_char = (delete $opts{quote_char}) || $self->sql_quote_char || do {
+ my $s_class = (ref $self) || $self;
+ carp (
+ "You requested 'quote_names' but your storage class ($s_class) does "
+ . 'not explicitly define a default sql_quote_char and you have not '
+ . 'supplied a quote_char as part of your connection_info. DBIC will '
+ .q{default to the ANSI SQL standard quote '"', which works most of }
+ . "the time. Please file an RT ticket against '$s_class'."
+ );
+
+ '"'; # RV
+ };
+
+ $name_sep = (delete $opts{name_sep}) || $self->sql_name_sep;
+ }
+
$self->_sql_maker($sql_maker_class->new(
bindtype=>'columns',
array_datatypes => 1,
limit_dialect => $dialect,
- name_sep => '.',
+ ($quote_char ? (quote_char => $quote_char) : ()),
+ name_sep => ($name_sep || '.'),
%opts,
));
}
--- /dev/null
+use strict;
+use warnings;
+use Test::More;
+use Test::Exception;
+use Data::Dumper::Concise;
+use lib qw(t/lib);
+use DBICTest;
+
+my %expected = (
+ 'DBIx::Class::Storage::DBI' =>
+ # no default quote_char
+ { name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::MSSQL' =>
+ { quote_char => [ '[', ']' ], name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::DB2' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::Informix' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::InterBase' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::mysql' =>
+ { quote_char => '`', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::Pg' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::ODBC::ACCESS' =>
+ { quote_char => [ '[', ']' ], name_sep => '.' },
+
+# Not testing this one, it's a pain.
+# 'DBIx::Class::Storage::DBI::ODBC::DB2_400_SQL' =>
+# { quote_char => '"', name_sep => qr/must be connected/ },
+
+ 'DBIx::Class::Storage::DBI::Oracle::Generic' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::SQLAnywhere' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::SQLite' =>
+ { quote_char => '"', name_sep => '.' },
+
+ 'DBIx::Class::Storage::DBI::Sybase::ASE' =>
+ { quote_char => [ '[', ']' ], name_sep => '.' },
+);
+
+while (my ($class, $mapping) = each %expected) {
+ my ($quote_char, $name_sep) = @$mapping{qw/quote_char name_sep/};
+ eval "require ${class};";
+ die $@ if $@;
+ my $instance = $class->new;
+
+ my $quote_char_text = dumper($quote_char);
+
+ if (exists $mapping->{quote_char}) {
+ is_deeply $instance->sql_quote_char, $quote_char,
+ "sql_quote_char for $class is $quote_char_text";
+ }
+
+ is $instance->sql_name_sep, $name_sep,
+ "sql_name_sep for $class is '$name_sep'";
+}
+
+# Try quote_names with available DBs.
+
+# SQLite first.
+
+my $schema = DBICTest->init_schema(quote_names => 1);
+
+is $schema->storage->sql_maker->quote_char, '"',
+ q{quote_names => 1 sets correct quote_char for SQLite ('"')};
+
+is $schema->storage->sql_maker->name_sep, '.',
+ q{quote_names => 1 sets correct name_sep for SQLite (".")};
+
+# Now the others.
+
+# Env var to base class mapping, these are the DBs I actually have.
+# -- Caelum
+my %dbs = (
+ ORA => 'DBIx::Class::Storage::DBI::Oracle::Generic',
+ PG => 'DBIx::Class::Storage::DBI::Pg',
+ MYSQL => 'DBIx::Class::Storage::DBI::mysql',
+ DB2 => 'DBIx::Class::Storage::DBI::DB2',
+ SYBASE => 'DBIx::Class::Storage::DBI::Sybase::ASE',
+ SQLANYWHERE => 'DBIx::Class::Storage::DBI::SQLAnywhere',
+ SQLANYWHERE_ODBC => 'DBIx::Class::Storage::DBI::SQLAnywhere',
+ FIREBIRD => 'DBIx::Class::Storage::DBI::InterBase',
+ FIREBIRD_ODBC => 'DBIx::Class::Storage::DBI::InterBase',
+ INFORMIX => 'DBIx::Class::Storage::DBI::Informix',
+ MSSQL_ODBC => 'DBIx::Class::Storage::DBI::MSSQL',
+);
+
+while (my ($db, $base_class) = each %dbs) {
+ my ($dsn, $user, $pass) = map $ENV{"DBICTEST_${db}_$_"}, qw/DSN USER PASS/;
+
+ next unless $dsn;
+
+ my $schema = DBICTest::Schema->connect($dsn, $user, $pass, {
+ quote_names => 1
+ });
+
+ my $expected_quote_char = $expected{$base_class}{quote_char};
+ my $quote_char_text = dumper($expected_quote_char);
+
+ is_deeply $schema->storage->sql_maker->quote_char,
+ $expected_quote_char,
+ "$db quote_char with quote_names => 1 is $quote_char_text";
+
+ my $expected_name_sep = $expected{$base_class}{name_sep};
+
+ is $schema->storage->sql_maker->name_sep,
+ $expected_name_sep,
+ "$db name_sep with quote_names => 1 is '$expected_name_sep'";
+}
+
+done_testing;
+
+sub dumper {
+ my $val = shift;
+
+ my $dd = DumperObject;
+ $dd->Indent(0);
+ return $dd->Values([ $val ])->Dump;
+}
+
+1;