A couple of typos, and general whitespace cleanup (ick)
[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
92bc2a19 8DBIx::Class::Storage::DBI::Oracle::Generic - Automatic primary key class for Oracle
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
55 $dbh->{LongReadLen} = 64 * 1024 if ($dbh->{LongReadLen} < 64 * 1024);
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;
198 my $dbh = $self->dbh;
199
200 my $date_format = $ENV{NLS_DATE_FORMAT} ||= 'YYYY-MM-DD HH24:MI:SS';
201 my $timestamp_format = $ENV{NLS_TIMESTAMP_FORMAT} ||=
202 'YYYY-MM-DD HH24:MI:SS.FF';
203 my $timestamp_tz_format = $ENV{NLS_TIMESTAMP_TZ_FORMAT} ||=
204 'YYYY-MM-DD HH24:MI:SS.FF TZHTZM';
205
206 $dbh->do("alter session set nls_date_format = '$date_format'");
207 $dbh->do("alter session set nls_timestamp_format = '$timestamp_format'");
d9e53b85 208 $dbh->do("alter session set nls_timestamp_tz_format='$timestamp_tz_format'");
d2a3958e 209}
210
281719d2 211sub _svp_begin {
212 my ($self, $name) = @_;
d4daee7b 213
281719d2 214 $self->dbh->do("SAVEPOINT $name");
215}
216
5db2758d 217=head2 source_bind_attributes
218
219Handle LOB types in Oracle. Under a certain size (4k?), you can get away
220with the driver assuming your input is the deprecated LONG type if you
221encode it as a hex string. That ain't gonna fly at larger values, where
222you'll discover you have to do what this does.
223
224This method had to be overridden because we need to set ora_field to the
225actual column, and that isn't passed to the call (provided by Storage) to
226bind_attribute_by_data_type.
227
228According to L<DBD::Oracle>, the ora_field isn't always necessary, but
229adding it doesn't hurt, and will save your bacon if you're modifying a
230table with more than one LOB column.
231
232=cut
233
234sub source_bind_attributes
235{
236 my $self = shift;
237 my($source) = @_;
238
239 my %bind_attributes;
240
241 foreach my $column ($source->columns) {
242 my $data_type = $source->column_info($column)->{data_type} || '';
243 next unless $data_type;
244
245 my %column_bind_attrs = $self->bind_attribute_by_data_type($data_type);
246
247 if ($data_type =~ /^[BC]LOB$/i) {
248 $column_bind_attrs{'ora_type'}
249 = uc($data_type) eq 'CLOB' ? ORA_CLOB : ORA_BLOB;
250 $column_bind_attrs{'ora_field'} = $column;
251 }
252
253 $bind_attributes{$column} = \%column_bind_attrs;
254 }
255
256 return \%bind_attributes;
257}
258
281719d2 259# Oracle automatically releases a savepoint when you start another one with the
260# same name.
261sub _svp_release { 1 }
262
263sub _svp_rollback {
264 my ($self, $name) = @_;
265
266 $self->dbh->do("ROLLBACK TO SAVEPOINT $name")
267}
268
18360aed 269=head1 AUTHORS
270
271Andy Grundman <andy@hybridized.org>
272
273Scott Connelly <scottsweep@yahoo.com>
274
275=head1 LICENSE
276
277You may distribute this code under the same terms as Perl itself.
278
279=cut
7137528d 280
2811;