1 package SQL::Translator::Schema;
3 # ----------------------------------------------------------------------
4 # Copyright (C) 2002-2009 SQLFairy Authors
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; version 2.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 # -------------------------------------------------------------------
25 SQL::Translator::Schema - SQL::Translator schema object
29 use SQL::Translator::Schema;
30 my $schema = SQL::Translator::Schema->new(
34 my $table = $schema->add_table( name => 'foo' );
35 my $view = $schema->add_view( name => 'bar', sql => '...' );
40 C<SQL::Translator::Schema> is the object that accepts, validates, and
41 returns the database structure.
48 use SQL::Translator::Schema::Constants;
49 use SQL::Translator::Schema::Procedure;
50 use SQL::Translator::Schema::Table;
51 use SQL::Translator::Schema::Trigger;
52 use SQL::Translator::Schema::View;
54 use SQL::Translator::Utils 'parse_list_arg';
56 use base 'SQL::Translator::Schema::Object';
57 use vars qw[ $VERSION ];
61 __PACKAGE__->_attributes(qw/name database translator/);
65 my $self = $class->SUPER::new (@_)
68 $self->{_order} = { map { $_ => 0 } qw/
78 # ----------------------------------------------------------------------
85 Returns the schema as an L<SQL::Translator::Schema::Graph> object.
88 require SQL::Translator::Schema::Graph;
92 return SQL::Translator::Schema::Graph->new(
93 translator => $self->translator );
96 # ----------------------------------------------------------------------
103 Returns a Graph::Directed object with the table names for nodes.
107 require Graph::Directed;
110 my $g = Graph::Directed->new;
112 for my $table ( $self->get_tables ) {
113 my $tname = $table->name;
114 $g->add_vertex( $tname );
116 for my $field ( $table->get_fields ) {
117 if ( $field->is_foreign_key ) {
118 my $fktable = $field->foreign_key_reference->reference_table;
120 $g->add_edge( $fktable, $tname );
128 # ----------------------------------------------------------------------
135 Add a table object. Returns the new SQL::Translator::Schema::Table object.
136 The "name" parameter is required. If you try to create a table with the
137 same name as an existing table, you will get an error and the table will
140 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
141 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
142 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
147 my $table_class = 'SQL::Translator::Schema::Table';
150 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
152 $table->schema($self);
155 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
156 $args{'schema'} = $self;
157 $table = $table_class->new( \%args )
158 or return $self->error( $table_class->error );
161 $table->order( ++$self->{_order}{table} );
163 # We know we have a name as the Table->new above errors if none given.
164 my $table_name = $table->name;
166 if ( defined $self->{'tables'}{$table_name} ) {
167 return $self->error(qq[Can't create table: "$table_name" exists]);
170 $self->{'tables'}{$table_name} = $table;
176 # ----------------------------------------------------------------------
183 Remove a table from the schema. Returns the table object if the table was found
184 and removed, an error otherwise. The single parameter can be either a table
185 name or an C<SQL::Translator::Schema::Table> object. The "cascade" parameter
186 can be set to 1 to also drop all triggers on the table, default is 0.
188 $schema->drop_table('mytable');
189 $schema->drop_table('mytable', cascade => 1);
194 my $table_class = 'SQL::Translator::Schema::Table';
197 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
198 $table_name = shift->name;
204 my $cascade = $args{'cascade'};
206 if ( !exists $self->{'tables'}{$table_name} ) {
207 return $self->error(qq[Can't drop table: $table_name" doesn't exist]);
210 my $table = delete $self->{'tables'}{$table_name};
214 # Drop all triggers on this table
215 $self->drop_trigger()
216 for ( grep { $_->on_table eq $table_name } @{ $self->{'triggers'} } );
221 # ----------------------------------------------------------------------
228 Add a procedure object. Returns the new SQL::Translator::Schema::Procedure
229 object. The "name" parameter is required. If you try to create a procedure
230 with the same name as an existing procedure, you will get an error and the
231 procedure will not be created.
233 my $p1 = $schema->add_procedure( name => 'foo' );
234 my $p2 = SQL::Translator::Schema::Procedure->new( name => 'bar' );
235 $p2 = $schema->add_procedure( $procedure_bar ) or die $schema->error;
240 my $procedure_class = 'SQL::Translator::Schema::Procedure';
243 if ( UNIVERSAL::isa( $_[0], $procedure_class ) ) {
245 $procedure->schema($self);
248 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
249 $args{'schema'} = $self;
250 return $self->error('No procedure name') unless $args{'name'};
251 $procedure = $procedure_class->new( \%args )
252 or return $self->error( $procedure_class->error );
255 $procedure->order( ++$self->{_order}{proc} );
256 my $procedure_name = $procedure->name
257 or return $self->error('No procedure name');
259 if ( defined $self->{'procedures'}{$procedure_name} ) {
261 qq[Can't create procedure: "$procedure_name" exists] );
264 $self->{'procedures'}{$procedure_name} = $procedure;
270 # ----------------------------------------------------------------------
275 =head2 drop_procedure
277 Remove a procedure from the schema. Returns the procedure object if the
278 procedure was found and removed, an error otherwise. The single parameter
279 can be either a procedure name or an C<SQL::Translator::Schema::Procedure>
282 $schema->drop_procedure('myprocedure');
287 my $proc_class = 'SQL::Translator::Schema::Procedure';
290 if ( UNIVERSAL::isa( $_[0], $proc_class ) ) {
291 $proc_name = shift->name;
297 if ( !exists $self->{'procedures'}{$proc_name} ) {
299 qq[Can't drop procedure: $proc_name" doesn't exist]);
302 my $proc = delete $self->{'procedures'}{$proc_name};
307 # ----------------------------------------------------------------------
314 Add a trigger object. Returns the new SQL::Translator::Schema::Trigger object.
315 The "name" parameter is required. If you try to create a trigger with the
316 same name as an existing trigger, you will get an error and the trigger will
319 my $t1 = $schema->add_trigger( name => 'foo' );
320 my $t2 = SQL::Translator::Schema::Trigger->new( name => 'bar' );
321 $t2 = $schema->add_trigger( $trigger_bar ) or die $schema->error;
326 my $trigger_class = 'SQL::Translator::Schema::Trigger';
329 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
331 $trigger->schema($self);
334 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
335 $args{'schema'} = $self;
336 return $self->error('No trigger name') unless $args{'name'};
337 $trigger = $trigger_class->new( \%args )
338 or return $self->error( $trigger_class->error );
341 $trigger->order( ++$self->{_order}{trigger} );
343 my $trigger_name = $trigger->name or return $self->error('No trigger name');
344 if ( defined $self->{'triggers'}{$trigger_name} ) {
345 return $self->error(qq[Can't create trigger: "$trigger_name" exists]);
348 $self->{'triggers'}{$trigger_name} = $trigger;
354 # ----------------------------------------------------------------------
361 Remove a trigger from the schema. Returns the trigger object if the trigger was
362 found and removed, an error otherwise. The single parameter can be either a
363 trigger name or an C<SQL::Translator::Schema::Trigger> object.
365 $schema->drop_trigger('mytrigger');
370 my $trigger_class = 'SQL::Translator::Schema::Trigger';
373 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
374 $trigger_name = shift->name;
377 $trigger_name = shift;
380 if ( !exists $self->{'triggers'}{$trigger_name} ) {
382 qq[Can't drop trigger: $trigger_name" doesn't exist]);
385 my $trigger = delete $self->{'triggers'}{$trigger_name};
390 # ----------------------------------------------------------------------
397 Add a view object. Returns the new SQL::Translator::Schema::View object.
398 The "name" parameter is required. If you try to create a view with the
399 same name as an existing view, you will get an error and the view will
402 my $v1 = $schema->add_view( name => 'foo' );
403 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
404 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
409 my $view_class = 'SQL::Translator::Schema::View';
412 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
414 $view->schema($self);
417 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
418 $args{'schema'} = $self;
419 return $self->error('No view name') unless $args{'name'};
420 $view = $view_class->new( \%args ) or return $view_class->error;
423 $view->order( ++$self->{_order}{view} );
424 my $view_name = $view->name or return $self->error('No view name');
426 if ( defined $self->{'views'}{$view_name} ) {
427 return $self->error(qq[Can't create view: "$view_name" exists]);
430 $self->{'views'}{$view_name} = $view;
436 # ----------------------------------------------------------------------
443 Remove a view from the schema. Returns the view object if the view was found
444 and removed, an error otherwise. The single parameter can be either a view
445 name or an C<SQL::Translator::Schema::View> object.
447 $schema->drop_view('myview');
452 my $view_class = 'SQL::Translator::Schema::View';
455 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
456 $view_name = shift->name;
462 if ( !exists $self->{'views'}{$view_name} ) {
463 return $self->error(qq[Can't drop view: $view_name" doesn't exist]);
466 my $view = delete $self->{'views'}{$view_name};
471 # ----------------------------------------------------------------------
478 Get or set the schema's database. (optional)
480 my $database = $schema->database('PostgreSQL');
485 $self->{'database'} = shift if @_;
486 return $self->{'database'} || '';
489 # ----------------------------------------------------------------------
496 Returns true if all the tables and views are valid.
498 my $ok = $schema->is_valid or die $schema->error;
504 return $self->error('No tables') unless $self->get_tables;
506 for my $object ( $self->get_tables, $self->get_views ) {
507 return $object->error unless $object->is_valid;
513 # ----------------------------------------------------------------------
520 Returns a procedure by the name provided.
522 my $procedure = $schema->get_procedure('foo');
527 my $procedure_name = shift or return $self->error('No procedure name');
528 return $self->error(qq[Table "$procedure_name" does not exist])
529 unless exists $self->{'procedures'}{$procedure_name};
530 return $self->{'procedures'}{$procedure_name};
533 # ----------------------------------------------------------------------
538 =head2 get_procedures
540 Returns all the procedures as an array or array reference.
542 my @procedures = $schema->get_procedures;
549 sort { $a->[0] <=> $b->[0] }
550 map { [ $_->order, $_ ] } values %{ $self->{'procedures'} };
553 return wantarray ? @procedures : \@procedures;
556 $self->error('No procedures');
557 return wantarray ? () : undef;
561 # ----------------------------------------------------------------------
568 Returns a table by the name provided.
570 my $table = $schema->get_table('foo');
575 my $table_name = shift or return $self->error('No table name');
576 my $case_insensitive = shift;
577 if ( $case_insensitive ) {
578 $table_name = uc($table_name);
579 foreach my $table ( keys %{$self->{tables}} ) {
580 return $self->{tables}{$table} if $table_name eq uc($table);
582 return $self->error(qq[Table "$table_name" does not exist]);
584 return $self->error(qq[Table "$table_name" does not exist])
585 unless exists $self->{'tables'}{$table_name};
586 return $self->{'tables'}{$table_name};
589 # ----------------------------------------------------------------------
596 Returns all the tables as an array or array reference.
598 my @tables = $schema->get_tables;
605 sort { $a->[0] <=> $b->[0] }
606 map { [ $_->order, $_ ] } values %{ $self->{'tables'} };
609 return wantarray ? @tables : \@tables;
612 $self->error('No tables');
613 return wantarray ? () : undef;
617 # ----------------------------------------------------------------------
624 Returns a trigger by the name provided.
626 my $trigger = $schema->get_trigger('foo');
631 my $trigger_name = shift or return $self->error('No trigger name');
632 return $self->error(qq[Table "$trigger_name" does not exist])
633 unless exists $self->{'triggers'}{$trigger_name};
634 return $self->{'triggers'}{$trigger_name};
637 # ----------------------------------------------------------------------
644 Returns all the triggers as an array or array reference.
646 my @triggers = $schema->get_triggers;
653 sort { $a->[0] <=> $b->[0] }
654 map { [ $_->order, $_ ] } values %{ $self->{'triggers'} };
657 return wantarray ? @triggers : \@triggers;
660 $self->error('No triggers');
661 return wantarray ? () : undef;
665 # ----------------------------------------------------------------------
672 Returns a view by the name provided.
674 my $view = $schema->get_view('foo');
679 my $view_name = shift or return $self->error('No view name');
680 return $self->error('View "$view_name" does not exist')
681 unless exists $self->{'views'}{$view_name};
682 return $self->{'views'}{$view_name};
685 # ----------------------------------------------------------------------
692 Returns all the views as an array or array reference.
694 my @views = $schema->get_views;
701 sort { $a->[0] <=> $b->[0] }
702 map { [ $_->order, $_ ] } values %{ $self->{'views'} };
705 return wantarray ? @views : \@views;
708 $self->error('No views');
709 return wantarray ? () : undef;
713 # ----------------------------------------------------------------------
714 sub make_natural_joins {
718 =head2 make_natural_joins
720 Creates foriegn key relationships among like-named fields in different
721 tables. Accepts the following arguments:
727 A True or False argument which determins whether or not to perform
728 the joins from primary keys to fields of the same name in other tables
732 A list of fields to skip in the joins
736 $schema->make_natural_joins(
738 skip_fields => 'name,department_id',
745 my $join_pk_only = $args{'join_pk_only'} || 0;
747 map { s/^\s+|\s+$//g; $_, 1 } @{ parse_list_arg( $args{'skip_fields'} ) };
749 my ( %common_keys, %pk );
750 for my $table ( $self->get_tables ) {
751 for my $field ( $table->get_fields ) {
752 my $field_name = $field->name or next;
753 next if $skip_fields{$field_name};
754 $pk{$field_name} = 1 if $field->is_primary_key;
755 push @{ $common_keys{$field_name} }, $table->name;
759 for my $field ( keys %common_keys ) {
760 next if $join_pk_only and !defined $pk{$field};
762 my @table_names = @{ $common_keys{$field} };
763 next unless scalar @table_names > 1;
765 for my $i ( 0 .. $#table_names ) {
766 my $table1 = $self->get_table( $table_names[$i] ) or next;
768 for my $j ( 1 .. $#table_names ) {
769 my $table2 = $self->get_table( $table_names[$j] ) or next;
770 next if $table1->name eq $table2->name;
772 $table1->add_constraint(
775 reference_table => $table2->name,
776 reference_fields => $field,
785 # ----------------------------------------------------------------------
792 Get or set the schema's name. (optional)
794 my $schema_name = $schema->name('Foo Database');
799 $self->{'name'} = shift if @_;
800 return $self->{'name'} || '';
803 # ----------------------------------------------------------------------
810 Get the SQL::Translator instance that instantiated the parser.
815 $self->{'translator'} = shift if @_;
816 return $self->{'translator'};
819 # ----------------------------------------------------------------------
822 undef $_ for values %{ $self->{'tables'} };
823 undef $_ for values %{ $self->{'views'} };
828 # ----------------------------------------------------------------------
834 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.