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