fix freetds
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Sybase.pm
1 package DBIx::Class::Storage::DBI::Sybase;
2
3 use strict;
4 use warnings;
5
6 use base qw/
7     DBIx::Class::Storage::DBI::Sybase::Common
8     DBIx::Class::Storage::DBI::AutoCast
9 /;
10 use mro 'c3';
11 use Carp::Clan qw/^DBIx::Class/;
12 use List::Util ();
13 use 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
20 my @also_proxy_to_writer_storage = qw/
21   connect_call_set_auto_cast auto_cast connect_call_blob_setup
22   connect_call_datetime_setup
23
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
30 DBIx::Class::Storage::DBI::Sybase - Sybase support for DBIx::Class
31
32 =head1 SYNOPSIS
33
34 This subclass supports L<DBD::Sybase> for real Sybase databases.  If you are
35 using an MSSQL database via L<DBD::Sybase>, your storage will be reblessed to
36 L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
37
38 =head1 DESCRIPTION
39
40 If your version of Sybase does not support placeholders, then your storage
41 will be reblessed to L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>. You can
42 also enable that driver explicitly, see the documentation for more details.
43
44 With this driver there is unfortunately no way to get the C<last_insert_id>
45 without doing a C<SELECT MAX(col)>. This is done safely in a transaction
46 (locking the table.) See L</INSERTS WITH PLACEHOLDERS>.
47
48 A 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
55
56 sub _rebless {
57   my $self = shift;
58
59   if (ref($self) eq 'DBIx::Class::Storage::DBI::Sybase') {
60     my $dbtype = eval {
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
77 You are using FreeTDS with Sybase.
78
79 We will do our best to support this configuration, but please consider this
80 support experimental.
81
82 TEXT/IMAGE columns will definitely not work.
83
84 You are encouraged to recompile DBD::Sybase with the Sybase Open Client libraries
85 instead.
86
87 See perldoc DBIx::Class::Storage::DBI::Sybase for more details.
88
89 To turn off this warning set the DBIC_SYBASE_FREETDS_NOWARN environment
90 variable.
91 EOF
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;
98             $self->_rebless;
99           }
100         }
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
115 sub _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);
130   $writer_storage->auto_cast($self->auto_cast);
131
132   $self->_writer_storage($writer_storage);
133 }
134
135 for my $method (@also_proxy_to_writer_storage) {
136   no strict 'refs';
137   no warnings 'redefine';
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.
152 sub _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
170 Used as:
171
172   on_connect_call => [ [ 'blob_setup', log_on_update => 0 ] ]
173
174 Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
175 instead of as a hex string.
176
177 Recommended.
178
179 Also sets the C<log_on_update> value for blob write operations. The default is
180 C<1>, but C<0> is better if your database is configured for it.
181
182 See
183 L<DBD::Sybase/Handling_IMAGE/TEXT_data_with_syb_ct_get_data()/syb_ct_send_data()>.
184
185 =cut
186
187 sub 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
197 sub _is_lob_type {
198   my $self = shift;
199   my $type = shift;
200   $type && $type =~ /(?:text|image|lob|bytea|binary|memo)/i;
201 }
202
203 sub _is_lob_column {
204   my ($self, $source, $column) = @_;
205
206   return $self->_is_lob_type($source->column_info($column)->{data_type});
207 }
208
209 sub _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.
253 our %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
271 sub _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
280 sub _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
288 sub _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
302 sub last_insert_id { shift->_identity }
303
304 # handles TEXT/IMAGE and transaction for last_insert_id
305 sub 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
348 sub _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
364 sub update {
365   my $self = shift;
366   my ($source, $fields, $where, @rest) = @_;
367
368   my $wantarray = wantarray;
369
370   my $blob_cols = $self->_remove_blob_cols($source, $fields);
371
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
380   if (not $blob_cols) {
381     $self->_set_identity_insert($table, 'update')   if $is_identity_update;
382     return $self->next::method(@_);
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     }
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
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;
402
403   $self->next::method($source, \%blobs_to_empty, $where, @rest);
404
405 # Now update the blobs before the other columns in case the update of other
406 # columns makes the search condition invalid.
407   $self->_update_blobs($source, $blob_cols, $where);
408
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
426   $guard->commit;
427
428   return $wantarray ? @res : $res[0];
429 }
430
431 ### the insert_bulk stuff stolen from DBI/MSSQL.pm
432
433 sub _set_identity_insert {
434   my ($self, $table, $op) = @_;
435
436   my $sql = sprintf (
437     'SET IDENTITY_%s %s ON',
438     (uc($op) || 'INSERT'),
439     $self->sql_maker->_quote ($table),
440   );
441
442   $self->_query_start($sql);
443
444   my $dbh = $self->_get_dbh;
445   eval { $dbh->do ($sql) };
446   my $exception = $@;
447
448   $self->_query_end($sql);
449
450   if ($exception) {
451     $self->throw_exception (sprintf "Error executing '%s': %s",
452       $sql,
453       $dbh->errstr,
454     );
455   }
456 }
457
458 sub _unset_identity_insert {
459   my ($self, $table, $op) = @_;
460
461   my $sql = sprintf (
462     'SET IDENTITY_%s %s OFF',
463     (uc($op) || 'INSERT'),
464     $self->sql_maker->_quote ($table),
465   );
466
467   $self->_query_start($sql);
468
469   my $dbh = $self->_get_dbh;
470   $dbh->do ($sql);
471
472   $self->_query_end($sql);
473 }
474
475 # for tests
476 sub _can_insert_bulk { 1 }
477
478 # XXX this should use the DBD::Sybase bulk API, where possible
479 sub 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
503 # Make sure blobs are not bound as placeholders, and return any non-empty ones
504 # as a hash.
505 sub _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})) {
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       }
520     }
521   }
522
523   return keys %blob_cols ? \%blob_cols : undef;
524 }
525
526 sub _update_blobs {
527   my ($self, $source, $blob_cols, $where) = @_;
528
529   my (@primary_cols) = $source->primary_columns;
530
531   croak "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
557 sub _insert_blobs {
558   my ($self, $source, $blob_cols, $row) = @_;
559   my $dbh = $self->_get_dbh;
560
561   my $table = $source->name;
562
563   my %row = %$row;
564   my (@primary_cols) = $source->primary_columns;
565
566   croak "Cannot update TEXT/IMAGE column(s) without a primary key"
567     unless @primary_cols;
568
569   if ((grep { defined $row{$_} } @primary_cols) != @primary_cols) {
570     croak "Cannot update TEXT/IMAGE column(s) without primary key values";
571   }
572
573   for my $col (keys %$blob_cols) {
574     my $blob = $blob_cols->{$col};
575
576     my %where = map { ($_, $row{$_}) } @primary_cols;
577
578     my $cursor = $self->select ($source, [$col], \%where, {});
579     $cursor->next;
580     my $sth = $cursor->sth;
581
582     if (not $sth) {
583       require Data::Dumper;
584       local $Data::Dumper::Terse = 1;
585       local $Data::Dumper::Indent = 1;
586       local $Data::Dumper::Useqq = 1;
587       local $Data::Dumper::Quotekeys = 0;
588       local $Data::Dumper::Sortkeys = 1;
589
590       croak "\nCould not find row in table '$table' for blob update:\n".
591         Data::Dumper::Dumper(\%where)."\n";
592     }
593
594     eval {
595       do {
596         $sth->func('CS_GET', 1, 'ct_data_info') or die $sth->errstr;
597       } while $sth->fetch;
598
599       $sth->func('ct_prepare_send') or die $sth->errstr;
600
601       my $log_on_update = $self->_blob_log_on_update;
602       $log_on_update    = 1 if not defined $log_on_update;
603
604       $sth->func('CS_SET', 1, {
605         total_txtlen => length($blob),
606         log_on_update => $log_on_update
607       }, 'ct_data_info') or die $sth->errstr;
608
609       $sth->func($blob, length($blob), 'ct_send_data') or die $sth->errstr;
610
611       $sth->func('ct_finish_send') or die $sth->errstr;
612     };
613     my $exception = $@;
614     $sth->finish if $sth;
615     if ($exception) {
616       if ($self->using_freetds) {
617         croak (
618           'TEXT/IMAGE operation failed, probably because you are using FreeTDS: '
619           . $exception
620         );
621       } else {
622         croak $exception;
623       }
624     }
625   }
626 }
627
628 =head2 connect_call_datetime_setup
629
630 Used as:
631
632   on_connect_call => 'datetime_setup'
633
634 In L<DBIx::Class::Storage::DBI/connect_info> to set:
635
636   $dbh->syb_date_fmt('ISO_strict'); # output fmt: 2004-08-21T14:36:48.080Z
637   $dbh->do('set dateformat mdy');   # input fmt:  08/13/1979 18:08:55.080
638
639 On connection for use with L<DBIx::Class::InflateColumn::DateTime>, using
640 L<DateTime::Format::Sybase>, which you will need to install.
641
642 This works for both C<DATETIME> and C<SMALLDATETIME> columns, although
643 C<SMALLDATETIME> columns only have minute precision.
644
645 =cut
646
647 {
648   my $old_dbd_warned = 0;
649
650   sub connect_call_datetime_setup {
651     my $self = shift;
652     my $dbh = $self->_dbh;
653
654     if ($dbh->can('syb_date_fmt')) {
655       # amazingly, this works with FreeTDS
656       $dbh->syb_date_fmt('ISO_strict');
657     } elsif (not $old_dbd_warned) {
658       carp "Your DBD::Sybase is too old to support ".
659       "DBIx::Class::InflateColumn::DateTime, please upgrade!";
660       $old_dbd_warned = 1;
661     }
662
663     $dbh->do('SET DATEFORMAT mdy');
664
665     1;
666   }
667 }
668
669 sub datetime_parser_type { "DateTime::Format::Sybase" }
670
671 # ->begin_work and such have no effect with FreeTDS but we run them anyway to
672 # let the DBD keep any state it needs to.
673 #
674 # If they ever do start working, the extra statements will do no harm (because
675 # Sybase supports nested transactions.)
676
677 sub _dbh_begin_work {
678   my $self = shift;
679   $self->next::method(@_);
680   if ($self->using_freetds) {
681     $self->_get_dbh->do('BEGIN TRAN');
682   }
683 }
684
685 sub _dbh_commit {
686   my $self = shift;
687   if ($self->using_freetds) {
688     $self->_dbh->do('COMMIT');
689   }
690   return $self->next::method(@_);
691 }
692
693 sub _dbh_rollback {
694   my $self = shift;
695   if ($self->using_freetds) {
696     $self->_dbh->do('ROLLBACK');
697   }
698   return $self->next::method(@_);
699 }
700
701 # savepoint support using ASE syntax
702
703 sub _svp_begin {
704   my ($self, $name) = @_;
705
706   $self->_get_dbh->do("SAVE TRANSACTION $name");
707 }
708
709 # A new SAVE TRANSACTION with the same name releases the previous one.
710 sub _svp_release { 1 }
711
712 sub _svp_rollback {
713   my ($self, $name) = @_;
714
715   $self->_get_dbh->do("ROLLBACK TRANSACTION $name");
716 }
717
718 1;
719
720 =head1 Schema::Loader Support
721
722 There is an experimental branch of L<DBIx::Class::Schema::Loader> that will
723 allow you to dump a schema from most (if not all) versions of Sybase.
724
725 It is available via subversion from:
726
727   http://dev.catalyst.perl.org/repos/bast/branches/DBIx-Class-Schema-Loader/current/
728
729 =head1 FreeTDS
730
731 This driver supports L<DBD::Sybase> compiled against FreeTDS
732 (L<http://www.freetds.org/>) to the best of our ability, however it is
733 recommended that you recompile L<DBD::Sybase> against the Sybase Open Client
734 libraries. They are a part of the Sybase ASE distribution:
735
736 The Open Client FAQ is here:
737 L<http://www.isug.com/Sybase_FAQ/ASE/section7.html>.
738
739 Sybase ASE for Linux (which comes with the Open Client libraries) may be
740 downloaded here: L<http://response.sybase.com/forms/ASE_Linux_Download>.
741
742 To see if you're using FreeTDS check C<< $schema->storage->using_freetds >>, or run:
743
744   perl -MDBI -le 'my $dbh = DBI->connect($dsn, $user, $pass); print $dbh->{syb_oc_version}'
745
746 Some versions of the libraries involved will not support placeholders, in which
747 case the storage will be reblessed to
748 L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>.
749
750 In some configurations, placeholders will work but will throw implicit type
751 conversion errors for anything that's not expecting a string. In such a case,
752 the C<auto_cast> option from L<DBIx::Class::Storage::DBI::AutoCast> is
753 automatically set, which you may enable on connection with
754 L<DBIx::Class::Storage::DBI::AutoCast/connect_call_set_auto_cast>. The type info
755 for the C<CAST>s is taken from the L<DBIx::Class::ResultSource/data_type>
756 definitions in your Result classes, and are mapped to a Sybase type (if it isn't
757 already) using a mapping based on L<SQL::Translator>.
758
759 In other configurations, placeholers will work just as they do with the Sybase
760 Open Client libraries.
761
762 Inserts or updates of TEXT/IMAGE columns will B<NOT> work with FreeTDS.
763
764 =head1 INSERTS WITH PLACEHOLDERS
765
766 With placeholders enabled, inserts are done in a transaction so that there are
767 no concurrency issues with getting the inserted identity value using
768 C<SELECT MAX(col)>, which is the only way to get the C<IDENTITY> value in this
769 mode.
770
771 In addition, they are done on a separate connection so that it's possible to
772 have active cursors when doing an insert.
773
774 When using C<DBIx::Class::Storage::DBI::Sybase::NoBindVars> transactions are
775 disabled, as there are no concurrency issues with C<SELECT @@IDENTITY> as it's a
776 session variable.
777
778 =head1 TRANSACTIONS
779
780 Due to limitations of the TDS protocol, L<DBD::Sybase>, or both; you cannot
781 begin a transaction while there are active cursors. An active cursor is, for
782 example, a L<ResultSet|DBIx::Class::ResultSet> that has been executed using
783 C<next> or C<first> but has not been exhausted or
784 L<reset|DBIx::Class::ResultSet/reset>.
785
786 For example, this will not work:
787
788   $schema->txn_do(sub {
789     my $rs = $schema->resultset('Book');
790     while (my $row = $rs->next) {
791       $schema->resultset('MetaData')->create({
792         book_id => $row->id,
793         ...
794       });
795     }
796   });
797
798 Transactions done for inserts in C<AutoCommit> mode when placeholders are in use
799 are not affected, as they are done on an extra database handle.
800
801 Some workarounds:
802
803 =over 4
804
805 =item * use L<DBIx::Class::Storage::DBI::Replicated>
806
807 =item * L<connect|DBIx::Class::Schema/connect> another L<Schema|DBIx::Class::Schema>
808
809 =item * load the data from your cursor with L<DBIx::Class::ResultSet/all>
810
811 =back
812
813 =head1 MAXIMUM CONNECTIONS
814
815 The TDS protocol makes separate connections to the server for active statements
816 in the background. By default the number of such connections is limited to 25,
817 on both the client side and the server side.
818
819 This is a bit too low for a complex L<DBIx::Class> application, so on connection
820 the client side setting is set to C<256> (see L<DBD::Sybase/maxConnect>.) You
821 can override it to whatever setting you like in the DSN.
822
823 See
824 L<http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1272.htm>
825 for information on changing the setting on the server side.
826
827 =head1 DATES
828
829 See L</connect_call_datetime_setup> to setup date formats
830 for L<DBIx::Class::InflateColumn::DateTime>.
831
832 =head1 TEXT/IMAGE COLUMNS
833
834 L<DBD::Sybase> compiled with FreeTDS will B<NOT> allow you to insert or update
835 C<TEXT/IMAGE> columns.
836
837 Setting C<< $dbh->{LongReadLen} >> will also not work with FreeTDS use either:
838
839   $schema->storage->dbh->do("SET TEXTSIZE $bytes");
840
841 or
842
843   $schema->storage->set_textsize($bytes);
844
845 instead.
846
847 However, the C<LongReadLen> you pass in
848 L<DBIx::Class::Storage::DBI/connect_info> is used to execute the equivalent
849 C<SET TEXTSIZE> command on connection.
850
851 See L</connect_call_blob_setup> for a L<DBIx::Class::Storage::DBI/connect_info>
852 setting you need to work with C<IMAGE> columns.
853
854 =head1 AUTHOR
855
856 See L<DBIx::Class/CONTRIBUTORS>.
857
858 =head1 LICENSE
859
860 You may distribute this code under the same terms as Perl itself.
861
862 =cut
863 # vim:sts=2 sw=2: