Release 0.08127
[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
129 warn "Bare DSN in ODBC connect string, rewriting to DSN=$data_source\n";
130 $dsn = "dbi:ODBC:DSN=$data_source";
131 }
132
133 $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes";
134 $self->disconnect;
135 $self->ensure_connected;
136 }
137}
138
139sub connect_call_use_MARS {
140 carp "'connect_call_use_MARS' has been deprecated, use "
141 ."'connect_call_use_mars' instead.";
142 shift->connect_call_use_mars(@_)
143}
7b1b2582 144
145=head2 connect_call_use_dynamic_cursors
146
147Use as:
148
149 on_connect_call => 'use_dynamic_cursors'
150
8384a713 151in your L<connect_info|DBIx::Class::Storage::DBI/connect_info> as one way to enable multiple
7b1b2582 152concurrent statements.
153
154Will add C<< odbc_cursortype => 2 >> to your DBI connection attributes. See
155L<DBD::ODBC/odbc_cursortype> for more information.
156
41dd5d30 157Alternatively, you can add it yourself and dynamic cursor support will be
158automatically enabled.
7b1b2582 159
41dd5d30 160If you're using FreeTDS, C<tds_version> must be set to at least C<8.0>.
161
162This will not work with CODE ref connect_info's.
7b1b2582 163
164B<WARNING:> this will break C<SCOPE_IDENTITY()>, and C<SELECT @@IDENTITY> will
165be used instead, which on SQL Server 2005 and later will return erroneous
166results on tables which have an on insert trigger that inserts into another
167table with an C<IDENTITY> column.
168
169=cut
170
171sub connect_call_use_dynamic_cursors {
172 my $self = shift;
173
174 if (ref($self->_dbi_connect_info->[0]) eq 'CODE') {
0a9a9955 175 $self->throw_exception ('Cannot set DBI attributes on a CODE ref connect_info');
7b1b2582 176 }
177
178 my $dbi_attrs = $self->_dbi_connect_info->[-1];
179
384b8bce 180 unless (ref $dbi_attrs eq 'HASH') {
7b1b2582 181 $dbi_attrs = {};
182 push @{ $self->_dbi_connect_info }, $dbi_attrs;
183 }
184
185 if (not exists $dbi_attrs->{odbc_cursortype}) {
186 # turn on support for multiple concurrent statements, unless overridden
187 $dbi_attrs->{odbc_cursortype} = 2;
75517ea9 188 $self->disconnect; # resetting dbi attrs, so have to reconnect
189 $self->ensure_connected;
7b1b2582 190 $self->_set_dynamic_cursors;
191 }
192}
193
194sub _set_dynamic_cursors {
195 my $self = shift;
cbc0e07a 196 my $dbh = $self->_get_dbh;
41dd5d30 197
ed7ab0f4 198 try {
41dd5d30 199 local $dbh->{RaiseError} = 1;
200 local $dbh->{PrintError} = 0;
201 $dbh->do('SELECT @@IDENTITY');
ed7ab0f4 202 } catch {
1a58752c 203 $self->throw_exception (<<'EOF');
41dd5d30 204
205Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2),
206if you're using FreeTDS, make sure to set tds_version to 8.0 or greater.
207EOF
1b300062 208 };
41dd5d30 209
7b1b2582 210 $self->_using_dynamic_cursors(1);
211 $self->_identity_method('@@identity');
212}
213
37b17a93 214sub _init {
7b1b2582 215 my $self = shift;
216
1a58752c 217 if (
218 ref($self->_dbi_connect_info->[0]) ne 'CODE'
219 &&
220 ref ($self->_dbi_connect_info->[-1]) eq 'HASH'
221 &&
384b8bce 222 ($self->_dbi_connect_info->[-1]{odbc_cursortype} || 0) > 1
1a58752c 223 ) {
7b1b2582 224 $self->_set_dynamic_cursors;
7b1b2582 225 }
384b8bce 226 else {
227 $self->_using_dynamic_cursors(0);
228 }
7b1b2582 229}
230
231=head2 connect_call_use_server_cursors
232
233Use as:
234
235 on_connect_call => 'use_server_cursors'
236
237May allow multiple active select statements. See
238L<DBD::ODBC/odbc_SQL_ROWSET_SIZE> for more information.
239
240Takes an optional parameter for the value to set the attribute to, default is
241C<2>.
242
243B<WARNING>: this does not work on all versions of SQL Server, and may lock up
244your database!
245
384b8bce 246At the time of writing, this option only works on Microsoft's Windows drivers,
247later versions of the ODBC driver and the Native Client driver.
248
7b1b2582 249=cut
250
251sub connect_call_use_server_cursors {
252 my $self = shift;
253 my $sql_rowset_size = shift || 2;
254
384b8bce 255 if ($^O !~ /win32|cygwin/i) {
256 $self->throw_exception('Server cursors only work on Windows platforms at '
257 .'the time of writing.');
258 }
259
9ae966b9 260 $self->_get_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size;
7b1b2582 261}
262
384b8bce 263=head2 using_freetds
7b1b2582 264
384b8bce 265Tries to determine, to the best of our ability, whether or not you are using the
266FreeTDS driver with L<DBD::ODBC>.
7b1b2582 267
268=cut
269
384b8bce 270sub using_freetds {
7b1b2582 271 my $self = shift;
272
273 my $dsn = $self->_dbi_connect_info->[0];
274
384b8bce 275 $dsn = '' if ref $dsn eq 'CODE';
7b1b2582 276
384b8bce 277 my $dbh = $self->_get_dbh;
278
279 return 1 if $dsn =~ /driver=FreeTDS/i
280 || (try { $dbh->get_info(6) }||'') =~ /tdsodbc/i;
281
282 return 0;
7b1b2582 283}
284
2851;
286
5a77aa8b 287=head1 AUTHOR
c1cac633 288
5a77aa8b 289See L<DBIx::Class/CONTRIBUTORS>.
c1cac633 290
291=head1 LICENSE
292
293You may distribute this code under the same terms as Perl itself.
294
295=cut
259c0e40 296# vim: sw=2 sts=2