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