1 package DBIx::Class::Storage::DBI::Sybase;
7 DBIx::Class::Storage::DBI::Sybase::Base
8 DBIx::Class::Storage::DBI
11 use Carp::Clan qw/^DBIx::Class/;
14 __PACKAGE__->mk_group_accessors('simple' =>
15 qw/_identity _blob_log_on_update/
20 DBIx::Class::Storage::DBI::Sybase - Sybase support for DBIx::Class
24 This subclass supports L<DBD::Sybase> for real Sybase databases. If you are
25 using an MSSQL database via L<DBD::Sybase>, your storage will be reblessed to
26 L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
30 If your version of Sybase does not support placeholders, then your storage
31 will be reblessed to L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>. You can
32 also enable that driver explicitly, see the documentation for more details.
34 With this driver there is unfortunately no way to get the C<last_insert_id>
35 without doing a C<select max(col)>.
37 But your queries will be cached.
39 You need a version of L<DBD::Sybase> compiled with the Sybase OpenClient
40 libraries, B<NOT> FreeTDS, for placeholder support. Otherwise your storage will
41 be automatically reblessed into C<::NoBindVars>.
43 A recommended L<DBIx::Class::Storage::DBI/connect_info> setting:
45 on_connect_call => [['datetime_setup'], ['blob_setup', log_on_update => 0]]
54 if (ref($self) eq 'DBIx::Class::Storage::DBI::Sybase') {
56 @{$self->dbh->selectrow_arrayref(qq{sp_server_info \@attribute_id=1})}[2]
61 my $subclass = "DBIx::Class::Storage::DBI::Sybase::${dbtype}";
63 if (!$exception && $dbtype && $self->load_optional_class($subclass)) {
64 bless $self, $subclass;
66 } else { # real Sybase
67 my $no_bind_vars = 'DBIx::Class::Storage::DBI::Sybase::NoBindVars';
69 if ($self->_using_freetds) {
72 Your version of Sybase potentially supports placeholders and query caching,
73 however you seem to be using FreeTDS which does not (yet?) support this.
75 Please recompile DBD::Sybase with the Sybase OpenClient libraries if you want
78 TEXT/IMAGE column support will also not work under FreeTDS.
80 See perldoc DBIx::Class::Storage::DBI::Sybase for more details.
82 $self->ensure_class_loaded($no_bind_vars);
83 bless $self, $no_bind_vars;
87 if (not $self->dbh->{syb_dynamic_supported}) {
88 $self->ensure_class_loaded($no_bind_vars);
89 bless $self, $no_bind_vars;
93 $self->_set_maxConnect;
101 return $self->_dbh->{syb_oc_version} =~ /freetds/i;
104 sub _set_maxConnect {
107 my $dsn = $self->_dbi_connect_info->[0];
109 return if ref($dsn) eq 'CODE';
111 if ($dsn !~ /maxConnect=/) {
112 $self->_dbi_connect_info->[0] = "$dsn;maxConnect=256";
113 my $connected = defined $self->_dbh;
115 $self->ensure_connected if $connected;
119 =head2 connect_call_blob_setup
123 on_connect_call => [ [ 'blob_setup', log_on_update => 0 ] ]
125 Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
126 instead of as a hex string.
130 Also sets the C<log_on_update> value for blob write operations. The default is
131 C<1>, but C<0> is better if your database is configured for it.
134 L<DBD::Sybase/Handling_IMAGE/TEXT_data_with_syb_ct_get_data()/syb_ct_send_data()>.
138 sub connect_call_blob_setup {
141 my $dbh = $self->_dbh;
142 $dbh->{syb_binary_images} = 1;
144 $self->_blob_log_on_update($args{log_on_update})
145 if exists $args{log_on_update};
151 $type && $type =~ /(?:text|image|lob|bytea|binary|memo)/i;
154 ## This will be useful if we ever implement BLOB filehandle inflation and will
155 ## need to use the API, but for now it isn't.
157 #sub order_columns_for_select {
158 # my ($self, $source, $columns) = @_;
160 # my (@non_blobs, @blobs);
162 # for my $col (@$columns) {
163 # if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
166 # push @non_blobs, $col;
170 # croak "cannot select more than a one TEXT/IMAGE column at a time"
173 # return (@non_blobs, @blobs);
176 # the select-piggybacking-on-insert trick stolen from odbc/mssql
177 sub _prep_for_execute {
179 my ($op, $extra_bind, $ident, $args) = @_;
181 my ($sql, $bind) = $self->next::method (@_);
183 if ($op eq 'insert') {
184 my ($identity_insert_on, $identity_insert_off, $identity_col);
185 my $table = $ident->from;
187 my $bind_info = $self->_resolve_column_info($ident, [map $_->[0], @{$bind}]);
189 List::Util::first { $bind_info->{$_}{is_auto_increment} } (keys %$bind_info);
192 $identity_insert_on = "SET IDENTITY_INSERT $table ON";
193 $identity_insert_off = "SET IDENTITY_INSERT $table OFF";
195 $identity_col = List::Util::first {
196 $ident->column_info($_)->{is_auto_increment}
201 # Sybase has nested transactions, only the outermost is actually committed
203 "BEGIN TRANSACTION\n" .
204 ($identity_insert_on ? "$identity_insert_on\n" : '') .
206 ($identity_insert_off ? "$identity_insert_off\n" : '') .
207 $self->_fetch_identity_sql($ident, $identity_col) . "\n" .
212 return ($sql, $bind);
215 sub _fetch_identity_sql {
216 my ($self, $source, $col) = @_;
218 return "SELECT MAX($col) FROM ".$source->from;
225 my ($rv, $sth, @bind) = $self->dbh_do($self->can('_dbh_execute'), @_);
227 if ($op eq 'insert') {
228 $self->_identity($sth->fetchrow_array);
232 return wantarray ? ($rv, $sth, @bind) : $rv;
235 sub last_insert_id { shift->_identity }
237 # override to handle TEXT/IMAGE
239 my ($self, $source, $to_insert) = splice @_, 0, 3;
240 my $dbh = $self->_dbh;
242 my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
244 my $updated_cols = $self->next::method($source, $to_insert, @_);
246 $self->_insert_blobs($source, $blob_cols, $to_insert) if %$blob_cols;
248 return $updated_cols;
252 my ($self, $source) = splice @_, 0, 2;
253 my ($fields, $where) = @_;
254 my $wantarray = wantarray;
256 my $blob_cols = $self->_remove_blob_cols($source, $fields);
260 @res = $self->next::method($source, @_);
262 $res[0] = $self->next::method($source, @_);
265 $self->_update_blobs($source, $blob_cols, $where) if %$blob_cols;
267 return $wantarray ? @res : $res[0];
270 sub _remove_blob_cols {
271 my ($self, $source, $fields) = @_;
275 for my $col (keys %$fields) {
276 if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
277 $blob_cols{$col} = delete $fields->{$col};
278 $fields->{$col} = \"''";
286 my ($self, $source, $blob_cols, $where) = @_;
288 my (@primary_cols) = $source->primary_columns;
290 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
291 unless @primary_cols;
293 # check if we're updating a single row by PK
294 my $pk_cols_in_where = 0;
295 for my $col (@primary_cols) {
296 $pk_cols_in_where++ if defined $where->{$col};
300 if ($pk_cols_in_where == @primary_cols) {
302 @row_to_update{@primary_cols} = @{$where}{@primary_cols};
303 @rows = \%row_to_update;
305 my $rs = $source->resultset->search(
308 result_class => 'DBIx::Class::ResultClass::HashRefInflator',
309 select => \@primary_cols
312 @rows = $rs->all; # statement must finish
315 for my $row (@rows) {
316 $self->_insert_blobs($source, $blob_cols, $row);
321 my ($self, $source, $blob_cols, $row) = @_;
322 my $dbh = $self->dbh;
324 my $table = $source->from;
327 my (@primary_cols) = $source->primary_columns;
329 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
330 unless @primary_cols;
332 if ((grep { defined $row{$_} } @primary_cols) != @primary_cols) {
333 if (@primary_cols == 1) {
334 my $col = $primary_cols[0];
335 $row{$col} = $self->last_insert_id($source, $col);
337 croak "Cannot update TEXT/IMAGE column(s) without primary key values";
341 for my $col (keys %$blob_cols) {
342 my $blob = $blob_cols->{$col};
345 if (not $self->isa('DBIx::Class::Storage::DBI::NoBindVars')) {
346 my $search_cond = join ',' => map "$_ = ?", @primary_cols;
349 "select $col from $table where $search_cond"
351 $sth->execute(map $row{$_}, @primary_cols);
353 my $search_cond = join ',' => map "$_ = $row{$_}", @primary_cols;
355 $sth = $dbh->prepare(
356 "select $col from $table where $search_cond"
362 while ($sth->fetch) {
363 $sth->func('CS_GET', 1, 'ct_data_info') or die $sth->errstr;
365 $sth->func('ct_prepare_send') or die $sth->errstr;
367 my $log_on_update = $self->_blob_log_on_update;
368 $log_on_update = 1 if not defined $log_on_update;
370 $sth->func('CS_SET', 1, {
371 total_txtlen => length($blob),
372 log_on_update => $log_on_update
373 }, 'ct_data_info') or die $sth->errstr;
375 $sth->func($blob, length($blob), 'ct_send_data') or die $sth->errstr;
377 $sth->func('ct_finish_send') or die $sth->errstr;
381 croak $exception if $exception;
385 =head2 connect_call_datetime_setup
389 on_connect_call => 'datetime_setup'
391 In L<DBIx::Class::Storage::DBI/connect_info> to set:
393 $dbh->syb_date_fmt('ISO_strict'); # output fmt: 2004-08-21T14:36:48.080Z
394 $dbh->do('set dateformat mdy'); # input fmt: 08/13/1979 18:08:55.080
396 On connection for use with L<DBIx::Class::InflateColumn::DateTime>, using
397 L<DateTime::Format::Sybase>, which you will need to install.
399 This works for both C<DATETIME> and C<SMALLDATETIME> columns, although
400 C<SMALLDATETIME> columns only have minute precision.
405 my $old_dbd_warned = 0;
407 sub connect_call_datetime_setup {
409 my $dbh = $self->_dbh;
411 if ($dbh->can('syb_date_fmt')) {
412 $dbh->syb_date_fmt('ISO_strict');
413 } elsif (not $old_dbd_warned) {
414 carp "Your DBD::Sybase is too old to support ".
415 "DBIx::Class::InflateColumn::DateTime, please upgrade!";
419 $dbh->do('set dateformat mdy');
425 sub datetime_parser_type { "DateTime::Format::Sybase" }
427 # savepoint support using ASE syntax
430 my ($self, $name) = @_;
432 $self->dbh->do("SAVE TRANSACTION $name");
435 # A new SAVE TRANSACTION with the same name releases the previous one.
436 sub _svp_release { 1 }
439 my ($self, $name) = @_;
441 $self->dbh->do("ROLLBACK TRANSACTION $name");
446 =head1 MAXIMUM CONNECTIONS
448 L<DBD::Sybase> makes separate connections to the server for active statements in
449 the background. By default the number of such connections is limited to 25, on
450 both the client side and the server side.
452 This is a bit too low, so on connection the clientside setting is set to C<256>
453 (see L<DBD::Sybase/maxConnect>.) You can override it to whatever setting you
457 L<http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1272.htm>
458 for information on changing the setting on the server side.
462 See L</connect_call_datetime_setup> to setup date formats
463 for L<DBIx::Class::InflateColumn::DateTime>.
465 =head1 IMAGE AND TEXT COLUMNS
467 L<DBD::Sybase> compiled with FreeTDS will B<NOT> work with C<TEXT/IMAGE>
470 See L</connect_call_blob_setup> for a L<DBIx::Class::Storage::DBI/connect_info>
471 setting you need to work with C<IMAGE> columns.
475 See L<DBIx::Class/CONTRIBUTORS>.
479 You may distribute this code under the same terms as Perl itself.