add a comment
[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
48fe9087 6use base qw/DBIx::Class::Storage::DBI::AmbiguousGlob DBIx::Class::Storage::DBI/;
2ad62d97 7use mro 'c3';
3885cff6 8
5a77aa8b 9use List::Util();
10
7b1b2582 11__PACKAGE__->mk_group_accessors(simple => qw/
12 _identity _identity_method
13/);
14
ac93965c 15__PACKAGE__->sql_maker_class('DBIx::Class::SQLAHacks::MSSQL');
16
5a77aa8b 17sub insert_bulk {
18 my $self = shift;
19 my ($source, $cols, $data) = @_;
20
21 my $identity_insert = 0;
22
23 COLUMNS:
24 foreach my $col (@{$cols}) {
25 if ($source->column_info($col)->{is_auto_increment}) {
26 $identity_insert = 1;
27 last COLUMNS;
28 }
29 }
30
31 if ($identity_insert) {
32 my $table = $source->from;
33 $self->dbh->do("SET IDENTITY_INSERT $table ON");
34 }
35
36 $self->next::method(@_);
37
38 if ($identity_insert) {
39 my $table = $source->from;
40 $self->dbh->do("SET IDENTITY_INSERT $table OFF");
41 }
42}
43
44sub _prep_for_execute {
45 my $self = shift;
46 my ($op, $extra_bind, $ident, $args) = @_;
47
48# cast MONEY values properly
49 if ($op eq 'insert' || $op eq 'update') {
50 my $fields = $args->[0];
51 my $col_info = $self->_resolve_column_info($ident, [keys %$fields]);
52
53 for my $col (keys %$fields) {
54 if ($col_info->{$col}{data_type} =~ /^money\z/i) {
55 my $val = $fields->{$col};
56 $fields->{$col} = \['CAST(? AS MONEY)', [ $col => $val ]];
57 }
58 }
59 }
60
61 my ($sql, $bind) = $self->next::method (@_);
62
63 if ($op eq 'insert') {
64 $sql .= ';SELECT SCOPE_IDENTITY()';
65
66 my $col_info = $self->_resolve_column_info($ident, [map $_->[0], @{$bind}]);
67 if (List::Util::first { $_->{is_auto_increment} } (values %$col_info) ) {
68
69 my $table = $ident->from;
70 my $identity_insert_on = "SET IDENTITY_INSERT $table ON";
71 my $identity_insert_off = "SET IDENTITY_INSERT $table OFF";
72 $sql = "$identity_insert_on; $sql; $identity_insert_off";
73 }
74 }
75
76 return ($sql, $bind);
77}
78
79sub _execute {
80 my $self = shift;
81 my ($op) = @_;
82
83 my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
84 if ($op eq 'insert') {
7b1b2582 85 $self->_identity($self->_fetch_identity($sth));
5a77aa8b 86 }
87
88 return wantarray ? ($rv, $sth, @bind) : $rv;
75d07914 89}
ed8de058 90
7b1b2582 91sub _fetch_identity {
92 my ($self, $sth) = @_;
93 my ($identity) = $sth->fetchrow_array;
94 $sth->finish;
95
96 if ((not defined $identity) && $self->_identity_method &&
97 $self->_identity_method eq '@@identity') {
98 ($identity) = $self->_dbh->selectrow_array('select @@identity');
99 }
100
101 return $identity;
102}
5a77aa8b 103
7b1b2582 104sub last_insert_id { shift->_identity }
5a77aa8b 105
ed8de058 106sub build_datetime_parser {
107 my $self = shift;
108 my $type = "DateTime::Format::Strptime";
109 eval "use ${type}";
110 $self->throw_exception("Couldn't load ${type}: $@") if $@;
eb0323df 111 return $type->new( pattern => '%Y-%m-%d %H:%M:%S' ); # %F %T
112}
113
114sub sqlt_type { 'SQLServer' }
115
116sub _sql_maker_opts {
5a77aa8b 117 my ( $self, $opts ) = @_;
eb0323df 118
5a77aa8b 119 if ( $opts ) {
120 $self->{_sql_maker_opts} = { %$opts };
121 }
eb0323df 122
5a77aa8b 123 return { limit_dialect => 'Top', %{$self->{_sql_maker_opts}||{}} };
ed8de058 124}
3885cff6 125
75d07914 1261;
3885cff6 127
75d07914 128=head1 NAME
3885cff6 129
5a77aa8b 130DBIx::Class::Storage::DBI::MSSQL - Base Class for Microsoft SQL Server support
131in DBIx::Class
3885cff6 132
75d07914 133=head1 SYNOPSIS
3885cff6 134
5a77aa8b 135This is the base class for Microsoft SQL Server support, used by
136L<DBIx::Class::Storage::DBI::ODBC::Microsoft_SQL_Server> and
137L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
eb0323df 138
5a77aa8b 139=head1 IMPLEMENTATION NOTES
eb0323df 140
5a77aa8b 141Microsoft SQL Server supports three methods of retrieving the IDENTITY
142value for inserted row: IDENT_CURRENT, @@IDENTITY, and SCOPE_IDENTITY().
143SCOPE_IDENTITY is used here because it is the safest. However, it must
144be called is the same execute statement, not just the same connection.
eb0323df 145
5a77aa8b 146So, this implementation appends a SELECT SCOPE_IDENTITY() statement
147onto each INSERT to accommodate that requirement.
eb0323df 148
7b1b2582 149C<SELECT @@IDENTITY> can also be used by issuing:
150
151 $self->_identity_method('@@identity');
152
08cdc412 153it will only be used if SCOPE_IDENTITY() fails.
154
155This is more dangerous, as inserting into a table with an on insert trigger that
156inserts into another table with an identity will give erroneous results on
157recent versions of SQL Server.
7b1b2582 158
5a77aa8b 159=head1 AUTHOR
3885cff6 160
5a77aa8b 161See L<DBIx::Class/CONTRIBUTORS>.
3885cff6 162
75d07914 163=head1 LICENSE
3885cff6 164
75d07914 165You may distribute this code under the same terms as Perl itself.
3885cff6 166
75d07914 167=cut