Change indentation after 384b8bce2 (whitespace changes only)
[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
7b1b2582 30=head1 MULTIPLE ACTIVE STATEMENTS
31
32The following options are alternative ways to enable concurrent executing
384b8bce 33statement support. Each has its own advantages and drawbacks and works on
34different platforms. Read each section carefully.
35
36In order of preference, they are:
37
38=over 8
39
40=item * L</connect_call_use_mars>
41
42=item * L</connect_call_use_dynamic_cursors>
43
44=item * L</connect_call_use_server_cursors>
45
46=back
47
48=head1 METHODS
49
50=head2 connect_call_use_mars
51
52Use as:
53
54 on_connect_call => 'use_mars'
55
56Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result
57Sets". See L<DBD::ODBC::FAQ/Does DBD::ODBC support Multiple Active Statements?>
58for more information.
59
60This does not work on FreeTDS drivers at the time of this writing, and only
61works with the Native Client, later versions of the Windows MS ODBC driver, and
62the Easysoft driver.
63
64=cut
65
66sub connect_call_use_mars {
67 my $self = shift;
68
69 my $dsn = $self->_dbi_connect_info->[0];
70
71 if (ref($dsn) eq 'CODE') {
72 $self->throw_exception('cannot change the DBI DSN on a CODE ref connect_info');
73 }
74
75 if ($dsn !~ /MARS_Connection=/) {
76 if ($self->using_freetds) {
77 $self->throw_exception('FreeTDS does not support MARS at the time of '
78 .'writing.');
79 }
80
81 if (exists $self->_server_info->{normalized_dbms_version} &&
82 $self->_server_info->{normalized_dbms_version} < 9) {
83 $self->throw_exception('SQL Server 2005 or later required to use MARS.');
84 }
85
86 if (my ($data_source) = $dsn =~ /^dbi:ODBC:([\w-]+)\z/i) { # prefix with DSN
87 warn "Bare DSN in ODBC connect string, rewriting to DSN=$data_source\n";
88 $dsn = "dbi:ODBC:DSN=$data_source";
89 }
90
91 $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes";
92 $self->disconnect;
93 $self->ensure_connected;
94 }
95}
96
97sub connect_call_use_MARS {
98 carp "'connect_call_use_MARS' has been deprecated, use "
99 ."'connect_call_use_mars' instead.";
100 shift->connect_call_use_mars(@_)
101}
7b1b2582 102
103=head2 connect_call_use_dynamic_cursors
104
105Use as:
106
107 on_connect_call => 'use_dynamic_cursors'
108
8384a713 109in your L<connect_info|DBIx::Class::Storage::DBI/connect_info> as one way to enable multiple
7b1b2582 110concurrent statements.
111
112Will add C<< odbc_cursortype => 2 >> to your DBI connection attributes. See
113L<DBD::ODBC/odbc_cursortype> for more information.
114
41dd5d30 115Alternatively, you can add it yourself and dynamic cursor support will be
116automatically enabled.
7b1b2582 117
41dd5d30 118If you're using FreeTDS, C<tds_version> must be set to at least C<8.0>.
119
120This will not work with CODE ref connect_info's.
7b1b2582 121
122B<WARNING:> this will break C<SCOPE_IDENTITY()>, and C<SELECT @@IDENTITY> will
123be used instead, which on SQL Server 2005 and later will return erroneous
124results on tables which have an on insert trigger that inserts into another
125table with an C<IDENTITY> column.
126
127=cut
128
129sub connect_call_use_dynamic_cursors {
130 my $self = shift;
131
132 if (ref($self->_dbi_connect_info->[0]) eq 'CODE') {
0a9a9955 133 $self->throw_exception ('Cannot set DBI attributes on a CODE ref connect_info');
7b1b2582 134 }
135
136 my $dbi_attrs = $self->_dbi_connect_info->[-1];
137
384b8bce 138 unless (ref $dbi_attrs eq 'HASH') {
7b1b2582 139 $dbi_attrs = {};
140 push @{ $self->_dbi_connect_info }, $dbi_attrs;
141 }
142
143 if (not exists $dbi_attrs->{odbc_cursortype}) {
144 # turn on support for multiple concurrent statements, unless overridden
145 $dbi_attrs->{odbc_cursortype} = 2;
75517ea9 146 $self->disconnect; # resetting dbi attrs, so have to reconnect
147 $self->ensure_connected;
7b1b2582 148 $self->_set_dynamic_cursors;
149 }
150}
151
152sub _set_dynamic_cursors {
153 my $self = shift;
cbc0e07a 154 my $dbh = $self->_get_dbh;
41dd5d30 155
ed7ab0f4 156 try {
41dd5d30 157 local $dbh->{RaiseError} = 1;
158 local $dbh->{PrintError} = 0;
159 $dbh->do('SELECT @@IDENTITY');
ed7ab0f4 160 } catch {
1a58752c 161 $self->throw_exception (<<'EOF');
41dd5d30 162
163Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2),
164if you're using FreeTDS, make sure to set tds_version to 8.0 or greater.
165EOF
1b300062 166 };
41dd5d30 167
7b1b2582 168 $self->_using_dynamic_cursors(1);
169 $self->_identity_method('@@identity');
170}
171
37b17a93 172sub _init {
7b1b2582 173 my $self = shift;
174
1a58752c 175 if (
176 ref($self->_dbi_connect_info->[0]) ne 'CODE'
177 &&
178 ref ($self->_dbi_connect_info->[-1]) eq 'HASH'
179 &&
384b8bce 180 ($self->_dbi_connect_info->[-1]{odbc_cursortype} || 0) > 1
1a58752c 181 ) {
7b1b2582 182 $self->_set_dynamic_cursors;
7b1b2582 183 }
384b8bce 184 else {
185 $self->_using_dynamic_cursors(0);
186 }
7b1b2582 187}
188
189=head2 connect_call_use_server_cursors
190
191Use as:
192
193 on_connect_call => 'use_server_cursors'
194
195May allow multiple active select statements. See
196L<DBD::ODBC/odbc_SQL_ROWSET_SIZE> for more information.
197
198Takes an optional parameter for the value to set the attribute to, default is
199C<2>.
200
201B<WARNING>: this does not work on all versions of SQL Server, and may lock up
202your database!
203
384b8bce 204At the time of writing, this option only works on Microsoft's Windows drivers,
205later versions of the ODBC driver and the Native Client driver.
206
7b1b2582 207=cut
208
209sub connect_call_use_server_cursors {
210 my $self = shift;
211 my $sql_rowset_size = shift || 2;
212
384b8bce 213 if ($^O !~ /win32|cygwin/i) {
214 $self->throw_exception('Server cursors only work on Windows platforms at '
215 .'the time of writing.');
216 }
217
9ae966b9 218 $self->_get_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size;
7b1b2582 219}
220
384b8bce 221=head2 using_freetds
7b1b2582 222
384b8bce 223Tries to determine, to the best of our ability, whether or not you are using the
224FreeTDS driver with L<DBD::ODBC>.
7b1b2582 225
226=cut
227
384b8bce 228sub using_freetds {
7b1b2582 229 my $self = shift;
230
231 my $dsn = $self->_dbi_connect_info->[0];
232
384b8bce 233 $dsn = '' if ref $dsn eq 'CODE';
7b1b2582 234
384b8bce 235 my $dbh = $self->_get_dbh;
236
237 return 1 if $dsn =~ /driver=FreeTDS/i
238 || (try { $dbh->get_info(6) }||'') =~ /tdsodbc/i;
239
240 return 0;
7b1b2582 241}
242
2431;
244
5a77aa8b 245=head1 AUTHOR
c1cac633 246
5a77aa8b 247See L<DBIx::Class/CONTRIBUTORS>.
c1cac633 248
249=head1 LICENSE
250
251You may distribute this code under the same terms as Perl itself.
252
253=cut
259c0e40 254# vim: sw=2 sts=2