Tab/WS crusade
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Parser / DB2.pm
1 package SQL::Translator::Parser::DB2;
2
3 use warnings;
4 use strict;
5
6 use base qw(Exporter);
7 our @EXPORT_OK = qw(parse);
8
9 our $DEBUG;
10
11 use Data::Dumper;
12 use SQL::Translator::Utils qw/ddl_parser_instance/;
13
14 our $GRAMMAR = <<'END_OF_GRAMMAR';
15
16 {
17     my ( %tables, $table_order, @table_comments, @views, @triggers );
18 }
19
20 #
21 # The "eofile" rule makes the parser fail if any "statement" rule
22 # fails.  Otherwise, the first successful match by a "statement"
23 # won't cause the failure needed to know that the parse, as a whole,
24 # failed. -ky
25 #
26 startrule : statement(s) eofile {
27     $return      = {
28         tables   => \%tables,
29         views    => \@views,
30         triggers => \@triggers,
31     }
32 }
33
34 eofile : /^\Z/
35
36 statement :
37     comment
38     | create
39     | <error>
40
41 comment : /^\s*-{2}.*\n/
42     {
43         my $comment =  $item[1];
44         $comment    =~ s/^\s*(-{2})\s*//;
45         $comment    =~ s/\s*$//;
46         $return     = $comment;
47     }
48
49
50 create: CREATE TRIGGER trigger_name before type /ON/i table_name reference_b(?) /FOR EACH ROW/i 'MODE DB2SQL' triggered_action
51 {
52     my $table_name = $item{'table_name'}{'name'};
53     $return =  {
54         table      => $table_name,
55         schema     => $item{'trigger_name'}{'schema'},
56         name       => $item{'trigger_name'}{'name'},
57         when       => 'before',
58         db_event   => $item{'type'}->{'event'},
59         fields     => $item{'type'}{'fields'},
60         condition  => $item{'triggered_action'}{'condition'},
61         reference  => $item{'reference_b'},
62         granularity => $item[9],
63         action     => $item{'triggered_action'}{'statement'}
64     };
65
66     push @triggers, $return;
67 }
68
69 create: CREATE TRIGGER trigger_name after type /ON/i table_name reference_a(?) /FOR EACH ROW|FOR EACH STATEMENT/i 'MODE DB2SQL' triggered_action
70 {
71     my $table_name = $item{'table_name'}{'name'};
72     $return = {
73         table      => $table_name,
74         schema     => $item{'trigger_name'}{'schema'},
75         name       => $item{'trigger_name'}{'name'},
76         when       => 'after',
77         db_event   => $item{'type'}{'event'},
78         fields     => $item{'type'}{'fields'},
79         condition  => $item{'triggered_action'}{'condition'},
80         reference  => $item{'reference_a'},
81         granularity => $item[9],
82         action     => $item{'triggered_action'}{'statement'}
83     };
84
85     push @triggers, $return;
86 }
87
88 create: CREATE /FEDERATED|/i VIEW view_name column_list(?) /AS/i with_expression(?) SQL_procedure_statement
89 {
90     $return = {
91         name   => $item{view_name}{name},
92         sql    => $item{SQL_procedure_statement},
93         with   => $item{'with_expression(?)'},
94         fields => $item{'column_list(?)'}
95     };
96     push @views, $return;
97 }
98
99 # create: CREATE /FEDERATED/i VIEW view_name col_list_or_of(?) /AS/i with_expression(?) fullselect options(?)
100
101 # col_list_or_of: column_list | /OF/i ( root_view_definition | subview_definition )
102
103 with_expression: /WITH/i common_table_expression(s /,/)
104 {
105     $return = $item{'common_table_expression'};
106 }
107
108 SQL_procedure_statement: /[^;]*/ /(;|\z)/ { $return = $item[1] . $item[2] }
109
110 column_list: '(' column_name(s /,/) ')'
111 {
112     $return = join(' ', '(', @{$item[2]}, ')');
113 }
114
115 CREATE: /create/i
116
117 TRIGGER: /trigger/i
118
119 VIEW: /view/i
120
121 INNER: /inner/i
122
123 LEFT: /left/i
124
125 RIGHT: /right/i
126
127 FULL: /full/i
128
129 OUTER: /outer/i
130
131 WHERE: /where/i
132
133 trigger_name: SCHEMA '.' NAME
134     { $return = { schema => $item[1], name => $item[3] } }
135     | NAME
136     { $return = { name => $item[1] } }
137
138 table_name: SCHEMA '.' NAME
139     { $return = { schema => $item[1], name => $item[3] } }
140     | NAME
141     { $return = { name => $item[1] } }
142
143 view_name: SCHEMA '.' NAME
144     { $return = { schema => $item[1], name => $item[3] } }
145     | NAME
146     { $return = { name => $item[1] } }
147
148 column_name: NAME
149
150 identifier: NAME
151
152 correlation_name: NAME
153
154 numeric_constant: /\d+/
155
156 SCHEMA: /\w+/
157
158 SCHEMA: /\w{1,128}/
159
160 NAME: /\w+/
161
162 NAME: /\w{1,18}/
163
164 options: /WITH/i ( /CASCADED/i | /LOCAL/i ) /CHECK\s+OPTION/i
165
166 # root_view_definition: /MODE\s+DB2SQL/i '(' oid_column ( /,/ with_options )(?) ')'
167
168 # subview_definition: /MODE\s+DB2SQL/i under_clause ( '(' with_options ')' )(?) /EXTEND/i(?)
169
170 # oid_column: /REF\s+IS/i oid_column_name /USER\s+GENERATED\s+UNCHECKED/i(?)
171
172 # with_options: ( column_name /WITH\s+OPTIONS/i ( /SCOPE/i ( typed_table_name | typed_view_name ) | /READ\s+ONLY/i )(s /,/) )(s /,/)
173
174 # under_clause: /UNDER/i superview_name /INHERIT\s+SELECT\s+PRIVILEGES/i
175
176 common_table_expression: table_name column_list /AS/i get_bracketed
177 {
178     $return = { name  => $item{table_name}{name},
179                 query => $item[4]
180                 };
181 }
182
183 get_bracketed:
184 {
185     extract_bracketed($text, '(');
186 }
187
188 common_table_expression: table_name column_list /AS/i '(' fullselect ')'
189
190 # fullselect: ( subselect | '(' fullselect ')' | values_clause ) ( ( /UNION/i | /UNION/i /ALL/i | /EXCEPT/i | /EXCEPT/i /ALL/i | /INTERSECT/i | /INTERSECT/i /ALL/i ) ( subselect | '(' fullselect ')' | values_clause ) )(s)
191
192 # values_clause: /VALUES/i values_row(s /,/)
193
194 # values_row: ( expression | /NULL/i ) | '(' ( expression | /NULL/i )(s /,/) ')'
195
196 # subselect:  select_clause from_clause where_clause(?) group_by_clause(?) having_clause(?)
197
198 # select_clause: SELECT ( /ALL/i | /DISTINCT )(?) ( '*' | ( expression ( /AS|/i new_column_name )(?) | exposed_name '.*' )(s /,/) )
199
200 # from_clause: /FROM/i table_name(s /,/)
201
202 # from_clause: /FROM/i table_reference(s /,/)
203
204 # table_reference:
205 #     (
206 #       ( nickname
207 #       | table_name
208 #       | view_name
209 #       )
210 #     | ( /ONLY/i
211 #       | /OUTER/i
212 #       ) '('
213 #       ( table_name
214 #       | view_name
215 #       ) ')'
216 #     ) correlation_clause(?)
217 #   | TABLE '(' function_name '(' expression(s? /,/) ')' ')'  correlation_clause
218 #   | TABLE(?) '(' fullselect ')' correlation_clause
219 #   | joined_table
220
221
222 # correlation_clause: /AS/i(?) correlation_name column_list(?)
223
224 # joined_table:
225 #    table_reference ( INNER
226 #                     | outer
227 #                     )(?) JOIN table_reference ON join_condition
228 #   | '(' joined_table ')'
229
230 # outer: ( LEFT | RIGHT | FULL ) OUTER(?)
231
232 where_clause: WHERE search_condition
233
234 # group_by_clause: /GROUP\s+BY/i ( grouping_expression
235 #                                | grouping_sets
236 #                                | super_groups
237 #                                )(s /,/)
238
239 # grouping_expression: expression
240
241 # orderby_clause: /ORDER\s+BY/i ( sort_key ( /ASC/i | /DESC/i)(?) )(s /,/)
242
243 # sort_key: simple_column_name | simple_integer | sort_key_expression
244
245 # # Name of one of the selected columns!
246 # simple_column_name: NAME
247
248 # simple_integer: /\d+/
249 #   { $item[1] <= $numberofcolumns && $item[1] > 1 }
250
251 # sort_key_expression: expression
252 #   { expression from select columns list, grouping_expression, column function.. }
253
254 # grouping_sets: /GROUPING\s+SETS/i '(' (
255 #                                         ( grouping_expression
256 #                                         | super_groups
257 #                                         )
258 #                                       | '(' ( grouping_expression
259 #                                             | super_groups
260 #                                             )(s /,/) ')'
261 #                                       )(s /,/) ')'
262
263 # super_groups: /ROLLUP/i '(' grouping_expression_list ')'
264 #            | /CUBE/i '(' grouping_expression_list ')'
265 #            | grand_total
266
267 # grouping_expression_list:  ( grouping_expression
268 #                            | '(' grouping_expression(s /,/) ')'
269 #                            )(s /,/)
270
271 # grand_total: '(' ')'
272
273 # having_clause: /HAVING/i search_condition
274
275 when_clause: /WHEN/i '(' search_condition ')' {$return = $item[3]}
276
277 triggered_action: when_clause(?) SQL_procedure_statement
278 { $return = { 'condition' => $item[1][0],
279               'statement' => $item{'SQL_procedure_statement'} };
280 }
281
282 before: /NO CASCADE BEFORE/i
283
284 after: /AFTER/i
285
286 type: /UPDATE/i /OF/i column_name(s /,/)
287 { $return = { event  => 'update_on',
288               fields => $item[3] }
289 }
290
291 type: ( /INSERT/i | /DELETE/i | /UPDATE/i )
292 { $return = { event => $item[1] } }
293
294 reference_b: /REFERENCING/i old_new_corr(0..2)
295 { $return = join(' ', $item[1], join(' ', @{$item[2]}) ) }
296
297 reference_a: /REFERENCING/i old_new_corr(0..2) old_new_table(0..2)
298 { $return = join(' ', $item[1], join(' ', @{$item[2]}), join(' ', @{$item[3]})  ) }
299
300 old_new_corr: /OLD/i /(AS)?/i correlation_name
301 { $return = join(' ', @item[1..3] ) }
302 | /NEW/i /(AS)?/i correlation_name
303 { $return = join(' ', @item[1..3] ) }
304
305 old_new_table: /OLD_TABLE/i /(AS)?/i identifier
306 { $return = join(' ', @item[1..3] ) }
307 | /NEW_TABLE/i /(AS)?/i identifier
308 { $return = join(' ', @item[1..3] ) }
309
310 # Just parsing simple search conditions for now.
311 search_condition: /[^)]+/
312
313 expression: (
314               ( '+'
315               | '-'
316               )(?)
317               ( function
318               | '(' expression ')'
319               | constant
320               | column_name
321               | host_variable
322               | special_register
323               | '(' scalar_fullselect ')'
324               | labeled_duration
325               | case_expression
326               | cast_specification
327 #              | dereference_operation
328               | OLAP_function
329               | method_invocation
330               | subtype_treatment
331               | sequence_reference
332               )
333             )(s /operator/)
334
335 operator: ( /CONCAT/i | '||' ) | '/' | '*' | '+' | '-'
336
337 function: ( /SYSIBM\.|/i sysibm_function
338           | /SYSFUN\.|/i sysfun_function
339           | userdefined_function
340           ) '(' func_args(s /,/)  ')'
341
342 constant: int_const | float_const | dec_const | char_const | hex_const | grastr_const
343
344 func_args: expression
345
346 sysibm_function: ( /ABS/i | /ABSVAL/i )
347                 | /AVG/i
348                 | /BIGINT/i
349                 | /BLOB/i
350                 | /CHAR/i
351                 | /CLOB/i
352                 | /COALESCE/i
353                 | ( /CONCAT/ | '||' )
354                 | ( /CORRELATION/i | /CORR/ )
355                 | /COUNT/i
356                 | /COUNT_BIG/i
357                 | (/COVARIANCE/i | /COVAR/i )
358                 | /DATE/i
359                 | /DAY/i
360                 | /DAYS/i
361                 | /DBCLOB/i
362                 | ( /DECIMAL/i | /DEC/i )
363                 | /DECRYPT_BIN/i
364                 | /DECRYPT_CHAR/i
365                 | /DEREF/i
366                 | /DIGITS/i
367                 | /DLCOMMENT/i
368                 | /DLLINKTYPE/i
369                 | /DLURLCOMPLETE/i
370                 | /DLURLPATH/i
371                 | /DLURLPATHONLY/i
372                 | /DLURLSCHEME/i
373                 | /DLURLSERVER/i
374                 | /DLVALUE/i
375                 | ( /DOUBLE/i | /DOUBLE_PRECISION/i )
376                 | /ENCRYPT/i
377                 | /EVENT_MON_STATE/i
378                 | /FLOAT/i
379                 | /GETHINT/i
380                 | /GENERATE_UNIQUE/i
381                 | /GRAPHIC/i
382                 | /GROUPING/i
383                 | /HEX/i
384                 | /HOUR/i
385                 | /IDENTITY_VAL_LOCAL/i
386                 | ( /INTEGER/i | /INT/ )
387                 | ( /LCASE/i | /LOWER/ )
388                 | /LENGTH/i
389                 | /LONG_VARCHAR/i
390                 | /LONG_VARGRAPHIC/i
391                 | /LTRIM/i
392                 | /MAX/i
393                 | /MICROSECOND/i
394                 | /MIN/i
395                 | /MINUTE/i
396                 | /MONTH/i
397                 | /MULTIPLY_ACT/i
398                 | /NODENUMBER/i
399                 | /NULLIF/i
400                 | /PARTITON/i
401                 | /POSSTR/i
402                 | /RAISE_ERROR/i
403                 | /REAL/i
404                 | /REC2XML/i
405                 | /REGR_AVGX/i
406                 | /REGR_AVGY/i
407                 | /REGR_COUNT/i
408                 | ( /REGR_INTERCEPT/i | /REGR_ICPT/i )
409                 | /REGR_R2/i
410                 | /REGR_SLOPE/i
411                 | /REGR_SXX/i
412                 | /REGR_SXY/i
413                 | /REGR_SYY/i
414                 | /RTRIM/i
415                 | /SECOND/i
416                 | /SMALLINT/i
417                 | /STDDEV/i
418                 | /SUBSTR/i
419                 | /SUM/i
420                 | /TABLE_NAME/i
421                 | /TABLE_SCHEMA/i
422                 | /TIME/i
423                 | /TIMESTAMP/i
424                 | /TRANSLATE/i
425                 | /TYPE_ID/i
426                 | /TYPE_NAME/i
427                 | /TYPE_SCHEMA/i
428                 | ( /UCASE/i | /UPPER/i )
429                 | /VALUE/i
430                 | /VARCHAR/i
431                 | /VARGRAPHIC/i
432                 | ( /VARIANCE/i | /VAR/i )
433                 | /YEAR/i
434
435 sysfun: ( /ABS/i | /ABSVAL/i )
436                 | /ACOS/i
437                 | /ASCII/i
438                 | /ASIN/i
439                 | /ATAN/i
440                 | /ATAN2/i
441                 | ( /CEIL/i | /CEILING/i )
442                 | /CHAR/i
443                 | /CHR/i
444                 | /COS/i
445                 | /COT/i
446                 | /DAYNAME/i
447                 | /DAYOFWEEK/i
448                 | /DAYOFWEEK_ISO/i
449                 | /DAYOFYEAR/i
450                 | /DEGREES/i
451                 | /DIFFERENCE/i
452                 | /DOUBLE/i
453                 | /EXP/i
454                 | /FLOOR/i
455                 | /GET_ROUTINE_SAR/i
456                 | /INSERT/i
457                 | /JULIAN_DAY/i
458                 | /LCASE/i
459                 | /LEFT/i
460                 | /LN/i
461                 | /LOCATE/i
462                 | /LOG/i
463                 | /LOG10/i
464                 | /LTRIM/i
465                 | /MIDNIGHT_SECONDS/i
466                 | /MOD/i
467                 | /MONTHNAME/i
468                 | /POWER/i
469                 | /PUT_ROUTINE_SAR/i
470                 | /QUARTER/i
471                 | /RADIANS/i
472                 | /RAND/i
473                 | /REPEAT/i
474                 | /REPLACE/i
475                 | /RIGHT/i
476                 | /ROUND/i
477                 | /RTRIM/I
478                 | /SIGN/i
479                 | /SIN/i
480                 | /SOUNDEX/i
481                 | /SPACE/i
482                 | /SQLCACHE_SNAPSHOT/i
483                 | /SQRT/i
484                 | /TAN/i
485                 | /TIMESTAMP_ISO/i
486                 | /TIMESTAMPDIFF/i
487                 | ( /TRUNCATE/i | /TRUNC/i )
488                 | /UCASE/i
489                 | /WEEK/i
490                 | /WEEK_ISO/i
491
492 scalar_fullselect: '(' fullselect ')'
493
494 labeled_duration: ld_type ld_duration
495
496 ld_type: function
497        | '(' expression ')'
498        | constant
499        | column_name
500        | host_variable
501
502 ld_duration: /YEARS?/i
503            | /MONTHS?/i
504            | /DAYS?/i
505            | /HOURS?/i
506            | /MINUTES?/i
507            | /SECONDS?/i
508            | /MICROSECONDS?/i
509
510 case_expression: /CASE/i ( searched_when_clause
511                          | simple_when_clause
512                          )
513                          ( /ELSE\s+NULL/i
514                          | /ELSE/i result_expression
515                          )(?) /END/i
516
517 searched_when_clause: ( /WHEN/i search_condition /THEN/i
518                         ( result_expression
519                         | /NULL/i
520                         )
521                       )(s)
522
523 simple_when_clause: expression ( /WHEN/i search_condition /THEN/i
524                                  ( result_expression
525                                  | /NULL/i
526                                  )
527                                )(s)
528
529 result_expression: expression
530
531 cast_specification: /CAST/i '(' ( expression
532                                 | /NULL/i
533                                 | parameter_marker
534                                 ) /AS/i data_type
535                                   ( /SCOPE/ ( typed_table_name
536                                             | typed_view_name
537                                             )
538                                   )(?) ')'
539
540 dereference_operation: scoped_reference_expression '->' name1
541                       (  '(' expression(s) ')' )(?)
542 #                         ( '(' expression(s /,/) ')' )(?)
543
544
545
546 scoped_reference_expression: expression
547 { # scoped, reference
548 }
549
550 name1: NAME
551
552 OLAP_function: ranking_function
553              | numbering_function
554              | aggregation_function
555
556 ranking_function: ( /RANK/ '()'
557                   | /DENSE_RANK|DENSERANK/i '()'
558                   ) /OVER/i '(' window_partition_clause(?) window_order_clause ')'
559
560 numbering_function: /ROW_NUMBER|ROWNUMBER/i '()' /OVER/i '(' window_partition_clause(?)
561                       ( window_order_clause window_aggregation_group_clause(?)
562                       )(?)
563                       ( /RANGE\s+BETWEEN\s+UNBOUNDED\s+PRECEDING\s+AND\s+UNBBOUNDED\s+FOLLOWING/i
564                       | window_aggregation_group_clause
565                       )(?) ')'
566
567 window_partition_clause: /PARTITION\s+BY/i partitioning_expression(s /,/)
568
569 window_order_clause: /ORDER\s+BY/i
570                       ( sort_key_expression
571                         ( asc_option
572                         | desc_option
573                         )(?)
574                       )(s /,/)
575
576 asc_option: /ASC/i ( /NULLS\s+FIRST/i | /NULLS\s+LAST/i )(?)
577
578 desc_option: /DESC/i ( /NULLS\s+FIRST/i | /NULLS\s+LAST/i )(?)
579
580 window_aggregation_group_clause: ( /ROWS/i
581                                  | /RANGE/i
582                                  )
583                                  ( group_start
584                                  | group_between
585                                  | group_end
586                                  )
587
588 group_start: /UNBOUNDED\s+PRECEDING/i
589            | unsigned_constant /PRECEDING/i
590            | /CURRENT\s+ROW/i
591
592 group_between: /BETWEEN/i group_bound1 /AND/i group_bound2
593
594 group_bound1: /UNBOUNDED\s+PRECEDING/i
595            | unsigned_constant /PRECEDING/i
596            | unsigned_constant /FOLLOWING/i
597            | /CURRENT\s+ROW/i
598
599 group_bound2: /UNBOUNDED\s+PRECEDING/i
600            | unsigned_constant /PRECEDING/i
601            | unsigned_constant /FOLLOWING/i
602            | /CURRENT\s+ROW/i
603
604 group_end: /UNBOUNDED\s+PRECEDING/i
605            | unsigned_constant /FOLLOWING/i
606
607 method_invocation: subject_expression '..' method_name
608                     ( '(' expression(s) ')'
609 #                    ( '(' expression(s /,/) ')'
610                     )(?)
611
612 subject_expression: expression
613 { # with static result type that is a used-defined struct type
614 }
615
616 method_name: NAME
617 { # must be a method of subject_expression
618 }
619
620 subtype_treatment: /TREAT/i '(' expression /AS/i data_type ')'
621
622 sequence_reference: nextval_expression
623                   | prevval_expression
624
625 nextval_expression: /NEXTVAL\s+FOR/i sequence_name
626
627 prevval_expression: /PREVVAL\s+FOR/i sequence_name
628
629 sequence_name: NAME
630
631
632 search_condition: /NOT|/i ( predicate ( /SELECTIVITY/i numeric_constant )(?) | '(' search_condition ')' ) cond(s?)
633
634 cond: ( /AND/i | /OR/i ) /NOT|/i ( predicate ( /SELECTIVITY/i numeric_constant )(?) | '(' search_condition ')' )
635
636 predicate: basic_p | quantified_p | between_p | exists_p | in_p | like_p | null_p | type_p
637
638 basic_p: expression /(=|<>|<|>|<=|=>|\^=|\^<|\^>|\!=)/ expression
639
640 quantified_p: expression1 /(=|<>|<|>|<=|=>|\^=|\^<|\^>|\!=)/ /SOME|ANY|ALL/i '(' fullselect ')'
641
642 END_OF_GRAMMAR
643
644 sub parse {
645     my ( $translator, $data ) = @_;
646
647     # Enable warnings within the Parse::RecDescent module.
648     local $::RD_ERRORS = 1 unless defined $::RD_ERRORS; # Make sure the parser dies when it encounters an error
649     local $::RD_WARN   = 1 unless defined $::RD_WARN; # Enable warnings. This will warn on unused rules &c.
650     local $::RD_HINT   = 1 unless defined $::RD_HINT; # Give out hints to help fix problems.
651
652     local $::RD_TRACE  = $translator->trace ? 1 : undef;
653     local $DEBUG       = $translator->debug;
654
655     my $parser = ddl_parser_instance('DB2');
656
657     my $result = $parser->startrule($data);
658     return $translator->error( "Parse failed." ) unless defined $result;
659     warn Dumper( $result ) if $DEBUG;
660
661     my $schema = $translator->schema;
662     my @tables =
663         map   { $_->[1] }
664         sort  { $a->[0] <=> $b->[0] }
665         map   { [ $result->{'tables'}{ $_ }->{'order'}, $_ ] }
666         keys %{ $result->{'tables'} };
667
668     for my $table_name ( @tables ) {
669         my $tdata =  $result->{'tables'}{ $table_name };
670         my $table =  $schema->add_table(
671             name  => $tdata->{'name'},
672         ) or die $schema->error;
673
674         $table->comments( $tdata->{'comments'} );
675
676         for my $fdata ( @{ $tdata->{'fields'} } ) {
677             my $field = $table->add_field(
678                 name              => $fdata->{'name'},
679                 data_type         => $fdata->{'data_type'},
680                 size              => $fdata->{'size'},
681                 default_value     => $fdata->{'default'},
682                 is_auto_increment => $fdata->{'is_auto_inc'},
683                 is_nullable       => $fdata->{'is_nullable'},
684                 comments          => $fdata->{'comments'},
685             ) or die $table->error;
686
687             $table->primary_key( $field->name ) if $fdata->{'is_primary_key'};
688
689             for my $cdata ( @{ $fdata->{'constraints'} } ) {
690                 next unless $cdata->{'type'} eq 'foreign_key';
691                 $cdata->{'fields'} ||= [ $field->name ];
692                 push @{ $tdata->{'constraints'} }, $cdata;
693             }
694         }
695
696         for my $idata ( @{ $tdata->{'indices'} || [] } ) {
697             my $index  =  $table->add_index(
698                 name   => $idata->{'name'},
699                 type   => uc $idata->{'type'},
700                 fields => $idata->{'fields'},
701             ) or die $table->error;
702         }
703
704         for my $cdata ( @{ $tdata->{'constraints'} || [] } ) {
705             my $constraint       =  $table->add_constraint(
706                 name             => $cdata->{'name'},
707                 type             => $cdata->{'type'},
708                 fields           => $cdata->{'fields'},
709                 reference_table  => $cdata->{'reference_table'},
710                 reference_fields => $cdata->{'reference_fields'},
711                 match_type       => $cdata->{'match_type'} || '',
712                 on_delete        => $cdata->{'on_delete'} || $cdata->{'on_delete_do'},
713                 on_update        => $cdata->{'on_update'} || $cdata->{'on_update_do'},
714             ) or die $table->error;
715         }
716     }
717
718     for my $def ( @{ $result->{'views'} || [] } ) {
719         my $view = $schema->add_view(
720             name => $def->{'name'},
721             sql  => $def->{'sql'},
722         );
723     }
724
725     for my $def ( @{ $result->{'triggers'} || [] } ) {
726         my $trig                = $schema->add_trigger(
727             name                => $def->{'name'},
728             perform_action_when => $def->{'when'},
729             database_event      => $def->{'db_event'},
730             action              => $def->{'action'},
731             fields              => $def->{'fields'},
732             on_table            => $def->{'table'}
733                                                        );
734         $trig->extra( reference => $def->{'reference'},
735                       condition => $def->{'condition'},
736                       granularity => $def->{'granularity'} );
737     }
738
739     return 1;
740 }
741
742 1;