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