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