• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.9.5/CPANInternal-140/DBIx-Class-Schema-Loader-0.07033/lib/DBIx/Class/Schema/Loader/DBI/
1package DBIx::Class::Schema::Loader::DBI::Oracle;
2
3use strict;
4use warnings;
5use base 'DBIx::Class::Schema::Loader::DBI::Component::QuotedDefault';
6use mro 'c3';
7use Try::Tiny;
8use namespace::clean;
9
10our $VERSION = '0.07033';
11
12=head1 NAME
13
14DBIx::Class::Schema::Loader::DBI::Oracle - DBIx::Class::Schema::Loader::DBI
15Oracle Implementation.
16
17=head1 DESCRIPTION
18
19See L<DBIx::Class::Schema::Loader> and L<DBIx::Class::Schema::Loader::Base>.
20
21=cut
22
23sub _setup {
24    my $self = shift;
25
26    $self->next::method(@_);
27
28    my ($current_schema) = $self->dbh->selectrow_array('SELECT USER FROM DUAL');
29
30    $self->db_schema([ $current_schema ]) unless $self->db_schema;
31
32    if (@{ $self->db_schema } == 1 && $self->db_schema->[0] ne '%'
33        && lc($self->db_schema->[0]) ne lc($current_schema)) {
34        $self->dbh->do('ALTER SESSION SET current_schema=' . $self->db_schema->[0]);
35    }
36
37    if (not defined $self->preserve_case) {
38        $self->preserve_case(0);
39    }
40    elsif ($self->preserve_case) {
41        $self->schema->storage->sql_maker->quote_char('"');
42        $self->schema->storage->sql_maker->name_sep('.');
43    }
44}
45
46sub _build_name_sep { '.' }
47
48sub _system_schemas {
49    my $self = shift;
50
51    # From http://www.adp-gmbh.ch/ora/misc/known_schemas.html
52
53    return ($self->next::method(@_), qw/ANONYMOUS APEX_PUBLIC_USER APEX_030200 APPQOSSYS CTXSYS DBSNMP DIP DMSYS EXFSYS LBACSYS MDDATA MDSYS MGMT_VIEW OLAPSYS ORACLE_OCM ORDDATA ORDPLUGINS ORDSYS OUTLN SI_INFORMTN_SCHEMA SPATIAL_CSW_ADMIN_USR SPATIAL_WFS_ADMIN_USR SYS SYSMAN SYSTEM TRACESRV MTSSYS OASPUBLIC OWBSYS OWBSYS_AUDIT WEBSYS WK_PROXY WKSYS WK_TEST WMSYS XDB OSE$HTTP$ADMIN AURORA$JIS$UTILITY$ AURORA$ORB$UNAUTHENTICATED/, qr/^FLOWS_\d\d\d\d\d\d\z/);
54}
55
56sub _system_tables {
57    my $self = shift;
58
59    return ($self->next::method(@_), 'PLAN_TABLE');
60}
61
62sub _dbh_tables {
63    my ($self, $schema) = @_;
64
65    return $self->dbh->tables(undef, $schema, '%', 'TABLE,VIEW');
66}
67
68sub _filter_tables {
69    my $self = shift;
70
71    # silence a warning from older DBD::Oracles in tests
72    my $warn_handler = $SIG{__WARN__} || sub { warn @_ };
73    local $SIG{__WARN__} = sub {
74        $warn_handler->(@_)
75        unless $_[0] =~ /^Field \d+ has an Oracle type \(\d+\) which is not explicitly supported/;
76    };
77
78    return $self->next::method(@_);
79}
80
81sub _table_fk_info {
82    my $self = shift;
83    my ($table) = @_;
84
85    my $rels = $self->next::method(@_);
86
87    my $deferrable_sth = $self->dbh->prepare_cached(<<'EOF');
88select deferrable from all_constraints
89where owner = ? and table_name = ? and constraint_name = ?
90EOF
91
92    foreach my $rel (@$rels) {
93        # Oracle does not have update rules
94        $rel->{attrs}{on_update} = 'NO ACTION';;
95
96        # DBD::Oracle's foreign_key_info does not return DEFERRABILITY, so we get it ourselves
97        my ($deferrable) = $self->dbh->selectrow_array(
98            $deferrable_sth, undef, $table->schema, $table->name, $rel->{_constraint_name}
99        );
100
101        $rel->{attrs}{is_deferrable} = $deferrable && $deferrable =~ /^DEFERRABLE/i ? 1 : 0;
102    }
103
104    return $rels;
105}
106
107sub _table_uniq_info {
108    my ($self, $table) = @_;
109
110    my $sth = $self->dbh->prepare_cached(<<'EOF', {}, 1);
111SELECT ac.constraint_name, acc.column_name
112FROM all_constraints ac, all_cons_columns acc
113WHERE acc.table_name=? AND acc.owner = ?
114    AND ac.table_name = acc.table_name AND ac.owner = acc.owner
115    AND acc.constraint_name = ac.constraint_name
116    AND ac.constraint_type='U'
117ORDER BY acc.position
118EOF
119
120    $sth->execute($table->name, $table->schema);
121
122    my %constr_names;
123
124    while(my $constr = $sth->fetchrow_arrayref) {
125        my $constr_name = $self->_lc($constr->[0]);
126        my $constr_col  = $self->_lc($constr->[1]);
127        push @{$constr_names{$constr_name}}, $constr_col;
128    }
129
130    my @uniqs = map { [ $_ => $constr_names{$_} ] } keys %constr_names;
131    return \@uniqs;
132}
133
134sub _table_comment {
135    my $self = shift;
136    my ($table) = @_;
137
138    my $table_comment = $self->next::method(@_);
139
140    return $table_comment if $table_comment;
141
142    ($table_comment) = $self->dbh->selectrow_array(<<'EOF', {}, $table->schema, $table->name);
143SELECT comments FROM all_tab_comments
144WHERE owner = ?
145  AND table_name = ?
146  AND (table_type = 'TABLE' OR table_type = 'VIEW')
147EOF
148
149    return $table_comment
150}
151
152sub _column_comment {
153    my $self = shift;
154    my ($table, $column_number, $column_name) = @_;
155
156    my $column_comment = $self->next::method(@_);
157
158    return $column_comment if $column_comment;
159
160    ($column_comment) = $self->dbh->selectrow_array(<<'EOF', {}, $table->schema, $table->name, $self->_uc($column_name));
161SELECT comments FROM all_col_comments
162WHERE owner = ?
163  AND table_name = ?
164  AND column_name = ?
165EOF
166
167    return $column_comment
168}
169
170sub _columns_info_for {
171    my $self = shift;
172    my ($table) = @_;
173
174    my $result = $self->next::method(@_);
175
176    local $self->dbh->{LongReadLen} = 1_000_000;
177    local $self->dbh->{LongTruncOk} = 1;
178
179    my $sth = $self->dbh->prepare_cached(<<'EOF', {}, 1);
180SELECT trigger_body
181FROM all_triggers
182WHERE table_name = ? AND table_owner = ?
183AND upper(trigger_type) LIKE '%BEFORE EACH ROW%' AND lower(triggering_event) LIKE '%insert%'
184EOF
185
186    $sth->execute($table->name, $table->schema);
187
188    while (my ($trigger_body) = $sth->fetchrow_array) {
189        if (my ($seq_schema, $seq_name) = $trigger_body =~ /(?:\."?(\w+)"?)?"?(\w+)"?\.nextval/i) {
190            if (my ($col_name) = $trigger_body =~ /:new\.(\w+)/i) {
191                $col_name = $self->_lc($col_name);
192
193                $result->{$col_name}{is_auto_increment} = 1;
194
195                $seq_schema = $self->_lc($seq_schema || $table->schema);
196                $seq_name   = $self->_lc($seq_name);
197
198                $result->{$col_name}{sequence} = ($self->qualify_objects ? ($seq_schema . '.') : '') . $seq_name;
199            }
200        }
201    }
202
203    while (my ($col, $info) = each %$result) {
204        no warnings 'uninitialized';
205
206        my $sth = $self->dbh->prepare_cached(<<'EOF', {}, 1);
207SELECT data_type, data_length
208FROM all_tab_columns
209WHERE column_name = ? AND table_name = ? AND owner = ?
210EOF
211        $sth->execute($self->_uc($col), $table->name, $table->schema);
212        my ($data_type, $data_length) = $sth->fetchrow_array;
213        $sth->finish;
214        $data_type = lc $data_type;
215
216        if ($data_type =~ /^(?:n(?:var)?char2?|u?rowid|nclob|timestamp\(\d+\)(?: with(?: local)? time zone)?|binary_(?:float|double))\z/i) {
217            $info->{data_type} = $data_type;
218
219            if ($data_type =~ /^u?rowid\z/i) {
220                $info->{size} = $data_length;
221            }
222        }
223
224        if ($info->{data_type} =~ /^(?:n?[cb]lob|long(?: raw)?|bfile|date|binary_(?:float|double)|rowid)\z/i) {
225            delete $info->{size};
226        }
227
228        if ($info->{data_type} =~ /^n(?:var)?char2?\z/i) {
229            if (ref $info->{size}) {
230                $info->{size} = $info->{size}[0] / 8;
231            }
232            else {
233                $info->{size} = $info->{size} / 2;
234            }
235        }
236        elsif ($info->{data_type} =~ /^(?:var)?char2?\z/i) {
237            if (ref $info->{size}) {
238                $info->{size} = $info->{size}[0];
239            }
240        }
241        elsif (lc($info->{data_type}) =~ /^(?:number|decimal)\z/i) {
242            $info->{original}{data_type} = 'number';
243            $info->{data_type}           = 'numeric';
244
245            if (try { $info->{size}[0] == 38 && $info->{size}[1] == 0 }) {
246                $info->{original}{size} = $info->{size};
247
248                $info->{data_type} = 'integer';
249                delete $info->{size};
250            }
251        }
252        elsif (my ($precision) = $info->{data_type} =~ /^timestamp\((\d+)\)(?: with (?:local )?time zone)?\z/i) {
253            $info->{data_type} = join ' ', $info->{data_type} =~ /[a-z]+/ig;
254
255            if ($precision == 6) {
256                delete $info->{size};
257            }
258            else {
259                $info->{size} = $precision;
260            }
261        }
262        elsif ($info->{data_type} =~ /timestamp/i && ref $info->{size} && $info->{size}[0] == 0) {
263            my $size = $info->{size}[1];
264            delete $info->{size};
265            $info->{size} = $size unless $size == 6;
266        }
267        elsif (($precision) = $info->{data_type} =~ /^interval year\((\d+)\) to month\z/i) {
268            $info->{data_type} = join ' ', $info->{data_type} =~ /[a-z]+/ig;
269
270            if ($precision == 2) {
271                delete $info->{size};
272            }
273            else {
274                $info->{size} = $precision;
275            }
276        }
277        elsif (my ($day_precision, $second_precision) = $info->{data_type} =~ /^interval day\((\d+)\) to second\((\d+)\)\z/i) {
278            $info->{data_type} = join ' ', $info->{data_type} =~ /[a-z]+/ig;
279
280            if ($day_precision == 2 && $second_precision == 6) {
281                delete $info->{size};
282            }
283            else {
284                $info->{size} = [ $day_precision, $second_precision ];
285            }
286        }
287        elsif ($info->{data_type} =~ /^interval year to month\z/i && ref $info->{size}) {
288            my $precision = $info->{size}[0];
289
290            if ($precision == 2) {
291                delete $info->{size};
292            }
293            else {
294                $info->{size} = $precision;
295            }
296        }
297        elsif ($info->{data_type} =~ /^interval day to second\z/i && ref $info->{size}) {
298            if ($info->{size}[0] == 2 && $info->{size}[1] == 6) {
299                delete $info->{size};
300            }
301        }
302        elsif (lc($info->{data_type}) eq 'float') {
303            $info->{original}{data_type} = 'float';
304            $info->{original}{size}      = $info->{size};
305
306            if ($info->{size} <= 63) {
307                $info->{data_type} = 'real';
308            }
309            else {
310                $info->{data_type} = 'double precision';
311            }
312            delete $info->{size};
313        }
314        elsif (lc($info->{data_type}) eq 'double precision') {
315            $info->{original}{data_type} = 'float';
316
317            my $size = try { $info->{size}[0] };
318
319            $info->{original}{size} = $size;
320
321            if ($size <= 63) {
322                $info->{data_type} = 'real';
323            }
324            delete $info->{size};
325        }
326        elsif (lc($info->{data_type}) eq 'urowid' && $info->{size} == 4000) {
327            delete $info->{size};
328        }
329        elsif ($info->{data_type} eq '-9104') {
330            $info->{data_type} = 'rowid';
331            delete $info->{size};
332        }
333        elsif ($info->{data_type} eq '-2') {
334            $info->{data_type} = 'raw';
335            $info->{size} = try { $info->{size}[0] / 2 };
336        }
337        elsif (lc($info->{data_type}) eq 'date') {
338            $info->{data_type}           = 'datetime';
339            $info->{original}{data_type} = 'date';
340        }
341        elsif (lc($info->{data_type}) eq 'binary_float') {
342            $info->{data_type}           = 'real';
343            $info->{original}{data_type} = 'binary_float';
344        }
345        elsif (lc($info->{data_type}) eq 'binary_double') {
346            $info->{data_type}           = 'double precision';
347            $info->{original}{data_type} = 'binary_double';
348        }
349
350        # DEFAULT could be missed by ::DBI because of ORA-24345
351        if (not defined $info->{default_value}) {
352            local $self->dbh->{LongReadLen} = 1_000_000;
353            local $self->dbh->{LongTruncOk} = 1;
354            my $sth = $self->dbh->prepare_cached(<<'EOF', {}, 1);
355SELECT data_default
356FROM all_tab_columns
357WHERE column_name = ? AND table_name = ? AND owner = ?
358EOF
359            $sth->execute($self->_uc($col), $table->name, $table->schema);
360            my ($default) = $sth->fetchrow_array;
361            $sth->finish;
362
363            # this is mostly copied from ::DBI::QuotedDefault
364            if (defined $default) {
365                s/^\s+//, s/\s+\z// for $default;
366
367                if ($default =~ /^'(.*?)'\z/) {
368                    $info->{default_value} = $1;
369                }
370                elsif ($default =~ /^(-?\d.*?)\z/) {
371                    $info->{default_value} = $1;
372                }
373                elsif ($default =~ /^NULL\z/i) {
374                    my $null = 'null';
375                    $info->{default_value} = \$null;
376                }
377                elsif ($default ne '') {
378                    my $val = $default;
379                    $info->{default_value} = \$val;
380                }
381            }
382        }
383
384        if ((try { lc(${ $info->{default_value} }) }||'') eq 'sysdate') {
385            my $current_timestamp  = 'current_timestamp';
386            $info->{default_value} = \$current_timestamp;
387
388            my $sysdate = 'sysdate';
389            $info->{original}{default_value} = \$sysdate;
390        }
391    }
392
393    return $result;
394}
395
396sub _dbh_column_info {
397    my $self  = shift;
398    my ($dbh) = @_;
399
400    # try to avoid ORA-24345
401    local $dbh->{LongReadLen} = 1_000_000;
402    local $dbh->{LongTruncOk} = 1;
403
404    return $self->next::method(@_);
405}
406
407=head1 SEE ALSO
408
409L<DBIx::Class::Schema::Loader>, L<DBIx::Class::Schema::Loader::Base>,
410L<DBIx::Class::Schema::Loader::DBI>
411
412=head1 AUTHOR
413
414See L<DBIx::Class::Schema::Loader/AUTHOR> and L<DBIx::Class::Schema::Loader/CONTRIBUTORS>.
415
416=head1 LICENSE
417
418This library is free software; you can redistribute it and/or modify it under
419the same terms as Perl itself.
420
421=cut
422
4231;
424# vim:et sts=4 sw=4 tw=0:
425