improve bare ODBC dsn warning
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / ODBC / Microsoft_SQL_Server.pm
CommitLineData
c1cac633 1package DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server;
2use strict;
3use warnings;
4
eb0323df 5use base qw/DBIx::Class::Storage::DBI::MSSQL/;
2ad62d97 6use mro 'c3';
6298a324 7use Scalar::Util 'reftype';
ed7ab0f4 8use Try::Tiny;
384b8bce 9use Carp::Clan qw/^DBIx::Class/;
fd323bf1 10use namespace::clean;
c1cac633 11
7b1b2582 12__PACKAGE__->mk_group_accessors(simple => qw/
13 _using_dynamic_cursors
14/);
c1cac633 15
16=head1 NAME
17
a89c6fc0 18DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server - Support specific
19to Microsoft SQL Server over ODBC
c1cac633 20
21=head1 DESCRIPTION
22
5a77aa8b 23This class implements support specific to Microsoft SQL Server over ODBC. It is
24loaded automatically by by DBIx::Class::Storage::DBI::ODBC when it detects a
25MSSQL back-end.
c1cac633 26
5a77aa8b 27Most of the functionality is provided from the superclass
28L<DBIx::Class::Storage::DBI::MSSQL>.
c1cac633 29
dcc7ddff 30=head1 USAGE NOTES
31
32=head2 Basic Linux Setup (Debian)
33
34 sudo aptitude install tdsodbc libdbd-odbc-perl unixodbc
35
36In case it is not already there put the following in C</etc/odbcinst.ini>:
37
38 [FreeTDS]
39 Description = FreeTDS
40 Driver = /usr/lib/odbc/libtdsodbc.so
41 Setup = /usr/lib/odbc/libtdsS.so
42 UsageCount = 1
43
44Set your C<$dsn> in L<connect_info|DBIx::Class::Storage::DBI/connect_info> as follows:
45
46 dbi:ODBC:server=<my.host.name>;port=1433;driver=FreeTDS;tds_version=8.0
47
48If you use the EasySoft driver (L<http://www.easysoft.com>):
49
50 dbi:ODBC:server=<my.host.name>;port=1433;driver=Easysoft ODBC-SQL Server
51
52=head2 Basic Windows Setup
53
54Use the following C<$dsn> for the Microsoft ODBC driver:
55
56 dbi:ODBC:driver={SQL Server};server=SERVER\SQL_SERVER_INSTANCE_NAME
57
58And for the Native Client:
59
60 dbi:ODBC:driver={SQL Server Native Client 10.0};server=SERVER\SQL_SERVER_INSTANCE_NAME
61
62Go into Control Panel -> System and Security -> Administrative Tools -> Data
63Sources (ODBC) to check driver names and to set up data sources.
64
65Use System DSNs, not User DSNs if you want to use DSNs.
66
67If you set up a DSN, use the following C<$dsn> for
68L<connect_info|DBIx::Class::Storage::DBI/connect_info>:
69
70 dbi:ODBC:dsn=MY_DSN
71
7b1b2582 72=head1 MULTIPLE ACTIVE STATEMENTS
73
74The following options are alternative ways to enable concurrent executing
384b8bce 75statement support. Each has its own advantages and drawbacks and works on
76different platforms. Read each section carefully.
77
78In order of preference, they are:
79
80=over 8
81
82=item * L</connect_call_use_mars>
83
84=item * L</connect_call_use_dynamic_cursors>
85
86=item * L</connect_call_use_server_cursors>
87
88=back
89
90=head1 METHODS
91
92=head2 connect_call_use_mars
93
94Use as:
95
96 on_connect_call => 'use_mars'
97
98Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result
99Sets". See L<DBD::ODBC::FAQ/Does DBD::ODBC support Multiple Active Statements?>
100for more information.
101
102This does not work on FreeTDS drivers at the time of this writing, and only
103works with the Native Client, later versions of the Windows MS ODBC driver, and
104the Easysoft driver.
105
106=cut
107
108sub connect_call_use_mars {
109 my $self = shift;
110
111 my $dsn = $self->_dbi_connect_info->[0];
112
113 if (ref($dsn) eq 'CODE') {
114 $self->throw_exception('cannot change the DBI DSN on a CODE ref connect_info');
115 }
116
117 if ($dsn !~ /MARS_Connection=/) {
118 if ($self->using_freetds) {
119 $self->throw_exception('FreeTDS does not support MARS at the time of '
120 .'writing.');
121 }
122
123 if (exists $self->_server_info->{normalized_dbms_version} &&
124 $self->_server_info->{normalized_dbms_version} < 9) {
125 $self->throw_exception('SQL Server 2005 or later required to use MARS.');
126 }
127
128 if (my ($data_source) = $dsn =~ /^dbi:ODBC:([\w-]+)\z/i) { # prefix with DSN
a469c045 129 warn "Bare DSN in ODBC connect string, rewriting as 'dsn=$data_source'"
130 ." for MARS\n";
131 $dsn = "dbi:ODBC:dsn=$data_source";
384b8bce 132 }
133
134 $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes";
135 $self->disconnect;
136 $self->ensure_connected;
137 }
138}
139
140sub connect_call_use_MARS {
141 carp "'connect_call_use_MARS' has been deprecated, use "
142 ."'connect_call_use_mars' instead.";
143 shift->connect_call_use_mars(@_)
144}
7b1b2582 145
146=head2 connect_call_use_dynamic_cursors
147
148Use as:
149
150 on_connect_call => 'use_dynamic_cursors'
151
8384a713 152in your L<connect_info|DBIx::Class::Storage::DBI/connect_info> as one way to enable multiple
7b1b2582 153concurrent statements.
154
155Will add C<< odbc_cursortype => 2 >> to your DBI connection attributes. See
156L<DBD::ODBC/odbc_cursortype> for more information.
157
41dd5d30 158Alternatively, you can add it yourself and dynamic cursor support will be
159automatically enabled.
7b1b2582 160
41dd5d30 161If you're using FreeTDS, C<tds_version> must be set to at least C<8.0>.
162
163This will not work with CODE ref connect_info's.
7b1b2582 164
165B<WARNING:> this will break C<SCOPE_IDENTITY()>, and C<SELECT @@IDENTITY> will
166be used instead, which on SQL Server 2005 and later will return erroneous
167results on tables which have an on insert trigger that inserts into another
168table with an C<IDENTITY> column.
169
170=cut
171
172sub connect_call_use_dynamic_cursors {
173 my $self = shift;
174
175 if (ref($self->_dbi_connect_info->[0]) eq 'CODE') {
0a9a9955 176 $self->throw_exception ('Cannot set DBI attributes on a CODE ref connect_info');
7b1b2582 177 }
178
179 my $dbi_attrs = $self->_dbi_connect_info->[-1];
180
384b8bce 181 unless (ref $dbi_attrs eq 'HASH') {
7b1b2582 182 $dbi_attrs = {};
183 push @{ $self->_dbi_connect_info }, $dbi_attrs;
184 }
185
186 if (not exists $dbi_attrs->{odbc_cursortype}) {
187 # turn on support for multiple concurrent statements, unless overridden
188 $dbi_attrs->{odbc_cursortype} = 2;
75517ea9 189 $self->disconnect; # resetting dbi attrs, so have to reconnect
190 $self->ensure_connected;
7b1b2582 191 $self->_set_dynamic_cursors;
192 }
193}
194
195sub _set_dynamic_cursors {
196 my $self = shift;
cbc0e07a 197 my $dbh = $self->_get_dbh;
41dd5d30 198
ed7ab0f4 199 try {
41dd5d30 200 local $dbh->{RaiseError} = 1;
201 local $dbh->{PrintError} = 0;
202 $dbh->do('SELECT @@IDENTITY');
ed7ab0f4 203 } catch {
1a58752c 204 $self->throw_exception (<<'EOF');
41dd5d30 205
206Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2),
207if you're using FreeTDS, make sure to set tds_version to 8.0 or greater.
208EOF
1b300062 209 };
41dd5d30 210
7b1b2582 211 $self->_using_dynamic_cursors(1);
212 $self->_identity_method('@@identity');
213}
214
37b17a93 215sub _init {
7b1b2582 216 my $self = shift;
217
1a58752c 218 if (
219 ref($self->_dbi_connect_info->[0]) ne 'CODE'
220 &&
221 ref ($self->_dbi_connect_info->[-1]) eq 'HASH'
222 &&
384b8bce 223 ($self->_dbi_connect_info->[-1]{odbc_cursortype} || 0) > 1
1a58752c 224 ) {
7b1b2582 225 $self->_set_dynamic_cursors;
7b1b2582 226 }
384b8bce 227 else {
228 $self->_using_dynamic_cursors(0);
229 }
7b1b2582 230}
231
232=head2 connect_call_use_server_cursors
233
234Use as:
235
236 on_connect_call => 'use_server_cursors'
237
238May allow multiple active select statements. See
239L<DBD::ODBC/odbc_SQL_ROWSET_SIZE> for more information.
240
241Takes an optional parameter for the value to set the attribute to, default is
242C<2>.
243
244B<WARNING>: this does not work on all versions of SQL Server, and may lock up
245your database!
246
384b8bce 247At the time of writing, this option only works on Microsoft's Windows drivers,
248later versions of the ODBC driver and the Native Client driver.
249
7b1b2582 250=cut
251
252sub connect_call_use_server_cursors {
253 my $self = shift;
254 my $sql_rowset_size = shift || 2;
255
384b8bce 256 if ($^O !~ /win32|cygwin/i) {
257 $self->throw_exception('Server cursors only work on Windows platforms at '
258 .'the time of writing.');
259 }
260
9ae966b9 261 $self->_get_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size;
7b1b2582 262}
263
384b8bce 264=head2 using_freetds
7b1b2582 265
384b8bce 266Tries to determine, to the best of our ability, whether or not you are using the
267FreeTDS driver with L<DBD::ODBC>.
7b1b2582 268
269=cut
270
384b8bce 271sub using_freetds {
7b1b2582 272 my $self = shift;
273
274 my $dsn = $self->_dbi_connect_info->[0];
275
384b8bce 276 $dsn = '' if ref $dsn eq 'CODE';
7b1b2582 277
384b8bce 278 my $dbh = $self->_get_dbh;
279
280 return 1 if $dsn =~ /driver=FreeTDS/i
281 || (try { $dbh->get_info(6) }||'') =~ /tdsodbc/i;
282
283 return 0;
7b1b2582 284}
285
2861;
287
5a77aa8b 288=head1 AUTHOR
c1cac633 289
5a77aa8b 290See L<DBIx::Class/CONTRIBUTORS>.
c1cac633 291
292=head1 LICENSE
293
294You may distribute this code under the same terms as Perl itself.
295
296=cut
259c0e40 297# vim: sw=2 sts=2