SQLT::Parser::PostgreSQL parses table def with default values
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema;
2
3c5de62a 3=pod
4
5=head1 NAME
6
7SQL::Translator::Schema - SQL::Translator schema object
8
9=head1 SYNOPSIS
10
11 use SQL::Translator::Schema;
97382c6d 12 my $schema = SQL::Translator::Schema->new(
13 name => 'Foo',
14 database => 'MySQL',
15 );
16 my $table = $schema->add_table( name => 'foo' );
17 my $view = $schema->add_view( name => 'bar', sql => '...' );
18
3c5de62a 19
20=head1 DESCSIPTION
21
22C<SQL::Translator::Schema> is the object that accepts, validates, and
23returns the database structure.
24
25=head1 METHODS
26
27=cut
28
29use strict;
f27f9229 30use warnings;
9480e70b 31use SQL::Translator::Schema::Constants;
daf24e05 32use SQL::Translator::Schema::Procedure;
3c5de62a 33use SQL::Translator::Schema::Table;
5974bee7 34use SQL::Translator::Schema::Trigger;
3c5de62a 35use SQL::Translator::Schema::View;
4c4636ed 36
5974bee7 37use SQL::Translator::Utils 'parse_list_arg';
3c5de62a 38
b6a880d1 39use base 'SQL::Translator::Schema::Object';
0c04c5a2 40our $VERSION = '1.59';
9371be50 41
da8ab524 42__PACKAGE__->_attributes(qw/name database translator/);
3c5de62a 43
d37416fd 44sub new {
45 my $class = shift;
46 my $self = $class->SUPER::new (@_)
47 or return;
48
49 $self->{_order} = { map { $_ => 0 } qw/
50 table
51 view
52 trigger
53 proc
54 /};
55
56 return $self;
57}
58
97382c6d 59sub as_graph {
60
3c5de62a 61=pod
62
97382c6d 63=head2 as_graph
3c5de62a 64
97382c6d 65Returns the schema as an L<SQL::Translator::Schema::Graph> object.
3c5de62a 66
67=cut
4c4636ed 68 require SQL::Translator::Schema::Graph;
3c5de62a 69
da8ab524 70 my $self = shift;
4c4636ed 71
97382c6d 72 return SQL::Translator::Schema::Graph->new(
da8ab524 73 translator => $self->translator );
10f36920 74}
75
e7ca845a 76sub as_graph_pm {
77
78=pod
79
da5a1bae 80=head2 as_graph_pm
e7ca845a 81
82Returns a Graph::Directed object with the table names for nodes.
83
84=cut
85
da5a1bae 86 require Graph::Directed;
87
e7ca845a 88 my $self = shift;
89 my $g = Graph::Directed->new;
ea93df61 90
91 for my $table ( $self->get_tables ) {
e7ca845a 92 my $tname = $table->name;
93 $g->add_vertex( $tname );
ea93df61 94
e7ca845a 95 for my $field ( $table->get_fields ) {
96 if ( $field->is_foreign_key ) {
97 my $fktable = $field->foreign_key_reference->reference_table;
98
99 $g->add_edge( $fktable, $tname );
100 }
101 }
102 }
103
104 return $g;
105}
106
76dce619 107sub add_table {
3c5de62a 108
109=pod
110
76dce619 111=head2 add_table
3c5de62a 112
76dce619 113Add a table object. Returns the new SQL::Translator::Schema::Table object.
99248301 114The "name" parameter is required. If you try to create a table with the
ea93df61 115same name as an existing table, you will get an error and the table will
99248301 116not be created.
3c5de62a 117
68e8e2e1 118 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
119 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
120 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
3c5de62a 121
122=cut
123
99248301 124 my $self = shift;
125 my $table_class = 'SQL::Translator::Schema::Table';
126 my $table;
127
128 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
129 $table = shift;
da8ab524 130 $table->schema($self);
99248301 131 }
132 else {
53032df3 133 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
99248301 134 $args{'schema'} = $self;
da8ab524 135 $table = $table_class->new( \%args )
136 or return $self->error( $table_class->error );
99248301 137 }
3c5de62a 138
d37416fd 139 $table->order( ++$self->{_order}{table} );
da8ab524 140
23044758 141 # We know we have a name as the Table->new above errors if none given.
142 my $table_name = $table->name;
99248301 143
da8ab524 144 if ( defined $self->{'tables'}{$table_name} ) {
99248301 145 return $self->error(qq[Can't create table: "$table_name" exists]);
146 }
147 else {
da8ab524 148 $self->{'tables'}{$table_name} = $table;
99248301 149 }
3c5de62a 150
151 return $table;
152}
153
650f87eb 154sub drop_table {
155
156=pod
157
158=head2 drop_table
159
160Remove a table from the schema. Returns the table object if the table was found
161and removed, an error otherwise. The single parameter can be either a table
162name or an C<SQL::Translator::Schema::Table> object. The "cascade" parameter
163can be set to 1 to also drop all triggers on the table, default is 0.
164
165 $schema->drop_table('mytable');
166 $schema->drop_table('mytable', cascade => 1);
167
168=cut
169
da8ab524 170 my $self = shift;
171 my $table_class = 'SQL::Translator::Schema::Table';
650f87eb 172 my $table_name;
173
174 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
175 $table_name = shift->name;
176 }
177 else {
178 $table_name = shift;
179 }
da8ab524 180 my %args = @_;
650f87eb 181 my $cascade = $args{'cascade'};
182
da8ab524 183 if ( !exists $self->{'tables'}{$table_name} ) {
650f87eb 184 return $self->error(qq[Can't drop table: $table_name" doesn't exist]);
185 }
186
da8ab524 187 my $table = delete $self->{'tables'}{$table_name};
188
189 if ($cascade) {
650f87eb 190
650f87eb 191 # Drop all triggers on this table
da8ab524 192 $self->drop_trigger()
193 for ( grep { $_->on_table eq $table_name } @{ $self->{'triggers'} } );
650f87eb 194 }
195 return $table;
196}
197
daf24e05 198sub add_procedure {
199
200=pod
201
202=head2 add_procedure
203
650f87eb 204Add a procedure object. Returns the new SQL::Translator::Schema::Procedure
205object. The "name" parameter is required. If you try to create a procedure
206with the same name as an existing procedure, you will get an error and the
207procedure will not be created.
daf24e05 208
209 my $p1 = $schema->add_procedure( name => 'foo' );
210 my $p2 = SQL::Translator::Schema::Procedure->new( name => 'bar' );
211 $p2 = $schema->add_procedure( $procedure_bar ) or die $schema->error;
212
213=cut
214
215 my $self = shift;
216 my $procedure_class = 'SQL::Translator::Schema::Procedure';
217 my $procedure;
218
219 if ( UNIVERSAL::isa( $_[0], $procedure_class ) ) {
220 $procedure = shift;
da8ab524 221 $procedure->schema($self);
daf24e05 222 }
223 else {
53032df3 224 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
daf24e05 225 $args{'schema'} = $self;
226 return $self->error('No procedure name') unless $args{'name'};
da8ab524 227 $procedure = $procedure_class->new( \%args )
228 or return $self->error( $procedure_class->error );
daf24e05 229 }
230
d37416fd 231 $procedure->order( ++$self->{_order}{proc} );
da8ab524 232 my $procedure_name = $procedure->name
233 or return $self->error('No procedure name');
daf24e05 234
da8ab524 235 if ( defined $self->{'procedures'}{$procedure_name} ) {
daf24e05 236 return $self->error(
da8ab524 237 qq[Can't create procedure: "$procedure_name" exists] );
daf24e05 238 }
239 else {
da8ab524 240 $self->{'procedures'}{$procedure_name} = $procedure;
daf24e05 241 }
242
243 return $procedure;
244}
da8ab524 245
650f87eb 246sub drop_procedure {
247
248=pod
249
250=head2 drop_procedure
251
252Remove a procedure from the schema. Returns the procedure object if the
253procedure was found and removed, an error otherwise. The single parameter
254can be either a procedure name or an C<SQL::Translator::Schema::Procedure>
255object.
256
257 $schema->drop_procedure('myprocedure');
258
259=cut
260
da8ab524 261 my $self = shift;
262 my $proc_class = 'SQL::Translator::Schema::Procedure';
650f87eb 263 my $proc_name;
264
265 if ( UNIVERSAL::isa( $_[0], $proc_class ) ) {
266 $proc_name = shift->name;
267 }
268 else {
269 $proc_name = shift;
270 }
271
da8ab524 272 if ( !exists $self->{'procedures'}{$proc_name} ) {
273 return $self->error(
274 qq[Can't drop procedure: $proc_name" doesn't exist]);
650f87eb 275 }
276
da8ab524 277 my $proc = delete $self->{'procedures'}{$proc_name};
650f87eb 278
279 return $proc;
280}
daf24e05 281
5974bee7 282sub add_trigger {
283
284=pod
285
286=head2 add_trigger
287
288Add a trigger object. Returns the new SQL::Translator::Schema::Trigger object.
289The "name" parameter is required. If you try to create a trigger with the
ea93df61 290same name as an existing trigger, you will get an error and the trigger will
5974bee7 291not be created.
292
293 my $t1 = $schema->add_trigger( name => 'foo' );
294 my $t2 = SQL::Translator::Schema::Trigger->new( name => 'bar' );
295 $t2 = $schema->add_trigger( $trigger_bar ) or die $schema->error;
296
297=cut
298
299 my $self = shift;
300 my $trigger_class = 'SQL::Translator::Schema::Trigger';
301 my $trigger;
302
303 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
304 $trigger = shift;
da8ab524 305 $trigger->schema($self);
5974bee7 306 }
307 else {
53032df3 308 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
daf24e05 309 $args{'schema'} = $self;
5974bee7 310 return $self->error('No trigger name') unless $args{'name'};
da8ab524 311 $trigger = $trigger_class->new( \%args )
312 or return $self->error( $trigger_class->error );
5974bee7 313 }
314
d37416fd 315 $trigger->order( ++$self->{_order}{trigger} );
5974bee7 316
4faaaac6 317 my $trigger_name = $trigger->name or return $self->error('No trigger name');
da8ab524 318 if ( defined $self->{'triggers'}{$trigger_name} ) {
5974bee7 319 return $self->error(qq[Can't create trigger: "$trigger_name" exists]);
320 }
321 else {
da8ab524 322 $self->{'triggers'}{$trigger_name} = $trigger;
5974bee7 323 }
324
325 return $trigger;
326}
da8ab524 327
650f87eb 328sub drop_trigger {
329
330=pod
331
332=head2 drop_trigger
333
334Remove a trigger from the schema. Returns the trigger object if the trigger was
335found and removed, an error otherwise. The single parameter can be either a
336trigger name or an C<SQL::Translator::Schema::Trigger> object.
337
338 $schema->drop_trigger('mytrigger');
339
340=cut
341
da8ab524 342 my $self = shift;
343 my $trigger_class = 'SQL::Translator::Schema::Trigger';
650f87eb 344 my $trigger_name;
345
346 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
347 $trigger_name = shift->name;
348 }
349 else {
350 $trigger_name = shift;
351 }
352
da8ab524 353 if ( !exists $self->{'triggers'}{$trigger_name} ) {
354 return $self->error(
355 qq[Can't drop trigger: $trigger_name" doesn't exist]);
650f87eb 356 }
357
da8ab524 358 my $trigger = delete $self->{'triggers'}{$trigger_name};
650f87eb 359
360 return $trigger;
361}
5974bee7 362
76dce619 363sub add_view {
3c5de62a 364
365=pod
366
76dce619 367=head2 add_view
3c5de62a 368
76dce619 369Add a view object. Returns the new SQL::Translator::Schema::View object.
99248301 370The "name" parameter is required. If you try to create a view with the
ea93df61 371same name as an existing view, you will get an error and the view will
99248301 372not be created.
373
68e8e2e1 374 my $v1 = $schema->add_view( name => 'foo' );
375 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
376 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
3c5de62a 377
378=cut
379
da8ab524 380 my $self = shift;
99248301 381 my $view_class = 'SQL::Translator::Schema::View';
382 my $view;
383
384 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
385 $view = shift;
da8ab524 386 $view->schema($self);
99248301 387 }
388 else {
53032df3 389 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
daf24e05 390 $args{'schema'} = $self;
99248301 391 return $self->error('No view name') unless $args{'name'};
392 $view = $view_class->new( \%args ) or return $view_class->error;
393 }
3c5de62a 394
d37416fd 395 $view->order( ++$self->{_order}{view} );
99248301 396 my $view_name = $view->name or return $self->error('No view name');
397
da8ab524 398 if ( defined $self->{'views'}{$view_name} ) {
99248301 399 return $self->error(qq[Can't create view: "$view_name" exists]);
400 }
401 else {
da8ab524 402 $self->{'views'}{$view_name} = $view;
99248301 403 }
3c5de62a 404
76dce619 405 return $view;
3c5de62a 406}
407
650f87eb 408sub drop_view {
409
410=pod
411
412=head2 drop_view
413
414Remove a view from the schema. Returns the view object if the view was found
415and removed, an error otherwise. The single parameter can be either a view
416name or an C<SQL::Translator::Schema::View> object.
417
418 $schema->drop_view('myview');
419
420=cut
421
da8ab524 422 my $self = shift;
423 my $view_class = 'SQL::Translator::Schema::View';
650f87eb 424 my $view_name;
425
426 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
427 $view_name = shift->name;
428 }
429 else {
430 $view_name = shift;
431 }
432
da8ab524 433 if ( !exists $self->{'views'}{$view_name} ) {
650f87eb 434 return $self->error(qq[Can't drop view: $view_name" doesn't exist]);
435 }
436
da8ab524 437 my $view = delete $self->{'views'}{$view_name};
650f87eb 438
439 return $view;
440}
441
99248301 442sub database {
443
444=pod
445
446=head2 database
447
448Get or set the schema's database. (optional)
449
450 my $database = $schema->database('PostgreSQL');
451
452=cut
453
454 my $self = shift;
455 $self->{'database'} = shift if @_;
456 return $self->{'database'} || '';
457}
458
76dce619 459sub is_valid {
3c5de62a 460
461=pod
462
76dce619 463=head2 is_valid
3c5de62a 464
76dce619 465Returns true if all the tables and views are valid.
3c5de62a 466
76dce619 467 my $ok = $schema->is_valid or die $schema->error;
468
469=cut
470
471 my $self = shift;
472
473 return $self->error('No tables') unless $self->get_tables;
474
475 for my $object ( $self->get_tables, $self->get_views ) {
476 return $object->error unless $object->is_valid;
477 }
478
479 return 1;
480}
481
daf24e05 482sub get_procedure {
483
484=pod
485
486=head2 get_procedure
487
488Returns a procedure by the name provided.
489
490 my $procedure = $schema->get_procedure('foo');
491
492=cut
493
da8ab524 494 my $self = shift;
daf24e05 495 my $procedure_name = shift or return $self->error('No procedure name');
da8ab524 496 return $self->error(qq[Table "$procedure_name" does not exist])
497 unless exists $self->{'procedures'}{$procedure_name};
498 return $self->{'procedures'}{$procedure_name};
daf24e05 499}
500
daf24e05 501sub get_procedures {
502
503=pod
504
505=head2 get_procedures
506
507Returns all the procedures as an array or array reference.
508
509 my @procedures = $schema->get_procedures;
510
511=cut
512
da8ab524 513 my $self = shift;
514 my @procedures =
515 map { $_->[1] }
516 sort { $a->[0] <=> $b->[0] }
517 map { [ $_->order, $_ ] } values %{ $self->{'procedures'} };
daf24e05 518
da8ab524 519 if (@procedures) {
daf24e05 520 return wantarray ? @procedures : \@procedures;
521 }
522 else {
523 $self->error('No procedures');
524 return wantarray ? () : undef;
525 }
526}
527
76dce619 528sub get_table {
529
530=pod
531
532=head2 get_table
533
534Returns a table by the name provided.
535
536 my $table = $schema->get_table('foo');
537
538=cut
539
da8ab524 540 my $self = shift;
76dce619 541 my $table_name = shift or return $self->error('No table name');
6a25a87d 542 my $case_insensitive = shift;
543 if ( $case_insensitive ) {
ea93df61 544 $table_name = uc($table_name);
545 foreach my $table ( keys %{$self->{tables}} ) {
546 return $self->{tables}{$table} if $table_name eq uc($table);
547 }
548 return $self->error(qq[Table "$table_name" does not exist]);
6a25a87d 549 }
da8ab524 550 return $self->error(qq[Table "$table_name" does not exist])
551 unless exists $self->{'tables'}{$table_name};
552 return $self->{'tables'}{$table_name};
76dce619 553}
554
76dce619 555sub get_tables {
556
557=pod
558
559=head2 get_tables
560
561Returns all the tables as an array or array reference.
562
563 my @tables = $schema->get_tables;
564
565=cut
566
567 my $self = shift;
da8ab524 568 my @tables =
569 map { $_->[1] }
570 sort { $a->[0] <=> $b->[0] }
571 map { [ $_->order, $_ ] } values %{ $self->{'tables'} };
76dce619 572
da8ab524 573 if (@tables) {
76dce619 574 return wantarray ? @tables : \@tables;
575 }
576 else {
577 $self->error('No tables');
578 return wantarray ? () : undef;
579 }
580}
581
daf24e05 582sub get_trigger {
583
584=pod
585
586=head2 get_trigger
587
588Returns a trigger by the name provided.
589
590 my $trigger = $schema->get_trigger('foo');
591
592=cut
593
da8ab524 594 my $self = shift;
daf24e05 595 my $trigger_name = shift or return $self->error('No trigger name');
da8ab524 596 return $self->error(qq[Table "$trigger_name" does not exist])
597 unless exists $self->{'triggers'}{$trigger_name};
598 return $self->{'triggers'}{$trigger_name};
daf24e05 599}
600
daf24e05 601sub get_triggers {
602
603=pod
604
605=head2 get_triggers
606
607Returns all the triggers as an array or array reference.
608
609 my @triggers = $schema->get_triggers;
610
611=cut
612
da8ab524 613 my $self = shift;
614 my @triggers =
615 map { $_->[1] }
616 sort { $a->[0] <=> $b->[0] }
617 map { [ $_->order, $_ ] } values %{ $self->{'triggers'} };
daf24e05 618
da8ab524 619 if (@triggers) {
daf24e05 620 return wantarray ? @triggers : \@triggers;
621 }
622 else {
623 $self->error('No triggers');
624 return wantarray ? () : undef;
625 }
626}
627
76dce619 628sub get_view {
629
630=pod
631
632=head2 get_view
633
634Returns a view by the name provided.
635
636 my $view = $schema->get_view('foo');
3c5de62a 637
638=cut
639
da8ab524 640 my $self = shift;
76dce619 641 my $view_name = shift or return $self->error('No view name');
da8ab524 642 return $self->error('View "$view_name" does not exist')
643 unless exists $self->{'views'}{$view_name};
644 return $self->{'views'}{$view_name};
76dce619 645}
3c5de62a 646
76dce619 647sub get_views {
3c5de62a 648
76dce619 649=pod
650
651=head2 get_views
652
653Returns all the views as an array or array reference.
654
655 my @views = $schema->get_views;
656
657=cut
658
659 my $self = shift;
da8ab524 660 my @views =
661 map { $_->[1] }
662 sort { $a->[0] <=> $b->[0] }
663 map { [ $_->order, $_ ] } values %{ $self->{'views'} };
76dce619 664
da8ab524 665 if (@views) {
76dce619 666 return wantarray ? @views : \@views;
667 }
668 else {
669 $self->error('No views');
670 return wantarray ? () : undef;
671 }
3c5de62a 672}
673
9480e70b 674sub make_natural_joins {
675
676=pod
677
678=head2 make_natural_joins
679
680Creates foriegn key relationships among like-named fields in different
681tables. Accepts the following arguments:
682
683=over 4
684
650f87eb 685=item * join_pk_only
9480e70b 686
ea93df61 687A True or False argument which determins whether or not to perform
9480e70b 688the joins from primary keys to fields of the same name in other tables
689
690=item * skip_fields
691
692A list of fields to skip in the joins
693
0e42fda6 694=back
9480e70b 695
696 $schema->make_natural_joins(
697 join_pk_only => 1,
698 skip_fields => 'name,department_id',
699 );
700
701=cut
702
703 my $self = shift;
704 my %args = @_;
705 my $join_pk_only = $args{'join_pk_only'} || 0;
da8ab524 706 my %skip_fields =
707 map { s/^\s+|\s+$//g; $_, 1 } @{ parse_list_arg( $args{'skip_fields'} ) };
9480e70b 708
709 my ( %common_keys, %pk );
710 for my $table ( $self->get_tables ) {
711 for my $field ( $table->get_fields ) {
712 my $field_name = $field->name or next;
da8ab524 713 next if $skip_fields{$field_name};
714 $pk{$field_name} = 1 if $field->is_primary_key;
715 push @{ $common_keys{$field_name} }, $table->name;
9480e70b 716 }
da8ab524 717 }
718
9480e70b 719 for my $field ( keys %common_keys ) {
da8ab524 720 next if $join_pk_only and !defined $pk{$field};
9480e70b 721
da8ab524 722 my @table_names = @{ $common_keys{$field} };
9480e70b 723 next unless scalar @table_names > 1;
724
725 for my $i ( 0 .. $#table_names ) {
da8ab524 726 my $table1 = $self->get_table( $table_names[$i] ) or next;
9480e70b 727
728 for my $j ( 1 .. $#table_names ) {
da8ab524 729 my $table2 = $self->get_table( $table_names[$j] ) or next;
9480e70b 730 next if $table1->name eq $table2->name;
731
732 $table1->add_constraint(
733 type => FOREIGN_KEY,
734 fields => $field,
735 reference_table => $table2->name,
736 reference_fields => $field,
737 );
650f87eb 738 }
9480e70b 739 }
650f87eb 740 }
9480e70b 741
742 return 1;
743}
744
99248301 745sub name {
746
747=pod
748
749=head2 name
750
751Get or set the schema's name. (optional)
752
753 my $schema_name = $schema->name('Foo Database');
754
755=cut
756
757 my $self = shift;
758 $self->{'name'} = shift if @_;
759 return $self->{'name'} || '';
760}
761
97382c6d 762sub translator {
763
764=pod
765
10f36920 766=head2 translator
47fed978 767
97382c6d 768Get the SQL::Translator instance that instantiated the parser.
47fed978 769
770=cut
771
47fed978 772 my $self = shift;
10f36920 773 $self->{'translator'} = shift if @_;
774 return $self->{'translator'};
47fed978 775}
776
d0b43695 777sub DESTROY {
778 my $self = shift;
779 undef $_ for values %{ $self->{'tables'} };
da8ab524 780 undef $_ for values %{ $self->{'views'} };
d0b43695 781}
782
3c5de62a 7831;
784
3c5de62a 785=pod
786
787=head1 AUTHOR
788
97382c6d 789Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 790
791=cut
da8ab524 792