Merge 'trunk' into 'sybase'
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Sybase.pm
CommitLineData
3885cff6 1package DBIx::Class::Storage::DBI::Sybase;
2
3use strict;
4use warnings;
2ad62d97 5
eabab5d0 6use base qw/
64f4e691 7 DBIx::Class::Storage::DBI::Sybase::Base
b0b44f97 8 DBIx::Class::Storage::DBI
eabab5d0 9/;
2ad62d97 10use mro 'c3';
6b1f5ef7 11use Carp::Clan qw/^DBIx::Class/;
289877b0 12use List::Util ();
6b1f5ef7 13
98259fe4 14=head1 NAME
15
928f0af8 16DBIx::Class::Storage::DBI::Sybase - Sybase support for DBIx::Class
98259fe4 17
18=head1 SYNOPSIS
19
20This subclass supports L<DBD::Sybase> for real Sybase databases. If you are
21using an MSSQL database via L<DBD::Sybase>, your storage will be reblessed to
22L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
23
24=head1 DESCRIPTION
25
26If your version of Sybase does not support placeholders, then your storage
27will be reblessed to L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>. You can
28also enable that driver explicitly, see the documentation for more details.
29
30With this driver there is unfortunately no way to get the C<last_insert_id>
31without doing a C<select max(col)>.
32
33But your queries will be cached.
34
8c4b6c50 35You need a version of L<DBD::Sybase> compiled with the Sybase OpenClient
36libraries, B<NOT> FreeTDS, for placeholder support. Otherwise your storage will
37be automatically reblessed into C<::NoBindVars>.
5703eb14 38
61cfaef7 39A recommended L<DBIx::Class::Storage::DBI/connect_info> setting:
98259fe4 40
61cfaef7 41 on_connect_call => [['datetime_setup'], ['blob_setup', log_on_update => 0]]
98259fe4 42
43=head1 METHODS
44
45=cut
46
fd5a07e4 47__PACKAGE__->mk_group_accessors('simple' =>
48 qw/_blob_log_on_update/
49);
50
47d9646a 51sub _rebless {
b50a5275 52 my $self = shift;
c5ce7cd6 53
54 if (ref($self) eq 'DBIx::Class::Storage::DBI::Sybase') {
55 my $dbtype = eval {
56 @{$self->dbh->selectrow_arrayref(qq{sp_server_info \@attribute_id=1})}[2]
57 } || '';
58
59 my $exception = $@;
60 $dbtype =~ s/\W/_/gi;
61 my $subclass = "DBIx::Class::Storage::DBI::Sybase::${dbtype}";
62
63 if (!$exception && $dbtype && $self->load_optional_class($subclass)) {
64 bless $self, $subclass;
65 $self->_rebless;
5703eb14 66 } else { # real Sybase
67 my $no_bind_vars = 'DBIx::Class::Storage::DBI::Sybase::NoBindVars';
68
8c4b6c50 69 if ($self->_using_freetds) {
5703eb14 70 carp <<'EOF';
71
72Your version of Sybase potentially supports placeholders and query caching,
8c4b6c50 73however you seem to be using FreeTDS which does not (yet?) support this.
5703eb14 74
8c4b6c50 75Please recompile DBD::Sybase with the Sybase OpenClient libraries if you want
76these features.
77
78TEXT/IMAGE column support will also not work under FreeTDS.
5703eb14 79
80See perldoc DBIx::Class::Storage::DBI::Sybase for more details.
81EOF
82 $self->ensure_class_loaded($no_bind_vars);
83 bless $self, $no_bind_vars;
683f73ec 84 $self->_rebless;
85 }
61cfaef7 86
87 if (not $self->dbh->{syb_dynamic_supported}) {
88 $self->ensure_class_loaded($no_bind_vars);
89 bless $self, $no_bind_vars;
90 $self->_rebless;
91 }
92
41c93b1b 93 $self->_set_maxConnect;
47d9646a 94 }
c5ce7cd6 95 }
b50a5275 96}
97
61cfaef7 98sub _using_freetds {
99 my $self = shift;
8c4b6c50 100
61cfaef7 101 return $self->_dbh->{syb_oc_version} =~ /freetds/i;
8c4b6c50 102}
103
41c93b1b 104sub _set_maxConnect {
105 my $self = shift;
106
107 my $dsn = $self->_dbi_connect_info->[0];
108
109 return if ref($dsn) eq 'CODE';
110
111 if ($dsn !~ /maxConnect=/) {
112 $self->_dbi_connect_info->[0] = "$dsn;maxConnect=256";
41c93b1b 113 my $connected = defined $self->_dbh;
114 $self->disconnect;
115 $self->ensure_connected if $connected;
116 }
117}
118
63d46bb3 119=head2 connect_call_blob_setup
120
121Used as:
122
61cfaef7 123 on_connect_call => [ [ 'blob_setup', log_on_update => 0 ] ]
63d46bb3 124
125Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
126instead of as a hex string.
127
6636ad53 128Recommended.
129
fd5a07e4 130Also sets the C<log_on_update> value for blob write operations. The default is
131C<1>, but C<0> is better if your database is configured for it.
132
133See
134L<DBD::Sybase/Handling_IMAGE/TEXT_data_with_syb_ct_get_data()/syb_ct_send_data()>.
135
63d46bb3 136=cut
137
138sub connect_call_blob_setup {
139 my $self = shift;
fd5a07e4 140 my %args = @_;
63d46bb3 141 my $dbh = $self->_dbh;
142 $dbh->{syb_binary_images} = 1;
fd5a07e4 143
144 $self->_blob_log_on_update($args{log_on_update})
145 if exists $args{log_on_update};
146}
147
148sub _is_lob_type {
149 my $self = shift;
5703eb14 150 my $type = shift;
078a332f 151 $type && $type =~ /(?:text|image|lob|bytea|binary|memo)/i;
fd5a07e4 152}
153
b5453fbb 154## This will be useful if we ever implement BLOB filehandle inflation and will
155## need to use the API, but for now it isn't.
5703eb14 156#
b5453fbb 157#sub order_columns_for_select {
61cfaef7 158# my ($self, $source, $columns) = @_;
5703eb14 159#
160# my (@non_blobs, @blobs);
161#
61cfaef7 162# for my $col (@$columns) {
b5453fbb 163# if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
5703eb14 164# push @blobs, $col;
165# } else {
166# push @non_blobs, $col;
167# }
168# }
169#
b5453fbb 170# croak "cannot select more than a one TEXT/IMAGE column at a time"
5703eb14 171# if @blobs > 1;
172#
b5453fbb 173# return (@non_blobs, @blobs);
5703eb14 174#}
175
176# override to handle TEXT/IMAGE
fd5a07e4 177sub insert {
7d17f469 178 my ($self, $source, $to_insert) = splice @_, 0, 3;
289877b0 179 my $dbh = $self->_dbh;
7d17f469 180
181 my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
182
289877b0 183# check if we need to set IDENTITY_INSERT
184 my $identity_insert = 0;
185 my %col_info = map { ($_, $source->column_info($_)) } keys %$to_insert;
186 my $table = $source->from;
187
188 if (List::Util::first { $_->{is_auto_increment} } (values %col_info)) {
189 $identity_insert = 1;
190 $dbh->do("SET IDENTITY_INSERT $table ON");
191 }
192
7d17f469 193 my $updated_cols = $self->next::method($source, $to_insert, @_);
194
289877b0 195 if ($identity_insert) {
196 $dbh->do("SET IDENTITY_INSERT $table OFF");
197 }
198
078a332f 199 $self->_insert_blobs($source, $blob_cols, $to_insert) if %$blob_cols;
7d17f469 200
201 return $updated_cols;
202}
203
078a332f 204sub update {
205 my ($self, $source) = splice @_, 0, 2;
206 my ($fields, $where) = @_;
207 my $wantarray = wantarray;
208
209 my $blob_cols = $self->_remove_blob_cols($source, $fields);
210
211 my @res;
212 if ($wantarray) {
213 @res = $self->next::method($source, @_);
214 } else {
215 $res[0] = $self->next::method($source, @_);
216 }
217
218 $self->_update_blobs($source, $blob_cols, $where) if %$blob_cols;
219
220 return $wantarray ? @res : $res[0];
221}
7d17f469 222
223sub _remove_blob_cols {
224 my ($self, $source, $fields) = @_;
fd5a07e4 225
226 my %blob_cols;
227
7d17f469 228 for my $col (keys %$fields) {
9b3dabe0 229 if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
230 $blob_cols{$col} = delete $fields->{$col};
231 $fields->{$col} = \"''";
232 }
fd5a07e4 233 }
234
7d17f469 235 return \%blob_cols;
fd5a07e4 236}
237
238sub _update_blobs {
078a332f 239 my ($self, $source, $blob_cols, $where) = @_;
240
241 my (@primary_cols) = $source->primary_columns;
242
243 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
244 unless @primary_cols;
245
246# check if we're updating a single row by PK
247 my $pk_cols_in_where = 0;
248 for my $col (@primary_cols) {
249 $pk_cols_in_where++ if defined $where->{$col};
250 }
251 my @rows;
252
253 if ($pk_cols_in_where == @primary_cols) {
254 my %row_to_update;
255 @row_to_update{@primary_cols} = @{$where}{@primary_cols};
256 @rows = \%row_to_update;
257 } else {
258 my $rs = $source->resultset->search(
259 $where,
260 {
261 result_class => 'DBIx::Class::ResultClass::HashRefInflator',
262 select => \@primary_cols
263 }
264 );
265 @rows = $rs->all; # statement must finish
266 }
267
268 for my $row (@rows) {
269 $self->_insert_blobs($source, $blob_cols, $row);
270 }
271}
272
273sub _insert_blobs {
274 my ($self, $source, $blob_cols, $row) = @_;
fd5a07e4 275 my $dbh = $self->dbh;
276
277 my $table = $source->from;
278
078a332f 279 my %row = %$row;
fd5a07e4 280 my (@primary_cols) = $source->primary_columns;
281
9b3dabe0 282 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
fd5a07e4 283 unless @primary_cols;
284
078a332f 285 if ((grep { defined $row{$_} } @primary_cols) != @primary_cols) {
9b3dabe0 286 if (@primary_cols == 1) {
287 my $col = $primary_cols[0];
078a332f 288 $row{$col} = $self->last_insert_id($source, $col);
9b3dabe0 289 } else {
290 croak "Cannot update TEXT/IMAGE column(s) without primary key values";
291 }
292 }
fd5a07e4 293
294 for my $col (keys %$blob_cols) {
295 my $blob = $blob_cols->{$col};
9b3dabe0 296 my $sth;
fd5a07e4 297
9b3dabe0 298 if (not $self->isa('DBIx::Class::Storage::DBI::NoBindVars')) {
299 my $search_cond = join ',' => map "$_ = ?", @primary_cols;
300
301 $sth = $self->sth(
302 "select $col from $table where $search_cond"
303 );
078a332f 304 $sth->execute(map $row{$_}, @primary_cols);
9b3dabe0 305 } else {
078a332f 306 my $search_cond = join ',' => map "$_ = $row{$_}", @primary_cols;
9b3dabe0 307
308 $sth = $dbh->prepare(
309 "select $col from $table where $search_cond"
310 );
311 $sth->execute;
312 }
fd5a07e4 313
314 eval {
315 while ($sth->fetch) {
316 $sth->func('CS_GET', 1, 'ct_data_info') or die $sth->errstr;
317 }
318 $sth->func('ct_prepare_send') or die $sth->errstr;
319
320 my $log_on_update = $self->_blob_log_on_update;
321 $log_on_update = 1 if not defined $log_on_update;
322
323 $sth->func('CS_SET', 1, {
324 total_txtlen => length($blob),
325 log_on_update => $log_on_update
326 }, 'ct_data_info') or die $sth->errstr;
327
328 $sth->func($blob, length($blob), 'ct_send_data') or die $sth->errstr;
329
330 $sth->func('ct_finish_send') or die $sth->errstr;
331 };
332 my $exception = $@;
333 $sth->finish;
334 croak $exception if $exception;
335 }
63d46bb3 336}
337
9539eeb1 338=head2 connect_call_datetime_setup
339
340Used as:
341
342 on_connect_call => 'datetime_setup'
343
344In L<DBIx::Class::Storage::DBI/connect_info> to set:
345
3abafb11 346 $dbh->syb_date_fmt('ISO_strict'); # output fmt: 2004-08-21T14:36:48.080Z
347 $dbh->do('set dateformat mdy'); # input fmt: 08/13/1979 18:08:55.080
9539eeb1 348
349On connection for use with L<DBIx::Class::InflateColumn::DateTime>, using
3abafb11 350L<DateTime::Format::Sybase>, which you will need to install.
351
352This works for both C<DATETIME> and C<SMALLDATETIME> columns, although
353C<SMALLDATETIME> columns only have minute precision.
9539eeb1 354
355=cut
356
9041a97a 357{
358 my $old_dbd_warned = 0;
359
9539eeb1 360 sub connect_call_datetime_setup {
6b1f5ef7 361 my $self = shift;
6b1f5ef7 362 my $dbh = $self->_dbh;
363
364 if ($dbh->can('syb_date_fmt')) {
365 $dbh->syb_date_fmt('ISO_strict');
366 } elsif (not $old_dbd_warned) {
367 carp "Your DBD::Sybase is too old to support ".
368 "DBIx::Class::InflateColumn::DateTime, please upgrade!";
369 $old_dbd_warned = 1;
370 }
371
372 $dbh->do('set dateformat mdy');
c5ce7cd6 373
6b1f5ef7 374 1;
c5ce7cd6 375 }
6b1f5ef7 376}
377
6636ad53 378sub datetime_parser_type { "DateTime::Format::Sybase" }
379
6b1f5ef7 380sub _dbh_last_insert_id {
381 my ($self, $dbh, $source, $col) = @_;
c5ce7cd6 382
383 # sorry, there's no other way!
23419345 384 my $sth = $self->sth("select max($col) from ".$source->from);
385 my ($id) = $dbh->selectrow_array($sth);
386 $sth->finish;
387
388 return $id;
a964a928 389}
390
3885cff6 3911;
392
41c93b1b 393=head1 MAXIMUM CONNECTIONS
394
395L<DBD::Sybase> makes separate connections to the server for active statements in
396the background. By default the number of such connections is limited to 25, on
397both the client side and the server side.
398
399This is a bit too low, so on connection the clientside setting is set to C<256>
400(see L<DBD::Sybase/maxConnect>.) You can override it to whatever setting you
401like in the DSN.
402
403See
404L<http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1272.htm>
405for information on changing the setting on the server side.
406
c5ce7cd6 407=head1 DATES
408
3abafb11 409See L</connect_call_datetime_setup> to setup date formats
410for L<DBIx::Class::InflateColumn::DateTime>.
c5ce7cd6 411
6636ad53 412=head1 IMAGE AND TEXT COLUMNS
63d46bb3 413
8c4b6c50 414L<DBD::Sybase> compiled with FreeTDS will B<NOT> work with C<TEXT/IMAGE>
415columns.
5703eb14 416
63d46bb3 417See L</connect_call_blob_setup> for a L<DBIx::Class::Storage::DBI/connect_info>
418setting you need to work with C<IMAGE> columns.
419
3885cff6 420=head1 AUTHORS
421
7e8cecc1 422See L<DBIx::Class/CONTRIBUTORS>.
c5ce7cd6 423
3885cff6 424=head1 LICENSE
425
426You may distribute this code under the same terms as Perl itself.
427
428=cut
c5ce7cd6 429# vim:sts=2 sw=2: