File Coverage

blib/lib/SQL/Translator/Parser/Oracle.pm
Criterion Covered Total %
statement 69 72 95.8
branch 19 30 63.3
condition 4 8 50.0
subroutine 6 6 100.0
pod 0 1 0.0
total 98 117 83.7


line stmt bran cond sub pod time code
1             package SQL::Translator::Parser::Oracle;
2              
3             =head1 NAME
4              
5             SQL::Translator::Parser::Oracle - parser for Oracle
6              
7             =head1 SYNOPSIS
8              
9             use SQL::Translator;
10             use SQL::Translator::Parser::Oracle;
11              
12             my $translator = SQL::Translator->new;
13             $translator->parser("SQL::Translator::Parser::Oracle");
14              
15             =head1 DESCRIPTION
16              
17             From http://www.ss64.com/ora/table_c.html:
18              
19             CREATE [GLOBAL TEMPORARY] TABLE [schema.]table (tbl_defs,...)
20             [ON COMMIT {DELETE|PRESERVE} ROWS]
21             [storage_options | CLUSTER cluster_name (col1, col2,... )
22             | ORGANIZATION {HEAP [storage_options]
23             | INDEX idx_organized_tbl_clause}]
24             [LOB_storage_clause][varray_clause][nested_storage_clause]
25             partitioning_options
26             [[NO]CACHE] [[NO]MONITORING] [PARALLEL parallel_clause]
27             [ENABLE enable_clause | DISABLE disable_clause]
28             [AS subquery]
29              
30             tbl_defs:
31             column datatype [DEFAULT expr] [column_constraint(s)]
32             table_ref_constraint
33              
34             storage_options:
35             PCTFREE int
36             PCTUSED int
37             INITTRANS int
38             MAXTRANS int
39             STORAGE storage_clause
40             TABLESPACE tablespace
41             [LOGGING|NOLOGGING]
42              
43             idx_organized_tbl_clause:
44             storage_option(s) [PCTTHRESHOLD int]
45             [COMPRESS int|NOCOMPRESS]
46             [ [INCLUDING column_name] OVERFLOW [storage_option(s)] ]
47              
48             nested_storage_clause:
49             NESTED TABLE nested_item STORE AS storage_table
50             [RETURN AS {LOCATOR|VALUE} ]
51              
52             partitioning_options:
53             Partition_clause {ENABLE|DISABLE} ROW MOVEMENT
54              
55             Column Constraints
56             (http://www.ss64.com/ora/clause_constraint_col.html)
57              
58             CONSTRAINT constrnt_name {UNIQUE|PRIMARY KEY} constrnt_state
59              
60             CONSTRAINT constrnt_name CHECK(condition) constrnt_state
61              
62             CONSTRAINT constrnt_name [NOT] NULL constrnt_state
63              
64             CONSTRAINT constrnt_name REFERENCES [schema.]table[(column)]
65             [ON DELETE {CASCADE|SET NULL}] constrnt_state
66              
67             constrnt_state
68             [[NOT] DEFERRABLE] [INITIALLY {IMMEDIATE|DEFERRED}]
69             [RELY | NORELY] [USING INDEX using_index_clause]
70             [ENABLE|DISABLE] [VALIDATE|NOVALIDATE]
71             [EXCEPTIONS INTO [schema.]table]
72              
73             Note that probably not all of the above syntax is supported, but the grammar
74             was altered to better handle the syntax created by DDL::Oracle.
75              
76             =cut
77              
78 4     4   1194 use strict;
  4         8  
  4         150  
79 4     4   32 use warnings;
  4         15  
  4         281  
80              
81             our $VERSION = '1.62';
82              
83             our $DEBUG;
84             $DEBUG = 0 unless defined $DEBUG;
85              
86 4     4   28 use Data::Dumper;
  4         9  
  4         259  
87 4     4   36 use SQL::Translator::Utils qw/ddl_parser_instance/;
  4         9  
  4         205  
88              
89 4     4   26 use base qw(Exporter);
  4         10  
  4         4273  
90             our @EXPORT_OK = qw(parse);
91              
92             our $GRAMMAR = <<'END_OF_GRAMMAR';
93              
94             { my ( %tables, %indices, %constraints, $table_order, @table_comments, %views, $view_order, %procedures, $proc_order, %triggers, $trigger_order ) }
95              
96             #
97             # The "eofile" rule makes the parser fail if any "statement" rule
98             # fails. Otherwise, the first successful match by a "statement"
99             # won't cause the failure needed to know that the parse, as a whole,
100             # failed. -ky
101             #
102             startrule : statement(s) eofile
103             {
104             $return = {
105             tables => \%tables,
106             indices => \%indices,
107             constraints => \%constraints,
108             views => \%views,
109             procedures => \%procedures,
110             triggers => \%triggers,
111             };
112             }
113              
114             eofile : /^\Z/
115              
116             statement : remark
117             | run
118             | prompt
119             | create
120             | table_comment
121             | comment_on_table
122             | comment_on_column
123             | alter
124             | drop
125             |
126              
127             alter: /alter/i TABLE table_name /add/i table_constraint ';'
128             {
129             my $constraint = $item{table_constraint};
130             $constraint->{type} = $constraint->{constraint_type};
131             push @{$tables{$item{table_name}}{constraints}}, $constraint;
132             }
133              
134             alter : /alter/i WORD /[^;]+/ ';'
135             { @table_comments = () }
136              
137             drop : /drop/i WORD(s) NAME WORD(s?) ';'
138             { @table_comments = () }
139              
140             create : create_table table_name '(' create_definition(s /,/) ')' table_option(s?) ';'
141             {
142             my $table_name = $item{'table_name'};
143             $tables{ $table_name }{'order'} = ++$table_order;
144             $tables{ $table_name }{'table_name'} = $table_name;
145              
146             if ( @table_comments ) {
147             $tables{ $table_name }{'comments'} = [ @table_comments ];
148             @table_comments = ();
149             }
150              
151             my $i = 1;
152             my @constraints;
153             for my $definition ( @{ $item[4] } ) {
154             if ( $definition->{'type'} eq 'field' ) {
155             my $field_name = $definition->{'name'};
156             $tables{ $table_name }{'fields'}{ $field_name } =
157             { %$definition, order => $i };
158             $i++;
159              
160             for my $constraint ( @{ $definition->{'constraints'} || [] } ) {
161             $constraint->{'fields'} = [ $field_name ];
162             push @{ $tables{ $table_name }{'constraints'} },
163             $constraint;
164             }
165             }
166             elsif ( $definition->{'type'} eq 'constraint' ) {
167             $definition->{'type'} = $definition->{'constraint_type'};
168             push @{ $tables{ $table_name }{'constraints'} }, $definition;
169             }
170             else {
171             push @{ $tables{ $table_name }{'indices'} }, $definition;
172             }
173             }
174              
175             for my $option ( @{ $item[6] } ) {
176             push @{ $tables{ $table_name }{'table_options'} }, $option;
177             }
178              
179             1;
180             }
181              
182             create : create_index index_name /on/i table_name index_expr table_option(?) ';'
183             {
184             my $table_name = $item[4];
185             if ( $item[1] ) {
186             push @{ $constraints{ $table_name } }, {
187             name => $item[2],
188             type => 'unique',
189             fields => $item[5],
190             };
191             }
192             else {
193             push @{ $indices{ $table_name } }, {
194             name => $item[2],
195             type => 'normal',
196             fields => $item[5],
197             };
198             }
199             }
200              
201             index_expr: parens_name_list
202             { $item[1] }
203             | '(' WORD parens_name_list ')'
204             {
205             my $arg_list = join(",", @{$item[3]});
206             $return = "$item[2]($arg_list)";
207             }
208              
209             create : /create/i /or replace/i /trigger/i table_name not_end m#^/$#im
210             {
211             @table_comments = ();
212             my $trigger_name = $item[4];
213             # Hack to strip owner from trigger name
214             $trigger_name =~ s#.*\.##;
215             my $owner = '';
216             my $action = "$item[1] $item[2] $item[3] $item[4] $item[5]";
217              
218             $triggers{ $trigger_name }{'order'} = ++$trigger_order;
219             $triggers{ $trigger_name }{'name'} = $trigger_name;
220             $triggers{ $trigger_name }{'owner'} = $owner;
221             $triggers{ $trigger_name }{'action'} = $action;
222             }
223              
224             create : /create/i /or replace/i /procedure/i table_name not_end m#^/$#im
225             {
226             @table_comments = ();
227             my $proc_name = $item[4];
228             # Hack to strip owner from procedure name
229             $proc_name =~ s#.*\.##;
230             my $owner = '';
231             my $sql = "$item[1] $item[2] $item[3] $item[4] $item[5]";
232              
233             $procedures{ $proc_name }{'order'} = ++$proc_order;
234             $procedures{ $proc_name }{'name'} = $proc_name;
235             $procedures{ $proc_name }{'owner'} = $owner;
236             $procedures{ $proc_name }{'sql'} = $sql;
237             }
238              
239             not_end: m#.*?(?=^/$)#ism
240              
241             create : /create/i /or replace/i /force/i /view/i table_name not_delimiter ';'
242             {
243             @table_comments = ();
244             my $view_name = $item[5];
245             # Hack to strip owner from view name
246             $view_name =~ s#.*\.##;
247             my $sql = "$item[1] $item[2] $item[3] $item[4] $item[5] $item[6] $item[7]";
248              
249             $views{ $view_name }{'order'} = ++$view_order;
250             $views{ $view_name }{'name'} = $view_name;
251             $views{ $view_name }{'sql'} = $sql;
252             }
253              
254             not_delimiter: /.*?(?=;)/is
255              
256             # Create anything else (e.g., domain, function, etc.)
257             create : ...!create_table ...!create_index /create/i WORD /[^;]+/ ';'
258             { @table_comments = () }
259              
260             create_index : /create/i UNIQUE(?) /index/i
261             { $return = @{$item[2]} }
262              
263             index_name : NAME '.' NAME
264             { $item[3] }
265             | NAME
266             { $item[1] }
267              
268             global_temporary: /global/i /temporary/i
269              
270             table_name : NAME '.' NAME
271             { $item[3] }
272             | NAME
273             { $item[1] }
274              
275             create_definition : table_constraint
276             | field
277             |
278              
279             table_comment : comment
280             {
281             my $comment = $item[1];
282             $return = $comment;
283             push @table_comments, $comment;
284             }
285              
286             comment : /^\s*(?:#|-{2}).*\n/
287             {
288             my $comment = $item[1];
289             $comment =~ s/^\s*(#|-{2})\s*//;
290             $comment =~ s/\s*$//;
291             $return = $comment;
292             }
293              
294             comment : /\/\*/ /[^\*]+/ /\*\//
295             {
296             my $comment = $item[2];
297             $comment =~ s/^\s*|\s*$//g;
298             $return = $comment;
299             }
300              
301             remark : /^REM\s+.*\n/
302              
303             run : /^(RUN|\/)\s+.*\n/
304              
305             prompt : /prompt/i /(table|index|sequence|trigger)/i ';'
306              
307             prompt : /prompt\s+create\s+.*\n/i
308              
309             comment_on_table : /comment/i /on/i /table/i table_name /is/i comment_phrase ';'
310             {
311             push @{ $tables{ $item{'table_name'} }{'comments'} }, $item{'comment_phrase'};
312             }
313              
314             comment_on_column : /comment/i /on/i /column/i column_name /is/i comment_phrase ';'
315             {
316             my $table_name = $item[4]->{'table'};
317             my $field_name = $item[4]->{'field'};
318             push @{ $tables{ $table_name }{'fields'}{ $field_name }{'comments'} },
319             $item{'comment_phrase'};
320             }
321              
322             column_name : NAME '.' NAME
323             { $return = { table => $item[1], field => $item[3] } }
324              
325             comment_phrase : /'.*?'/
326             {
327             my $val = $item[1];
328             $val =~ s/^'|'$//g;
329             $return = $val;
330             }
331              
332             field : comment(s?) field_name data_type field_meta(s?) comment(s?)
333             {
334             my ( $is_pk, $default, @constraints );
335             my $null = 1;
336             for my $meta ( @{ $item[4] } ) {
337             if ( $meta->{'type'} eq 'default' ) {
338             $default = $meta;
339             next;
340             }
341             elsif ( $meta->{'type'} eq 'not_null' ) {
342             $null = 0;
343             next;
344             }
345             elsif ( $meta->{'type'} eq 'primary_key' ) {
346             $is_pk = 1;
347             }
348              
349             push @constraints, $meta if $meta->{'supertype'} eq 'constraint';
350             }
351              
352             my @comments = ( @{ $item[1] }, @{ $item[5] } );
353              
354             $return = {
355             type => 'field',
356             name => $item{'field_name'},
357             data_type => $item{'data_type'}{'type'},
358             size => $item{'data_type'}{'size'},
359             null => $null,
360             default => $default->{'value'},
361             is_primary_key => $is_pk,
362             constraints => [ @constraints ],
363             comments => [ @comments ],
364             }
365             }
366             |
367              
368             field_name : NAME
369              
370             data_type : ora_data_type data_size(?)
371             {
372             $return = {
373             type => $item[1],
374             size => $item[2][0] || '',
375             }
376             }
377              
378             data_size : '(' VALUE(s /,/) data_size_modifier(?) ')'
379             { $item[2] }
380              
381             data_size_modifier: /byte/i
382             | /char/i
383              
384             column_constraint : constraint_name(?) column_constraint_type constraint_state(s?)
385             {
386             my $desc = $item{'column_constraint_type'};
387             my $type = $desc->{'type'};
388             my $fields = $desc->{'fields'} || [];
389             my $expression = $desc->{'expression'} || '';
390              
391             $return = {
392             supertype => 'constraint',
393             name => $item{'constraint_name(?)'}[0] || '',
394             type => $type,
395             expression => $type eq 'check' ? $expression : '',
396             deferrable => $desc->{'deferrable'},
397             deferred => $desc->{'deferred'},
398             reference_table => $desc->{'reference_table'},
399             reference_fields => $desc->{'reference_fields'},
400             # match_type => $desc->{'match_type'},
401             # on_update => $desc->{'on_update'},
402             }
403             }
404              
405             constraint_name : /constraint/i NAME { $item[2] }
406              
407             column_constraint_type : /not\s+null/i { $return = { type => 'not_null' } }
408             | /unique/i
409             { $return = { type => 'unique' } }
410             | /primary\s+key/i
411             { $return = { type => 'primary_key' } }
412             | /check/i check_expression
413             {
414             $return = {
415             type => 'check',
416             expression => $item[2],
417             };
418             }
419             | /references/i table_name parens_name_list(?) on_delete(?)
420             {
421             $return = {
422             type => 'foreign_key',
423             reference_table => $item[2],
424             reference_fields => $item[3][0],
425             # match_type => $item[4][0],
426             on_delete => $item[5][0],
427             }
428             }
429              
430             LPAREN : '('
431              
432             RPAREN : ')'
433              
434             check_condition_text : /.+\s+in\s+\([^)]+\)/i
435             | /[^)]+/
436              
437             check_expression : LPAREN check_condition_text RPAREN
438             { $return = join( ' ', map { $_ || () }
439             $item[1], $item[2], $item[3], $item[4][0] )
440             }
441              
442             constraint_state : deferrable { $return = { type => $item[1] } }
443             | deferred { $return = { type => $item[1] } }
444             | /(no)?rely/i { $return = { type => $item[1] } }
445             # | /using/i /index/i using_index_clause
446             # { $return = { type => 'using_index', index => $item[3] } }
447             | /(dis|en)able/i { $return = { type => $item[1] } }
448             | /(no)?validate/i { $return = { type => $item[1] } }
449             | /exceptions/i /into/i table_name
450             { $return = { type => 'exceptions_into', table => $item[3] } }
451              
452             deferrable : /not/i /deferrable/i
453             { $return = 'not_deferrable' }
454             | /deferrable/i
455             { $return = 'deferrable' }
456              
457             deferred : /initially/i /(deferred|immediate)/i { $item[2] }
458              
459             ora_data_type :
460             /(n?varchar2|varchar)/i { $return = 'varchar2' }
461             |
462             /n?char/i { $return = 'character' }
463             |
464             /n?dec/i { $return = 'decimal' }
465             |
466             /number/i { $return = 'number' }
467             |
468             /integer/i { $return = 'integer' }
469             |
470             /(pls_integer|binary_integer)/i { $return = 'integer' }
471             |
472             /interval\s+day/i { $return = 'interval day' }
473             |
474             /interval\s+year/i { $return = 'interval year' }
475             |
476             /long\s+raw/i { $return = 'long raw' }
477             |
478             /(long|date|timestamp|raw|rowid|urowid|mlslabel|clob|nclob|blob|bfile|float|double)/i { $item[1] }
479              
480             parens_value_list : '(' VALUE(s /,/) ')'
481             { $item[2] }
482              
483             parens_word_list : '(' WORD(s /,/) ')'
484             { $item[2] }
485              
486             parens_name_list : '(' NAME(s /,/) ')'
487             { $item[2] }
488              
489             field_meta : default_val
490             | column_constraint
491              
492             default_val : /default/i VALUE
493             {
494             my $val = $item[2];
495             $return = {
496             supertype => 'constraint',
497             type => 'default',
498             value => $val,
499             }
500             }
501             | /null/i
502             {
503             $return = {
504             supertype => 'constraint',
505             type => 'default',
506             value => 'NULL',
507             }
508             }
509              
510             create_table : /create/i global_temporary(?) /table/i
511              
512             table_option : /organization/i WORD
513             {
514             $return = { 'ORGANIZATION' => $item[2] }
515             }
516              
517             table_option : /nomonitoring/i
518             {
519             $return = { 'NOMONITORING' => undef }
520             }
521              
522             table_option : /parallel/i '(' key_value(s) ')'
523             {
524             $return = { 'PARALLEL' => $item[3] }
525             }
526              
527             key_value : WORD VALUE
528             {
529             $return = { $item[1], $item[2] }
530             }
531              
532             table_option : /[^;]+/
533              
534             table_constraint : comment(s?) constraint_name(?) table_constraint_type deferrable(?) deferred(?) constraint_state(s?) comment(s?)
535             {
536             my $desc = $item{'table_constraint_type'};
537             my $type = $desc->{'type'};
538             my $fields = $desc->{'fields'};
539             my $expression = $desc->{'expression'};
540             my @comments = ( @{ $item[1] }, @{ $item[-1] } );
541              
542             $return = {
543             name => $item{'constraint_name(?)'}[0] || '',
544             type => 'constraint',
545             constraint_type => $type,
546             fields => $type ne 'check' ? $fields : [],
547             expression => $type eq 'check' ? $expression : '',
548             deferrable => $item{'deferrable(?)'},
549             deferred => $item{'deferred(?)'},
550             reference_table => $desc->{'reference_table'},
551             reference_fields => $desc->{'reference_fields'},
552             # match_type => $desc->{'match_type'}[0],
553             on_delete => $desc->{'on_delete'} || $desc->{'on_delete_do'},
554             on_update => $desc->{'on_update'} || $desc->{'on_update_do'},
555             comments => [ @comments ],
556             }
557             }
558              
559             table_constraint_type : /primary key/i '(' NAME(s /,/) ')'
560             {
561             $return = {
562             type => 'primary_key',
563             fields => $item[3],
564             }
565             }
566             |
567             /unique/i '(' NAME(s /,/) ')'
568             {
569             $return = {
570             type => 'unique',
571             fields => $item[3],
572             }
573             }
574             |
575             /check/i check_expression /^(en|dis)able/i
576             {
577             $return = {
578             type => 'check',
579             expression => join(' ', $item[2], $item[3]),
580             }
581             }
582             |
583             /foreign key/i '(' NAME(s /,/) ')' /references/i table_name parens_name_list(?) on_delete(?)
584             {
585             $return = {
586             type => 'foreign_key',
587             fields => $item[3],
588             reference_table => $item[6],
589             reference_fields => $item[7][0],
590             # match_type => $item[8][0],
591             on_delete => $item[8][0],
592             # on_update => $item[9][0],
593             }
594             }
595              
596             on_delete : /on delete/i WORD(s)
597             { join(' ', @{$item[2]}) }
598              
599             UNIQUE : /unique/i { $return = 1 }
600              
601             WORD : /\w+/
602              
603             NAME : /\w+/ { $item[1] }
604             | DQSTRING
605              
606             TABLE : /table/i
607              
608             DQSTRING : '"' /((?:[^"]|"")+)/ '"'
609             { ($return = $item[3]) =~ s/""/"/g; }
610              
611             SQSTRING : "'" /((?:[^']|'')*)/ "'"
612             { ($return = $item[3]) =~ s/''/'/g }
613              
614             VALUE : /[-+]?\d*\.?\d+(?:[eE]\d+)?/
615             | SQSTRING
616             | /null/i
617             { 'NULL' }
618              
619             END_OF_GRAMMAR
620              
621             sub parse {
622 4     4 0 44 my ( $translator, $data ) = @_;
623              
624             # Enable warnings within the Parse::RecDescent module.
625 4 100       17 local $::RD_ERRORS = 1 unless defined $::RD_ERRORS; # Make sure the parser dies when it encounters an error
626 4 100       25 local $::RD_WARN = 1 unless defined $::RD_WARN; # Enable warnings. This will warn on unused rules &c.
627 4 50       16 local $::RD_HINT = 1 unless defined $::RD_HINT; # Give out hints to help fix problems.
628              
629 4 50       81 local $::RD_TRACE = $translator->trace ? 1 : undef;
630 4         43 local $DEBUG = $translator->debug;
631              
632 4         40 my $parser = ddl_parser_instance('Oracle');
633              
634 4         1786161 my $result = $parser->startrule( $data );
635 4 50       559247 die "Parse failed.\n" unless defined $result;
636 4 50       20 if ( $DEBUG ) {
637 0         0 warn "Parser results =\n", Dumper($result), "\n";
638             }
639              
640 4         119 my $schema = $translator->schema;
641 4         322 my $indices = $result->{'indices'};
642 4         139 my $constraints = $result->{'constraints'};
643             my @tables = sort {
644             $result->{'tables'}{ $a }{'order'}
645             <=>
646 9         35 $result->{'tables'}{ $b }{'order'}
647 4         14 } keys %{ $result->{'tables'} };
  4         36  
648              
649 4         16 for my $table_name ( @tables ) {
650 10         40 my $tdata = $result->{'tables'}{ $table_name };
651 10 50       54 next unless $tdata->{'table_name'};
652             my $table = $schema->add_table(
653             name => $tdata->{'table_name'},
654 10 50       99 comments => $tdata->{'comments'},
655             ) or die $schema->error;
656              
657 10         410 $table->options( $tdata->{'table_options'} );
658              
659             my @fields = sort {
660             $tdata->{'fields'}->{$a}->{'order'}
661             <=>
662 56         138 $tdata->{'fields'}->{$b}->{'order'}
663 10         28 } keys %{ $tdata->{'fields'} };
  10         87  
664              
665 10         32 for my $fname ( @fields ) {
666 35         588 my $fdata = $tdata->{'fields'}{ $fname };
667             my $field = $table->add_field(
668             name => $fdata->{'name'},
669             data_type => $fdata->{'data_type'},
670             size => $fdata->{'size'},
671             default_value => $fdata->{'default'},
672             is_auto_increment => $fdata->{'is_auto_inc'},
673             is_nullable => $fdata->{'null'},
674 35 50       326 comments => $fdata->{'comments'},
675             ) or die $table->error;
676             }
677              
678 10 100       209 push @{ $tdata->{'indices'} }, @{ $indices->{ $table_name } || [] };
  10         34  
  10         72  
679 10         28 push @{ $tdata->{'constraints'} },
680 10 100       28 @{ $constraints->{ $table_name } || [] };
  10         54  
681              
682 10 50       20 for my $idata ( @{ $tdata->{'indices'} || [] } ) {
  10         46  
683             my $index = $table->add_index(
684             name => $idata->{'name'},
685             type => uc $idata->{'type'},
686 2 50       23 fields => $idata->{'fields'},
687             ) or die $table->error;
688             }
689              
690 10 50       21 for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
  10         44  
691             my $constraint = $table->add_constraint(
692             name => $cdata->{'name'},
693             type => $cdata->{'type'},
694             fields => $cdata->{'fields'},
695             expression => $cdata->{'expression'},
696             reference_table => $cdata->{'reference_table'},
697             reference_fields => $cdata->{'reference_fields'},
698             match_type => $cdata->{'match_type'} || '',
699             on_delete => $cdata->{'on_delete'}
700             || $cdata->{'on_delete_do'},
701             on_update => $cdata->{'on_update'}
702 23 50 50     302 || $cdata->{'on_update_do'},
      66        
      33        
703             ) or die $table->error;
704             }
705             }
706              
707             my @procedures = sort {
708 0         0 $result->{procedures}->{ $a }->{'order'} <=> $result->{procedures}->{ $b }->{'order'}
709 4         14 } keys %{ $result->{procedures} };
  4         30  
710 4         14 foreach my $proc_name (@procedures) {
711             $schema->add_procedure(
712             name => $proc_name,
713             owner => $result->{procedures}->{$proc_name}->{owner},
714             sql => $result->{procedures}->{$proc_name}->{sql},
715 1         9 );
716             }
717              
718             my @views = sort {
719 0         0 $result->{views}->{ $a }->{'order'} <=> $result->{views}->{ $b }->{'order'}
720 4         15 } keys %{ $result->{views} };
  4         22  
721 4         13 foreach my $view_name (keys %{ $result->{views} }) {
  4         15  
722             $schema->add_view(
723             name => $view_name,
724             sql => $result->{views}->{$view_name}->{sql},
725 1         8 );
726             }
727              
728             my @triggers = sort {
729 5         21 $result->{triggers}->{ $a }->{'order'} <=> $result->{triggers}->{ $b }->{'order'}
730 4         10 } keys %{ $result->{triggers} };
  4         25  
731 4         15 foreach my $trigger_name (@triggers) {
732             $schema->add_trigger(
733             name => $trigger_name,
734             action => $result->{triggers}->{$trigger_name}->{action},
735 4         27 );
736             }
737              
738 4         53 return 1;
739             }
740              
741             1;
742              
743             # -------------------------------------------------------------------
744             # Something there is that doesn't love a wall.
745             # Robert Frost
746             # -------------------------------------------------------------------
747              
748             =pod
749              
750             =head1 AUTHOR
751              
752             Ken Youens-Clark Ekclark@cpan.orgE.
753              
754             =head1 SEE ALSO
755              
756             SQL::Translator, Parse::RecDescent, DDL::Oracle.
757              
758             =cut