All expected evals converted to try, except where no test is done,
[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
4ffa5700 132 my ($identity) = eval { $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.
219 $have_rno = 1 if (eval { local $@; ($self->_get_dbh
220 ->selectrow_array('SELECT row_number() OVER (ORDER BY rand())')
68bb0d2e 221 )[0] });
a218ef4e 222 }
eb0323df 223
50772633 224 $self->{_sql_maker_opts} = {
a218ef4e 225 limit_dialect => ($have_rno ? 'RowNumberOver' : 'Top'),
50772633 226 %{$self->{_sql_maker_opts}||{}}
227 };
228 }
229
230 my $maker = $self->next::method (@_);
231 }
e76e7b5c 232
50772633 233 return $self->_sql_maker;
ed8de058 234}
3885cff6 235
ecdf1ac8 236sub _ping {
237 my $self = shift;
238
239 my $dbh = $self->_dbh or return 0;
240
241 local $dbh->{RaiseError} = 1;
242 local $dbh->{PrintError} = 0;
243
ed7ab0f4 244 my $rc = 1;
245 try {
ecdf1ac8 246 $dbh->do('select 1');
ed7ab0f4 247 } catch {
248 $rc = 0;
ecdf1ac8 249 };
250
ed7ab0f4 251 return $rc;
ecdf1ac8 252}
253
fb95dc4d 254package # hide from PAUSE
255 DBIx::Class::Storage::DBI::MSSQL::DateTime::Format;
256
257my $datetime_format = '%Y-%m-%d %H:%M:%S.%3N'; # %F %T
258my $smalldatetime_format = '%Y-%m-%d %H:%M:%S';
259
260my ($datetime_parser, $smalldatetime_parser);
261
262sub parse_datetime {
263 shift;
264 require DateTime::Format::Strptime;
265 $datetime_parser ||= DateTime::Format::Strptime->new(
266 pattern => $datetime_format,
267 on_error => 'croak',
268 );
269 return $datetime_parser->parse_datetime(shift);
270}
271
272sub format_datetime {
273 shift;
274 require DateTime::Format::Strptime;
275 $datetime_parser ||= DateTime::Format::Strptime->new(
276 pattern => $datetime_format,
277 on_error => 'croak',
278 );
279 return $datetime_parser->format_datetime(shift);
280}
281
282sub parse_smalldatetime {
283 shift;
284 require DateTime::Format::Strptime;
285 $smalldatetime_parser ||= DateTime::Format::Strptime->new(
286 pattern => $smalldatetime_format,
287 on_error => 'croak',
288 );
289 return $smalldatetime_parser->parse_datetime(shift);
290}
291
292sub format_smalldatetime {
293 shift;
294 require DateTime::Format::Strptime;
295 $smalldatetime_parser ||= DateTime::Format::Strptime->new(
296 pattern => $smalldatetime_format,
297 on_error => 'croak',
298 );
299 return $smalldatetime_parser->format_datetime(shift);
300}
301
75d07914 3021;
3885cff6 303
75d07914 304=head1 NAME
3885cff6 305
5a77aa8b 306DBIx::Class::Storage::DBI::MSSQL - Base Class for Microsoft SQL Server support
307in DBIx::Class
3885cff6 308
75d07914 309=head1 SYNOPSIS
3885cff6 310
5a77aa8b 311This is the base class for Microsoft SQL Server support, used by
312L<DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server> and
313L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
eb0323df 314
5a77aa8b 315=head1 IMPLEMENTATION NOTES
eb0323df 316
fd05d10a 317=head2 IDENTITY information
318
5a77aa8b 319Microsoft SQL Server supports three methods of retrieving the IDENTITY
320value for inserted row: IDENT_CURRENT, @@IDENTITY, and SCOPE_IDENTITY().
321SCOPE_IDENTITY is used here because it is the safest. However, it must
322be called is the same execute statement, not just the same connection.
eb0323df 323
5a77aa8b 324So, this implementation appends a SELECT SCOPE_IDENTITY() statement
325onto each INSERT to accommodate that requirement.
eb0323df 326
7b1b2582 327C<SELECT @@IDENTITY> can also be used by issuing:
328
329 $self->_identity_method('@@identity');
330
08cdc412 331it will only be used if SCOPE_IDENTITY() fails.
332
333This is more dangerous, as inserting into a table with an on insert trigger that
334inserts into another table with an identity will give erroneous results on
335recent versions of SQL Server.
7b1b2582 336
c84189e1 337=head2 identity insert
fd05d10a 338
339Be aware that we have tried to make things as simple as possible for our users.
c84189e1 340For MSSQL that means that when a user tries to create a row, while supplying an
341explicit value for an autoincrementing column, we will try to issue the
342appropriate database call to make this possible, namely C<SET IDENTITY_INSERT
343$table_name ON>. Unfortunately this operation in MSSQL requires the
344C<db_ddladmin> privilege, which is normally not included in the standard
345write-permissions.
fd05d10a 346
d74f2da9 347=head2 Ordered Subselects
6de07ea3 348
d74f2da9 349If you attempted the following query (among many others) in Microsoft SQL
350Server
6de07ea3 351
6de07ea3 352 $rs->search ({}, {
6de07ea3 353 prefetch => 'relation',
354 rows => 2,
355 offset => 3,
356 });
357
d74f2da9 358You may be surprised to receive an exception. The reason for this is a quirk
359in the MSSQL engine itself, and sadly doesn't have a sensible workaround due
360to the way DBIC is built. DBIC can do truly wonderful things with the aid of
361subselects, and does so automatically when necessary. The list of situations
362when a subselect is necessary is long and still changes often, so it can not
363be exhaustively enumerated here. The general rule of thumb is a joined
364L<has_many|DBIx::Class::Relationship/has_many> relationship with limit/group
365applied to the left part of the join.
366
367In its "pursuit of standards" Microsft SQL Server goes to great lengths to
368forbid the use of ordered subselects. This breaks a very useful group of
369searches like "Give me things number 4 to 6 (ordered by name), and prefetch
370all their relations, no matter how many". While there is a hack which fools
371the syntax checker, the optimizer may B<still elect to break the subselect>.
372Testing has determined that while such breakage does occur (the test suite
373contains an explicit test which demonstrates the problem), it is relative
374rare. The benefits of ordered subselects are on the other hand too great to be
375outright disabled for MSSQL.
6de07ea3 376
377Thus compromise between usability and perfection is the MSSQL-specific
69a8b315 378L<resultset attribute|DBIx::Class::ResultSet/ATTRIBUTES> C<unsafe_subselect_ok>.
6de07ea3 379It is deliberately not possible to set this on the Storage level, as the user
48580715 380should inspect (and preferably regression-test) the return of every such
d74f2da9 381ResultSet individually. The example above would work if written like:
382
383 $rs->search ({}, {
69a8b315 384 unsafe_subselect_ok => 1,
d74f2da9 385 prefetch => 'relation',
386 rows => 2,
387 offset => 3,
388 });
6de07ea3 389
390If it is possible to rewrite the search() in a way that will avoid the need
391for this flag - you are urged to do so. If DBIC internals insist that an
d74f2da9 392ordered subselect is necessary for an operation, and you believe there is a
48580715 393different/better way to get the same result - please file a bugreport.
6de07ea3 394
5a77aa8b 395=head1 AUTHOR
3885cff6 396
548d1627 397See L<DBIx::Class/AUTHOR> and L<DBIx::Class/CONTRIBUTORS>.
3885cff6 398
75d07914 399=head1 LICENSE
3885cff6 400
75d07914 401You may distribute this code under the same terms as Perl itself.
3885cff6 402
75d07914 403=cut