File Coverage

blib/lib/DBIx/Class/Storage/DBI/ADO/Microsoft_SQL_Server.pm
Criterion Covered Total %
statement 21 87 24.1
branch 0 18 0.0
condition 0 18 0.0
subroutine 7 16 43.7
pod 2 2 100.0
total 30 141 21.2


line stmt bran cond sub pod time code
1             package DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server;
2              
3 2     2   1211 use strict;
  2         4  
  2         56  
4 2     2   9 use warnings;
  2         4  
  2         55  
5              
6 2         666 use base qw/
7             DBIx::Class::Storage::DBI::ADO
8             DBIx::Class::Storage::DBI::MSSQL
9 2     2   9 /;
  2         5  
10 2     2   14 use mro 'c3';
  2         4  
  2         9  
11 2     2   72 use DBIx::Class::Carp;
  2         4  
  2         17  
12 2     2   13 use DBIx::Class::Storage::DBI::ADO::CursorUtils qw/_normalize_guids _strip_trailing_binary_nulls/;
  2         5  
  2         100  
13 2     2   13 use namespace::clean;
  2         4  
  2         15  
14              
15             __PACKAGE__->cursor_class(
16             'DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::Cursor'
17             );
18              
19             __PACKAGE__->datetime_parser_type (
20             'DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::DateTime::Format'
21             );
22              
23             __PACKAGE__->new_guid(sub {
24             my $self = shift;
25             my $guid = $self->_get_dbh->selectrow_array('SELECT NEWID()');
26             $guid =~ s/\A \{ (.+) \} \z/$1/xs;
27             return $guid;
28             });
29              
30             =head1 NAME
31              
32             DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server - Support for Microsoft
33             SQL Server via DBD::ADO
34              
35             =head1 SYNOPSIS
36              
37             This subclass supports MSSQL server connections via L<DBD::ADO>.
38              
39             =head1 DESCRIPTION
40              
41             The MSSQL specific functionality is provided by
42             L<DBIx::Class::Storage::DBI::MSSQL>.
43              
44             =head1 EXAMPLE DSN
45              
46             dbi:ADO:provider=sqlncli10;server=EEEBOX\SQLEXPRESS
47              
48             =head1 CAVEATS
49              
50             =head2 identities
51              
52             C<_identity_method> is set to C<@@identity>, as C<SCOPE_IDENTITY()> doesn't work
53             with L<DBD::ADO>. See L<DBIx::Class::Storage::DBI::MSSQL/IMPLEMENTATION NOTES>
54             for caveats regarding this.
55              
56             =head2 truncation bug
57              
58             There is a bug with MSSQL ADO providers where data gets truncated based on the
59             size of the bind sizes in the first prepare call:
60              
61             L<https://rt.cpan.org/Ticket/Display.html?id=52048>
62              
63             The C<ado_size> workaround is used (see L<DBD::ADO/ADO providers>) with the
64             approximate maximum size of the data_type of the bound column, or 8000 (maximum
65             VARCHAR size) if the data_type is not available.
66              
67             Please report problems with this driver and send patches.
68              
69             =head2 LongReadLen
70              
71             C<LongReadLen> is set to C<LongReadLen * 2 + 1> on connection as it is necessary
72             for some LOB types. Be aware of this if you localize this value on the C<$dbh>
73             directly.
74              
75             =head2 binary data
76              
77             Due perhaps to the ado_size workaround we use, and/or other reasons, binary data
78             such as C<varbinary> column data comes back padded with trailing C<NULL> chars.
79             The Cursor class for this driver
80             (L<DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::Cursor>) removes them,
81             of course if your binary data is actually C<NULL> padded that may be an issue to
82             keep in mind when using this driver.
83              
84             =head2 uniqueidentifier columns
85              
86             uniqueidentifier columns come back from ADO wrapped in braces and must be
87             submitted to the MSSQL ADO driver wrapped in braces. We take care of this
88             transparently in this driver and the associated Cursor class
89             (L<DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::Cursor>) so that you
90             don't have to use braces in most cases (except in literal SQL, in those cases
91             you will have to add the braces yourself.)
92              
93             =head2 fractional seconds
94              
95             Fractional seconds with L<DBIx::Class::InflateColumn::DateTime> are not
96             currently supported, datetimes are truncated at the second.
97              
98             =cut
99              
100             sub _init {
101 0     0     my $self = shift;
102              
103             # SCOPE_IDENTITY() doesn't work
104 0           $self->_identity_method('@@identity');
105 0           $self->_no_scope_identity_query(1);
106              
107 0           return $self->next::method(@_);
108             }
109              
110             sub _run_connection_actions {
111 0     0     my $self = shift;
112              
113             # make transactions work
114 0           require DBD::ADO::Const;
115             $self->_dbh->{ado_conn}{CursorLocation} =
116 0           DBD::ADO::Const->Enums->{CursorLocationEnum}{adUseClient};
117              
118             # set LongReadLen = LongReadLen * 2 + 1
119             # this may need to be in ADO.pm, being conservative for now...
120 0           my $long_read_len = $self->_dbh->{LongReadLen};
121              
122             # This is the DBD::ADO default.
123 0 0         if ($long_read_len != 2147483647) {
124 0           $self->_dbh->{LongReadLen} = $long_read_len * 2 + 1;
125             }
126              
127 0           return $self->next::method(@_);
128             }
129              
130              
131             # Fix up binary data and GUIDs for ->find, for cursors see the cursor_class
132             # above.
133             sub select_single {
134 0     0 1   my $self = shift;
135 0           my ($ident, $select) = @_;
136              
137 0           my @row = $self->next::method(@_);
138              
139 0 0         return @row unless $self->cursor_class->isa(
140             'DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::Cursor'
141             );
142              
143 0           my $col_infos = $self->_resolve_column_info($ident);
144              
145 0           _normalize_guids($select, $col_infos, \@row, $self);
146              
147 0           _strip_trailing_binary_nulls($select, $col_infos, \@row, $self);
148              
149 0           return @row;
150             }
151              
152             # We need to catch VARCHAR(max) before bind_attribute_by_data_type because it
153             # could be specified by size, also if bind_attribute_by_data_type fails we want
154             # to specify the default ado_size of 8000.
155             # Also make sure GUID binds have braces on them or else ADO throws an "Invalid
156             # character value for cast specification"
157              
158             sub _dbi_attrs_for_bind {
159 0     0     my $self = shift;
160 0           my ($ident, $bind) = @_;
161              
162 0   0       my $lob_max = $self->_get_dbh->{LongReadLen} || 32768;
163              
164 0           foreach my $bind (@$bind) {
165 0           my $attrs = $bind->[0];
166 0           my $data_type = $attrs->{sqlt_datatype};
167 0           my $size = $attrs->{sqlt_size};
168              
169 0 0 0       if ($size && lc($size) eq 'max') {
170 0 0         if ($data_type =~ /^(?:varchar|character varying|nvarchar|national char varying|national character varying|varbinary)\z/i) {
171 0           $attrs->{dbd_attrs} = { ado_size => $lob_max };
172             }
173             else {
174 0           carp_unique "bizarre data_type '$data_type' with size => 'max'";
175             }
176             }
177              
178 0 0 0       if ($self->_is_guid_type($data_type) && substr($bind->[1], 0, 1) ne '{') {
179 0           $bind->[1] = '{' . $bind->[1] . '}';
180             }
181             }
182              
183 0           my $attrs = $self->next::method(@_);
184              
185 0           foreach my $attr (@$attrs) {
186 0 0 0       $attr->{ado_size} ||= 8000 if $attr;
187             }
188              
189 0           return $attrs;
190             }
191              
192             # Can't edit all the binds in _dbi_attrs_for_bind for _insert_bulk, so we take
193             # care of those GUIDs here.
194             sub _insert_bulk {
195 0     0     my $self = shift;
196 0           my ($source, $cols, $data) = @_;
197              
198 0           my $columns_info = $source->columns_info($cols);
199              
200 0           my $col_idx = 0;
201 0           foreach my $col (@$cols) {
202 0 0         if ($self->_is_guid_type($columns_info->{$col}{data_type})) {
203 0           foreach my $data_row (@$data) {
204 0 0         if (substr($data_row->[$col_idx], 0, 1) ne '{') {
205 0           $data_row->[$col_idx] = '{' . $data_row->[$col_idx] . '}';
206             }
207             }
208             }
209 0           $col_idx++;
210             }
211              
212 0           return $self->next::method(@_);
213             }
214              
215             sub bind_attribute_by_data_type {
216 0     0 1   my ($self, $data_type) = @_;
217              
218 0           $data_type = lc $data_type;
219              
220             my $max_size =
221 0           $self->_mssql_max_data_type_representation_size_in_bytes->{$data_type};
222              
223 0           my $res = {};
224              
225 0 0         if ($max_size) {
226 0           $res->{ado_size} = $max_size;
227             }
228             else {
229 0           carp_unique "could not map data_type '$data_type' to a max size for ado_size: defaulting to 8000";
230             }
231              
232 0           return $res;
233             }
234              
235             # FIXME This list is an abomination. We need a way to do this outside
236             # of the scope of DBIC, as it is right now nobody will ever think to
237             # even look here to diagnose some sort of misbehavior.
238             sub _mssql_max_data_type_representation_size_in_bytes {
239 0     0     my $self = shift;
240              
241 0   0       my $lob_max = $self->_get_dbh->{LongReadLen} || 32768;
242              
243             return +{
244             # MSSQL types
245 0           char => 8000,
246             character => 8000,
247             varchar => 8000,
248             'varchar(max)' => $lob_max,
249             'character varying' => 8000,
250             binary => 8000,
251             varbinary => 8000,
252             'varbinary(max)' => $lob_max,
253             nchar => 16000,
254             'national character' => 16000,
255             'national char' => 16000,
256             nvarchar => 16000,
257             'nvarchar(max)' => ($lob_max*2),
258             'national character varying' => 16000,
259             'national char varying' => 16000,
260             numeric => 100,
261             smallint => 100,
262             tinyint => 100,
263             smallmoney => 100,
264             bigint => 100,
265             bit => 100,
266             decimal => 100,
267             dec => 100,
268             integer => 100,
269             int => 100,
270             'int identity' => 100,
271             'integer identity' => 100,
272             money => 100,
273             float => 100,
274             double => 100,
275             'double precision' => 100,
276             real => 100,
277             uniqueidentifier => 100,
278             ntext => $lob_max,
279             text => $lob_max,
280             image => $lob_max,
281             date => 100,
282             datetime => 100,
283             datetime2 => 100,
284             datetimeoffset => 100,
285             smalldatetime => 100,
286             time => 100,
287             timestamp => 100,
288             cursor => 100,
289             hierarchyid => 100,
290             rowversion => 100,
291             sql_variant => 100,
292             table => $lob_max,
293             xml => $lob_max,
294              
295             # mysql types
296             bool => 100,
297             boolean => 100,
298             'tinyint unsigned' => 100,
299             'smallint unsigned' => 100,
300             'mediumint unsigned' => 100,
301             'int unsigned' => 100,
302             'integer unsigned' => 100,
303             'bigint unsigned' => 100,
304             'float unsigned' => 100,
305             'double unsigned' => 100,
306             'double precision unsigned' => 100,
307             'decimal unsigned' => 100,
308             'fixed' => 100,
309             'year' => 100,
310             tinyblob => $lob_max,
311             tinytext => $lob_max,
312             blob => $lob_max,
313             text => $lob_max,
314             mediumblob => $lob_max,
315             mediumtext => $lob_max,
316             longblob => $lob_max,
317             longtext => $lob_max,
318             enum => 100,
319             set => 8000,
320              
321             # Pg types
322             serial => 100,
323             bigserial => 100,
324             int8 => 100,
325             integer8 => 100,
326             serial8 => 100,
327             int4 => 100,
328             integer4 => 100,
329             serial4 => 100,
330             int2 => 100,
331             integer2 => 100,
332             float8 => 100,
333             float4 => 100,
334             'bit varying' => 8000,
335             'varbit' => 8000,
336             inet => 100,
337             cidr => 100,
338             macaddr => 100,
339             'time without time zone' => 100,
340             'time with time zone' => 100,
341             'timestamp without time zone' => 100,
342             'timestamp with time zone' => 100,
343             bytea => $lob_max,
344              
345             # DB2 types
346             graphic => 8000,
347             vargraphic => 8000,
348             'long vargraphic' => $lob_max,
349             dbclob => $lob_max,
350             clob => $lob_max,
351             'char for bit data' => 8000,
352             'varchar for bit data' => 8000,
353             'long varchar for bit data' => $lob_max,
354              
355             # oracle types
356             varchar2 => 8000,
357             binary_float => 100,
358             binary_double => 100,
359             raw => 8000,
360             nclob => $lob_max,
361             long => $lob_max,
362             'long raw' => $lob_max,
363             'timestamp with local time zone' => 100,
364              
365             # Sybase ASE types
366             unitext => $lob_max,
367             unichar => 16000,
368             univarchar => 16000,
369              
370             # SQL Anywhere types
371             'long varbit' => $lob_max,
372             'long bit varying' => $lob_max,
373             uniqueidentifierstr => 100,
374             'long binary' => $lob_max,
375             'long varchar' => $lob_max,
376             'long nvarchar' => $lob_max,
377              
378             # Firebird types
379             'char(x) character set unicode_fss' => 16000,
380             'varchar(x) character set unicode_fss' => 16000,
381             'blob sub_type text' => $lob_max,
382             'blob sub_type text character set unicode_fss' => $lob_max,
383              
384             # Informix types
385             smallfloat => 100,
386             byte => $lob_max,
387             lvarchar => 8000,
388             'datetime year to fraction(5)' => 100,
389             # FIXME add other datetime types
390              
391             # MS Access types
392             autoincrement => 100,
393             long => 100,
394             integer4 => 100,
395             integer2 => 100,
396             integer1 => 100,
397             logical => 100,
398             logical1 => 100,
399             yesno => 100,
400             currency => 100,
401             single => 100,
402             ieeesingle => 100,
403             ieeedouble => 100,
404             number => 100,
405             string => 8000,
406             guid => 100,
407             longchar => $lob_max,
408             memo => $lob_max,
409             longbinary => $lob_max,
410             }
411             }
412              
413             package # hide from PAUSE
414             DBIx::Class::Storage::DBI::ADO::Microsoft_SQL_Server::DateTime::Format;
415              
416             my $datetime_format = '%m/%d/%Y %I:%M:%S %p';
417             my $datetime_parser;
418              
419             sub parse_datetime {
420 0     0     shift;
421 0           require DateTime::Format::Strptime;
422 0   0       $datetime_parser ||= DateTime::Format::Strptime->new(
423             pattern => $datetime_format,
424             on_error => 'croak',
425             );
426 0           return $datetime_parser->parse_datetime(shift);
427             }
428              
429             sub format_datetime {
430 0     0     shift;
431 0           require DateTime::Format::Strptime;
432 0   0       $datetime_parser ||= DateTime::Format::Strptime->new(
433             pattern => $datetime_format,
434             on_error => 'croak',
435             );
436 0           return $datetime_parser->format_datetime(shift);
437             }
438              
439             =head1 FURTHER QUESTIONS?
440              
441             Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
442              
443             =head1 COPYRIGHT AND LICENSE
444              
445             This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
446             by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
447             redistribute it and/or modify it under the same terms as the
448             L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
449              
450             =cut
451              
452             1;
453              
454             # vim:sts=2 sw=2: