File Coverage

blib/lib/DBIx/Class/Storage/DBI/ADO/Microsoft_SQL_Server.pm
Criterion Covered Total %
statement 24 89 26.9
branch 0 16 0.0
condition 0 21 0.0
subroutine 8 17 47.0
pod 2 2 100.0
total 34 145 23.4


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