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