Fixed caveats
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Oracle / Generic.pm
CommitLineData
18360aed 1package DBIx::Class::Storage::DBI::Oracle::Generic;
2
3use strict;
4use warnings;
5
7137528d 6=head1 NAME
7
7a84c41b 8DBIx::Class::Storage::DBI::Oracle::Generic - Oracle Support for DBIx::Class
7137528d 9
10=head1 SYNOPSIS
11
12 # In your table classes
13 __PACKAGE__->load_components(qw/PK::Auto Core/);
2e46b6eb 14 __PACKAGE__->add_columns({ id => { sequence => 'mysequence', auto_nextval => 1 } });
7137528d 15 __PACKAGE__->set_primary_key('id');
16 __PACKAGE__->sequence('mysequence');
17
18=head1 DESCRIPTION
19
20This class implements autoincrements for Oracle.
21
22=head1 METHODS
23
24=cut
25
db56cf3d 26use base qw/DBIx::Class::Storage::DBI/;
2ad62d97 27use mro 'c3';
18360aed 28
66cf3a84 29# For ORA_BLOB => 113, ORA_CLOB => 112
5db2758d 30use DBD::Oracle qw( :ora_types );
5db2758d 31
18360aed 32sub _dbh_last_insert_id {
2e46b6eb 33 my ($self, $dbh, $source, @columns) = @_;
34 my @ids = ();
35 foreach my $col (@columns) {
36 my $seq = ($source->column_info($col)->{sequence} ||= $self->get_autoinc_seq($source,$col));
37 my $id = $self->_sequence_fetch( 'currval', $seq );
38 push @ids, $id;
39 }
40 return @ids;
18360aed 41}
42
43sub _dbh_get_autoinc_seq {
44 my ($self, $dbh, $source, $col) = @_;
45
46 # look up the correct sequence automatically
47 my $sql = q{
48 SELECT trigger_body FROM ALL_TRIGGERS t
49 WHERE t.table_name = ?
50 AND t.triggering_event = 'INSERT'
51 AND t.status = 'ENABLED'
52 };
53
54 # trigger_body is a LONG
7a84c41b 55 local $dbh->{LongReadLen} = 64 * 1024 if ($dbh->{LongReadLen} < 64 * 1024);
18360aed 56
cb464582 57 my $sth;
58
59 # check for fully-qualified name (eg. SCHEMA.TABLENAME)
60 if ( my ( $schema, $table ) = $source->name =~ /(\w+)\.(\w+)/ ) {
61 $sql = q{
62 SELECT trigger_body FROM ALL_TRIGGERS t
63 WHERE t.owner = ? AND t.table_name = ?
64 AND t.triggering_event = 'INSERT'
65 AND t.status = 'ENABLED'
66 };
67 $sth = $dbh->prepare($sql);
68 $sth->execute( uc($schema), uc($table) );
69 }
70 else {
71 $sth = $dbh->prepare($sql);
72 $sth->execute( uc( $source->name ) );
73 }
18360aed 74 while (my ($insert_trigger) = $sth->fetchrow_array) {
75 return uc($1) if $insert_trigger =~ m!(\w+)\.nextval!i; # col name goes here???
76 }
66cab05c 77 $self->throw_exception("Unable to find a sequence INSERT trigger on table '" . $source->name . "'.");
18360aed 78}
79
2e46b6eb 80sub _sequence_fetch {
81 my ( $self, $type, $seq ) = @_;
82 my ($id) = $self->dbh->selectrow_array("SELECT ${seq}.${type} FROM DUAL");
83 return $id;
84}
85
6dc4be0f 86sub _ping {
c2481821 87 my $self = shift;
7ba7a57d 88
6dc4be0f 89 my $dbh = $self->_dbh or return 0;
7ba7a57d 90
6dc4be0f 91 local $dbh->{RaiseError} = 1;
c2d7baef 92
6dc4be0f 93 eval {
94 $dbh->do("select 1 from dual");
95 };
7ba7a57d 96
6dc4be0f 97 return $@ ? 0 : 1;
c2481821 98}
99
d789fa99 100sub _dbh_execute {
101 my $self = shift;
102 my ($dbh, $op, $extra_bind, $ident, $bind_attributes, @args) = @_;
103
104 my $wantarray = wantarray;
d789fa99 105
c2d7baef 106 my (@res, $exception, $retried);
107
0f0abc97 108 RETRY: {
109 do {
110 eval {
111 if ($wantarray) {
c3515436 112 @res = $self->next::method(@_);
0f0abc97 113 } else {
c3515436 114 $res[0] = $self->next::method(@_);
0f0abc97 115 }
116 };
117 $exception = $@;
118 if ($exception =~ /ORA-01003/) {
119 # ORA-01003: no statement parsed (someone changed the table somehow,
120 # invalidating your cursor.)
121 my ($sql, $bind) = $self->_prep_for_execute($op, $extra_bind, $ident, \@args);
122 delete $dbh->{CachedKids}{$sql};
d789fa99 123 } else {
0f0abc97 124 last RETRY;
d789fa99 125 }
0f0abc97 126 } while (not $retried++);
127 }
d789fa99 128
129 $self->throw_exception($exception) if $exception;
130
131 wantarray ? @res : $res[0]
132}
133
7137528d 134=head2 get_autoinc_seq
135
136Returns the sequence name for an autoincrement column
137
138=cut
139
18360aed 140sub get_autoinc_seq {
141 my ($self, $source, $col) = @_;
d4daee7b 142
373940e1 143 $self->dbh_do('_dbh_get_autoinc_seq', $source, $col);
18360aed 144}
145
7137528d 146=head2 columns_info_for
147
148This wraps the superclass version of this method to force table
149names to uppercase
150
151=cut
152
18360aed 153sub columns_info_for {
154 my ($self, $table) = @_;
155
156 $self->next::method(uc($table));
157}
158
8f7e044c 159=head2 datetime_parser_type
160
161This sets the proper DateTime::Format module for use with
162L<DBIx::Class::InflateColumn::DateTime>.
163
164=cut
165
166sub datetime_parser_type { return "DateTime::Format::Oracle"; }
167
9900b569 168=head2 connect_call_datetime_setup
d2a3958e 169
170Used as:
171
9900b569 172 on_connect_call => 'datetime_setup'
d2a3958e 173
82f6f45f 174In L<DBIx::Class::Storage::DBI/connect_info> to set the session nls date, and
175timestamp values for use with L<DBIx::Class::InflateColumn::DateTime> and the
176necessary environment variables for L<DateTime::Format::Oracle>, which is used
177by it.
d2a3958e 178
82f6f45f 179Maximum allowable precision is used, unless the environment variables have
180already been set.
d2a3958e 181
9900b569 182These are the defaults used:
183
184 $ENV{NLS_DATE_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS';
185 $ENV{NLS_TIMESTAMP_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS.FF';
186 $ENV{NLS_TIMESTAMP_TZ_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS.FF TZHTZM';
187
d9e53b85 188To get more than second precision with L<DBIx::Class::InflateColumn::DateTime>
189for your timestamps, use something like this:
190
191 use Time::HiRes 'time';
192 my $ts = DateTime->from_epoch(epoch => time);
193
d2a3958e 194=cut
195
9900b569 196sub connect_call_datetime_setup {
d2a3958e 197 my $self = shift;
d2a3958e 198
199 my $date_format = $ENV{NLS_DATE_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS';
200 my $timestamp_format = $ENV{NLS_TIMESTAMP_FORMAT} ||=
201 'YYYY-MM-DD HH24:MI:SS.FF';
202 my $timestamp_tz_format = $ENV{NLS_TIMESTAMP_TZ_FORMAT} ||=
203 'YYYY-MM-DD HH24:MI:SS.FF TZHTZM';
204
7a84c41b 205 $self->_do_query("alter session set nls_date_format = '$date_format'");
206 $self->_do_query(
207"alter session set nls_timestamp_format = '$timestamp_format'");
208 $self->_do_query(
209"alter session set nls_timestamp_tz_format='$timestamp_tz_format'");
d2a3958e 210}
211
281719d2 212sub _svp_begin {
213 my ($self, $name) = @_;
d4daee7b 214
281719d2 215 $self->dbh->do("SAVEPOINT $name");
216}
217
5db2758d 218=head2 source_bind_attributes
219
220Handle LOB types in Oracle. Under a certain size (4k?), you can get away
221with the driver assuming your input is the deprecated LONG type if you
222encode it as a hex string. That ain't gonna fly at larger values, where
223you'll discover you have to do what this does.
224
225This method had to be overridden because we need to set ora_field to the
226actual column, and that isn't passed to the call (provided by Storage) to
227bind_attribute_by_data_type.
228
229According to L<DBD::Oracle>, the ora_field isn't always necessary, but
230adding it doesn't hurt, and will save your bacon if you're modifying a
231table with more than one LOB column.
232
233=cut
234
235sub source_bind_attributes
236{
237 my $self = shift;
238 my($source) = @_;
239
240 my %bind_attributes;
241
242 foreach my $column ($source->columns) {
243 my $data_type = $source->column_info($column)->{data_type} || '';
244 next unless $data_type;
245
246 my %column_bind_attrs = $self->bind_attribute_by_data_type($data_type);
247
248 if ($data_type =~ /^[BC]LOB$/i) {
249 $column_bind_attrs{'ora_type'}
250 = uc($data_type) eq 'CLOB' ? ORA_CLOB : ORA_BLOB;
251 $column_bind_attrs{'ora_field'} = $column;
252 }
253
254 $bind_attributes{$column} = \%column_bind_attrs;
255 }
256
257 return \%bind_attributes;
258}
259
281719d2 260# Oracle automatically releases a savepoint when you start another one with the
261# same name.
262sub _svp_release { 1 }
263
264sub _svp_rollback {
265 my ($self, $name) = @_;
266
267 $self->dbh->do("ROLLBACK TO SAVEPOINT $name")
268}
269
7a84c41b 270=head1 AUTHOR
18360aed 271
7a84c41b 272See L<DBIx::Class/CONTRIBUTORS>.
18360aed 273
274=head1 LICENSE
275
276You may distribute this code under the same terms as Perl itself.
277
278=cut
7137528d 279
2801;