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