1package DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server; 2use strict; 3use warnings; 4 5use base qw/DBIx::Class::Storage::DBI::MSSQL/; 6use mro 'c3'; 7 8use List::Util(); 9use Scalar::Util (); 10 11__PACKAGE__->mk_group_accessors(simple => qw/ 12 _using_dynamic_cursors 13/); 14 15=head1 NAME 16 17DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server - Support specific 18to Microsoft SQL Server over ODBC 19 20=head1 DESCRIPTION 21 22This class implements support specific to Microsoft SQL Server over ODBC. It is 23loaded automatically by by DBIx::Class::Storage::DBI::ODBC when it detects a 24MSSQL back-end. 25 26Most of the functionality is provided from the superclass 27L<DBIx::Class::Storage::DBI::MSSQL>. 28 29=head1 MULTIPLE ACTIVE STATEMENTS 30 31The following options are alternative ways to enable concurrent executing 32statement support. Each has its own advantages and drawbacks. 33 34=head2 connect_call_use_dynamic_cursors 35 36Use as: 37 38 on_connect_call => 'use_dynamic_cursors' 39 40in your L<DBIx::Class::Storage::DBI/connect_info> as one way to enable multiple 41concurrent statements. 42 43Will add C<< odbc_cursortype => 2 >> to your DBI connection attributes. See 44L<DBD::ODBC/odbc_cursortype> for more information. 45 46Alternatively, you can add it yourself and dynamic cursor support will be 47automatically enabled. 48 49If you're using FreeTDS, C<tds_version> must be set to at least C<8.0>. 50 51This will not work with CODE ref connect_info's. 52 53B<WARNING:> this will break C<SCOPE_IDENTITY()>, and C<SELECT @@IDENTITY> will 54be used instead, which on SQL Server 2005 and later will return erroneous 55results on tables which have an on insert trigger that inserts into another 56table with an C<IDENTITY> column. 57 58=cut 59 60sub connect_call_use_dynamic_cursors { 61 my $self = shift; 62 63 if (ref($self->_dbi_connect_info->[0]) eq 'CODE') { 64 $self->throw_exception ('Cannot set DBI attributes on a CODE ref connect_info'); 65 } 66 67 my $dbi_attrs = $self->_dbi_connect_info->[-1]; 68 69 unless (ref($dbi_attrs) && Scalar::Util::reftype($dbi_attrs) eq 'HASH') { 70 $dbi_attrs = {}; 71 push @{ $self->_dbi_connect_info }, $dbi_attrs; 72 } 73 74 if (not exists $dbi_attrs->{odbc_cursortype}) { 75 # turn on support for multiple concurrent statements, unless overridden 76 $dbi_attrs->{odbc_cursortype} = 2; 77 $self->disconnect; # resetting dbi attrs, so have to reconnect 78 $self->ensure_connected; 79 $self->_set_dynamic_cursors; 80 } 81} 82 83sub _set_dynamic_cursors { 84 my $self = shift; 85 my $dbh = $self->_get_dbh; 86 87 eval { 88 local $dbh->{RaiseError} = 1; 89 local $dbh->{PrintError} = 0; 90 $dbh->do('SELECT @@IDENTITY'); 91 }; 92 if ($@) { 93 $self->throw_exception (<<'EOF'); 94 95Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2), 96if you're using FreeTDS, make sure to set tds_version to 8.0 or greater. 97EOF 98 } 99 100 $self->_using_dynamic_cursors(1); 101 $self->_identity_method('@@identity'); 102} 103 104sub _init { 105 my $self = shift; 106 107 no warnings qw/uninitialized/; 108 109 if ( 110 ref($self->_dbi_connect_info->[0]) ne 'CODE' 111 && 112 ref ($self->_dbi_connect_info->[-1]) eq 'HASH' 113 && 114 $self->_dbi_connect_info->[-1]{odbc_cursortype} == 2 115 ) { 116 $self->_set_dynamic_cursors; 117 return; 118 } 119 120 $self->_using_dynamic_cursors(0); 121} 122 123=head2 connect_call_use_server_cursors 124 125Use as: 126 127 on_connect_call => 'use_server_cursors' 128 129May allow multiple active select statements. See 130L<DBD::ODBC/odbc_SQL_ROWSET_SIZE> for more information. 131 132Takes an optional parameter for the value to set the attribute to, default is 133C<2>. 134 135B<WARNING>: this does not work on all versions of SQL Server, and may lock up 136your database! 137 138=cut 139 140sub connect_call_use_server_cursors { 141 my $self = shift; 142 my $sql_rowset_size = shift || 2; 143 144 $self->_get_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size; 145} 146 147=head2 connect_call_use_MARS 148 149Use as: 150 151 on_connect_call => 'use_MARS' 152 153Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result 154Sets". See L<DBD::ODBC::FAQ/Does DBD::ODBC support Multiple Active Statements?> 155for more information. 156 157B<WARNING>: This has implications for the way transactions are handled. 158 159=cut 160 161sub connect_call_use_MARS { 162 my $self = shift; 163 164 my $dsn = $self->_dbi_connect_info->[0]; 165 166 if (ref($dsn) eq 'CODE') { 167 $self->throw_exception('cannot change the DBI DSN on a CODE ref connect_info'); 168 } 169 170 if ($dsn !~ /MARS_Connection=/) { 171 $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes"; 172 my $was_connected = defined $self->_dbh; 173 $self->disconnect; 174 $self->ensure_connected if $was_connected; 175 } 176} 177 178sub _get_mssql_version { 179 my $self = shift; 180 181 my ($version) = $self->_get_dbh->get_info(18) =~ /^(\d+)/; 182 183 return $version; 184} 185 1861; 187 188=head1 AUTHOR 189 190See L<DBIx::Class/CONTRIBUTORS>. 191 192=head1 LICENSE 193 194You may distribute this code under the same terms as Perl itself. 195 196=cut 197# vim: sw=2 sts=2 198