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