mangling _select_args turned out to be unnecessary
[dbsrgits/DBIx-Class-Historic.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/
64f4e691 7 DBIx::Class::Storage::DBI::Sybase::Base
b0b44f97 8 DBIx::Class::Storage::DBI
eabab5d0 9/;
2ad62d97 10use mro 'c3';
6b1f5ef7 11use Carp::Clan qw/^DBIx::Class/;
12
98259fe4 13=head1 NAME
14
15DBIx::Class::Storage::DBI::Sybase - Storage::DBI subclass for Sybase
16
17=head1 SYNOPSIS
18
19This subclass supports L<DBD::Sybase> for real Sybase databases. If you are
20using an MSSQL database via L<DBD::Sybase>, your storage will be reblessed to
21L<DBIx::Class::Storage::DBI::Sybase::Microsoft_SQL_Server>.
22
23=head1 DESCRIPTION
24
25If your version of Sybase does not support placeholders, then your storage
26will be reblessed to L<DBIx::Class::Storage::DBI::Sybase::NoBindVars>. You can
27also enable that driver explicitly, see the documentation for more details.
28
29With this driver there is unfortunately no way to get the C<last_insert_id>
30without doing a C<select max(col)>.
31
32But your queries will be cached.
33
5703eb14 34You need at least version C<1.09> of L<DBD::Sybase> for placeholder support.
35Otherwise your storage will be automatically reblessed into C<::NoBindVars>.
36
fd5a07e4 37A recommended L<DBIx::Class::Storage::DBI/connect_info> settings:
98259fe4 38
fd5a07e4 39 on_connect_call => [['datetime_setup'], [blob_setup => log_on_update => 0]]
98259fe4 40
41=head1 METHODS
42
43=cut
44
fd5a07e4 45__PACKAGE__->mk_group_accessors('simple' =>
46 qw/_blob_log_on_update/
47);
48
47d9646a 49sub _rebless {
b50a5275 50 my $self = shift;
c5ce7cd6 51
52 if (ref($self) eq 'DBIx::Class::Storage::DBI::Sybase') {
53 my $dbtype = eval {
54 @{$self->dbh->selectrow_arrayref(qq{sp_server_info \@attribute_id=1})}[2]
55 } || '';
56
57 my $exception = $@;
58 $dbtype =~ s/\W/_/gi;
59 my $subclass = "DBIx::Class::Storage::DBI::Sybase::${dbtype}";
60
61 if (!$exception && $dbtype && $self->load_optional_class($subclass)) {
62 bless $self, $subclass;
63 $self->_rebless;
5703eb14 64 } else { # real Sybase
65 my $no_bind_vars = 'DBIx::Class::Storage::DBI::Sybase::NoBindVars';
66
683f73ec 67 if (not $self->dbh->{syb_dynamic_supported}) {
5703eb14 68 $self->ensure_class_loaded($no_bind_vars);
69 bless $self, $no_bind_vars;
70 $self->_rebless;
71 }
72
73 if ($DBD::Sybase::VERSION < 1.09) {
74 carp <<'EOF';
75
76Your version of Sybase potentially supports placeholders and query caching,
77however your version of DBD::Sybase is too old to do this properly. Please
78upgrade to at least version 1.09 if you want this feature.
79
80TEXT/IMAGE column support will also not work in older versions of DBD::Sybase.
81
82See perldoc DBIx::Class::Storage::DBI::Sybase for more details.
83EOF
84 $self->ensure_class_loaded($no_bind_vars);
85 bless $self, $no_bind_vars;
683f73ec 86 $self->_rebless;
87 }
41c93b1b 88 $self->_set_maxConnect;
47d9646a 89 }
c5ce7cd6 90 }
b50a5275 91}
92
41c93b1b 93sub _set_maxConnect {
94 my $self = shift;
95
96 my $dsn = $self->_dbi_connect_info->[0];
97
98 return if ref($dsn) eq 'CODE';
99
100 if ($dsn !~ /maxConnect=/) {
101 $self->_dbi_connect_info->[0] = "$dsn;maxConnect=256";
41c93b1b 102 my $connected = defined $self->_dbh;
103 $self->disconnect;
104 $self->ensure_connected if $connected;
105 }
106}
107
63d46bb3 108=head2 connect_call_blob_setup
109
110Used as:
111
fd5a07e4 112 on_connect_call => [ [ blob_setup => log_on_update => 0 ] ]
63d46bb3 113
114Does C<< $dbh->{syb_binary_images} = 1; >> to return C<IMAGE> data as raw binary
115instead of as a hex string.
116
6636ad53 117Recommended.
118
fd5a07e4 119Also sets the C<log_on_update> value for blob write operations. The default is
120C<1>, but C<0> is better if your database is configured for it.
121
122See
123L<DBD::Sybase/Handling_IMAGE/TEXT_data_with_syb_ct_get_data()/syb_ct_send_data()>.
124
63d46bb3 125=cut
126
127sub connect_call_blob_setup {
128 my $self = shift;
fd5a07e4 129 my %args = @_;
63d46bb3 130 my $dbh = $self->_dbh;
131 $dbh->{syb_binary_images} = 1;
fd5a07e4 132
133 $self->_blob_log_on_update($args{log_on_update})
134 if exists $args{log_on_update};
135}
136
137sub _is_lob_type {
138 my $self = shift;
5703eb14 139 my $type = shift;
140 $type && $type =~ /(?:text|image|lob|bytea|binary)/i;
fd5a07e4 141}
142
b5453fbb 143## This will be useful if we ever implement BLOB filehandle inflation and will
144## need to use the API, but for now it isn't.
5703eb14 145#
b5453fbb 146#sub order_columns_for_select {
147# my ($self, $source) = @_;
5703eb14 148#
149# my (@non_blobs, @blobs);
150#
b5453fbb 151# for my $col ($source->columns) {
152# if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
5703eb14 153# push @blobs, $col;
154# } else {
155# push @non_blobs, $col;
156# }
157# }
158#
b5453fbb 159# croak "cannot select more than a one TEXT/IMAGE column at a time"
5703eb14 160# if @blobs > 1;
161#
b5453fbb 162# return (@non_blobs, @blobs);
5703eb14 163#}
164
165# override to handle TEXT/IMAGE
fd5a07e4 166sub insert {
7d17f469 167 my ($self, $source, $to_insert) = splice @_, 0, 3;
168
169 my $blob_cols = $self->_remove_blob_cols($source, $to_insert);
170
171 my $updated_cols = $self->next::method($source, $to_insert, @_);
172
173 $self->_update_blobs($source, $blob_cols, $to_insert) if %$blob_cols;
174
175 return $updated_cols;
176}
177
178#sub update {
179# my ($self, $source) = splice @_, 0, 2;
180# my ($fields) = @_;
181#
182# my $blob_cols = $self->_remove_blob_cols($source, $fields);
183#
184# my @res = 1;
185#
186# if (%$fields) {
187# if (wantarray) {
188# @res = $self->next::method($source, @_);
189# } else {
190# $res[0] = $self->next::method($source, @_);
191# }
192# }
193#
194# $self->_update_blobs($source, $blob_cols, $fields) if %$blob_cols;
195#
196# return wantarray ? @res : $res[0];
197#}
198
199sub _remove_blob_cols {
200 my ($self, $source, $fields) = @_;
fd5a07e4 201
202 my %blob_cols;
203
7d17f469 204 for my $col (keys %$fields) {
9b3dabe0 205 if ($self->_is_lob_type($source->column_info($col)->{data_type})) {
206 $blob_cols{$col} = delete $fields->{$col};
207 $fields->{$col} = \"''";
208 }
fd5a07e4 209 }
210
7d17f469 211 return \%blob_cols;
fd5a07e4 212}
213
214sub _update_blobs {
215 my ($self, $source, $blob_cols, $inserted) = @_;
216 my $dbh = $self->dbh;
217
218 my $table = $source->from;
219
9b3dabe0 220 my %inserted = %$inserted;
fd5a07e4 221 my (@primary_cols) = $source->primary_columns;
222
9b3dabe0 223 croak "Cannot update TEXT/IMAGE column(s) without a primary key"
fd5a07e4 224 unless @primary_cols;
225
9b3dabe0 226 if ((grep { defined $inserted{$_} } @primary_cols) != @primary_cols) {
227 if (@primary_cols == 1) {
228 my $col = $primary_cols[0];
229 $inserted{$col} = $self->last_insert_id($source, $col);
230 } else {
231 croak "Cannot update TEXT/IMAGE column(s) without primary key values";
232 }
233 }
fd5a07e4 234
235 for my $col (keys %$blob_cols) {
236 my $blob = $blob_cols->{$col};
9b3dabe0 237 my $sth;
fd5a07e4 238
9b3dabe0 239 if (not $self->isa('DBIx::Class::Storage::DBI::NoBindVars')) {
240 my $search_cond = join ',' => map "$_ = ?", @primary_cols;
241
242 $sth = $self->sth(
243 "select $col from $table where $search_cond"
244 );
245 $sth->execute(map $inserted{$_}, @primary_cols);
246 } else {
247 my $search_cond = join ',' => map "$_ = $inserted{$_}", @primary_cols;
248
249 $sth = $dbh->prepare(
250 "select $col from $table where $search_cond"
251 );
252 $sth->execute;
253 }
fd5a07e4 254
255 eval {
256 while ($sth->fetch) {
257 $sth->func('CS_GET', 1, 'ct_data_info') or die $sth->errstr;
258 }
259 $sth->func('ct_prepare_send') or die $sth->errstr;
260
261 my $log_on_update = $self->_blob_log_on_update;
262 $log_on_update = 1 if not defined $log_on_update;
263
264 $sth->func('CS_SET', 1, {
265 total_txtlen => length($blob),
266 log_on_update => $log_on_update
267 }, 'ct_data_info') or die $sth->errstr;
268
269 $sth->func($blob, length($blob), 'ct_send_data') or die $sth->errstr;
270
271 $sth->func('ct_finish_send') or die $sth->errstr;
272 };
273 my $exception = $@;
274 $sth->finish;
275 croak $exception if $exception;
276 }
63d46bb3 277}
278
9539eeb1 279=head2 connect_call_datetime_setup
280
281Used as:
282
283 on_connect_call => 'datetime_setup'
284
285In L<DBIx::Class::Storage::DBI/connect_info> to set:
286
3abafb11 287 $dbh->syb_date_fmt('ISO_strict'); # output fmt: 2004-08-21T14:36:48.080Z
288 $dbh->do('set dateformat mdy'); # input fmt: 08/13/1979 18:08:55.080
9539eeb1 289
290On connection for use with L<DBIx::Class::InflateColumn::DateTime>, using
3abafb11 291L<DateTime::Format::Sybase>, which you will need to install.
292
293This works for both C<DATETIME> and C<SMALLDATETIME> columns, although
294C<SMALLDATETIME> columns only have minute precision.
9539eeb1 295
296=cut
297
9041a97a 298{
299 my $old_dbd_warned = 0;
300
9539eeb1 301 sub connect_call_datetime_setup {
6b1f5ef7 302 my $self = shift;
6b1f5ef7 303 my $dbh = $self->_dbh;
304
305 if ($dbh->can('syb_date_fmt')) {
306 $dbh->syb_date_fmt('ISO_strict');
307 } elsif (not $old_dbd_warned) {
308 carp "Your DBD::Sybase is too old to support ".
309 "DBIx::Class::InflateColumn::DateTime, please upgrade!";
310 $old_dbd_warned = 1;
311 }
312
313 $dbh->do('set dateformat mdy');
c5ce7cd6 314
6b1f5ef7 315 1;
c5ce7cd6 316 }
6b1f5ef7 317}
318
6636ad53 319sub datetime_parser_type { "DateTime::Format::Sybase" }
320
6b1f5ef7 321sub _dbh_last_insert_id {
322 my ($self, $dbh, $source, $col) = @_;
c5ce7cd6 323
324 # sorry, there's no other way!
325 my $sth = $dbh->prepare_cached("select max($col) from ".$source->from);
326 return ($dbh->selectrow_array($sth))[0];
a964a928 327}
328
3885cff6 3291;
330
41c93b1b 331=head1 MAXIMUM CONNECTIONS
332
333L<DBD::Sybase> makes separate connections to the server for active statements in
334the background. By default the number of such connections is limited to 25, on
335both the client side and the server side.
336
337This is a bit too low, so on connection the clientside setting is set to C<256>
338(see L<DBD::Sybase/maxConnect>.) You can override it to whatever setting you
339like in the DSN.
340
341See
342L<http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.help.ase_15.0.sag1/html/sag1/sag1272.htm>
343for information on changing the setting on the server side.
344
c5ce7cd6 345=head1 DATES
346
3abafb11 347See L</connect_call_datetime_setup> to setup date formats
348for L<DBIx::Class::InflateColumn::DateTime>.
c5ce7cd6 349
6636ad53 350=head1 IMAGE AND TEXT COLUMNS
63d46bb3 351
5703eb14 352You need at least version C<1.09> of L<DBD::Sybase> for C<TEXT/IMAGE> column
353support.
354
63d46bb3 355See L</connect_call_blob_setup> for a L<DBIx::Class::Storage::DBI/connect_info>
356setting you need to work with C<IMAGE> columns.
357
6636ad53 358Due to limitations in L<DBD::Sybase> and this driver, it is only possible to
5703eb14 359select one C<TEXT> or C<IMAGE> column at a time. This is handled automatically
360for tables with only one such column, if you have more than one, supply a
361C<< select => [qw/col list .../] >> key to your C<< ->search >> calls, with the
362single desired C<TEXT/IMAGE> column at the end of the list.
6636ad53 363
3885cff6 364=head1 AUTHORS
365
7e8cecc1 366See L<DBIx::Class/CONTRIBUTORS>.
c5ce7cd6 367
3885cff6 368=head1 LICENSE
369
370You may distribute this code under the same terms as Perl itself.
371
372=cut
c5ce7cd6 373# vim:sts=2 sw=2: