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