Centralize remaining uses of Sub::Name within _Util
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Sybase / ASE.pm
CommitLineData
057db5ce 1package DBIx::Class::Storage::DBI::Sybase::ASE;
2
3use strict;
4use warnings;
5
6use base qw/
fabbd5cc 7 DBIx::Class::Storage::DBI::Sybase
8 DBIx::Class::Storage::DBI::AutoCast
9 DBIx::Class::Storage::DBI::IdentityInsert
057db5ce 10/;
11use mro 'c3';
70c28808 12use DBIx::Class::Carp;
fabbd5cc 13use Scalar::Util qw/blessed weaken/;
ed7ab0f4 14use Try::Tiny;
fabbd5cc 15use Context::Preserve 'preserve_context';
514b84f6 16use DBIx::Class::_Util qw( sigwarn_silencer dbic_internal_try dump_value scope_guard set_subname );
fd323bf1 17use namespace::clean;
057db5ce 18
048c2440 19__PACKAGE__->sql_limit_dialect ('GenericSubQ');
2b8cc2f2 20__PACKAGE__->sql_quote_char ([qw/[ ]/]);
c6b7885f 21__PACKAGE__->datetime_parser_type(
22 'DBIx::Class::Storage::DBI::Sybase::ASE::DateTime::Format'
23);
6a247f33 24
057db5ce 25__PACKAGE__->mk_group_accessors('simple' =>
fabbd5cc 26 qw/_identity _identity_method _blob_log_on_update _parent_storage
27 _writer_storage _is_writer_storage
057db5ce 28 _bulk_storage _is_bulk_storage _began_bulk_work
fabbd5cc 29 /
057db5ce 30);
31
deabd575 32
057db5ce 33my @also_proxy_to_extra_storages = qw/
34 connect_call_set_auto_cast auto_cast connect_call_blob_setup
35 connect_call_datetime_setup
36
37 disconnect _connect_info _sql_maker _sql_maker_opts disable_sth_caching
38 auto_savepoint unsafe cursor_class debug debugobj schema
39/;
40
41=head1 NAME
42
43DBIx::Class::Storage::DBI::Sybase::ASE - Sybase ASE SQL Server support for
44DBIx::Class
45
46=head1 SYNOPSIS
47
48This subclass supports L<DBD::Sybase> for real (non-Microsoft) Sybase databases.
49
50=head1 DESCRIPTION
51
52If your version of Sybase does not support placeholders, then your storage will
53be reblessed to L<DBIx::Class::Storage::DBI::Sybase::ASE::NoBindVars>.
54You can also enable that driver explicitly, see the documentation for more
55details.
56
57With this driver there is unfortunately no way to get the C<last_insert_id>
58without doing a C<SELECT MAX(col)>. This is done safely in a transaction
59(locking the table.) See L</INSERTS WITH PLACEHOLDERS>.
60
8384a713 61A recommended L<connect_info|DBIx::Class::Storage::DBI/connect_info> setting:
057db5ce 62
63 on_connect_call => [['datetime_setup'], ['blob_setup', log_on_update => 0]]
64
65=head1 METHODS
66
67=cut
68
69sub _rebless {
70 my $self = shift;
71
72 my $no_bind_vars = __PACKAGE__ . '::NoBindVars';
73
aca3b4c3 74 if ($self->_using_freetds) {
70171cd7 75 carp_once <<'EOF' unless $ENV{DBIC_SYBASE_FREETDS_NOWARN};
057db5ce 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::ASE for more details.
88
89To turn off this warning set the DBIC_SYBASE_FREETDS_NOWARN environment
90variable.
91EOF
92
bbdda281 93 if (not $self->_use_typeless_placeholders) {
94 if ($self->_use_placeholders) {
057db5ce 95 $self->auto_cast(1);
96 }
97 else {
98 $self->ensure_class_loaded($no_bind_vars);
99 bless $self, $no_bind_vars;
100 $self->_rebless;
101 }
102 }
103 }
104
105 elsif (not $self->_get_dbh->{syb_dynamic_supported}) {
106 # not necessarily FreeTDS, but no placeholders nevertheless
107 $self->ensure_class_loaded($no_bind_vars);
108 bless $self, $no_bind_vars;
109 $self->_rebless;
110 }
111 # this is highly unlikely, but we check just in case
bbdda281 112 elsif (not $self->_use_typeless_placeholders) {
057db5ce 113 $self->auto_cast(1);
114 }
115}
116
117sub _init {
118 my $self = shift;
bfec318f 119
120 $self->next::method(@_);
121
122 if ($self->_using_freetds && (my $ver = $self->_using_freetds_version||999) > 0.82) {
123 carp_once(
5c6696c8 124 "Buggy FreeTDS version $ver detected, statement caching will not work and "
bfec318f 125 . 'will be disabled.'
126 );
127 $self->disable_sth_caching(1);
128 }
129
057db5ce 130 $self->_set_max_connect(256);
131
132# create storage for insert/(update blob) transactions,
133# unless this is that storage
fabbd5cc 134 return if $self->_parent_storage;
057db5ce 135
136 my $writer_storage = (ref $self)->new;
137
fabbd5cc 138 $writer_storage->_is_writer_storage(1); # just info
057db5ce 139 $writer_storage->connect_info($self->connect_info);
140 $writer_storage->auto_cast($self->auto_cast);
141
fabbd5cc 142 weaken ($writer_storage->{_parent_storage} = $self);
057db5ce 143 $self->_writer_storage($writer_storage);
144
145# create a bulk storage unless connect_info is a coderef
146 return if ref($self->_dbi_connect_info->[0]) eq 'CODE';
147
148 my $bulk_storage = (ref $self)->new;
149
057db5ce 150 $bulk_storage->_is_bulk_storage(1); # for special ->disconnect acrobatics
151 $bulk_storage->connect_info($self->connect_info);
152
153# this is why
154 $bulk_storage->_dbi_connect_info->[0] .= ';bulkLogin=1';
155
fabbd5cc 156 weaken ($bulk_storage->{_parent_storage} = $self);
057db5ce 157 $self->_bulk_storage($bulk_storage);
158}
159
160for my $method (@also_proxy_to_extra_storages) {
161 no strict 'refs';
162 no warnings 'redefine';
163
164 my $replaced = __PACKAGE__->can($method);
165
514b84f6 166 *{$method} = set_subname $method => sub {
057db5ce 167 my $self = shift;
168 $self->_writer_storage->$replaced(@_) if $self->_writer_storage;
169 $self->_bulk_storage->$replaced(@_) if $self->_bulk_storage;
170 return $self->$replaced(@_);
171 };
172}
173
174sub disconnect {
175 my $self = shift;
176
177# Even though we call $sth->finish for uses off the bulk API, there's still an
178# "active statement" warning on disconnect, which we throw away here.
2a6dda4b 179# This is due to the bug described in _insert_bulk.
057db5ce 180# Currently a noop because 'prepare' is used instead of 'prepare_cached'.
052a832c 181 local $SIG{__WARN__} = sigwarn_silencer(qr/active statement/i)
182 if $self->_is_bulk_storage;
057db5ce 183
184# so that next transaction gets a dbh
185 $self->_began_bulk_work(0) if $self->_is_bulk_storage;
186
187 $self->next::method;
188}
189
c1e5a9ac 190# This is only invoked for FreeTDS drivers by ::Storage::DBI::Sybase::FreeTDS
191sub _set_autocommit_stmt {
192 my ($self, $on) = @_;
193
194 return 'SET CHAINED ' . ($on ? 'OFF' : 'ON');
195}
196
057db5ce 197# Set up session settings for Sybase databases for the connection.
198#
199# Make sure we have CHAINED mode turned on if AutoCommit is off in non-FreeTDS
200# DBD::Sybase (since we don't know how DBD::Sybase was compiled.) If however
201# we're using FreeTDS, CHAINED mode turns on an implicit transaction which we
202# only want when AutoCommit is off.
057db5ce 203sub _run_connection_actions {
204 my $self = shift;
205
206 if ($self->_is_bulk_storage) {
c1e5a9ac 207 # this should be cleared on every reconnect
057db5ce 208 $self->_began_bulk_work(0);
209 return;
210 }
211
c1e5a9ac 212 $self->_dbh->{syb_chained_txn} = 1
aca3b4c3 213 unless $self->_using_freetds;
057db5ce 214
215 $self->next::method(@_);
216}
217
218=head2 connect_call_blob_setup
219
220Used as:
221
222 on_connect_call => [ [ 'blob_setup', log_on_update => 0 ] ]
223
224Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
225instead of as a hex string.
226
227Recommended.
228
229Also sets the C<log_on_update> value for blob write operations. The default is
230C<1>, but C<0> is better if your database is configured for it.
231
232See
5529838f 233L<DBD::Sybase/Handling IMAGE/TEXT data with syb_ct_get_data()/syb_ct_send_data()>.
057db5ce 234
235=cut
236
237sub connect_call_blob_setup {
238 my $self = shift;
239 my %args = @_;
240 my $dbh = $self->_dbh;
241 $dbh->{syb_binary_images} = 1;
242
243 $self->_blob_log_on_update($args{log_on_update})
244 if exists $args{log_on_update};
245}
246
057db5ce 247sub _is_lob_column {
248 my ($self, $source, $column) = @_;
249
250 return $self->_is_lob_type($source->column_info($column)->{data_type});
251}
252
253sub _prep_for_execute {
048c2440 254 my ($self, $op, $ident, $args) = @_;
fabbd5cc 255
048c2440 256 my $limit; # extract and use shortcut on limit without offset
257 if ($op eq 'select' and ! $args->[4] and $limit = $args->[3]) {
258 $args = [ @$args ];
259 $args->[3] = undef;
260 }
261
262 my ($sql, $bind) = $self->next::method($op, $ident, $args);
263
264 # $limit is already sanitized by now
265 $sql = join( "\n",
266 "SET ROWCOUNT $limit",
267 $sql,
268 "SET ROWCOUNT 0",
269 ) if $limit;
057db5ce 270
fabbd5cc 271 if (my $identity_col = $self->_perform_autoinc_retrieval) {
272 $sql .= "\n" . $self->_fetch_identity_sql($ident, $identity_col)
057db5ce 273 }
274
275 return ($sql, $bind);
276}
277
fabbd5cc 278sub _fetch_identity_sql {
279 my ($self, $source, $col) = @_;
057db5ce 280
fabbd5cc 281 return sprintf ("SELECT MAX(%s) FROM %s",
282 map { $self->sql_maker->_quote ($_) } ($col, $source->from)
283 );
057db5ce 284}
285
286# Stolen from SQLT, with some modifications. This is a makeshift
287# solution before a sane type-mapping library is available, thus
288# the 'our' for easy overrides.
289our %TYPE_MAPPING = (
290 number => 'numeric',
291 money => 'money',
292 varchar => 'varchar',
293 varchar2 => 'varchar',
294 timestamp => 'datetime',
295 text => 'varchar',
296 real => 'double precision',
297 comment => 'text',
298 bit => 'bit',
299 tinyint => 'smallint',
300 float => 'double precision',
301 serial => 'numeric',
302 bigserial => 'numeric',
303 boolean => 'varchar',
304 long => 'varchar',
305);
306
307sub _native_data_type {
308 my ($self, $type) = @_;
309
310 $type = lc $type;
311 $type =~ s/\s* identity//x;
312
313 return uc($TYPE_MAPPING{$type} || $type);
314}
315
057db5ce 316
317sub _execute {
318 my $self = shift;
0e773352 319 my ($rv, $sth, @bind) = $self->next::method(@_);
057db5ce 320
044f5b3e 321 $self->_identity( ($sth->fetchall_arrayref)->[0][0] )
fabbd5cc 322 if $self->_perform_autoinc_retrieval;
057db5ce 323
324 return wantarray ? ($rv, $sth, @bind) : $rv;
325}
326
327sub last_insert_id { shift->_identity }
328
329# handles TEXT/IMAGE and transaction for last_insert_id
330sub insert {
331 my $self = shift;
332 my ($source, $to_insert) = @_;
333
e366f807 334 my $columns_info = $source->columns_info;
335
7e017742 336 my ($identity_col) = grep
337 { $columns_info->{$_}{is_auto_increment} }
338 keys %$columns_info
339 ;
340
341 $identity_col = '' if ! defined $identity_col;
057db5ce 342
fabbd5cc 343 # FIXME - this is duplication from DBI.pm. When refactored towards
344 # the LobWriter this can be folded back where it belongs.
345 local $self->{_autoinc_supplied_for_op} = exists $to_insert->{$identity_col}
346 ? 1
347 : 0
348 ;
7e017742 349
350 local $self->{_perform_autoinc_retrieval} = $self->{_autoinc_supplied_for_op}
351 ? undef
352 : $identity_col
fabbd5cc 353 ;
354
057db5ce 355 # check for empty insert
356 # INSERT INTO foo DEFAULT VALUES -- does not work with Sybase
6469dabf 357 # try to insert explicit 'DEFAULT's instead (except for identity, timestamp
358 # and computed columns)
057db5ce 359 if (not %$to_insert) {
360 for my $col ($source->columns) {
361 next if $col eq $identity_col;
6469dabf 362
363 my $info = $source->column_info($col);
364
365 next if ref $info->{default_value} eq 'SCALAR'
366 || (exists $info->{data_type} && (not defined $info->{data_type}));
367
368 next if $info->{data_type} && $info->{data_type} =~ /^timestamp\z/i;
369
057db5ce 370 $to_insert->{$col} = \'DEFAULT';
371 }
372 }
373
374 my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
375
216f29d9 376 # if a new txn is needed - it must happen on the _writer/new connection (for now)
377 my $guard;
7e017742 378 if (
216f29d9 379 ! $self->transaction_depth
380 and
381 (
382 $blob_cols
383 or
384 # do we need the horrific SELECT MAX(COL) hack?
385 (
386 $self->_perform_autoinc_retrieval
387 and
388 ( ($self->_identity_method||'') ne '@@IDENTITY' )
389 )
390 )
057db5ce 391 ) {
216f29d9 392 $self = $self->_writer_storage;
393 $guard = $self->txn_scope_guard;
7e017742 394 }
057db5ce 395
216f29d9 396 my $updated_cols = $self->next::method ($source, $to_insert);
057db5ce 397
7e017742 398 $self->_insert_blobs (
399 $source,
400 $blob_cols,
401 {
402 ( $identity_col
403 ? ( $identity_col => $self->last_insert_id($source, $identity_col) )
404 : ()
405 ),
406 %$to_insert,
407 %$updated_cols,
408 },
409 ) if $blob_cols;
057db5ce 410
216f29d9 411 $guard->commit if $guard;
412
057db5ce 413 return $updated_cols;
414}
415
416sub update {
417 my $self = shift;
418 my ($source, $fields, $where, @rest) = @_;
419
fabbd5cc 420 #
421 # When *updating* identities, ASE requires SET IDENTITY_UPDATE called
422 #
423 if (my $blob_cols = $self->_remove_blob_cols($source, $fields)) {
057db5ce 424
fabbd5cc 425 # If there are any blobs in $where, Sybase will return a descriptive error
426 # message.
427 # XXX blobs can still be used with a LIKE query, and this should be handled.
057db5ce 428
fabbd5cc 429 # update+blob update(s) done atomically on separate connection
430 $self = $self->_writer_storage;
057db5ce 431
fabbd5cc 432 my $guard = $self->txn_scope_guard;
057db5ce 433
fabbd5cc 434 # First update the blob columns to be updated to '' (taken from $fields, where
435 # it is originally put by _remove_blob_cols .)
436 my %blobs_to_empty = map { ($_ => delete $fields->{$_}) } keys %$blob_cols;
057db5ce 437
fabbd5cc 438 # We can't only update NULL blobs, because blobs cannot be in the WHERE clause.
439 $self->next::method($source, \%blobs_to_empty, $where, @rest);
057db5ce 440
fabbd5cc 441 # Now update the blobs before the other columns in case the update of other
442 # columns makes the search condition invalid.
443 my $rv = $self->_update_blobs($source, $blob_cols, $where);
057db5ce 444
fabbd5cc 445 if (keys %$fields) {
057db5ce 446
fabbd5cc 447 # Now set the identity update flags for the actual update
87b12551 448 local $self->{_autoinc_supplied_for_op} = grep
fabbd5cc 449 { $_->{is_auto_increment} }
450 values %{ $source->columns_info([ keys %$fields ]) }
87b12551 451 ;
057db5ce 452
fabbd5cc 453 my $next = $self->next::can;
454 my $args = \@_;
455 return preserve_context {
456 $self->$next(@$args);
457 } after => sub { $guard->commit };
057db5ce 458 }
459 else {
fabbd5cc 460 $guard->commit;
461 return $rv;
057db5ce 462 }
463 }
fabbd5cc 464 else {
465 # Set the identity update flags for the actual update
87b12551 466 local $self->{_autoinc_supplied_for_op} = grep
fabbd5cc 467 { $_->{is_auto_increment} }
468 values %{ $source->columns_info([ keys %$fields ]) }
87b12551 469 ;
fabbd5cc 470
471 return $self->next::method(@_);
472 }
057db5ce 473}
474
2a6dda4b 475sub _insert_bulk {
057db5ce 476 my $self = shift;
477 my ($source, $cols, $data) = @_;
478
e366f807 479 my $columns_info = $source->columns_info;
480
87b12551 481 my ($identity_col) =
482 grep { $columns_info->{$_}{is_auto_increment} }
e366f807 483 keys %$columns_info;
057db5ce 484
fabbd5cc 485 # FIXME - this is duplication from DBI.pm. When refactored towards
486 # the LobWriter this can be folded back where it belongs.
87b12551 487 local $self->{_autoinc_supplied_for_op}
488 = grep { $_ eq $identity_col } @$cols;
057db5ce 489
057db5ce 490 my $use_bulk_api =
491 $self->_bulk_storage &&
492 $self->_get_dbh->{syb_has_blk};
493
6d5679b2 494 if (! $use_bulk_api and ref($self->_dbi_connect_info->[0]) eq 'CODE') {
495 carp_unique( join ' ',
496 'Bulk API support disabled due to use of a CODEREF connect_info.',
497 'Reverting to regular array inserts.',
498 );
057db5ce 499 }
500
501 if (not $use_bulk_api) {
502 my $blob_cols = $self->_remove_blob_cols_array($source, $cols, $data);
503
52cef7e3 504# next::method uses a txn anyway, but it ends too early in case we need to
057db5ce 505# select max(col) to get the identity for inserting blobs.
7e017742 506 ($self, my $guard) = $self->transaction_depth
507 ? ($self, undef)
508 : ($self->_writer_storage, $self->_writer_storage->txn_scope_guard)
509 ;
057db5ce 510
057db5ce 511 $self->next::method(@_);
512
513 if ($blob_cols) {
fabbd5cc 514 if ($self->_autoinc_supplied_for_op) {
057db5ce 515 $self->_insert_blobs_array ($source, $blob_cols, $cols, $data);
516 }
517 else {
518 my @cols_with_identities = (@$cols, $identity_col);
519
520 ## calculate identities
521 # XXX This assumes identities always increase by 1, which may or may not
522 # be true.
523 my ($last_identity) =
524 $self->_dbh->selectrow_array (
525 $self->_fetch_identity_sql($source, $identity_col)
526 );
527 my @identities = (($last_identity - @$data + 1) .. $last_identity);
528
529 my @data_with_identities = map [@$_, shift @identities], @$data;
530
531 $self->_insert_blobs_array (
532 $source, $blob_cols, \@cols_with_identities, \@data_with_identities
533 );
534 }
535 }
536
537 $guard->commit if $guard;
538
539 return;
540 }
541
542# otherwise, use the bulk API
543
544# rearrange @$data so that columns are in database order
6d5679b2 545# and so we submit a full column list
546 my %orig_order = map { $cols->[$_] => $_ } 0..$#$cols;
057db5ce 547
6d5679b2 548 my @source_columns = $source->columns;
549
550 # bcp identity index is 1-based
87b12551 551 my ($identity_idx) = grep { $source_columns[$_] eq $identity_col } (0..$#source_columns);
6d5679b2 552 $identity_idx = defined $identity_idx ? $identity_idx + 1 : 0;
057db5ce 553
554 my @new_data;
6d5679b2 555 for my $slice_idx (0..$#$data) {
556 push @new_data, [map {
fabbd5cc 557 # identity data will be 'undef' if not _autoinc_supplied_for_op()
6d5679b2 558 # columns with defaults will also be 'undef'
559 exists $orig_order{$_}
560 ? $data->[$slice_idx][$orig_order{$_}]
561 : undef
562 } @source_columns];
057db5ce 563 }
564
6d5679b2 565 my $proto_bind = $self->_resolve_bindattrs(
566 $source,
567 [map {
568 [ { dbic_colname => $source_columns[$_], _bind_data_slice_idx => $_ }
569 => $new_data[0][$_] ]
570 } (0 ..$#source_columns) ],
571 $columns_info
572 );
057db5ce 573
574## Set a client-side conversion error handler, straight from DBD::Sybase docs.
575# This ignores any data conversion errors detected by the client side libs, as
576# they are usually harmless.
577 my $orig_cslib_cb = DBD::Sybase::set_cslib_cb(
514b84f6 578 set_subname _insert_bulk_cslib_errhandler => sub {
057db5ce 579 my ($layer, $origin, $severity, $errno, $errmsg, $osmsg, $blkmsg) = @_;
580
581 return 1 if $errno == 36;
582
583 carp
584 "Layer: $layer, Origin: $origin, Severity: $severity, Error: $errno" .
585 ($errmsg ? "\n$errmsg" : '') .
586 ($osmsg ? "\n$osmsg" : '') .
587 ($blkmsg ? "\n$blkmsg" : '');
588
589 return 0;
590 });
591
4edfce2f 592 my $exception = '';
ddcc02d1 593 dbic_internal_try {
057db5ce 594 my $bulk = $self->_bulk_storage;
595
596 my $guard = $bulk->txn_scope_guard;
597
6d5679b2 598## FIXME - once this is done - address the FIXME on finish() below
057db5ce 599## XXX get this to work instead of our own $sth
600## will require SQLA or *Hacks changes for ordered columns
601# $bulk->next::method($source, \@source_columns, \@new_data, {
602# syb_bcp_attribs => {
fabbd5cc 603# identity_flag => $self->_autoinc_supplied_for_op ? 1 : 0,
057db5ce 604# identity_column => $identity_idx,
605# }
606# });
607 my $sql = 'INSERT INTO ' .
608 $bulk->sql_maker->_quote($source->name) . ' (' .
609# colname list is ignored for BCP, but does no harm
610 (join ', ', map $bulk->sql_maker->_quote($_), @source_columns) . ') '.
611 ' VALUES ('. (join ', ', ('?') x @source_columns) . ')';
612
613## XXX there's a bug in the DBD::Sybase bulk support that makes $sth->finish for
614## a prepare_cached statement ineffective. Replace with ->sth when fixed, or
615## better yet the version above. Should be fixed in DBD::Sybase .
616 my $sth = $bulk->_get_dbh->prepare($sql,
617# 'insert', # op
618 {
619 syb_bcp_attribs => {
fabbd5cc 620 identity_flag => $self->_autoinc_supplied_for_op ? 1 : 0,
057db5ce 621 identity_column => $identity_idx,
622 }
623 }
624 );
625
6d5679b2 626 {
627 # FIXME the $sth->finish in _execute_array does a rollback for some
628 # reason. Disable it temporarily until we fix the SQLMaker thing above
629 no warnings 'redefine';
630 no strict 'refs';
631 local *{ref($sth).'::finish'} = sub {};
057db5ce 632
52cef7e3 633 $self->_dbh_execute_for_fetch(
6d5679b2 634 $source, $sth, $proto_bind, \@source_columns, \@new_data
635 );
636 }
637
638 $guard->commit;
057db5ce 639
640 $bulk->_query_end($sql);
ed7ab0f4 641 } catch {
642 $exception = shift;
057db5ce 643 };
644
057db5ce 645 DBD::Sybase::set_cslib_cb($orig_cslib_cb);
646
647 if ($exception =~ /-Y option/) {
f32e99f9 648 my $w = 'Sybase bulk API operation failed due to character set incompatibility, '
5241250d 649 . 'reverting to regular array inserts. Try unsetting the LC_ALL environment variable'
f32e99f9 650 ;
651 $w .= "\n$exception" if $self->debug;
652 carp $w;
057db5ce 653
057db5ce 654 $self->_bulk_storage(undef);
655 unshift @_, $self;
2a6dda4b 656 goto \&_insert_bulk;
057db5ce 657 }
658 elsif ($exception) {
659# rollback makes the bulkLogin connection unusable
660 $self->_bulk_storage->disconnect;
661 $self->throw_exception($exception);
662 }
663}
664
057db5ce 665# Make sure blobs are not bound as placeholders, and return any non-empty ones
666# as a hash.
667sub _remove_blob_cols {
668 my ($self, $source, $fields) = @_;
669
670 my %blob_cols;
671
672 for my $col (keys %$fields) {
673 if ($self->_is_lob_column($source, $col)) {
674 my $blob_val = delete $fields->{$col};
675 if (not defined $blob_val) {
676 $fields->{$col} = \'NULL';
677 }
678 else {
679 $fields->{$col} = \"''";
d52c4a75 680 $blob_cols{$col} = $blob_val
681 if length $blob_val;
057db5ce 682 }
683 }
684 }
685
686 return %blob_cols ? \%blob_cols : undef;
687}
688
2a6dda4b 689# same for _insert_bulk
057db5ce 690sub _remove_blob_cols_array {
691 my ($self, $source, $cols, $data) = @_;
692
693 my @blob_cols;
694
695 for my $i (0..$#$cols) {
696 my $col = $cols->[$i];
697
698 if ($self->_is_lob_column($source, $col)) {
699 for my $j (0..$#$data) {
700 my $blob_val = delete $data->[$j][$i];
701 if (not defined $blob_val) {
702 $data->[$j][$i] = \'NULL';
703 }
704 else {
705 $data->[$j][$i] = \"''";
706 $blob_cols[$j][$i] = $blob_val
d52c4a75 707 if length $blob_val;
057db5ce 708 }
709 }
710 }
711 }
712
713 return @blob_cols ? \@blob_cols : undef;
714}
715
716sub _update_blobs {
717 my ($self, $source, $blob_cols, $where) = @_;
718
ddcc02d1 719 my @primary_cols = dbic_internal_try
56ad42bb 720 { $source->_pri_cols_or_die }
9780718f 721 catch {
722 $self->throw_exception("Cannot update TEXT/IMAGE column(s): $_")
723 };
057db5ce 724
564986d6 725 my @pks_to_update;
726 if (
727 ref $where eq 'HASH'
728 and
7e017742 729 ! grep { ! defined $where->{$_} } @primary_cols
564986d6 730 ) {
057db5ce 731 my %row_to_update;
732 @row_to_update{@primary_cols} = @{$where}{@primary_cols};
564986d6 733 @pks_to_update = \%row_to_update;
734 }
735 else {
057db5ce 736 my $cursor = $self->select ($source, \@primary_cols, $where, {});
564986d6 737 @pks_to_update = map {
057db5ce 738 my %row; @row{@primary_cols} = @$_; \%row
739 } $cursor->all;
740 }
741
564986d6 742 for my $ident (@pks_to_update) {
743 $self->_insert_blobs($source, $blob_cols, $ident);
057db5ce 744 }
745}
746
747sub _insert_blobs {
7e017742 748 my ($self, $source, $blob_cols, $row_data) = @_;
057db5ce 749
750 my $table = $source->name;
751
ddcc02d1 752 my @primary_cols = dbic_internal_try
56ad42bb 753 { $source->_pri_cols_or_die }
9780718f 754 catch {
755 $self->throw_exception("Cannot update TEXT/IMAGE column(s): $_")
756 };
057db5ce 757
758 $self->throw_exception('Cannot update TEXT/IMAGE column(s) without primary key values')
7e017742 759 if grep { ! defined $row_data->{$_} } @primary_cols;
760
b8e92dac 761 # if we are 2-phase inserting a blob - there is nothing to retrieve anymore,
762 # regardless of the previous state of the flag
763 local $self->{_perform_autoinc_retrieval}
764 if $self->_perform_autoinc_retrieval;
765
7e017742 766 my %where = map {( $_ => $row_data->{$_} )} @primary_cols;
057db5ce 767
768 for my $col (keys %$blob_cols) {
769 my $blob = $blob_cols->{$col};
770
057db5ce 771 my $cursor = $self->select ($source, [$col], \%where, {});
772 $cursor->next;
773 my $sth = $cursor->sth;
774
775 if (not $sth) {
057db5ce 776 $self->throw_exception(
777 "Could not find row in table '$table' for blob update:\n"
8fc4291e 778 . dump_value \%where
057db5ce 779 );
780 }
781
f9670042 782 # FIXME - it is not clear if this is needed at all. But it's been
783 # there since 2009 ( d867eedaa ), might as well let sleeping dogs
784 # lie... sigh.
785 weaken( my $wsth = $sth );
786 my $g = scope_guard { $wsth->finish if $wsth };
787
ddcc02d1 788 dbic_internal_try {
057db5ce 789 do {
790 $sth->func('CS_GET', 1, 'ct_data_info') or die $sth->errstr;
791 } while $sth->fetch;
792
793 $sth->func('ct_prepare_send') or die $sth->errstr;
794
795 my $log_on_update = $self->_blob_log_on_update;
796 $log_on_update = 1 if not defined $log_on_update;
797
798 $sth->func('CS_SET', 1, {
799 total_txtlen => length($blob),
800 log_on_update => $log_on_update
801 }, 'ct_data_info') or die $sth->errstr;
802
803 $sth->func($blob, length($blob), 'ct_send_data') or die $sth->errstr;
804
805 $sth->func('ct_finish_send') or die $sth->errstr;
9780718f 806 }
807 catch {
aca3b4c3 808 if ($self->_using_freetds) {
057db5ce 809 $self->throw_exception (
9780718f 810 "TEXT/IMAGE operation failed, probably because you are using FreeTDS: $_"
057db5ce 811 );
9780718f 812 }
813 else {
814 $self->throw_exception($_);
057db5ce 815 }
9780718f 816 };
057db5ce 817 }
818}
819
820sub _insert_blobs_array {
821 my ($self, $source, $blob_cols, $cols, $data) = @_;
822
823 for my $i (0..$#$data) {
824 my $datum = $data->[$i];
825
826 my %row;
827 @row{ @$cols } = @$datum;
828
829 my %blob_vals;
830 for my $j (0..$#$cols) {
831 if (exists $blob_cols->[$i][$j]) {
832 $blob_vals{ $cols->[$j] } = $blob_cols->[$i][$j];
833 }
834 }
835
836 $self->_insert_blobs ($source, \%blob_vals, \%row);
837 }
838}
839
840=head2 connect_call_datetime_setup
841
842Used as:
843
844 on_connect_call => 'datetime_setup'
845
8384a713 846In L<connect_info|DBIx::Class::Storage::DBI/connect_info> to set:
057db5ce 847
848 $dbh->syb_date_fmt('ISO_strict'); # output fmt: 2004-08-21T14:36:48.080Z
849 $dbh->do('set dateformat mdy'); # input fmt: 08/13/1979 18:08:55.080
850
c1e5a9ac 851This works for both C<DATETIME> and C<SMALLDATETIME> columns, note that
057db5ce 852C<SMALLDATETIME> columns only have minute precision.
853
854=cut
855
70c28808 856sub connect_call_datetime_setup {
857 my $self = shift;
858 my $dbh = $self->_get_dbh;
057db5ce 859
70c28808 860 if ($dbh->can('syb_date_fmt')) {
861 # amazingly, this works with FreeTDS
862 $dbh->syb_date_fmt('ISO_strict');
863 }
864 else {
865 carp_once
866 'Your DBD::Sybase is too old to support '
867 .'DBIx::Class::InflateColumn::DateTime, please upgrade!';
057db5ce 868
c6b7885f 869 # FIXME - in retrospect this is a rather bad US-centric choice
870 # of format. Not changing as a bugwards compat, though in reality
871 # the only piece that sees the results of $dt object formatting
872 # (as opposed to parsing) is the database itself, so theoretically
873 # changing both this SET command and the formatter definition of
874 # ::S::D::Sybase::ASE::DateTime::Format below should be safe and
875 # transparent
876
057db5ce 877 $dbh->do('SET DATEFORMAT mdy');
057db5ce 878 }
879}
880
057db5ce 881
90d7422f 882sub _exec_txn_begin {
057db5ce 883 my $self = shift;
884
885# bulkLogin=1 connections are always in a transaction, and can only call BEGIN
886# TRAN once. However, we need to make sure there's a $dbh.
887 return if $self->_is_bulk_storage && $self->_dbh && $self->_began_bulk_work;
888
889 $self->next::method(@_);
890
057db5ce 891 $self->_began_bulk_work(1) if $self->_is_bulk_storage;
892}
893
057db5ce 894# savepoint support using ASE syntax
895
90d7422f 896sub _exec_svp_begin {
057db5ce 897 my ($self, $name) = @_;
898
90d7422f 899 $self->_dbh->do("SAVE TRANSACTION $name");
057db5ce 900}
901
902# A new SAVE TRANSACTION with the same name releases the previous one.
90d7422f 903sub _exec_svp_release { 1 }
057db5ce 904
90d7422f 905sub _exec_svp_rollback {
057db5ce 906 my ($self, $name) = @_;
907
90d7422f 908 $self->_dbh->do("ROLLBACK TRANSACTION $name");
057db5ce 909}
910
deabd575 911package # hide from PAUSE
912 DBIx::Class::Storage::DBI::Sybase::ASE::DateTime::Format;
913
914my $datetime_parse_format = '%Y-%m-%dT%H:%M:%S.%3NZ';
915my $datetime_format_format = '%m/%d/%Y %H:%M:%S.%3N';
916
917my ($datetime_parser, $datetime_formatter);
918
919sub parse_datetime {
920 shift;
921 require DateTime::Format::Strptime;
922 $datetime_parser ||= DateTime::Format::Strptime->new(
923 pattern => $datetime_parse_format,
924 on_error => 'croak',
925 );
926 return $datetime_parser->parse_datetime(shift);
927}
928
929sub format_datetime {
930 shift;
931 require DateTime::Format::Strptime;
932 $datetime_formatter ||= DateTime::Format::Strptime->new(
933 pattern => $datetime_format_format,
934 on_error => 'croak',
935 );
936 return $datetime_formatter->format_datetime(shift);
937}
938
057db5ce 9391;
940
941=head1 Schema::Loader Support
942
290da7d6 943As of version C<0.05000>, L<DBIx::Class::Schema::Loader> should work well with
c1e5a9ac 944most versions of Sybase ASE.
057db5ce 945
946=head1 FreeTDS
947
948This driver supports L<DBD::Sybase> compiled against FreeTDS
949(L<http://www.freetds.org/>) to the best of our ability, however it is
950recommended that you recompile L<DBD::Sybase> against the Sybase Open Client
951libraries. They are a part of the Sybase ASE distribution:
952
953The Open Client FAQ is here:
954L<http://www.isug.com/Sybase_FAQ/ASE/section7.html>.
955
956Sybase ASE for Linux (which comes with the Open Client libraries) may be
957downloaded here: L<http://response.sybase.com/forms/ASE_Linux_Download>.
958
aca3b4c3 959To see if you're using FreeTDS run:
057db5ce 960
961 perl -MDBI -le 'my $dbh = DBI->connect($dsn, $user, $pass); print $dbh->{syb_oc_version}'
962
c1e5a9ac 963It is recommended to set C<tds version> for your ASE server to C<5.0> in
964C</etc/freetds/freetds.conf>.
965
966Some versions or configurations of the libraries involved will not support
967placeholders, in which case the storage will be reblessed to
057db5ce 968L<DBIx::Class::Storage::DBI::Sybase::ASE::NoBindVars>.
969
970In some configurations, placeholders will work but will throw implicit type
971conversion errors for anything that's not expecting a string. In such a case,
972the C<auto_cast> option from L<DBIx::Class::Storage::DBI::AutoCast> is
973automatically set, which you may enable on connection with
c1e5a9ac 974L<connect_call_set_auto_cast|DBIx::Class::Storage::DBI::AutoCast/connect_call_set_auto_cast>.
975The type info for the C<CAST>s is taken from the
976L<DBIx::Class::ResultSource/data_type> definitions in your Result classes, and
977are mapped to a Sybase type (if it isn't already) using a mapping based on
978L<SQL::Translator>.
057db5ce 979
48580715 980In other configurations, placeholders will work just as they do with the Sybase
057db5ce 981Open Client libraries.
982
983Inserts or updates of TEXT/IMAGE columns will B<NOT> work with FreeTDS.
984
985=head1 INSERTS WITH PLACEHOLDERS
986
987With placeholders enabled, inserts are done in a transaction so that there are
988no concurrency issues with getting the inserted identity value using
989C<SELECT MAX(col)>, which is the only way to get the C<IDENTITY> value in this
990mode.
991
992In addition, they are done on a separate connection so that it's possible to
993have active cursors when doing an insert.
994
995When using C<DBIx::Class::Storage::DBI::Sybase::ASE::NoBindVars> transactions
c1e5a9ac 996are unnecessary and not used, as there are no concurrency issues with C<SELECT
997@@IDENTITY> which is a session variable.
057db5ce 998
999=head1 TRANSACTIONS
1000
c1e5a9ac 1001Due to limitations of the TDS protocol and L<DBD::Sybase>, you cannot begin a
1002transaction while there are active cursors, nor can you use multiple active
1003cursors within a transaction. An active cursor is, for example, a
057db5ce 1004L<ResultSet|DBIx::Class::ResultSet> that has been executed using C<next> or
1005C<first> but has not been exhausted or L<reset|DBIx::Class::ResultSet/reset>.
1006
1007For example, this will not work:
1008
1009 $schema->txn_do(sub {
1010 my $rs = $schema->resultset('Book');
47d7b769 1011 while (my $result = $rs->next) {
057db5ce 1012 $schema->resultset('MetaData')->create({
47d7b769 1013 book_id => $result->id,
057db5ce 1014 ...
1015 });
1016 }
1017 });
1018
1019This won't either:
1020
1021 my $first_row = $large_rs->first;
1022 $schema->txn_do(sub { ... });
1023
1024Transactions done for inserts in C<AutoCommit> mode when placeholders are in use
1025are not affected, as they are done on an extra database handle.
1026
1027Some workarounds:
1028
1029=over 4
1030
1031=item * use L<DBIx::Class::Storage::DBI::Replicated>
1032
1033=item * L<connect|DBIx::Class::Schema/connect> another L<Schema|DBIx::Class::Schema>
1034
1035=item * load the data from your cursor with L<DBIx::Class::ResultSet/all>
1036
1037=back
1038
1039=head1 MAXIMUM CONNECTIONS
1040
1041The TDS protocol makes separate connections to the server for active statements
1042in the background. By default the number of such connections is limited to 25,
1043on both the client side and the server side.
1044
1045This is a bit too low for a complex L<DBIx::Class> application, so on connection
1046the client side setting is set to C<256> (see L<DBD::Sybase/maxConnect>.) You
1047can override it to whatever setting you like in the DSN.
1048
1049See
1050L<http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1272.htm>
1051for information on changing the setting on the server side.
1052
1053=head1 DATES
1054
1055See L</connect_call_datetime_setup> to setup date formats
1056for L<DBIx::Class::InflateColumn::DateTime>.
1057
9c510ba5 1058=head1 LIMITED QUERIES
1059
5529838f 1060Because ASE does not have a good way to limit results in SQL that works for
1061all types of queries, the limit dialect is set to
1062L<GenericSubQ|DBIx::Class::SQLMaker::LimitDialects/GenericSubQ>.
9c510ba5 1063
1064Fortunately, ASE and L<DBD::Sybase> support cursors properly, so when
5529838f 1065L<GenericSubQ|DBIx::Class::SQLMaker::LimitDialects/GenericSubQ> is too slow
1066you can use the L<software_limit|DBIx::Class::ResultSet/software_limit>
1067L<DBIx::Class::ResultSet> attribute to simulate limited queries by skipping
1068over records.
9c510ba5 1069
057db5ce 1070=head1 TEXT/IMAGE COLUMNS
1071
1072L<DBD::Sybase> compiled with FreeTDS will B<NOT> allow you to insert or update
1073C<TEXT/IMAGE> columns.
1074
1075Setting C<< $dbh->{LongReadLen} >> will also not work with FreeTDS use either:
1076
1077 $schema->storage->dbh->do("SET TEXTSIZE $bytes");
1078
1079or
1080
1081 $schema->storage->set_textsize($bytes);
1082
1083instead.
1084
1085However, the C<LongReadLen> you pass in
8384a713 1086L<connect_info|DBIx::Class::Storage::DBI/connect_info> is used to execute the
1087equivalent C<SET TEXTSIZE> command on connection.
057db5ce 1088
8384a713 1089See L</connect_call_blob_setup> for a
1090L<connect_info|DBIx::Class::Storage::DBI/connect_info> setting you need to work
1091with C<IMAGE> columns.
057db5ce 1092
1093=head1 BULK API
1094
1095The experimental L<DBD::Sybase> Bulk API support is used for
1096L<populate|DBIx::Class::ResultSet/populate> in B<void> context, in a transaction
1097on a separate connection.
1098
1099To use this feature effectively, use a large number of rows for each
1100L<populate|DBIx::Class::ResultSet/populate> call, eg.:
1101
1102 while (my $rows = $data_source->get_100_rows()) {
1103 $rs->populate($rows);
1104 }
1105
1106B<NOTE:> the L<add_columns|DBIx::Class::ResultSource/add_columns>
1107calls in your C<Result> classes B<must> list columns in database order for this
5241250d 1108to work. Also, you may have to unset the C<LC_ALL> environment variable before
c1e5a9ac 1109loading your app, as C<BCP -Y> is not yet supported in DBD::Sybase .
057db5ce 1110
1111When inserting IMAGE columns using this method, you'll need to use
1112L</connect_call_blob_setup> as well.
1113
6476fd66 1114=head1 COMPUTED COLUMNS
1115
1116If you have columns such as:
1117
1118 created_dtm AS getdate()
1119
1120represent them in your Result classes as:
1121
1122 created_dtm => {
1123 data_type => undef,
1124 default_value => \'getdate()',
1125 is_nullable => 0,
c1e5a9ac 1126 inflate_datetime => 1,
6476fd66 1127 }
1128
1129The C<data_type> must exist and must be C<undef>. Then empty inserts will work
1130on tables with such columns.
1131
1132=head1 TIMESTAMP COLUMNS
1133
1134C<timestamp> columns in Sybase ASE are not really timestamps, see:
1135L<http://dba.fyicenter.com/Interview-Questions/SYBASE/The_timestamp_datatype_in_Sybase_.html>.
1136
1137They should be defined in your Result classes as:
1138
1139 ts => {
1140 data_type => 'timestamp',
1141 is_nullable => 0,
1142 inflate_datetime => 0,
1143 }
1144
1145The C<<inflate_datetime => 0>> is necessary if you use
1146L<DBIx::Class::InflateColumn::DateTime>, and most people do, and still want to
1147be able to read these values.
1148
1149The values will come back as hexadecimal.
1150
057db5ce 1151=head1 TODO
1152
1153=over
1154
1155=item *
1156
1157Transitions to AutoCommit=0 (starting a transaction) mode by exhausting
1158any active cursors, using eager cursors.
1159
1160=item *
1161
1162Real limits and limited counts using stored procedures deployed on startup.
1163
1164=item *
1165
057db5ce 1166Blob update with a LIKE query on a blob, without invalidating the WHERE condition.
1167
1168=item *
1169
1170bulk_insert using prepare_cached (see comments.)
1171
1172=back
1173
a2bd3796 1174=head1 FURTHER QUESTIONS?
057db5ce 1175
a2bd3796 1176Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
057db5ce 1177
a2bd3796 1178=head1 COPYRIGHT AND LICENSE
057db5ce 1179
a2bd3796 1180This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
1181by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
1182redistribute it and/or modify it under the same terms as the
1183L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.