a11c24fce1283f131285fa1326a70ebe52c061fc
[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   disconnect _connect_info _sql_maker _sql_maker_opts disable_sth_caching
22   auto_savepoint unsafe cursor_class debug debugobj schema
23 /;
24
25 =head1 NAME
26
27 DBIx::Class::Storage::DBI::Sybase - Sybase support for DBIx::Class
28
29 =head1 SYNOPSIS
30
31 This subclass supports L<DBD::Sybase> for real Sybase databases.  If you are
32 using an MSSQL database via L<DBD::Sybase>, your storage will be reblessed to
33 L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
34
35 =head1 DESCRIPTION
36
37 If your version of Sybase does not support placeholders, then your storage
38 will be reblessed to L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>. You can
39 also enable that driver explicitly, see the documentation for more details.
40
41 With this driver there is unfortunately no way to get the C<last_insert_id>
42 without doing a C<SELECT MAX(col)>. This is done safely in a transaction
43 (locking the table.) See L</INSERTS WITH PLACEHOLDERS>.
44
45 A recommended L<DBIx::Class::Storage::DBI/connect_info> setting:
46
47   on_connect_call => [['datetime_setup'], ['blob_setup', log_on_update => 0]]
48
49 =head1 METHODS
50
51 =cut
52
53 sub _rebless {
54   my $self = shift;
55
56   if (ref($self) eq 'DBIx::Class::Storage::DBI::Sybase') {
57     my $dbtype = eval {
58       @{$self->_get_dbh->selectrow_arrayref(qq{sp_server_info \@attribute_id=1})}[2]
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;
68     } else { # real Sybase
69       my $no_bind_vars = 'DBIx::Class::Storage::DBI::Sybase::NoBindVars';
70
71       if ($self->using_freetds) {
72         carp <<'EOF' unless $ENV{DBIC_SYBASE_FREETDS_NOWARN};
73
74 You are using FreeTDS with Sybase.
75
76 We will do our best to support this configuration, but please consider this
77 support experimental.
78
79 TEXT/IMAGE columns will definitely not work.
80
81 You are encouraged to recompile DBD::Sybase with the Sybase Open Client libraries
82 instead.
83
84 See perldoc DBIx::Class::Storage::DBI::Sybase for more details.
85
86 To turn off this warning set the DBIC_SYBASE_FREETDS_NOWARN environment
87 variable.
88 EOF
89         if (not $self->_typeless_placeholders_supported) {
90           if ($self->_placeholders_supported) {
91             $self->auto_cast(1);
92           } else {
93             $self->ensure_class_loaded($no_bind_vars);
94             bless $self, $no_bind_vars;
95             $self->_rebless;
96           }
97         }
98       }
99       elsif (not $self->_get_dbh->{syb_dynamic_supported}) {
100         # not necessarily FreeTDS, but no placeholders nevertheless
101         $self->ensure_class_loaded($no_bind_vars);
102         bless $self, $no_bind_vars;
103         $self->_rebless;
104       } elsif (not $self->_typeless_placeholders_supported) {
105 # this is highly unlikely, but we check just in case
106         $self->auto_cast(1);
107       }
108     }
109   }
110 }
111
112 sub _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;
118
119 # create storage for insert/(update blob) transactions,
120 # unless this is that storage
121   return if $self->_is_writer_storage;
122
123   my $writer_storage = (ref $self)->new;
124
125   $writer_storage->_is_writer_storage(1);
126   $writer_storage->connect_info($self->connect_info);
127
128   $self->_writer_storage($writer_storage);
129 }
130
131 for my $method (@also_proxy_to_writer_storage) {
132   no strict 'refs';
133
134   my $replaced = __PACKAGE__->can($method);
135
136   *{$method} = Sub::Name::subname __PACKAGE__."::$method" => sub {
137     my $self = shift;
138     $self->_writer_storage->$replaced(@_) if $self->_writer_storage;
139     return $self->$replaced(@_);
140   };
141 }
142
143 # Make sure we have CHAINED mode turned on if AutoCommit is off in non-FreeTDS
144 # DBD::Sybase (since we don't know how DBD::Sybase was compiled.) If however
145 # we're using FreeTDS, CHAINED mode turns on an implicit transaction which we
146 # only want when AutoCommit is off.
147 sub _populate_dbh {
148   my $self = shift;
149
150   $self->next::method(@_);
151
152   if (not $self->using_freetds) {
153     $self->_dbh->{syb_chained_txn} = 1;
154   } else {
155     if ($self->_dbh_autocommit) {
156       $self->_dbh->do('SET CHAINED OFF');
157     } else {
158       $self->_dbh->do('SET CHAINED ON');
159     }
160   }
161 }
162
163 =head2 connect_call_blob_setup
164
165 Used as:
166
167   on_connect_call => [ [ 'blob_setup', log_on_update => 0 ] ]
168
169 Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
170 instead of as a hex string.
171
172 Recommended.
173
174 Also sets the C<log_on_update> value for blob write operations. The default is
175 C<1>, but C<0> is better if your database is configured for it.
176
177 See
178 L<DBD::Sybase/Handling_IMAGE/TEXT_data_with_syb_ct_get_data()/syb_ct_send_data()>.
179
180 =cut
181
182 sub connect_call_blob_setup {
183   my $self = shift;
184   my %args = @_;
185   my $dbh = $self->_dbh;
186   $dbh->{syb_binary_images} = 1;
187
188   $self->_blob_log_on_update($args{log_on_update})
189     if exists $args{log_on_update};
190 }
191
192 sub _is_lob_type {
193   my $self = shift;
194   my $type = shift;
195   $type && $type =~ /(?:text|image|lob|bytea|binary|memo)/i;
196 }
197
198 sub _prep_for_execute {
199   my $self = shift;
200   my ($op, $extra_bind, $ident, $args) = @_;
201
202   my ($sql, $bind) = $self->next::method (@_);
203
204   if ($op eq 'insert') {
205     my $table = $ident->from;
206
207     my $bind_info = $self->_resolve_column_info(
208       $ident, [map $_->[0], @{$bind}]
209     );
210     my $identity_col = List::Util::first
211       { $bind_info->{$_}{is_auto_increment} }
212       (keys %$bind_info)
213     ;
214
215     if ($identity_col) {
216       $sql = join ("\n",
217         "SET IDENTITY_INSERT $table ON",
218         $sql,
219         "SET IDENTITY_INSERT $table OFF",
220       );
221     }
222     else {
223       $identity_col = List::Util::first
224         { $ident->column_info($_)->{is_auto_increment} }
225         $ident->columns
226       ;
227     }
228
229     if ($identity_col) {
230       $sql =
231         "$sql\n" .
232         $self->_fetch_identity_sql($ident, $identity_col);
233     }
234   }
235
236   return ($sql, $bind);
237 }
238
239 # Stolen from SQLT, with some modifications. This is a makeshift
240 # solution before a sane type-mapping library is available, thus
241 # the 'our' for easy overrides.
242 our %TYPE_MAPPING  = (
243     number    => 'numeric',
244     money     => 'money',
245     varchar   => 'varchar',
246     varchar2  => 'varchar',
247     timestamp => 'datetime',
248     text      => 'varchar',
249     real      => 'double precision',
250     comment   => 'text',
251     bit       => 'bit',
252     tinyint   => 'smallint',
253     float     => 'double precision',
254     serial    => 'numeric',
255     bigserial => 'numeric',
256     boolean   => 'varchar',
257     long      => 'varchar',
258 );
259
260 sub _native_data_type {
261   my ($self, $type) = @_;
262
263   $type = lc $type;
264   $type =~ s/\s* identity//x;
265
266   return uc($TYPE_MAPPING{$type} || $type);
267 }
268
269 sub _fetch_identity_sql {
270   my ($self, $source, $col) = @_;
271
272   return sprintf ("SELECT MAX(%s) FROM %s",
273     map { $self->sql_maker->_quote ($_) } ($col, $source->from)
274   );
275 }
276
277 sub _execute {
278   my $self = shift;
279   my ($op) = @_;
280
281   my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
282
283   if ($op eq 'insert') {
284     $self->_identity($sth->fetchrow_array);
285     $sth->finish;
286   }
287
288   return wantarray ? ($rv, $sth, @bind) : $rv;
289 }
290
291 sub last_insert_id { shift->_identity }
292
293 # handles TEXT/IMAGE and transaction for last_insert_id
294 sub insert {
295   my $self = shift;
296   my ($source, $to_insert) = @_;
297
298   my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
299
300   my $identity_col = List::Util::first
301     { $source->column_info($_)->{is_auto_increment} }
302     $source->columns;
303
304   # do we need the horrific SELECT MAX(COL) hack?
305   my $dumb_last_insert_id =
306        $identity_col
307     && (not exists $to_insert->{$identity_col})
308     && ($self->_identity_method||'') ne '@@IDENTITY';
309
310   my $next = $self->next::can;
311
312   # we are already in a transaction, or there are no blobs
313   # and we don't need the PK - just (try to) do it
314   if ($self->{transaction_depth}
315         || (!$blob_cols && !$dumb_last_insert_id) 
316   ) {
317     return $self->_insert (
318       $next, $source, $to_insert, $blob_cols, $identity_col
319     );
320   }
321
322   # otherwise use the _writer_storage to do the insert+transaction on another
323   # connection
324   my $guard = $self->_writer_storage->txn_scope_guard;
325
326   my $updated_cols = $self->_writer_storage->_insert (
327     $next, $source, $to_insert, $blob_cols, $identity_col
328   );
329
330   $self->_identity($self->_writer_storage->_identity);
331
332   $guard->commit;
333
334   return $updated_cols;
335 }
336
337 sub _insert {
338   my ($self, $next, $source, $to_insert, $blob_cols, $identity_col) = @_;
339
340   my $updated_cols = $self->$next ($source, $to_insert);
341
342   my $final_row = {
343     $identity_col => $self->last_insert_id($source, $identity_col),
344     %$to_insert,
345     %$updated_cols,
346   };
347
348   $self->_insert_blobs ($source, $blob_cols, $final_row) if $blob_cols;
349
350   return $updated_cols;
351 }
352
353 sub update {
354   my $self = shift;
355   my ($source, $fields, $where) = @_;
356
357   my $wantarray = wantarray;
358
359   my $blob_cols = $self->_remove_blob_cols($source, $fields);
360
361   my $table = $source->name;
362
363   my $identity_col = List::Util::first
364     { $source->column_info($_)->{is_auto_increment} }
365     $source->columns;
366
367   my $is_identity_update = $identity_col && defined $fields->{$identity_col};
368
369   if (not $blob_cols) {
370     $self->_set_identity_insert($table, 'update')   if $is_identity_update;
371     return $self->next::method(@_);
372     $self->_unset_identity_insert($table, 'update') if $is_identity_update;
373   }
374
375 # check if condition and fields allow for a 2-step update
376   $self->_assert_blob_update_possible($source, $fields, $where);
377
378 # update+blob update(s) done atomically on separate connection
379   $self = $self->_writer_storage;
380
381   my $guard = $self->txn_scope_guard;
382
383   $self->_set_identity_insert($table, 'update')   if $is_identity_update;
384
385   my @res;
386   if ($wantarray) {
387     @res    = $self->next::method(@_);
388   }
389   elsif (defined $wantarray) {
390     $res[0] = $self->next::method(@_);
391   }
392   else {
393     $self->next::method(@_);
394   }
395
396   $self->_unset_identity_insert($table, 'update') if $is_identity_update;
397
398   my %new_where = map { $_ => ($fields->{$_} || $where->{$_}) } keys %$where;
399
400   $self->_update_blobs($source, $blob_cols, \%new_where);
401
402   $guard->commit;
403
404   return $wantarray ? @res : $res[0];
405 }
406
407 sub _assert_blob_update_possible {
408   my ($self, $source, $fields, $where) = @_;
409
410   my $table = $source->name;
411
412 # If $where condition is mutually exclusive from $fields (what gets updated)
413 # then update is safe.
414   my %count;
415   $count{$_}++ foreach keys %$where, keys %$fields;
416   return 1 unless List::Util::first { $_ == 2 } values %count;
417
418 # Otherwise check that what is updated includes either a primary or unique key.
419   my (@primary_cols) = $source->primary_columns;
420   return 1 if (grep exists $fields->{$_}, @primary_cols) == @primary_cols;
421
422   my %unique_constraints = $source->unique_constraints;
423   for my $uniq_constr (values %unique_constraints) {
424     return 1 if (grep exists $fields->{$_}, @$uniq_constr) == @$uniq_constr;
425   }
426
427 # otherwise throw exception
428   require Data::Dumper;
429   local $Data::Dumper::Terse = 1;
430   local $Data::Dumper::Indent = 1;
431   local $Data::Dumper::Useqq = 1;
432   local $Data::Dumper::Quotekeys = 0;
433   local $Data::Dumper::Sortkeys = 1;
434
435   croak sprintf
436 "2-step TEXT/IMAGE update on table '$table' impossible for condition: \n%s\n".
437 "Setting columns: \n%s\n",
438     Data::Dumper::Dumper($where),
439     Data::Dumper::Dumper($fields);
440 }
441
442 ### the insert_bulk stuff stolen from DBI/MSSQL.pm
443
444 sub _set_identity_insert {
445   my ($self, $table, $op) = @_;
446
447   my $sql = sprintf (
448     'SET IDENTITY_%s %s ON',
449     (uc($op) || 'INSERT'),
450     $self->sql_maker->_quote ($table),
451   );
452
453   $self->_query_start($sql);
454
455   my $dbh = $self->_get_dbh;
456   eval { $dbh->do ($sql) };
457   my $exception = $@;
458
459   $self->_query_end($sql);
460
461   if ($exception) {
462     $self->throw_exception (sprintf "Error executing '%s': %s",
463       $sql,
464       $dbh->errstr,
465     );
466   }
467 }
468
469 sub _unset_identity_insert {
470   my ($self, $table, $op) = @_;
471
472   my $sql = sprintf (
473     'SET IDENTITY_%s %s OFF',
474     (uc($op) || 'INSERT'),
475     $self->sql_maker->_quote ($table),
476   );
477
478   $self->_query_start($sql);
479
480   my $dbh = $self->_get_dbh;
481   $dbh->do ($sql);
482
483   $self->_query_end($sql);
484 }
485
486 # XXX this should use the DBD::Sybase bulk API, where possible
487 sub insert_bulk {
488   my $self = shift;
489   my ($source, $cols, $data) = @_;
490
491   my $is_identity_insert = (List::Util::first
492       { $source->column_info ($_)->{is_auto_increment} }
493       (@{$cols})
494   )
495      ? 1
496      : 0;
497
498   if ($is_identity_insert) {
499      $self->_set_identity_insert ($source->name);
500   }
501
502   $self->next::method(@_);
503
504   if ($is_identity_insert) {
505      $self->_unset_identity_insert ($source->name);
506   }
507 }
508
509 ### end of stolen insert_bulk section
510
511 sub _remove_blob_cols {
512   my ($self, $source, $fields) = @_;
513
514   my %blob_cols;
515
516   for my $col (keys %$fields) {
517     if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
518       $blob_cols{$col} = delete $fields->{$col};
519       $fields->{$col} = \"''";
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: