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