X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2FStorage%2FDBI%2FODBC%2FMicrosoft_SQL_Server.pm;h=88627d364ae497a1e321638bb28df07649ea9b94;hb=6f7a118e7728040e015699975dafde1a1fb81538;hp=77c20ada6d5b53af1206823dbf7e01a63f9d0049;hpb=445e08ff19df2f193061025646a5b77c7c7026eb;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm b/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm index 77c20ad..88627d3 100644 --- a/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm +++ b/lib/DBIx/Class/Storage/DBI/ODBC/Microsoft_SQL_Server.pm @@ -4,10 +4,10 @@ use warnings; use base qw/DBIx::Class::Storage::DBI::MSSQL/; use mro 'c3'; - +use Scalar::Util 'reftype'; +use Try::Tiny; use Carp::Clan qw/^DBIx::Class/; -use List::Util(); -use Scalar::Util (); +use namespace::clean; __PACKAGE__->mk_group_accessors(simple => qw/ _using_dynamic_cursors @@ -27,93 +27,217 @@ MSSQL back-end. Most of the functionality is provided from the superclass L. +=head1 USAGE NOTES + +=head2 Basic Linux Setup (Debian) + + sudo aptitude install tdsodbc libdbd-odbc-perl unixodbc + +In case it is not already there put the following in C: + + [FreeTDS] + Description = FreeTDS + Driver = /usr/lib/odbc/libtdsodbc.so + Setup = /usr/lib/odbc/libtdsS.so + UsageCount = 1 + +Set your C<$dsn> in L as follows: + + dbi:ODBC:server=;port=1433;driver=FreeTDS;tds_version=8.0 + +If you use the EasySoft driver (L): + + dbi:ODBC:server=;port=1433;driver=Easysoft ODBC-SQL Server + +=head2 Basic Windows Setup + +Use the following C<$dsn> for the Microsoft ODBC driver: + + dbi:ODBC:driver={SQL Server};server=SERVER\SQL_SERVER_INSTANCE_NAME + +And for the Native Client: + + dbi:ODBC:driver={SQL Server Native Client 10.0};server=SERVER\SQL_SERVER_INSTANCE_NAME + +Go into Control Panel -> System and Security -> Administrative Tools -> Data +Sources (ODBC) to check driver names and to set up data sources. + +Use System DSNs, not User DSNs if you want to use DSNs. + +If you set up a DSN, use the following C<$dsn> for +L: + + dbi:ODBC:dsn=MY_DSN + =head1 MULTIPLE ACTIVE STATEMENTS The following options are alternative ways to enable concurrent executing -statement support. Each has its own advantages and drawbacks. +statement support. Each has its own advantages and drawbacks and works on +different platforms. Read each section carefully. -=head2 connect_call_use_dynamic_cursors +In order of preference, they are: -Use as: +=over 8 - on_connect_call => 'use_dynamic_cursors' +=item * L -in your L as one way to enable multiple -concurrent statements. +=item * L -Will add C<< odbc_cursortype => 2 >> to your DBI connection attributes. See -L for more information. +=item * L -Alternatively, you can add it yourself and dynamic cursor support will be -automatically enabled. +=back -If you're using FreeTDS, C must be set to at least C<8.0>. +=head1 METHODS -This will not work with CODE ref connect_info's. +=head2 connect_call_use_mars + +Use as: + + on_connect_call => 'use_mars' + +in your connection info, or alternatively specify it directly: + + Your::Schema->connect ( + $original_dsn . '; MARS_Connection=Yes', + $user, + $pass, + \%attrs, + ) + +Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result +Sets". See L +for more information. -B this will break C, and C will be used instead, which on SQL +Server 2005 and later will return erroneous results on tables which have an on +insert trigger that inserts into another table with an C column. + +=cut + +sub connect_call_use_dynamic_cursors { my $self = shift; - my $dbh = $self->_dbh; - - eval { - local $dbh->{RaiseError} = 1; - local $dbh->{PrintError} = 0; - $dbh->do('SELECT @@IDENTITY'); - }; - if ($@) { - croak <<'EOF'; - -Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2), -if you're using FreeTDS, make sure to set tds_version to 8.0 or greater. -EOF + + my $conn_info = $self->_dbi_connect_info; + + if (ref($conn_info->[0]) eq 'CODE') { + $self->throw_exception ('Cannot set DBI attributes on a CODE ref connect_info'); } - $self->_using_dynamic_cursors(1); - $self->_identity_method('@@identity'); + if ( + ref($conn_info->[-1]) ne 'HASH' + or + ($conn_info->[-1]{odbc_cursortype}||0) < 2 + ) { + # reenter connection information with the attribute re-set + $self->connect_info( + @{$conn_info}[0,1,2], + { %{$self->_dbix_connect_attributes}, odbc_cursortype => 2 }, + ); + $self->disconnect; # resetting dbi attrs, so have to reconnect + $self->ensure_connected; + } } -sub _rebless { - no warnings 'uninitialized'; +sub _run_connection_actions { my $self = shift; - if (ref($self->_dbi_connect_info->[0]) ne 'CODE' && - eval { $self->_dbi_connect_info->[-1]{odbc_cursortype} } == 2) { - $self->_set_dynamic_cursors; - return; + # keep the dynamic_cursors_support and driver-state in sync + # on every reconnect + my $use_dyncursors = ($self->_dbic_connect_attributes->{odbc_cursortype} || 0) > 1; + if ( + $use_dyncursors + xor + !!$self->_using_dynamic_cursors + ) { + if ($use_dyncursors) { + try { + my $dbh = $self->_dbh; + local $dbh->{RaiseError} = 1; + local $dbh->{PrintError} = 0; + $dbh->do('SELECT @@IDENTITY'); + } catch { + $self->throw_exception ( + 'Your drivers do not seem to support dynamic cursors (odbc_cursortype => 2).' + . ( + $self->using_freetds + ? ' If you are using FreeTDS, make sure to set tds_version to 8.0 or greater.' + : '' + ) + ); + }; + + $self->_using_dynamic_cursors(1); + $self->_identity_method('@@identity'); + } + else { + $self->_using_dynamic_cursors(0); + $self->_identity_method(undef); + } } - $self->_using_dynamic_cursors(0); + $self->next::method (@_); } =head2 connect_call_use_server_cursors @@ -131,44 +255,41 @@ C<2>. B: this does not work on all versions of SQL Server, and may lock up your database! +At the time of writing, this option only works on Microsoft's Windows drivers, +later versions of the ODBC driver and the Native Client driver. + =cut sub connect_call_use_server_cursors { my $self = shift; my $sql_rowset_size = shift || 2; - $self->_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size; -} - -=head2 connect_call_use_MARS - -Use as: + if ($^O !~ /win32|cygwin/i) { + $self->throw_exception('Server cursors only work on Windows platforms at ' + .'the time of writing.'); + } - on_connect_call => 'use_MARS' + $self->_get_dbh->{odbc_SQL_ROWSET_SIZE} = $sql_rowset_size; +} -Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result -Sets". See L -for more information. +=head2 using_freetds -B: This has implications for the way transactions are handled. +Tries to determine, to the best of our ability, whether or not you are using the +FreeTDS driver with L. =cut -sub connect_call_use_MARS { +sub using_freetds { my $self = shift; my $dsn = $self->_dbi_connect_info->[0]; - if (ref($dsn) eq 'CODE') { - croak 'cannot change the DBI DSN on a CODE ref connect_info'; - } + $dsn = '' if ref $dsn eq 'CODE'; - if ($dsn !~ /MARS_Connection=/) { - $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes"; - my $connected = defined $self->_dbh; - $self->disconnect; - $self->ensure_connected if $connected; - } + return 1 if $dsn =~ /driver=FreeTDS/i + || ($self->_dbh_get_info(6)||'') =~ /tdsodbc/i; + + return 0; } 1;