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