Merge 'trunk' into 'try-tiny'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / MSSQL.pm
CommitLineData
75d07914 1package DBIx::Class::Storage::DBI::MSSQL;
3885cff6 2
75d07914 3use strict;
4use warnings;
3885cff6 5
548d1627 6use base qw/DBIx::Class::Storage::DBI::UniqueIdentifier/;
2ad62d97 7use mro 'c3';
ed7ab0f4 8use Try::Tiny;
3885cff6 9
5a77aa8b 10use List::Util();
11
7b1b2582 12__PACKAGE__->mk_group_accessors(simple => qw/
13 _identity _identity_method
14/);
15
ac93965c 16__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::MSSQL');
17
afcfff01 18sub _set_identity_insert {
19 my ($self, $table) = @_;
64690266 20
21 my $sql = sprintf (
afcfff01 22 'SET IDENTITY_INSERT %s ON',
64690266 23 $self->sql_maker->_quote ($table),
afcfff01 24 );
64690266 25
26 my $dbh = $self->_get_dbh;
ed7ab0f4 27 try { $dbh->do ($sql) }
28 catch {
64690266 29 $self->throw_exception (sprintf "Error executing '%s': %s",
30 $sql,
31 $dbh->errstr,
32 );
ed7ab0f4 33 };
afcfff01 34}
35
aac1a358 36sub _unset_identity_insert {
37 my ($self, $table) = @_;
38
39 my $sql = sprintf (
40 'SET IDENTITY_INSERT %s OFF',
41 $self->sql_maker->_quote ($table),
42 );
43
44 my $dbh = $self->_get_dbh;
45 $dbh->do ($sql);
46}
47
5a77aa8b 48sub insert_bulk {
49 my $self = shift;
50 my ($source, $cols, $data) = @_;
51
aac1a358 52 my $is_identity_insert = (List::Util::first
afcfff01 53 { $source->column_info ($_)->{is_auto_increment} }
54 (@{$cols})
aac1a358 55 )
56 ? 1
57 : 0;
5a77aa8b 58
aac1a358 59 if ($is_identity_insert) {
60 $self->_set_identity_insert ($source->name);
5a77aa8b 61 }
62
63 $self->next::method(@_);
64
aac1a358 65 if ($is_identity_insert) {
66 $self->_unset_identity_insert ($source->name);
5a77aa8b 67 }
68}
69
ca791b95 70sub insert {
71 my $self = shift;
72 my ($source, $to_insert) = @_;
73
afcfff01 74 my $supplied_col_info = $self->_resolve_column_info($source, [keys %$to_insert] );
ca791b95 75
aac1a358 76 my $is_identity_insert = (List::Util::first { $_->{is_auto_increment} } (values %$supplied_col_info) )
77 ? 1
78 : 0;
79
80 if ($is_identity_insert) {
81 $self->_set_identity_insert ($source->name);
afcfff01 82 }
83
548d1627 84 my $updated_cols = $self->next::method(@_);
ca791b95 85
aac1a358 86 if ($is_identity_insert) {
87 $self->_unset_identity_insert ($source->name);
88 }
89
ca791b95 90 return $updated_cols;
91}
92
5a77aa8b 93sub _prep_for_execute {
94 my $self = shift;
95 my ($op, $extra_bind, $ident, $args) = @_;
96
97# cast MONEY values properly
98 if ($op eq 'insert' || $op eq 'update') {
99 my $fields = $args->[0];
5a77aa8b 100
101 for my $col (keys %$fields) {
1537084d 102 # $ident is a result source object with INSERT/UPDATE ops
be294d66 103 if ($ident->column_info ($col)->{data_type}
104 &&
105 $ident->column_info ($col)->{data_type} =~ /^money\z/i) {
5a77aa8b 106 my $val = $fields->{$col};
107 $fields->{$col} = \['CAST(? AS MONEY)', [ $col => $val ]];
108 }
109 }
110 }
111
112 my ($sql, $bind) = $self->next::method (@_);
113
114 if ($op eq 'insert') {
115 $sql .= ';SELECT SCOPE_IDENTITY()';
116
5a77aa8b 117 }
118
119 return ($sql, $bind);
120}
121
122sub _execute {
123 my $self = shift;
124 my ($op) = @_;
125
126 my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
1537084d 127
5a77aa8b 128 if ($op eq 'insert') {
5a77aa8b 129
1537084d 130 # this should bring back the result of SELECT SCOPE_IDENTITY() we tacked
131 # on in _prep_for_execute above
9780718f 132 my ($identity) = try { $sth->fetchrow_array };
ed8de058 133
1537084d 134 # SCOPE_IDENTITY failed, but we can do something else
135 if ( (! $identity) && $self->_identity_method) {
136 ($identity) = $self->_dbh->selectrow_array(
137 'select ' . $self->_identity_method
138 );
139 }
7b1b2582 140
1537084d 141 $self->_identity($identity);
142 $sth->finish;
7b1b2582 143 }
144
1537084d 145 return wantarray ? ($rv, $sth, @bind) : $rv;
7b1b2582 146}
5a77aa8b 147
7b1b2582 148sub last_insert_id { shift->_identity }
5a77aa8b 149
f0bd60fc 150#
e74c68ce 151# MSSQL is retarded wrt ordered subselects. One needs to add a TOP
152# to *all* subqueries, but one also can't use TOP 100 PERCENT
153# http://sqladvice.com/forums/permalink/18496/22931/ShowThread.aspx#22931
f0bd60fc 154#
155sub _select_args_to_query {
156 my $self = shift;
157
b8d88d9b 158 my ($sql, $prep_bind, @rest) = $self->next::method (@_);
f0bd60fc 159
b8d88d9b 160 # see if this is an ordered subquery
161 my $attrs = $_[3];
aca481d8 162 if (
163 $sql !~ /^ \s* SELECT \s+ TOP \s+ \d+ \s+ /xi
164 &&
165 scalar $self->_parse_order_by ($attrs->{order_by})
166 ) {
6de07ea3 167 $self->throw_exception(
d74f2da9 168 'An ordered subselect encountered - this is not safe! Please see "Ordered Subselects" in DBIx::Class::Storage::DBI::MSSQL
69a8b315 169 ') unless $attrs->{unsafe_subselect_ok};
e74c68ce 170 my $max = 2 ** 32;
171 $sql =~ s/^ \s* SELECT \s/SELECT TOP $max /xi;
f0bd60fc 172 }
173
f0bd60fc 174 return wantarray
17555a0c 175 ? ($sql, $prep_bind, @rest)
176 : \[ "($sql)", @$prep_bind ]
f0bd60fc 177 ;
178}
179
180
4c0f4206 181# savepoint syntax is the same as in Sybase ASE
182
183sub _svp_begin {
184 my ($self, $name) = @_;
185
9ae966b9 186 $self->_get_dbh->do("SAVE TRANSACTION $name");
4c0f4206 187}
188
189# A new SAVE TRANSACTION with the same name releases the previous one.
190sub _svp_release { 1 }
191
192sub _svp_rollback {
193 my ($self, $name) = @_;
194
9ae966b9 195 $self->_get_dbh->do("ROLLBACK TRANSACTION $name");
4c0f4206 196}
197
fb95dc4d 198sub datetime_parser_type {
199 'DBIx::Class::Storage::DBI::MSSQL::DateTime::Format'
200}
eb0323df 201
202sub sqlt_type { 'SQLServer' }
203
50772633 204sub sql_maker {
205 my $self = shift;
eb0323df 206
50772633 207 unless ($self->_sql_maker) {
208 unless ($self->{_sql_maker_opts}{limit_dialect}) {
a218ef4e 209 my $have_rno = 0;
ff153e24 210
a218ef4e 211 if (exists $self->_server_info->{normalized_dbms_version}) {
212 $have_rno = 1 if $self->_server_info->{normalized_dbms_version} >= 9;
213 }
214 else {
215 # User is connecting via DBD::Sybase and has no permission to run
216 # stored procedures like xp_msver, or version detection failed for some
217 # other reason.
218 # So, we use a query to check if RNO is implemented.
9780718f 219 try {
220 $self->_get_dbh->selectrow_array('SELECT row_number() OVER (ORDER BY rand())');
221 $have_rno = 1;
222 };
a218ef4e 223 }
eb0323df 224
50772633 225 $self->{_sql_maker_opts} = {
a218ef4e 226 limit_dialect => ($have_rno ? 'RowNumberOver' : 'Top'),
50772633 227 %{$self->{_sql_maker_opts}||{}}
228 };
229 }
230
231 my $maker = $self->next::method (@_);
232 }
e76e7b5c 233
50772633 234 return $self->_sql_maker;
ed8de058 235}
3885cff6 236
ecdf1ac8 237sub _ping {
238 my $self = shift;
239
240 my $dbh = $self->_dbh or return 0;
241
242 local $dbh->{RaiseError} = 1;
243 local $dbh->{PrintError} = 0;
244
ed7ab0f4 245 my $rc = 1;
246 try {
ecdf1ac8 247 $dbh->do('select 1');
ed7ab0f4 248 } catch {
249 $rc = 0;
ecdf1ac8 250 };
251
ed7ab0f4 252 return $rc;
ecdf1ac8 253}
254
fb95dc4d 255package # hide from PAUSE
256 DBIx::Class::Storage::DBI::MSSQL::DateTime::Format;
257
258my $datetime_format = '%Y-%m-%d %H:%M:%S.%3N'; # %F %T
259my $smalldatetime_format = '%Y-%m-%d %H:%M:%S';
260
261my ($datetime_parser, $smalldatetime_parser);
262
263sub parse_datetime {
264 shift;
265 require DateTime::Format::Strptime;
266 $datetime_parser ||= DateTime::Format::Strptime->new(
267 pattern => $datetime_format,
268 on_error => 'croak',
269 );
270 return $datetime_parser->parse_datetime(shift);
271}
272
273sub format_datetime {
274 shift;
275 require DateTime::Format::Strptime;
276 $datetime_parser ||= DateTime::Format::Strptime->new(
277 pattern => $datetime_format,
278 on_error => 'croak',
279 );
280 return $datetime_parser->format_datetime(shift);
281}
282
283sub parse_smalldatetime {
284 shift;
285 require DateTime::Format::Strptime;
286 $smalldatetime_parser ||= DateTime::Format::Strptime->new(
287 pattern => $smalldatetime_format,
288 on_error => 'croak',
289 );
290 return $smalldatetime_parser->parse_datetime(shift);
291}
292
293sub format_smalldatetime {
294 shift;
295 require DateTime::Format::Strptime;
296 $smalldatetime_parser ||= DateTime::Format::Strptime->new(
297 pattern => $smalldatetime_format,
298 on_error => 'croak',
299 );
300 return $smalldatetime_parser->format_datetime(shift);
301}
302
75d07914 3031;
3885cff6 304
75d07914 305=head1 NAME
3885cff6 306
5a77aa8b 307DBIx::Class::Storage::DBI::MSSQL - Base Class for Microsoft SQL Server support
308in DBIx::Class
3885cff6 309
75d07914 310=head1 SYNOPSIS
3885cff6 311
5a77aa8b 312This is the base class for Microsoft SQL Server support, used by
313L<DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server> and
314L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
eb0323df 315
5a77aa8b 316=head1 IMPLEMENTATION NOTES
eb0323df 317
fd05d10a 318=head2 IDENTITY information
319
5a77aa8b 320Microsoft SQL Server supports three methods of retrieving the IDENTITY
321value for inserted row: IDENT_CURRENT, @@IDENTITY, and SCOPE_IDENTITY().
322SCOPE_IDENTITY is used here because it is the safest. However, it must
323be called is the same execute statement, not just the same connection.
eb0323df 324
5a77aa8b 325So, this implementation appends a SELECT SCOPE_IDENTITY() statement
326onto each INSERT to accommodate that requirement.
eb0323df 327
7b1b2582 328C<SELECT @@IDENTITY> can also be used by issuing:
329
330 $self->_identity_method('@@identity');
331
08cdc412 332it will only be used if SCOPE_IDENTITY() fails.
333
334This is more dangerous, as inserting into a table with an on insert trigger that
335inserts into another table with an identity will give erroneous results on
336recent versions of SQL Server.
7b1b2582 337
c84189e1 338=head2 identity insert
fd05d10a 339
340Be aware that we have tried to make things as simple as possible for our users.
c84189e1 341For MSSQL that means that when a user tries to create a row, while supplying an
342explicit value for an autoincrementing column, we will try to issue the
343appropriate database call to make this possible, namely C<SET IDENTITY_INSERT
344$table_name ON>. Unfortunately this operation in MSSQL requires the
345C<db_ddladmin> privilege, which is normally not included in the standard
346write-permissions.
fd05d10a 347
d74f2da9 348=head2 Ordered Subselects
6de07ea3 349
d74f2da9 350If you attempted the following query (among many others) in Microsoft SQL
351Server
6de07ea3 352
6de07ea3 353 $rs->search ({}, {
6de07ea3 354 prefetch => 'relation',
355 rows => 2,
356 offset => 3,
357 });
358
d74f2da9 359You may be surprised to receive an exception. The reason for this is a quirk
360in the MSSQL engine itself, and sadly doesn't have a sensible workaround due
361to the way DBIC is built. DBIC can do truly wonderful things with the aid of
362subselects, and does so automatically when necessary. The list of situations
363when a subselect is necessary is long and still changes often, so it can not
364be exhaustively enumerated here. The general rule of thumb is a joined
365L<has_many|DBIx::Class::Relationship/has_many> relationship with limit/group
366applied to the left part of the join.
367
368In its "pursuit of standards" Microsft SQL Server goes to great lengths to
369forbid the use of ordered subselects. This breaks a very useful group of
370searches like "Give me things number 4 to 6 (ordered by name), and prefetch
371all their relations, no matter how many". While there is a hack which fools
372the syntax checker, the optimizer may B<still elect to break the subselect>.
373Testing has determined that while such breakage does occur (the test suite
374contains an explicit test which demonstrates the problem), it is relative
375rare. The benefits of ordered subselects are on the other hand too great to be
376outright disabled for MSSQL.
6de07ea3 377
378Thus compromise between usability and perfection is the MSSQL-specific
69a8b315 379L<resultset attribute|DBIx::Class::ResultSet/ATTRIBUTES> C<unsafe_subselect_ok>.
6de07ea3 380It is deliberately not possible to set this on the Storage level, as the user
48580715 381should inspect (and preferably regression-test) the return of every such
d74f2da9 382ResultSet individually. The example above would work if written like:
383
384 $rs->search ({}, {
69a8b315 385 unsafe_subselect_ok => 1,
d74f2da9 386 prefetch => 'relation',
387 rows => 2,
388 offset => 3,
389 });
6de07ea3 390
391If it is possible to rewrite the search() in a way that will avoid the need
392for this flag - you are urged to do so. If DBIC internals insist that an
d74f2da9 393ordered subselect is necessary for an operation, and you believe there is a
48580715 394different/better way to get the same result - please file a bugreport.
6de07ea3 395
5a77aa8b 396=head1 AUTHOR
3885cff6 397
548d1627 398See L<DBIx::Class/AUTHOR> and L<DBIx::Class/CONTRIBUTORS>.
3885cff6 399
75d07914 400=head1 LICENSE
3885cff6 401
75d07914 402You may distribute this code under the same terms as Perl itself.
3885cff6 403
75d07914 404=cut