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