Merge 'trunk' into 'reduce_pings'
[dbsrgits/DBIx-Class-Historic.git] / lib / DBIx / Class / Storage / DBI / ODBC / Microsoft_SQL_Server.pm
CommitLineData
c1cac633 1package DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server;
2use strict;
3use warnings;
4
eb0323df 5use base qw/DBIx::Class::Storage::DBI::MSSQL/;
2ad62d97 6use mro 'c3';
ef131d82 7use Carp::Clan qw/^DBIx::Class/;
893403c8 8use List::Util();
c1cac633 9
ef131d82 10__PACKAGE__->mk_group_accessors(simple => qw/
11 _scope_identity _using_dynamic_cursors
12/);
13
14=head1 NAME
15
16DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server - Support specific
17to Microsoft SQL Server over ODBC
18
19=head1 DESCRIPTION
20
21This class implements support specific to Microsoft SQL Server over ODBC,
22including auto-increment primary keys and SQL::Abstract::Limit dialect. It
23is loaded automatically by by DBIx::Class::Storage::DBI::ODBC when it
24detects a MSSQL back-end.
25
26=head1 IMPLEMENTATION NOTES
27
28Microsoft SQL Server supports three methods of retrieving the C<IDENTITY>
29value for inserted row: C<IDENT_CURRENT>, C<@@IDENTITY>, and C<SCOPE_IDENTITY()>.
30C<SCOPE_IDENTITY()> is used here because it is the safest. However, it must
31be called is the same execute statement, not just the same connection.
32
33So, this implementation appends a C<SELECT SCOPE_IDENTITY()> statement
34onto each C<INSERT> to accommodate that requirement.
35
36If you use dynamic cursors with C<< odbc_cursortype => 2 >> or
37L</on_connect_call_use_dynamic_cursors> then the less accurate
38C<SELECT @@IDENTITY> is used instead.
39
40=head1 MULTIPLE ACTIVE STATEMENTS
41
42The following options are alternative ways to enable concurrent executing
43statement support. Each has its own advantages and drawbacks.
44
45=head2 connect_call_use_dynamic_cursors
46
47Use as:
48
49 on_connect_call => 'use_dynamic_cursors'
50
51in your L<DBIx::Class::Storage::DBI/connect_info> as one way to enable multiple
52concurrent statements.
53
54Will add C<< odbc_cursortype => 2 >> to your DBI connection attributes. See
55L<DBD::ODBC/odbc_cursortype> for more information.
56
57This will not work with CODE ref connect_info's and will do nothing if you set
58C<odbc_cursortype> yourself.
59
60B<WARNING:> this will break C<SCOPE_IDENTITY()>, and C<SELECT @@IDENTITY> will
61be used instead, which on SQL Server 2005 and later will return erroneous
62results on tables which have an on insert trigger that inserts into another
63table with an C<IDENTITY> column.
64
65=cut
66
67sub connect_call_use_dynamic_cursors {
68 my $self = shift;
69
70 if (ref($self->_dbi_connect_info->[0]) eq 'CODE') {
71 croak 'cannot set DBI attributes on CODE ref connect_infos';
72 }
73
74 my $dbi_attrs = $self->_dbi_connect_info->[-1];
75 $dbi_attrs ||= {};
76
77 if (not exists $dbi_attrs->{odbc_cursortype}) {
78 # turn on support for multiple concurrent statements, unless overridden
79 $self->_dbi_connect_info->[-1] = { %$dbi_attrs, odbc_cursortype => 2 };
80 # will take effect next connection
81 $self->disconnect;
82 $self->_using_dynamic_cursors(1);
83 }
84}
85
86sub _rebless {
87 no warnings 'uninitialized';
88 my $self = shift;
89
90 if (ref($self->_dbi_connect_info->[0]) ne 'CODE' &&
91 $self->_dbi_connect_info->[-1]{odbc_cursortype} == 2) {
92 $self->_using_dynamic_cursors(1);
93 return;
94 }
95
96 $self->_using_dynamic_cursors(0);
97}
98
c7963907 99sub insert_bulk {
76212d30 100 my $self = shift;
101 my ($source, $cols, $data) = @_;
134a3bb9 102
103 my $identity_insert = 0;
104
105 COLUMNS:
106 foreach my $col (@{$cols}) {
107 if ($source->column_info($col)->{is_auto_increment}) {
108 $identity_insert = 1;
109 last COLUMNS;
110 }
111 }
112
05f7f61a 113 if ($identity_insert) {
893403c8 114 my $table = $source->from;
ef131d82 115 $self->_get_dbh->do("SET IDENTITY_INSERT $table ON");
05f7f61a 116 }
134a3bb9 117
76212d30 118 $self->next::method(@_);
134a3bb9 119
05f7f61a 120 if ($identity_insert) {
893403c8 121 my $table = $source->from;
ef131d82 122 $self->_get_dbh->do("SET IDENTITY_INSERT $table OFF");
05f7f61a 123 }
c7963907 124}
125
c1cac633 126sub _prep_for_execute {
259c0e40 127 my $self = shift;
128 my ($op, $extra_bind, $ident, $args) = @_;
129
130 my ($sql, $bind) = $self->next::method (@_);
259c0e40 131
893403c8 132 if ($op eq 'insert') {
133 $sql .= ';SELECT SCOPE_IDENTITY()';
134
135 my $col_info = $self->_resolve_column_info($ident, [map $_->[0], @{$bind}]);
136 if (List::Util::first { $_->{is_auto_increment} } (values %$col_info) ) {
764a1b60 137
893403c8 138 my $table = $ident->from;
139 my $identity_insert_on = "SET IDENTITY_INSERT $table ON";
140 my $identity_insert_off = "SET IDENTITY_INSERT $table OFF";
141 $sql = "$identity_insert_on; $sql; $identity_insert_off";
259c0e40 142 }
143 }
c1cac633 144
259c0e40 145 return ($sql, $bind);
c1cac633 146}
147
2eebd801 148sub _execute {
149 my $self = shift;
150 my ($op) = @_;
c1cac633 151
2eebd801 152 my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
77af494b 153 if ($op eq 'insert') {
ef131d82 154 my ($identity) = $sth->fetchrow_array;
77af494b 155 $sth->finish;
ef131d82 156
157 if ((not defined $identity) && $self->_using_dynamic_cursors) {
158 ($identity) = $self->_dbh->selectrow_array('select @@identity');
159 }
160
161 $self->_scope_identity($identity);
77af494b 162 }
c1cac633 163
2eebd801 164 return wantarray ? ($rv, $sth, @bind) : $rv;
c1cac633 165}
166
ef131d82 167sub last_insert_id { shift->_scope_identity() }
c1cac633 168
c1cac633 1691;
170
ef131d82 171=head1 AUTHOR
c1cac633 172
ef131d82 173See L<DBIx::Class/CONTRIBUTORS>.
c1cac633 174
175=head1 LICENSE
176
177You may distribute this code under the same terms as Perl itself.
178
179=cut
ef131d82 180
259c0e40 181# vim: sw=2 sts=2