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 5     5   1391 use strict;
  5         16  
  5         176  
79 5     5   30 use warnings;
  5         9  
  5         330  
80              
81             our $VERSION = '1.63';
82              
83             our $DEBUG;
84             $DEBUG = 0 unless defined $DEBUG;
85              
86 5     5   41 use Data::Dumper;
  5         16  
  5         310  
87 5     5   38 use SQL::Translator::Utils qw/ddl_parser_instance/;
  5         12  
  5         237  
88              
89 5     5   31 use base qw(Exporter);
  5         14  
  5         5786  
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 :
493             /default/i CURRENT_TIMESTAMP
494             {
495             my $val = $item[2];
496             $return = {
497             supertype => 'constraint',
498             type => 'default',
499             value => $val,
500             }
501             }
502             | /default/i VALUE
503             {
504             my $val = $item[2];
505             $return = {
506             supertype => 'constraint',
507             type => 'default',
508             value => $val,
509             }
510             }
511             | /null/i
512             {
513             $return = {
514             supertype => 'constraint',
515             type => 'default',
516             value => 'NULL',
517             }
518             }
519              
520             create_table : /create/i global_temporary(?) /table/i
521              
522             table_option : /organization/i WORD
523             {
524             $return = { 'ORGANIZATION' => $item[2] }
525             }
526              
527             table_option : /nomonitoring/i
528             {
529             $return = { 'NOMONITORING' => undef }
530             }
531              
532             table_option : /parallel/i '(' key_value(s) ')'
533             {
534             $return = { 'PARALLEL' => $item[3] }
535             }
536              
537             key_value : WORD VALUE
538             {
539             $return = { $item[1], $item[2] }
540             }
541              
542             table_option : /[^;]+/
543              
544             table_constraint : comment(s?) constraint_name(?) table_constraint_type deferrable(?) deferred(?) constraint_state(s?) comment(s?)
545             {
546             my $desc = $item{'table_constraint_type'};
547             my $type = $desc->{'type'};
548             my $fields = $desc->{'fields'};
549             my $expression = $desc->{'expression'};
550             my @comments = ( @{ $item[1] }, @{ $item[-1] } );
551              
552             $return = {
553             name => $item{'constraint_name(?)'}[0] || '',
554             type => 'constraint',
555             constraint_type => $type,
556             fields => $type ne 'check' ? $fields : [],
557             expression => $type eq 'check' ? $expression : '',
558             deferrable => $item{'deferrable(?)'},
559             deferred => $item{'deferred(?)'},
560             reference_table => $desc->{'reference_table'},
561             reference_fields => $desc->{'reference_fields'},
562             # match_type => $desc->{'match_type'}[0],
563             on_delete => $desc->{'on_delete'} || $desc->{'on_delete_do'},
564             on_update => $desc->{'on_update'} || $desc->{'on_update_do'},
565             comments => [ @comments ],
566             }
567             }
568              
569             table_constraint_type : /primary key/i '(' NAME(s /,/) ')'
570             {
571             $return = {
572             type => 'primary_key',
573             fields => $item[3],
574             }
575             }
576             |
577             /unique/i '(' NAME(s /,/) ')'
578             {
579             $return = {
580             type => 'unique',
581             fields => $item[3],
582             }
583             }
584             |
585             /check/i check_expression /^(en|dis)able/i
586             {
587             $return = {
588             type => 'check',
589             expression => join(' ', $item[2], $item[3]),
590             }
591             }
592             |
593             /foreign key/i '(' NAME(s /,/) ')' /references/i table_name parens_name_list(?) on_delete(?)
594             {
595             $return = {
596             type => 'foreign_key',
597             fields => $item[3],
598             reference_table => $item[6],
599             reference_fields => $item[7][0],
600             # match_type => $item[8][0],
601             on_delete => $item[8][0],
602             # on_update => $item[9][0],
603             }
604             }
605              
606             on_delete : /on delete/i WORD(s)
607             { join(' ', @{$item[2]}) }
608              
609             UNIQUE : /unique/i { $return = 1 }
610              
611             WORD : /\w+/
612              
613             NAME : /\w+/ { $item[1] }
614             | DQSTRING
615              
616             TABLE : /table/i
617              
618             DQSTRING : '"' /((?:[^"]|"")+)/ '"'
619             { ($return = $item[3]) =~ s/""/"/g; }
620              
621             SQSTRING : "'" /((?:[^']|'')*)/ "'"
622             { ($return = $item[3]) =~ s/''/'/g }
623              
624             VALUE : /[-+]?\d*\.?\d+(?:[eE]\d+)?/
625             | SQSTRING
626             | /null/i
627             { 'NULL' }
628              
629             # always a scalar-ref, so that it is treated as a function and not quoted by consumers
630             CURRENT_TIMESTAMP :
631             /current_timestamp(\(\))?/i { \'CURRENT_TIMESTAMP' }
632             | /now\(\)/i { \'CURRENT_TIMESTAMP' }
633              
634             END_OF_GRAMMAR
635              
636             sub parse {
637 6     6 0 61 my ( $translator, $data ) = @_;
638              
639             # Enable warnings within the Parse::RecDescent module.
640 6 100       24 local $::RD_ERRORS = 1
641             unless defined
642             $::RD_ERRORS; # Make sure the parser dies when it encounters an error
643 6 100       25 local $::RD_WARN = 1
644             unless
645             defined $::RD_WARN; # Enable warnings. This will warn on unused rules &c.
646 6 50       21 local $::RD_HINT = 1
647             unless defined $::RD_HINT; # Give out hints to help fix problems.
648              
649 6 50       122 local $::RD_TRACE = $translator->trace ? 1 : undef;
650 6         111 local $DEBUG = $translator->debug;
651              
652 6         89 my $parser = ddl_parser_instance('Oracle');
653              
654 6         2993903 my $result = $parser->startrule($data);
655 6 50       2147812 die "Parse failed.\n" unless defined $result;
656 6 50       36 if ($DEBUG) {
657 0         0 warn "Parser results =\n", Dumper($result), "\n";
658             }
659              
660 6         216 my $schema = $translator->schema;
661 6         598 my $indices = $result->{'indices'};
662 6         188 my $constraints = $result->{'constraints'};
663             my @tables = sort {
664 54         169 $result->{'tables'}{$a}{'order'} <=> $result->{'tables'}{$b}{'order'}
665 6         23 } keys %{ $result->{'tables'} };
  6         73  
666              
667 6         42 for my $table_name (@tables) {
668 30         133 my $tdata = $result->{'tables'}{$table_name};
669 30 50       127 next unless $tdata->{'table_name'};
670             my $table = $schema->add_table(
671             name => $tdata->{'table_name'},
672 30 50       213 comments => $tdata->{'comments'},
673             ) or die $schema->error;
674              
675 30         1324 $table->options( $tdata->{'table_options'} );
676              
677             my @fields = sort {
678             $tdata->{'fields'}->{$a}->{'order'}
679 384         849 <=> $tdata->{'fields'}->{$b}->{'order'}
680 30         86 } keys %{ $tdata->{'fields'} };
  30         290  
681              
682 30         84 for my $fname (@fields) {
683 185         3436 my $fdata = $tdata->{'fields'}{$fname};
684             my $field = $table->add_field(
685             name => $fdata->{'name'},
686             data_type => $fdata->{'data_type'},
687             size => $fdata->{'size'},
688             default_value => $fdata->{'default'},
689             is_auto_increment => $fdata->{'is_auto_inc'},
690             is_nullable => $fdata->{'null'},
691 185 50       1423 comments => $fdata->{'comments'},
692             ) or die $table->error;
693             }
694              
695 30 100       701 push @{ $tdata->{'indices'} }, @{ $indices->{$table_name} || [] };
  30         122  
  30         210  
696 30         83 push @{ $tdata->{'constraints'} },
697 30 100       60 @{ $constraints->{$table_name} || [] };
  30         151  
698              
699 30 50       56 for my $idata ( @{ $tdata->{'indices'} || [] } ) {
  30         119  
700             my $index = $table->add_index(
701             name => $idata->{'name'},
702             type => uc $idata->{'type'},
703 18 50       159 fields => $idata->{'fields'},
704             ) or die $table->error;
705             }
706              
707 30 50       64 for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
  30         133  
708             my $constraint = $table->add_constraint(
709             name => $cdata->{'name'},
710             type => $cdata->{'type'},
711             fields => $cdata->{'fields'},
712             expression => $cdata->{'expression'},
713             reference_table => $cdata->{'reference_table'},
714             reference_fields => $cdata->{'reference_fields'},
715             match_type => $cdata->{'match_type'} || '',
716             on_delete => $cdata->{'on_delete'} || $cdata->{'on_delete_do'},
717 61 50 50     816 on_update => $cdata->{'on_update'} || $cdata->{'on_update_do'},
      66        
      33        
718             ) or die $table->error;
719             }
720             }
721              
722             my @procedures = sort {
723             $result->{procedures}->{$a}->{'order'}
724 0         0 <=> $result->{procedures}->{$b}->{'order'}
725 6         32 } keys %{ $result->{procedures} };
  6         44  
726 6         29 foreach my $proc_name (@procedures) {
727             $schema->add_procedure(
728             name => $proc_name,
729             owner => $result->{procedures}->{$proc_name}->{owner},
730             sql => $result->{procedures}->{$proc_name}->{sql},
731 1         10 );
732             }
733              
734             my @views = sort {
735 0         0 $result->{views}->{$a}->{'order'} <=> $result->{views}->{$b}->{'order'}
736 6         18 } keys %{ $result->{views} };
  6         30  
737 6         18 foreach my $view_name ( keys %{ $result->{views} } ) {
  6         32  
738             $schema->add_view(
739             name => $view_name,
740             sql => $result->{views}->{$view_name}->{sql},
741 1         8 );
742             }
743              
744             my @triggers = sort {
745             $result->{triggers}->{$a}->{'order'}
746 13         48 <=> $result->{triggers}->{$b}->{'order'}
747 6         19 } keys %{ $result->{triggers} };
  6         70  
748 6         27 foreach my $trigger_name (@triggers) {
749             $schema->add_trigger(
750             name => $trigger_name,
751             action => $result->{triggers}->{$trigger_name}->{action},
752 11         72 );
753             }
754              
755 6         112 return 1;
756             }
757              
758             1;
759              
760             # -------------------------------------------------------------------
761             # Something there is that doesn't love a wall.
762             # Robert Frost
763             # -------------------------------------------------------------------
764              
765             =pod
766              
767             =head1 AUTHOR
768              
769             Ken Youens-Clark Ekclark@cpan.orgE.
770              
771             =head1 SEE ALSO
772              
773             SQL::Translator, Parse::RecDescent, DDL::Oracle.
774              
775             =cut