Support for saving CLOB and BLOB types in Oracle.
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / Oracle / Generic.pm
CommitLineData
18360aed 1package DBIx::Class::Storage::DBI::Oracle::Generic;
e21dfd6a 2# -*- mode: cperl; cperl-indent-level: 2 -*-
18360aed 3
4use strict;
5use warnings;
6
7137528d 7=head1 NAME
8
92bc2a19 9DBIx::Class::Storage::DBI::Oracle::Generic - Automatic primary key class for Oracle
7137528d 10
11=head1 SYNOPSIS
12
13 # In your table classes
14 __PACKAGE__->load_components(qw/PK::Auto Core/);
2e46b6eb 15 __PACKAGE__->add_columns({ id => { sequence => 'mysequence', auto_nextval => 1 } });
7137528d 16 __PACKAGE__->set_primary_key('id');
17 __PACKAGE__->sequence('mysequence');
18
19=head1 DESCRIPTION
20
21This class implements autoincrements for Oracle.
22
23=head1 METHODS
24
25=cut
26
18360aed 27use Carp::Clan qw/^DBIx::Class/;
28
5db2758d 29use DBD::Oracle qw( :ora_types );
30#use constant ORA_BLOB => 113; ## ORA_CLOB is 112
31
18360aed 32use base qw/DBIx::Class::Storage::DBI::MultiDistinctEmulation/;
33
34# __PACKAGE__->load_components(qw/PK::Auto/);
35
36sub _dbh_last_insert_id {
2e46b6eb 37 my ($self, $dbh, $source, @columns) = @_;
38 my @ids = ();
39 foreach my $col (@columns) {
40 my $seq = ($source->column_info($col)->{sequence} ||= $self->get_autoinc_seq($source,$col));
41 my $id = $self->_sequence_fetch( 'currval', $seq );
42 push @ids, $id;
43 }
44 return @ids;
18360aed 45}
46
47sub _dbh_get_autoinc_seq {
48 my ($self, $dbh, $source, $col) = @_;
49
50 # look up the correct sequence automatically
51 my $sql = q{
52 SELECT trigger_body FROM ALL_TRIGGERS t
53 WHERE t.table_name = ?
54 AND t.triggering_event = 'INSERT'
55 AND t.status = 'ENABLED'
56 };
57
58 # trigger_body is a LONG
59 $dbh->{LongReadLen} = 64 * 1024 if ($dbh->{LongReadLen} < 64 * 1024);
60
cb464582 61 my $sth;
62
63 # check for fully-qualified name (eg. SCHEMA.TABLENAME)
64 if ( my ( $schema, $table ) = $source->name =~ /(\w+)\.(\w+)/ ) {
65 $sql = q{
66 SELECT trigger_body FROM ALL_TRIGGERS t
67 WHERE t.owner = ? AND t.table_name = ?
68 AND t.triggering_event = 'INSERT'
69 AND t.status = 'ENABLED'
70 };
71 $sth = $dbh->prepare($sql);
72 $sth->execute( uc($schema), uc($table) );
73 }
74 else {
75 $sth = $dbh->prepare($sql);
76 $sth->execute( uc( $source->name ) );
77 }
18360aed 78 while (my ($insert_trigger) = $sth->fetchrow_array) {
79 return uc($1) if $insert_trigger =~ m!(\w+)\.nextval!i; # col name goes here???
80 }
66cab05c 81 $self->throw_exception("Unable to find a sequence INSERT trigger on table '" . $source->name . "'.");
18360aed 82}
83
2e46b6eb 84sub _sequence_fetch {
85 my ( $self, $type, $seq ) = @_;
86 my ($id) = $self->dbh->selectrow_array("SELECT ${seq}.${type} FROM DUAL");
87 return $id;
88}
89
20f27051 90=head2 connected
91
92Returns true if we have an open (and working) database connection, false if it is not (yet)
93open (or does not work). (Executes a simple SELECT to make sure it works.)
94
0f0abc97 95The reason this is needed is that L<DBD::Oracle>'s ping() does not do a real
96OCIPing but just gets the server version, which doesn't help if someone killed
97your session.
98
20f27051 99=cut
100
c2481821 101sub connected {
102 my $self = shift;
7ba7a57d 103
c2d7baef 104 if (not $self->SUPER::connected(@_)) {
105 return 0;
106 }
107 else {
7ba7a57d 108 my $dbh = $self->_dbh;
109
7ba7a57d 110 local $dbh->{RaiseError} = 1;
c2d7baef 111
7ba7a57d 112 eval {
c2d7baef 113 my $ping_sth = $dbh->prepare_cached("select 1 from dual");
7ba7a57d 114 $ping_sth->execute;
115 $ping_sth->finish;
116 };
117
c2d7baef 118 return $@ ? 0 : 1;
c2481821 119 }
c2481821 120}
121
d789fa99 122sub _dbh_execute {
123 my $self = shift;
124 my ($dbh, $op, $extra_bind, $ident, $bind_attributes, @args) = @_;
125
126 my $wantarray = wantarray;
d789fa99 127
c2d7baef 128 my (@res, $exception, $retried);
129
0f0abc97 130 RETRY: {
131 do {
132 eval {
133 if ($wantarray) {
134 @res = $self->SUPER::_dbh_execute(@_);
135 } else {
136 $res[0] = $self->SUPER::_dbh_execute(@_);
137 }
138 };
139 $exception = $@;
140 if ($exception =~ /ORA-01003/) {
141 # ORA-01003: no statement parsed (someone changed the table somehow,
142 # invalidating your cursor.)
143 my ($sql, $bind) = $self->_prep_for_execute($op, $extra_bind, $ident, \@args);
144 delete $dbh->{CachedKids}{$sql};
d789fa99 145 } else {
0f0abc97 146 last RETRY;
d789fa99 147 }
0f0abc97 148 } while (not $retried++);
149 }
d789fa99 150
151 $self->throw_exception($exception) if $exception;
152
153 wantarray ? @res : $res[0]
154}
155
7137528d 156=head2 get_autoinc_seq
157
158Returns the sequence name for an autoincrement column
159
160=cut
161
18360aed 162sub get_autoinc_seq {
163 my ($self, $source, $col) = @_;
164
373940e1 165 $self->dbh_do('_dbh_get_autoinc_seq', $source, $col);
18360aed 166}
167
7137528d 168=head2 columns_info_for
169
170This wraps the superclass version of this method to force table
171names to uppercase
172
173=cut
174
18360aed 175sub columns_info_for {
176 my ($self, $table) = @_;
177
178 $self->next::method(uc($table));
179}
180
8f7e044c 181=head2 datetime_parser_type
182
183This sets the proper DateTime::Format module for use with
184L<DBIx::Class::InflateColumn::DateTime>.
185
186=cut
187
188sub datetime_parser_type { return "DateTime::Format::Oracle"; }
189
281719d2 190sub _svp_begin {
191 my ($self, $name) = @_;
192
193 $self->dbh->do("SAVEPOINT $name");
194}
195
5db2758d 196=head2 source_bind_attributes
197
198Handle LOB types in Oracle. Under a certain size (4k?), you can get away
199with the driver assuming your input is the deprecated LONG type if you
200encode it as a hex string. That ain't gonna fly at larger values, where
201you'll discover you have to do what this does.
202
203This method had to be overridden because we need to set ora_field to the
204actual column, and that isn't passed to the call (provided by Storage) to
205bind_attribute_by_data_type.
206
207According to L<DBD::Oracle>, the ora_field isn't always necessary, but
208adding it doesn't hurt, and will save your bacon if you're modifying a
209table with more than one LOB column.
210
211=cut
212
213sub source_bind_attributes
214{
215 my $self = shift;
216 my($source) = @_;
217
218 my %bind_attributes;
219
220 foreach my $column ($source->columns) {
221 my $data_type = $source->column_info($column)->{data_type} || '';
222 next unless $data_type;
223
224 my %column_bind_attrs = $self->bind_attribute_by_data_type($data_type);
225
226 if ($data_type =~ /^[BC]LOB$/i) {
227 $column_bind_attrs{'ora_type'}
228 = uc($data_type) eq 'CLOB' ? ORA_CLOB : ORA_BLOB;
229 $column_bind_attrs{'ora_field'} = $column;
230 }
231
232 $bind_attributes{$column} = \%column_bind_attrs;
233 }
234
235 return \%bind_attributes;
236}
237
281719d2 238# Oracle automatically releases a savepoint when you start another one with the
239# same name.
240sub _svp_release { 1 }
241
242sub _svp_rollback {
243 my ($self, $name) = @_;
244
245 $self->dbh->do("ROLLBACK TO SAVEPOINT $name")
246}
247
18360aed 248=head1 AUTHORS
249
250Andy Grundman <andy@hybridized.org>
251
252Scott Connelly <scottsweep@yahoo.com>
253
254=head1 LICENSE
255
256You may distribute this code under the same terms as Perl itself.
257
258=cut
7137528d 259
2601;