rename _scope_identity to _identity for odbc/mssql
[dbsrgits/DBIx-Class.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/
14c82fd4 11 _identity _using_dynamic_cursors
ef131d82 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') {
18ac986d 71 croak 'cannot set DBI attributes on a CODE ref connect_info';
ef131d82 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 };
1da8d083 80 my $connected = defined $self->_dbh;
ef131d82 81 $self->disconnect;
1da8d083 82 $self->ensure_connected if $connected;
ef131d82 83 $self->_using_dynamic_cursors(1);
84 }
85}
86
87sub _rebless {
88 no warnings 'uninitialized';
89 my $self = shift;
90
91 if (ref($self->_dbi_connect_info->[0]) ne 'CODE' &&
92 $self->_dbi_connect_info->[-1]{odbc_cursortype} == 2) {
93 $self->_using_dynamic_cursors(1);
94 return;
95 }
96
97 $self->_using_dynamic_cursors(0);
98}
99
18ac986d 100=head2 connect_call_use_server_cursors
101
102Use as:
103
104 on_connect_call => 'use_server_cursors'
105
106May allow multiple active select statements. See
107L<DBD::ODBC/odbc_SQL_ROWSET_SIZE> for more information.
108
109Takes an optional parameter for the value to set the attribute to, default is
110C<2>.
111
112B<WARNING>: this does not work on all versions of SQL Server, and may lock up
113your database!
114
115=cut
116
117=head2 connect_call_use_mars
118
119Use as:
120
121 on_connect_call => 'use_mars'
122
123Use to enable a feature of SQL Server 2005 and later, "Multiple Active Result
124Sets". See L<DBD::ODBC::FAQ/Does DBD::ODBC support Multiple Active Statements?>
125for more information.
126
127B<WARNING>: This has implications for the way transactions are handled.
128
129=cut
130
131sub connect_call_use_mars {
132 my $self = shift;
133
134 my $dsn = $self->_dbi_connect_info->[0];
135
136 if (ref($dsn) eq 'CODE') {
137 croak 'cannot change the DBI DSN on a CODE ref connect_info';
138 }
139
140 if ($dsn !~ /MARS_Connection=/) {
141 $self->_dbi_connect_info->[0] = "$dsn;MARS_Connection=Yes";
1da8d083 142 my $connected = defined $self->_dbh;
18ac986d 143 $self->disconnect;
1da8d083 144 $self->ensure_connected if $connected;
18ac986d 145 }
146}
147
c7963907 148sub insert_bulk {
76212d30 149 my $self = shift;
150 my ($source, $cols, $data) = @_;
134a3bb9 151
152 my $identity_insert = 0;
153
154 COLUMNS:
155 foreach my $col (@{$cols}) {
156 if ($source->column_info($col)->{is_auto_increment}) {
157 $identity_insert = 1;
158 last COLUMNS;
159 }
160 }
161
05f7f61a 162 if ($identity_insert) {
893403c8 163 my $table = $source->from;
ef131d82 164 $self->_get_dbh->do("SET IDENTITY_INSERT $table ON");
05f7f61a 165 }
134a3bb9 166
76212d30 167 $self->next::method(@_);
134a3bb9 168
05f7f61a 169 if ($identity_insert) {
893403c8 170 my $table = $source->from;
ef131d82 171 $self->_get_dbh->do("SET IDENTITY_INSERT $table OFF");
05f7f61a 172 }
c7963907 173}
174
c1cac633 175sub _prep_for_execute {
259c0e40 176 my $self = shift;
177 my ($op, $extra_bind, $ident, $args) = @_;
178
179 my ($sql, $bind) = $self->next::method (@_);
259c0e40 180
893403c8 181 if ($op eq 'insert') {
182 $sql .= ';SELECT SCOPE_IDENTITY()';
183
184 my $col_info = $self->_resolve_column_info($ident, [map $_->[0], @{$bind}]);
185 if (List::Util::first { $_->{is_auto_increment} } (values %$col_info) ) {
764a1b60 186
893403c8 187 my $table = $ident->from;
188 my $identity_insert_on = "SET IDENTITY_INSERT $table ON";
189 my $identity_insert_off = "SET IDENTITY_INSERT $table OFF";
190 $sql = "$identity_insert_on; $sql; $identity_insert_off";
259c0e40 191 }
192 }
c1cac633 193
259c0e40 194 return ($sql, $bind);
c1cac633 195}
196
2eebd801 197sub _execute {
198 my $self = shift;
199 my ($op) = @_;
c1cac633 200
2eebd801 201 my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
77af494b 202 if ($op eq 'insert') {
ef131d82 203 my ($identity) = $sth->fetchrow_array;
77af494b 204 $sth->finish;
ef131d82 205
206 if ((not defined $identity) && $self->_using_dynamic_cursors) {
207 ($identity) = $self->_dbh->selectrow_array('select @@identity');
208 }
209
14c82fd4 210 $self->_identity($identity);
77af494b 211 }
c1cac633 212
2eebd801 213 return wantarray ? ($rv, $sth, @bind) : $rv;
c1cac633 214}
215
14c82fd4 216sub last_insert_id { shift->_identity() }
c1cac633 217
c1cac633 2181;
219
ef131d82 220=head1 AUTHOR
c1cac633 221
ef131d82 222See L<DBIx::Class/CONTRIBUTORS>.
c1cac633 223
224=head1 LICENSE
225
226You may distribute this code under the same terms as Perl itself.
227
228=cut
ef131d82 229
259c0e40 230# vim: sw=2 sts=2