File Coverage

blib/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm
Criterion Covered Total %
statement 24 76 31.5
branch 0 32 0.0
condition 0 15 0.0
subroutine 8 15 53.3
pod 3 4 75.0
total 35 142 24.6


line stmt bran cond sub pod time code
1             package DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server;
2 2     2   1372 use strict;
  2         5  
  2         61  
3 2     2   11 use warnings;
  2         2  
  2         68  
4              
5 2         246 use base qw/
6             DBIx::Class::Storage::DBI::ODBC
7             DBIx::Class::Storage::DBI::MSSQL
8 2     2   7 /;
  2         3  
9 2     2   11 use mro 'c3';
  2         4  
  2         11  
10 2     2   59 use Scalar::Util 'reftype';
  2         4  
  2         113  
11 2     2   9 use Try::Tiny;
  2         4  
  2         108  
12 2     2   13 use DBIx::Class::Carp;
  2         3  
  2         16  
13 2     2   10 use namespace::clean;
  2         2  
  2         15  
14              
15             __PACKAGE__->mk_group_accessors(simple => qw/
16             _using_dynamic_cursors
17             /);
18              
19             =head1 NAME
20              
21             DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server - Support specific
22             to Microsoft SQL Server over ODBC
23              
24             =head1 DESCRIPTION
25              
26             This class implements support specific to Microsoft SQL Server over ODBC. It is
27             loaded automatically by DBIx::Class::Storage::DBI::ODBC when it detects a
28             MSSQL back-end.
29              
30             Most of the functionality is provided from the superclass
31             L.
32              
33             =head1 USAGE NOTES
34              
35             =head2 Basic Linux Setup (Debian)
36              
37             sudo aptitude install tdsodbc libdbd-odbc-perl unixodbc
38              
39             In case it is not already there put the following (adjust for non-64bit arch) in
40             C:
41              
42             [FreeTDS]
43             Description = FreeTDS
44             Driver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
45             Setup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so
46             UsageCount = 1
47              
48             Set your C<$dsn> in L as follows:
49              
50             dbi:ODBC:server=;port=1433;driver=FreeTDS;tds_version=8.0
51              
52             If you use the EasySoft driver (L):
53              
54             dbi:ODBC:server=;port=1433;driver=Easysoft ODBC-SQL Server
55              
56             =head2 Basic Windows Setup
57              
58             Use the following C<$dsn> for the Microsoft ODBC driver:
59              
60             dbi:ODBC:driver={SQL Server};server=SERVER\SQL_SERVER_INSTANCE_NAME
61              
62             And for the Native Client:
63              
64             dbi:ODBC:driver={SQL Server Native Client 10.0};server=SERVER\SQL_SERVER_INSTANCE_NAME
65              
66             Go into Control Panel -> System and Security -> Administrative Tools -> Data
67             Sources (ODBC) to check driver names and to set up data sources.
68              
69             Use System DSNs, not User DSNs if you want to use DSNs.
70              
71             If you set up a DSN, use the following C<$dsn> for
72             L:
73              
74             dbi:ODBC:dsn=MY_DSN
75              
76             =head1 MULTIPLE ACTIVE STATEMENTS
77              
78             The following options are alternative ways to enable concurrent executing
79             statement support. Each has its own advantages and drawbacks and works on
80             different platforms. Read each section carefully.
81              
82             For more details about using MAS in MSSQL over DBD::ODBC see this excellent
83             document provided by EasySoft:
84             L.
85              
86             In order of preference, they are:
87              
88             =over 8
89              
90             =item * L
91              
92             =item * L
93              
94             =item * L
95              
96             =back
97              
98             =head1 METHODS
99              
100             =head2 connect_call_use_mars
101              
102             Use as:
103              
104             on_connect_call => 'use_mars'
105              
106             in your connection info, or alternatively specify it directly:
107              
108             Your::Schema->connect (
109             $original_dsn . '; MARS_Connection=Yes',
110             $user,
111             $pass,
112             \%attrs,
113             )
114              
115             Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result
116             Sets". See L
117             for more information.
118              
119             This does not work on FreeTDS drivers at the time of this writing, and only
120             works with the Native Client, later versions of the Windows MS ODBC driver, and
121             the Easysoft driver.
122              
123             =cut
124              
125             sub connect_call_use_mars {
126 0     0 1   my $self = shift;
127              
128 0           my $dsn = $self->_dbi_connect_info->[0];
129              
130 0 0         if (ref($dsn) eq 'CODE') {
131 0           $self->throw_exception('cannot change the DBI DSN on a CODE ref connect_info');
132             }
133              
134 0 0         if ($dsn !~ /MARS_Connection=/) {
135 0 0         if ($self->_using_freetds) {
136 0           $self->throw_exception('FreeTDS does not support MARS at the time of '
137             .'writing.');
138             }
139              
140 0 0 0       if (exists $self->_server_info->{normalized_dbms_version} &&
141             $self->_server_info->{normalized_dbms_version} < 9) {
142 0           $self->throw_exception('SQL Server 2005 or later required to use MARS.');
143             }
144              
145 0 0         if (my ($data_source) = $dsn =~ /^dbi:ODBC:([\w-]+)\z/i) { # prefix with DSN
146 0           carp_unique "Bare DSN in ODBC connect string, rewriting as 'dsn=$data_source'"
147             ." for MARS\n";
148 0           $dsn = "dbi:ODBC:dsn=$data_source";
149             }
150              
151 0           $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes";
152 0           $self->disconnect;
153 0           $self->ensure_connected;
154             }
155             }
156              
157             sub connect_call_use_MARS {
158 0     0 0   carp "'connect_call_use_MARS' has been deprecated, use "
159             ."'connect_call_use_mars' instead.";
160 0           shift->connect_call_use_mars(@_)
161             }
162              
163             =head2 connect_call_use_dynamic_cursors
164              
165             Use as:
166              
167             on_connect_call => 'use_dynamic_cursors'
168              
169             Which will add C<< odbc_cursortype => 2 >> to your DBI connection
170             attributes, or alternatively specify the necessary flag directly:
171              
172             Your::Schema->connect (@dsn, { ... odbc_cursortype => 2 })
173              
174             See L for more information.
175              
176             If you're using FreeTDS, C must be set to at least C<8.0>.
177              
178             This will not work with CODE ref connect_info's.
179              
180             B on FreeTDS (and maybe some other drivers) this will break
181             C, and C
182             Server 2005 and later will return erroneous results on tables which have an on
183             insert trigger that inserts into another table with an C column.
184              
185             B on FreeTDS, changes made in one statement (e.g. an insert) may not
186             be visible from a following statement (e.g. a select.)
187              
188             B FreeTDS versions > 0.82 seem to have completely broken the ODBC
189             protocol. DBIC will not allow dynamic cursor support with such versions to
190             protect your data. Please hassle the authors of FreeTDS to act on the bugs that
191             make their driver not overly usable with DBD::ODBC.
192              
193             =cut
194              
195             sub connect_call_use_dynamic_cursors {
196 0     0 1   my $self = shift;
197              
198 0 0 0       if (($self->_dbic_connect_attributes->{odbc_cursortype} || 0) < 2) {
199              
200 0           my $dbi_inf = $self->_dbi_connect_info;
201              
202 0 0         $self->throw_exception ('Cannot set DBI attributes on a CODE ref connect_info')
203             if ref($dbi_inf->[0]) eq 'CODE';
204              
205             # reenter connection information with the attribute re-set
206 0 0         $dbi_inf->[3] = {} if @$dbi_inf <= 3;
207 0           $dbi_inf->[3]{odbc_cursortype} = 2;
208              
209 0           $self->_dbi_connect_info($dbi_inf);
210              
211 0           $self->disconnect; # resetting dbi attrs, so have to reconnect
212 0           $self->ensure_connected;
213             }
214             }
215              
216             sub _run_connection_actions {
217 0     0     my $self = shift;
218              
219 0           $self->next::method (@_);
220              
221             # keep the dynamic_cursors_support and driver-state in sync
222             # on every reconnect
223 0   0       my $use_dyncursors = ($self->_dbic_connect_attributes->{odbc_cursortype} || 0) > 1;
224 0 0 0       if (
225             $use_dyncursors
226             xor
227             !!$self->_using_dynamic_cursors
228             ) {
229 0 0         if ($use_dyncursors) {
230             try {
231 0     0     my $dbh = $self->_dbh;
232 0           local $dbh->{RaiseError} = 1;
233 0           local $dbh->{PrintError} = 0;
234 0           $dbh->do('SELECT @@IDENTITY');
235             } catch {
236 0 0   0     $self->throw_exception (
237             'Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2).'
238             . (
239             $self->_using_freetds
240             ? ' If you are using FreeTDS, make sure to set tds_version to 8.0 or greater.'
241             : ''
242             )
243             );
244 0           };
245              
246 0           $self->_using_dynamic_cursors(1);
247 0           $self->_identity_method('@@identity');
248             }
249             else {
250 0           $self->_using_dynamic_cursors(0);
251 0           $self->_identity_method(undef);
252             }
253             }
254              
255 0 0         $self->_no_scope_identity_query($self->_using_dynamic_cursors
256             ? $self->_using_freetds
257             : undef
258             );
259              
260             # freetds is too damn broken, some fixups
261 0 0         if ($self->_using_freetds) {
262              
263             # no dynamic cursors starting from 0.83
264 0 0         if ($self->_using_dynamic_cursors) {
265 0   0       my $fv = $self->_using_freetds_version || 999; # assume large if can't be determined
266 0 0         $self->throw_exception(
267             'Dynamic cursors (odbc_cursortype => 2) are not supported with FreeTDS > 0.82 '
268             . "(you have $fv). Please hassle FreeTDS authors to fix the outstanding bugs in "
269             . 'their driver.'
270             ) if $fv > 0.82
271             }
272              
273             # FreeTDS is too broken wrt execute_for_fetch batching
274             # just disable it outright until things quiet down
275 0           $self->_disable_odbc_array_ops;
276             }
277             }
278              
279             =head2 connect_call_use_server_cursors
280              
281             Use as:
282              
283             on_connect_call => 'use_server_cursors'
284              
285             May allow multiple active select statements. See
286             L for more information.
287              
288             Takes an optional parameter for the value to set the attribute to, default is
289             C<2>.
290              
291             B: this does not work on all versions of SQL Server, and may lock up
292             your database!
293              
294             At the time of writing, this option only works on Microsoft's Windows drivers,
295             later versions of the ODBC driver and the Native Client driver.
296              
297             =cut
298              
299             sub connect_call_use_server_cursors {
300 0     0 1   my $self = shift;
301 0   0       my $sql_rowset_size = shift || 2;
302              
303 0 0         if ($^O !~ /win32|cygwin/i) {
304 0           $self->throw_exception('Server cursors only work on Windows platforms at '
305             .'the time of writing.');
306             }
307              
308 0           $self->_get_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size;
309             }
310              
311             =head1 FURTHER QUESTIONS?
312              
313             Check the list of L.
314              
315             =head1 COPYRIGHT AND LICENSE
316              
317             This module is free software L
318             by the L. You can
319             redistribute it and/or modify it under the same terms as the
320             L.
321              
322             =cut
323              
324             1;
325              
326             # vim:sw=2 sts=2 et