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/
2f92e90b 7 DBIx::Class::Storage::DBI::Sybase::Common
07a5866e 8 DBIx::Class::Storage::DBI::AutoCast
eabab5d0 9/;
2ad62d97 10use mro 'c3';
6b1f5ef7 11use Carp::Clan qw/^DBIx::Class/;
289877b0 12use List::Util ();
bda35a4c 13use Sub::Name ();
6b1f5ef7 14
285baccb 15__PACKAGE__->mk_group_accessors('simple' =>
322b7a6b 16 qw/_identity _blob_log_on_update _insert_dbh _identity_method/
285baccb 17);
18
98259fe4 19=head1 NAME
20
928f0af8 21DBIx::Class::Storage::DBI::Sybase - Sybase support for DBIx::Class
98259fe4 22
23=head1 SYNOPSIS
24
25This subclass supports L<DBD::Sybase> for real Sybase databases. If you are
26using an MSSQL database via L<DBD::Sybase>, your storage will be reblessed to
27L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
28
29=head1 DESCRIPTION
30
31If your version of Sybase does not support placeholders, then your storage
32will be reblessed to L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>. You can
33also enable that driver explicitly, see the documentation for more details.
34
35With this driver there is unfortunately no way to get the C<last_insert_id>
310a0a0a 36without doing a C<SELECT MAX(col)>. This is done safely in a transaction
322b7a6b 37(locking the table.) See L</INSERTS WITH PLACEHOLDERS>.
98259fe4 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
47d9646a 47sub _rebless {
b50a5275 48 my $self = shift;
c5ce7cd6 49
50 if (ref($self) eq 'DBIx::Class::Storage::DBI::Sybase') {
51 my $dbtype = eval {
2eef8633 52 @{$self->_get_dbh->selectrow_arrayref(qq{sp_server_info \@attribute_id=1})}[2]
c5ce7cd6 53 } || '';
54
55 my $exception = $@;
56 $dbtype =~ s/\W/_/gi;
57 my $subclass = "DBIx::Class::Storage::DBI::Sybase::${dbtype}";
58
59 if (!$exception && $dbtype && $self->load_optional_class($subclass)) {
60 bless $self, $subclass;
61 $self->_rebless;
5703eb14 62 } else { # real Sybase
63 my $no_bind_vars = 'DBIx::Class::Storage::DBI::Sybase::NoBindVars';
64
e97a6ee2 65 if ($self->using_freetds) {
a3a526cc 66 carp <<'EOF' unless $ENV{DBIC_SYBASE_FREETDS_NOWARN};
67
68You are using FreeTDS with Sybase.
5703eb14 69
a3a526cc 70We will do our best to support this configuration, but please consider this
71support experimental.
5703eb14 72
a3a526cc 73TEXT/IMAGE columns will definitely not work.
8c4b6c50 74
e97a6ee2 75You are encouraged to recompile DBD::Sybase with the Sybase Open Client libraries
a3a526cc 76instead.
5703eb14 77
78See perldoc DBIx::Class::Storage::DBI::Sybase for more details.
a3a526cc 79
80To turn off this warning set the DBIC_SYBASE_FREETDS_NOWARN environment
81variable.
5703eb14 82EOF
70ced519 83 if (not $self->_typeless_placeholders_supported) {
84 if ($self->_placeholders_supported) {
e97a6ee2 85 $self->auto_cast(1);
a3a526cc 86 } else {
87 $self->ensure_class_loaded($no_bind_vars);
88 bless $self, $no_bind_vars;
89 $self->_rebless;
90 }
91 }
0ac07712 92 }
75227502 93 elsif (not $self->_get_dbh->{syb_dynamic_supported}) {
0ac07712 94 # not necessarily FreeTDS, but no placeholders nevertheless
61cfaef7 95 $self->ensure_class_loaded($no_bind_vars);
96 bless $self, $no_bind_vars;
97 $self->_rebless;
310a0a0a 98 } elsif (not $self->_typeless_placeholders_supported) {
99# this is highly unlikely, but we check just in case
100 $self->auto_cast(1);
61cfaef7 101 }
47d9646a 102 }
c5ce7cd6 103 }
b50a5275 104}
105
37b17a93 106sub _init {
107 my $self = shift;
108 $self->_set_max_connect(256);
109
110 # based on LongReadLen in connect_info
111 $self->set_textsize if $self->using_freetds;
112}
113
a3a526cc 114# Make sure we have CHAINED mode turned on if AutoCommit is off in non-FreeTDS
115# DBD::Sybase (since we don't know how DBD::Sybase was compiled.) If however
116# we're using FreeTDS, CHAINED mode turns on an implicit transaction which we
117# only want when AutoCommit is off.
f6de7111 118sub _populate_dbh {
119 my $self = shift;
41c93b1b 120
a3a526cc 121 $self->next::method(@_);
41c93b1b 122
e97a6ee2 123 if (not $self->using_freetds) {
a3a526cc 124 $self->_dbh->{syb_chained_txn} = 1;
125 } else {
126 if ($self->_dbh_autocommit) {
127 $self->_dbh->do('SET CHAINED OFF');
128 } else {
129 $self->_dbh->do('SET CHAINED ON');
130 }
41c93b1b 131 }
132}
133
63d46bb3 134=head2 connect_call_blob_setup
135
136Used as:
137
61cfaef7 138 on_connect_call => [ [ 'blob_setup', log_on_update => 0 ] ]
63d46bb3 139
140Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
141instead of as a hex string.
142
6636ad53 143Recommended.
144
fd5a07e4 145Also sets the C<log_on_update> value for blob write operations. The default is
146C<1>, but C<0> is better if your database is configured for it.
147
148See
149L<DBD::Sybase/Handling_IMAGE/TEXT_data_with_syb_ct_get_data()/syb_ct_send_data()>.
150
63d46bb3 151=cut
152
153sub connect_call_blob_setup {
154 my $self = shift;
fd5a07e4 155 my %args = @_;
63d46bb3 156 my $dbh = $self->_dbh;
157 $dbh->{syb_binary_images} = 1;
fd5a07e4 158
159 $self->_blob_log_on_update($args{log_on_update})
160 if exists $args{log_on_update};
161}
162
163sub _is_lob_type {
164 my $self = shift;
5703eb14 165 my $type = shift;
078a332f 166 $type && $type =~ /(?:text|image|lob|bytea|binary|memo)/i;
fd5a07e4 167}
168
285baccb 169sub _prep_for_execute {
170 my $self = shift;
171 my ($op, $extra_bind, $ident, $args) = @_;
172
173 my ($sql, $bind) = $self->next::method (@_);
174
175 if ($op eq 'insert') {
285baccb 176 my $table = $ident->from;
177
a3a526cc 178 my $bind_info = $self->_resolve_column_info(
179 $ident, [map $_->[0], @{$bind}]
180 );
0ac07712 181 my $identity_col = List::Util::first
182 { $bind_info->{$_}{is_auto_increment} }
183 (keys %$bind_info)
184 ;
285baccb 185
186 if ($identity_col) {
0ac07712 187 $sql = join ("\n",
188 "SET IDENTITY_INSERT $table ON",
189 $sql,
190 "SET IDENTITY_INSERT $table OFF",
191 );
192 }
193 else {
194 $identity_col = List::Util::first
195 { $ident->column_info($_)->{is_auto_increment} }
196 $ident->columns
197 ;
285baccb 198 }
199
200 if ($identity_col) {
285baccb 201 $sql =
285baccb 202 "$sql\n" .
a3a526cc 203 $self->_fetch_identity_sql($ident, $identity_col);
285baccb 204 }
205 }
206
207 return ($sql, $bind);
208}
209
0ac07712 210# Stolen from SQLT, with some modifications. This is a makeshift
211# solution before a sane type-mapping library is available, thus
212# the 'our' for easy overrides.
213our %TYPE_MAPPING = (
a3a526cc 214 number => 'numeric',
215 money => 'money',
216 varchar => 'varchar',
217 varchar2 => 'varchar',
218 timestamp => 'datetime',
219 text => 'varchar',
220 real => 'double precision',
221 comment => 'text',
222 bit => 'bit',
223 tinyint => 'smallint',
224 float => 'double precision',
225 serial => 'numeric',
226 bigserial => 'numeric',
227 boolean => 'varchar',
228 long => 'varchar',
229);
230
07a5866e 231sub _native_data_type {
a3a526cc 232 my ($self, $type) = @_;
233
234 $type = lc $type;
c9d9c670 235 $type =~ s/\s* identity//x;
a3a526cc 236
237 return uc($TYPE_MAPPING{$type} || $type);
238}
239
285baccb 240sub _fetch_identity_sql {
241 my ($self, $source, $col) = @_;
242
c453ddd9 243 return sprintf ("SELECT MAX(%s) FROM %s",
244 map { $self->sql_maker->_quote ($_) } ($col, $source->from)
245 );
285baccb 246}
247
248sub _execute {
249 my $self = shift;
250 my ($op) = @_;
251
252 my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
253
254 if ($op eq 'insert') {
255 $self->_identity($sth->fetchrow_array);
256 $sth->finish;
257 }
258
259 return wantarray ? ($rv, $sth, @bind) : $rv;
260}
261
262sub last_insert_id { shift->_identity }
263
aee988d2 264# handles TEXT/IMAGE and transaction for last_insert_id
fd5a07e4 265sub insert {
0ac07712 266 my $self = shift;
58e3556d 267 my ($source, $to_insert) = @_;
7d17f469 268
269 my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
270
c453ddd9 271 my $identity_col = List::Util::first
272 { $source->column_info($_)->{is_auto_increment} }
273 $source->columns;
322b7a6b 274
c453ddd9 275 # do we need the horrific SELECT MAX(COL) hack?
276 my $dumb_last_insert_id =
277 ( $identity_col
278 && (not exists $to_insert->{$identity_col})
279 && ($self->_identity_method||'') ne '@@IDENTITY'
280 ) ? 1 : 0;
281
282 # we are already in a transaction, or there are no blobs
283 # and we don't need the PK - just (try to) do it
284 if ($self->{transaction_depth}
285 || (!$blob_cols && !$dumb_last_insert_id)
286 ) {
287 return $self->_insert ($source, $to_insert, $blob_cols, $identity_col);
288 }
322b7a6b 289
c453ddd9 290 # this is tricky: a transaction needs to take place if we need
291 # either a last_insert_id or we will be doing blob insert.
292 # This will happen on a *seperate* connection, thus the local()
293 # acrobatics
322b7a6b 294
c453ddd9 295 local $self->{_dbh};
aee988d2 296
c453ddd9 297 # localize so it appears right if we blow out with an exception
298 local $self->{transaction_depth} = 0;
310a0a0a 299
c453ddd9 300 $self->_insert_dbh($self->_connect(@{ $self->_dbi_connect_info }))
301 unless $self->_insert_dbh;
310a0a0a 302
c453ddd9 303 $self->{_dbh} = $self->_insert_dbh;
304 my $guard = $self->txn_scope_guard;
310a0a0a 305
c453ddd9 306 # _dbh_begin_work in the guard may reconnect,
307 # so we update the accessor just in case
308 $self->_insert_dbh($self->_dbh);
322b7a6b 309
c453ddd9 310 my $updated_cols = $self->_insert ($source, $to_insert, $blob_cols, $identity_col);
322b7a6b 311
c453ddd9 312 $guard->commit;
322b7a6b 313
c453ddd9 314 return $updated_cols;
322b7a6b 315
c453ddd9 316}
7d17f469 317
c453ddd9 318sub _insert {
319 my ($self, $source, $to_insert, $blob_cols, $identity_col) = @_;
7d17f469 320
bda35a4c 321 my $updated_cols =
322 (Sub::Name::subname insert =>
323 sub { $self->next::method ($source, $to_insert) })->();
c453ddd9 324
325 my $final_row = {
326 $identity_col => $self->last_insert_id($source, $identity_col),
327 %$to_insert,
328 %$updated_cols,
329 };
330
331 $self->_insert_blobs ($source, $blob_cols, $final_row) if $blob_cols;
aee988d2 332
7d17f469 333 return $updated_cols;
334}
335
078a332f 336sub update {
0ac07712 337 my $self = shift;
5370e479 338 my ($source, $fields, $where) = @_;
0ac07712 339
340 my $wantarray = wantarray;
078a332f 341
342 my $blob_cols = $self->_remove_blob_cols($source, $fields);
343
aee988d2 344# update+blob update(s) done atomically
c966cf1b 345 my $guard = $self->txn_scope_guard if $blob_cols;
aee988d2 346
078a332f 347 my @res;
348 if ($wantarray) {
0ac07712 349 @res = $self->next::method(@_);
350 }
351 elsif (defined $wantarray) {
352 $res[0] = $self->next::method(@_);
353 }
354 else {
355 $self->next::method(@_);
078a332f 356 }
357
c966cf1b 358 $self->_update_blobs($source, $blob_cols, $where) if $blob_cols;
078a332f 359
c966cf1b 360 $guard->commit if $guard;
aee988d2 361
078a332f 362 return $wantarray ? @res : $res[0];
363}
7d17f469 364
365sub _remove_blob_cols {
366 my ($self, $source, $fields) = @_;
fd5a07e4 367
368 my %blob_cols;
369
7d17f469 370 for my $col (keys %$fields) {
9b3dabe0 371 if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
372 $blob_cols{$col} = delete $fields->{$col};
373 $fields->{$col} = \"''";
374 }
fd5a07e4 375 }
376
c966cf1b 377 return keys %blob_cols ? \%blob_cols : undef;
fd5a07e4 378}
379
380sub _update_blobs {
5370e479 381 my ($self, $source, $blob_cols, $where) = @_;
078a332f 382
383 my (@primary_cols) = $source->primary_columns;
384
385 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
386 unless @primary_cols;
387
388# check if we're updating a single row by PK
389 my $pk_cols_in_where = 0;
390 for my $col (@primary_cols) {
5370e479 391 $pk_cols_in_where++ if defined $where->{$col};
078a332f 392 }
393 my @rows;
394
395 if ($pk_cols_in_where == @primary_cols) {
396 my %row_to_update;
5370e479 397 @row_to_update{@primary_cols} = @{$where}{@primary_cols};
078a332f 398 @rows = \%row_to_update;
399 } else {
400 my $rs = $source->resultset->search(
5370e479 401 $where,
078a332f 402 {
403 result_class => 'DBIx::Class::ResultClass::HashRefInflator',
ba1f339d 404 columns => \@primary_cols
078a332f 405 }
406 );
407 @rows = $rs->all; # statement must finish
408 }
409
410 for my $row (@rows) {
411 $self->_insert_blobs($source, $blob_cols, $row);
412 }
413}
414
415sub _insert_blobs {
416 my ($self, $source, $blob_cols, $row) = @_;
75227502 417 my $dbh = $self->_get_dbh;
fd5a07e4 418
419 my $table = $source->from;
420
078a332f 421 my %row = %$row;
fd5a07e4 422 my (@primary_cols) = $source->primary_columns;
423
9b3dabe0 424 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
fd5a07e4 425 unless @primary_cols;
426
078a332f 427 if ((grep { defined $row{$_} } @primary_cols) != @primary_cols) {
c453ddd9 428 croak "Cannot update TEXT/IMAGE column(s) without primary key values";
9b3dabe0 429 }
fd5a07e4 430
431 for my $col (keys %$blob_cols) {
432 my $blob = $blob_cols->{$col};
433
a3a526cc 434 my %where = map { ($_, $row{$_}) } @primary_cols;
435 my $cursor = $source->resultset->search(\%where, {
436 select => [$col]
437 })->cursor;
438 $cursor->next;
5137d252 439 my $sth = $cursor->sth;
fd5a07e4 440
441 eval {
a3a526cc 442 do {
fd5a07e4 443 $sth->func('CS_GET', 1, 'ct_data_info') or die $sth->errstr;
a3a526cc 444 } while $sth->fetch;
445
fd5a07e4 446 $sth->func('ct_prepare_send') or die $sth->errstr;
447
448 my $log_on_update = $self->_blob_log_on_update;
449 $log_on_update = 1 if not defined $log_on_update;
450
451 $sth->func('CS_SET', 1, {
452 total_txtlen => length($blob),
453 log_on_update => $log_on_update
454 }, 'ct_data_info') or die $sth->errstr;
455
456 $sth->func($blob, length($blob), 'ct_send_data') or die $sth->errstr;
457
458 $sth->func('ct_finish_send') or die $sth->errstr;
459 };
460 my $exception = $@;
a3a526cc 461 $sth->finish if $sth;
462 if ($exception) {
e97a6ee2 463 if ($self->using_freetds) {
0ac07712 464 croak (
465 'TEXT/IMAGE operation failed, probably because you are using FreeTDS: '
466 . $exception
467 );
a3a526cc 468 } else {
469 croak $exception;
470 }
471 }
fd5a07e4 472 }
63d46bb3 473}
474
9539eeb1 475=head2 connect_call_datetime_setup
476
477Used as:
478
479 on_connect_call => 'datetime_setup'
480
481In L<DBIx::Class::Storage::DBI/connect_info> to set:
482
3abafb11 483 $dbh->syb_date_fmt('ISO_strict'); # output fmt: 2004-08-21T14:36:48.080Z
484 $dbh->do('set dateformat mdy'); # input fmt: 08/13/1979 18:08:55.080
9539eeb1 485
486On connection for use with L<DBIx::Class::InflateColumn::DateTime>, using
3abafb11 487L<DateTime::Format::Sybase>, which you will need to install.
488
489This works for both C<DATETIME> and C<SMALLDATETIME> columns, although
490C<SMALLDATETIME> columns only have minute precision.
9539eeb1 491
492=cut
493
9041a97a 494{
495 my $old_dbd_warned = 0;
496
9539eeb1 497 sub connect_call_datetime_setup {
6b1f5ef7 498 my $self = shift;
6b1f5ef7 499 my $dbh = $self->_dbh;
500
501 if ($dbh->can('syb_date_fmt')) {
0ac07712 502 # amazingly, this works with FreeTDS
6b1f5ef7 503 $dbh->syb_date_fmt('ISO_strict');
504 } elsif (not $old_dbd_warned) {
505 carp "Your DBD::Sybase is too old to support ".
506 "DBIx::Class::InflateColumn::DateTime, please upgrade!";
507 $old_dbd_warned = 1;
508 }
509
e97a6ee2 510 $dbh->do('SET DATEFORMAT mdy');
c5ce7cd6 511
6b1f5ef7 512 1;
c5ce7cd6 513 }
6b1f5ef7 514}
515
6636ad53 516sub datetime_parser_type { "DateTime::Format::Sybase" }
517
e97a6ee2 518# ->begin_work and such have no effect with FreeTDS but we run them anyway to
519# let the DBD keep any state it needs to.
520#
521# If they ever do start working, the extra statements will do no harm (because
522# Sybase supports nested transactions.)
a3a526cc 523
524sub _dbh_begin_work {
525 my $self = shift;
e97a6ee2 526 $self->next::method(@_);
527 if ($self->using_freetds) {
75227502 528 $self->_get_dbh->do('BEGIN TRAN');
a3a526cc 529 }
530}
531
532sub _dbh_commit {
533 my $self = shift;
e97a6ee2 534 if ($self->using_freetds) {
a3a526cc 535 $self->_dbh->do('COMMIT');
536 }
e97a6ee2 537 return $self->next::method(@_);
a3a526cc 538}
539
540sub _dbh_rollback {
541 my $self = shift;
e97a6ee2 542 if ($self->using_freetds) {
a3a526cc 543 $self->_dbh->do('ROLLBACK');
544 }
e97a6ee2 545 return $self->next::method(@_);
a3a526cc 546}
547
1816be4f 548# savepoint support using ASE syntax
549
550sub _svp_begin {
551 my ($self, $name) = @_;
552
75227502 553 $self->_get_dbh->do("SAVE TRANSACTION $name");
1816be4f 554}
555
556# A new SAVE TRANSACTION with the same name releases the previous one.
557sub _svp_release { 1 }
558
559sub _svp_rollback {
560 my ($self, $name) = @_;
561
75227502 562 $self->_get_dbh->do("ROLLBACK TRANSACTION $name");
1816be4f 563}
564
3885cff6 5651;
566
efe75aaa 567=head1 Schema::Loader Support
568
569There is an experimental branch of L<DBIx::Class::Schema::Loader> that will
570allow you to dump a schema from most (if not all) versions of Sybase.
571
572It is available via subversion from:
573
07a5866e 574 http://dev.catalyst.perl.org/repos/bast/branches/DBIx-Class-Schema-Loader/current/
efe75aaa 575
e97a6ee2 576=head1 FreeTDS
577
578This driver supports L<DBD::Sybase> compiled against FreeTDS
579(L<http://www.freetds.org/>) to the best of our ability, however it is
580recommended that you recompile L<DBD::Sybase> against the Sybase Open Client
581libraries. They are a part of the Sybase ASE distribution:
582
583The Open Client FAQ is here:
584L<http://www.isug.com/Sybase_FAQ/ASE/section7.html>.
585
586Sybase ASE for Linux (which comes with the Open Client libraries) may be
587downloaded here: L<http://response.sybase.com/forms/ASE_Linux_Download>.
588
589To see if you're using FreeTDS check C<< $schema->storage->using_freetds >>, or run:
590
591 perl -MDBI -le 'my $dbh = DBI->connect($dsn, $user, $pass); print $dbh->{syb_oc_version}'
592
593Some versions of the libraries involved will not support placeholders, in which
594case the storage will be reblessed to
595L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>.
596
07a5866e 597In some configurations, placeholders will work but will throw implicit type
e97a6ee2 598conversion errors for anything that's not expecting a string. In such a case,
07a5866e 599the C<auto_cast> option from L<DBIx::Class::Storage::DBI::AutoCast> is
600automatically set, which you may enable on connection with
601L<DBIx::Class::Storage::DBI::AutoCast/connect_call_set_auto_cast>. The type info
602for the C<CAST>s is taken from the L<DBIx::Class::ResultSource/data_type>
603definitions in your Result classes, and are mapped to a Sybase type (if it isn't
604already) using a mapping based on L<SQL::Translator>.
e97a6ee2 605
606In other configurations, placeholers will work just as they do with the Sybase
607Open Client libraries.
608
609Inserts or updates of TEXT/IMAGE columns will B<NOT> work with FreeTDS.
610
322b7a6b 611=head1 INSERTS WITH PLACEHOLDERS
612
613With placeholders enabled, inserts are done in a transaction so that there are
614no concurrency issues with getting the inserted identity value using
615C<SELECT MAX(col)>, which is the only way to get the C<IDENTITY> value in this
616mode.
617
618When using C<DBIx::Class::Storage::DBI::Sybase::NoBindVars> transactions are
619disabled, as there are no concurrency issues with C<SELECT @@IDENTITY> as it's a
620session variable.
621
166c6561 622=head1 TRANSACTIONS
623
624Due to limitations of the TDS protocol, L<DBD::Sybase>, or both; you cannot
625begin a transaction while there are active cursors. An active cursor is, for
626example, a L<ResultSet|DBIx::Class::ResultSet> that has been executed using
627C<next> or C<first> but has not been exhausted or
75227502 628L<reset|DBIx::Class::ResultSet/reset>.
166c6561 629
322b7a6b 630For example, this will not work:
631
632 $schema->txn_do(sub {
633 my $rs = $schema->resultset('Book');
634 while (my $row = $rs->next) {
635 $schema->resultset('MetaData')->create({
636 book_id => $row->id,
637 ...
638 });
639 }
640 });
641
166c6561 642Transactions done for inserts in C<AutoCommit> mode when placeholders are in use
51ac7136 643are not affected, as they use an extra database handle to do the insert.
75227502 644
645Some workarounds:
646
647=over 4
648
75227502 649=item * use L<DBIx::Class::Storage::DBI::Replicated>
650
651=item * L<connect|DBIx::Class::Schema/connect> another L<Schema|DBIx::Class::Schema>
652
653=item * load the data from your cursor with L<DBIx::Class::ResultSet/all>
654
75227502 655=back
166c6561 656
41c93b1b 657=head1 MAXIMUM CONNECTIONS
658
e97a6ee2 659The TDS protocol makes separate connections to the server for active statements
660in the background. By default the number of such connections is limited to 25,
661on both the client side and the server side.
41c93b1b 662
e97a6ee2 663This is a bit too low for a complex L<DBIx::Class> application, so on connection
664the client side setting is set to C<256> (see L<DBD::Sybase/maxConnect>.) You
665can override it to whatever setting you like in the DSN.
41c93b1b 666
667See
668L<http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1272.htm>
669for information on changing the setting on the server side.
670
c5ce7cd6 671=head1 DATES
672
3abafb11 673See L</connect_call_datetime_setup> to setup date formats
674for L<DBIx::Class::InflateColumn::DateTime>.
c5ce7cd6 675
e97a6ee2 676=head1 TEXT/IMAGE COLUMNS
63d46bb3 677
a3a526cc 678L<DBD::Sybase> compiled with FreeTDS will B<NOT> allow you to insert or update
679C<TEXT/IMAGE> columns.
680
e97a6ee2 681Setting C<< $dbh->{LongReadLen} >> will also not work with FreeTDS use either:
682
683 $schema->storage->dbh->do("SET TEXTSIZE $bytes");
a3a526cc 684
e97a6ee2 685or
686
687 $schema->storage->set_textsize($bytes);
a3a526cc 688
689instead.
5703eb14 690
e97a6ee2 691However, the C<LongReadLen> you pass in
692L<DBIx::Class::Storage::DBI/connect_info> is used to execute the equivalent
693C<SET TEXTSIZE> command on connection.
694
63d46bb3 695See L</connect_call_blob_setup> for a L<DBIx::Class::Storage::DBI/connect_info>
696setting you need to work with C<IMAGE> columns.
697
58e3556d 698=head1 AUTHOR
3885cff6 699
7e8cecc1 700See L<DBIx::Class/CONTRIBUTORS>.
c5ce7cd6 701
3885cff6 702=head1 LICENSE
703
704You may distribute this code under the same terms as Perl itself.
705
706=cut
c5ce7cd6 707# vim:sts=2 sw=2: