1 package SQL::Translator::Schema;
7 SQL::Translator::Schema - SQL::Translator schema object
11 use SQL::Translator::Schema;
12 my $schema = SQL::Translator::Schema->new(
16 my $table = $schema->add_table( name => 'foo' );
17 my $view = $schema->add_view( name => 'bar', sql => '...' );
22 C<SQL::Translator::Schema> is the object that accepts, validates, and
23 returns the database structure.
30 use SQL::Translator::Schema::Constants;
31 use SQL::Translator::Schema::Procedure;
32 use SQL::Translator::Schema::Table;
33 use SQL::Translator::Schema::Trigger;
34 use SQL::Translator::Schema::View;
36 use SQL::Translator::Utils 'parse_list_arg';
38 use base 'SQL::Translator::Schema::Object';
39 use vars qw[ $VERSION ];
43 __PACKAGE__->_attributes(qw/name database translator/);
47 my $self = $class->SUPER::new (@_)
50 $self->{_order} = { map { $_ => 0 } qw/
66 Returns the schema as an L<SQL::Translator::Schema::Graph> object.
69 require SQL::Translator::Schema::Graph;
73 return SQL::Translator::Schema::Graph->new(
74 translator => $self->translator );
83 Returns a Graph::Directed object with the table names for nodes.
87 require Graph::Directed;
90 my $g = Graph::Directed->new;
92 for my $table ( $self->get_tables ) {
93 my $tname = $table->name;
94 $g->add_vertex( $tname );
96 for my $field ( $table->get_fields ) {
97 if ( $field->is_foreign_key ) {
98 my $fktable = $field->foreign_key_reference->reference_table;
100 $g->add_edge( $fktable, $tname );
114 Add a table object. Returns the new SQL::Translator::Schema::Table object.
115 The "name" parameter is required. If you try to create a table with the
116 same name as an existing table, you will get an error and the table will
119 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
120 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
121 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
126 my $table_class = 'SQL::Translator::Schema::Table';
129 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
131 $table->schema($self);
134 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
135 $args{'schema'} = $self;
136 $table = $table_class->new( \%args )
137 or return $self->error( $table_class->error );
140 $table->order( ++$self->{_order}{table} );
142 # We know we have a name as the Table->new above errors if none given.
143 my $table_name = $table->name;
145 if ( defined $self->{'tables'}{$table_name} ) {
146 return $self->error(qq[Can't create table: "$table_name" exists]);
149 $self->{'tables'}{$table_name} = $table;
161 Remove a table from the schema. Returns the table object if the table was found
162 and removed, an error otherwise. The single parameter can be either a table
163 name or an C<SQL::Translator::Schema::Table> object. The "cascade" parameter
164 can be set to 1 to also drop all triggers on the table, default is 0.
166 $schema->drop_table('mytable');
167 $schema->drop_table('mytable', cascade => 1);
172 my $table_class = 'SQL::Translator::Schema::Table';
175 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
176 $table_name = shift->name;
182 my $cascade = $args{'cascade'};
184 if ( !exists $self->{'tables'}{$table_name} ) {
185 return $self->error(qq[Can't drop table: $table_name" doesn't exist]);
188 my $table = delete $self->{'tables'}{$table_name};
192 # Drop all triggers on this table
193 $self->drop_trigger()
194 for ( grep { $_->on_table eq $table_name } @{ $self->{'triggers'} } );
205 Add a procedure object. Returns the new SQL::Translator::Schema::Procedure
206 object. The "name" parameter is required. If you try to create a procedure
207 with the same name as an existing procedure, you will get an error and the
208 procedure will not be created.
210 my $p1 = $schema->add_procedure( name => 'foo' );
211 my $p2 = SQL::Translator::Schema::Procedure->new( name => 'bar' );
212 $p2 = $schema->add_procedure( $procedure_bar ) or die $schema->error;
217 my $procedure_class = 'SQL::Translator::Schema::Procedure';
220 if ( UNIVERSAL::isa( $_[0], $procedure_class ) ) {
222 $procedure->schema($self);
225 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
226 $args{'schema'} = $self;
227 return $self->error('No procedure name') unless $args{'name'};
228 $procedure = $procedure_class->new( \%args )
229 or return $self->error( $procedure_class->error );
232 $procedure->order( ++$self->{_order}{proc} );
233 my $procedure_name = $procedure->name
234 or return $self->error('No procedure name');
236 if ( defined $self->{'procedures'}{$procedure_name} ) {
238 qq[Can't create procedure: "$procedure_name" exists] );
241 $self->{'procedures'}{$procedure_name} = $procedure;
251 =head2 drop_procedure
253 Remove a procedure from the schema. Returns the procedure object if the
254 procedure was found and removed, an error otherwise. The single parameter
255 can be either a procedure name or an C<SQL::Translator::Schema::Procedure>
258 $schema->drop_procedure('myprocedure');
263 my $proc_class = 'SQL::Translator::Schema::Procedure';
266 if ( UNIVERSAL::isa( $_[0], $proc_class ) ) {
267 $proc_name = shift->name;
273 if ( !exists $self->{'procedures'}{$proc_name} ) {
275 qq[Can't drop procedure: $proc_name" doesn't exist]);
278 my $proc = delete $self->{'procedures'}{$proc_name};
289 Add a trigger object. Returns the new SQL::Translator::Schema::Trigger object.
290 The "name" parameter is required. If you try to create a trigger with the
291 same name as an existing trigger, you will get an error and the trigger will
294 my $t1 = $schema->add_trigger( name => 'foo' );
295 my $t2 = SQL::Translator::Schema::Trigger->new( name => 'bar' );
296 $t2 = $schema->add_trigger( $trigger_bar ) or die $schema->error;
301 my $trigger_class = 'SQL::Translator::Schema::Trigger';
304 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
306 $trigger->schema($self);
309 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
310 $args{'schema'} = $self;
311 return $self->error('No trigger name') unless $args{'name'};
312 $trigger = $trigger_class->new( \%args )
313 or return $self->error( $trigger_class->error );
316 $trigger->order( ++$self->{_order}{trigger} );
318 my $trigger_name = $trigger->name or return $self->error('No trigger name');
319 if ( defined $self->{'triggers'}{$trigger_name} ) {
320 return $self->error(qq[Can't create trigger: "$trigger_name" exists]);
323 $self->{'triggers'}{$trigger_name} = $trigger;
335 Remove a trigger from the schema. Returns the trigger object if the trigger was
336 found and removed, an error otherwise. The single parameter can be either a
337 trigger name or an C<SQL::Translator::Schema::Trigger> object.
339 $schema->drop_trigger('mytrigger');
344 my $trigger_class = 'SQL::Translator::Schema::Trigger';
347 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
348 $trigger_name = shift->name;
351 $trigger_name = shift;
354 if ( !exists $self->{'triggers'}{$trigger_name} ) {
356 qq[Can't drop trigger: $trigger_name" doesn't exist]);
359 my $trigger = delete $self->{'triggers'}{$trigger_name};
370 Add a view object. Returns the new SQL::Translator::Schema::View object.
371 The "name" parameter is required. If you try to create a view with the
372 same name as an existing view, you will get an error and the view will
375 my $v1 = $schema->add_view( name => 'foo' );
376 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
377 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
382 my $view_class = 'SQL::Translator::Schema::View';
385 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
387 $view->schema($self);
390 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
391 $args{'schema'} = $self;
392 return $self->error('No view name') unless $args{'name'};
393 $view = $view_class->new( \%args ) or return $view_class->error;
396 $view->order( ++$self->{_order}{view} );
397 my $view_name = $view->name or return $self->error('No view name');
399 if ( defined $self->{'views'}{$view_name} ) {
400 return $self->error(qq[Can't create view: "$view_name" exists]);
403 $self->{'views'}{$view_name} = $view;
415 Remove a view from the schema. Returns the view object if the view was found
416 and removed, an error otherwise. The single parameter can be either a view
417 name or an C<SQL::Translator::Schema::View> object.
419 $schema->drop_view('myview');
424 my $view_class = 'SQL::Translator::Schema::View';
427 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
428 $view_name = shift->name;
434 if ( !exists $self->{'views'}{$view_name} ) {
435 return $self->error(qq[Can't drop view: $view_name" doesn't exist]);
438 my $view = delete $self->{'views'}{$view_name};
449 Get or set the schema's database. (optional)
451 my $database = $schema->database('PostgreSQL');
456 $self->{'database'} = shift if @_;
457 return $self->{'database'} || '';
466 Returns true if all the tables and views are valid.
468 my $ok = $schema->is_valid or die $schema->error;
474 return $self->error('No tables') unless $self->get_tables;
476 for my $object ( $self->get_tables, $self->get_views ) {
477 return $object->error unless $object->is_valid;
489 Returns a procedure by the name provided.
491 my $procedure = $schema->get_procedure('foo');
496 my $procedure_name = shift or return $self->error('No procedure name');
497 return $self->error(qq[Table "$procedure_name" does not exist])
498 unless exists $self->{'procedures'}{$procedure_name};
499 return $self->{'procedures'}{$procedure_name};
506 =head2 get_procedures
508 Returns all the procedures as an array or array reference.
510 my @procedures = $schema->get_procedures;
517 sort { $a->[0] <=> $b->[0] }
518 map { [ $_->order, $_ ] } values %{ $self->{'procedures'} };
521 return wantarray ? @procedures : \@procedures;
524 $self->error('No procedures');
525 return wantarray ? () : undef;
535 Returns a table by the name provided.
537 my $table = $schema->get_table('foo');
542 my $table_name = shift or return $self->error('No table name');
543 my $case_insensitive = shift;
544 if ( $case_insensitive ) {
545 $table_name = uc($table_name);
546 foreach my $table ( keys %{$self->{tables}} ) {
547 return $self->{tables}{$table} if $table_name eq uc($table);
549 return $self->error(qq[Table "$table_name" does not exist]);
551 return $self->error(qq[Table "$table_name" does not exist])
552 unless exists $self->{'tables'}{$table_name};
553 return $self->{'tables'}{$table_name};
562 Returns all the tables as an array or array reference.
564 my @tables = $schema->get_tables;
571 sort { $a->[0] <=> $b->[0] }
572 map { [ $_->order, $_ ] } values %{ $self->{'tables'} };
575 return wantarray ? @tables : \@tables;
578 $self->error('No tables');
579 return wantarray ? () : undef;
589 Returns a trigger by the name provided.
591 my $trigger = $schema->get_trigger('foo');
596 my $trigger_name = shift or return $self->error('No trigger name');
597 return $self->error(qq[Table "$trigger_name" does not exist])
598 unless exists $self->{'triggers'}{$trigger_name};
599 return $self->{'triggers'}{$trigger_name};
608 Returns all the triggers as an array or array reference.
610 my @triggers = $schema->get_triggers;
617 sort { $a->[0] <=> $b->[0] }
618 map { [ $_->order, $_ ] } values %{ $self->{'triggers'} };
621 return wantarray ? @triggers : \@triggers;
624 $self->error('No triggers');
625 return wantarray ? () : undef;
635 Returns a view by the name provided.
637 my $view = $schema->get_view('foo');
642 my $view_name = shift or return $self->error('No view name');
643 return $self->error('View "$view_name" does not exist')
644 unless exists $self->{'views'}{$view_name};
645 return $self->{'views'}{$view_name};
654 Returns all the views as an array or array reference.
656 my @views = $schema->get_views;
663 sort { $a->[0] <=> $b->[0] }
664 map { [ $_->order, $_ ] } values %{ $self->{'views'} };
667 return wantarray ? @views : \@views;
670 $self->error('No views');
671 return wantarray ? () : undef;
675 sub make_natural_joins {
679 =head2 make_natural_joins
681 Creates foriegn key relationships among like-named fields in different
682 tables. Accepts the following arguments:
688 A True or False argument which determins whether or not to perform
689 the joins from primary keys to fields of the same name in other tables
693 A list of fields to skip in the joins
697 $schema->make_natural_joins(
699 skip_fields => 'name,department_id',
706 my $join_pk_only = $args{'join_pk_only'} || 0;
708 map { s/^\s+|\s+$//g; $_, 1 } @{ parse_list_arg( $args{'skip_fields'} ) };
710 my ( %common_keys, %pk );
711 for my $table ( $self->get_tables ) {
712 for my $field ( $table->get_fields ) {
713 my $field_name = $field->name or next;
714 next if $skip_fields{$field_name};
715 $pk{$field_name} = 1 if $field->is_primary_key;
716 push @{ $common_keys{$field_name} }, $table->name;
720 for my $field ( keys %common_keys ) {
721 next if $join_pk_only and !defined $pk{$field};
723 my @table_names = @{ $common_keys{$field} };
724 next unless scalar @table_names > 1;
726 for my $i ( 0 .. $#table_names ) {
727 my $table1 = $self->get_table( $table_names[$i] ) or next;
729 for my $j ( 1 .. $#table_names ) {
730 my $table2 = $self->get_table( $table_names[$j] ) or next;
731 next if $table1->name eq $table2->name;
733 $table1->add_constraint(
736 reference_table => $table2->name,
737 reference_fields => $field,
752 Get or set the schema's name. (optional)
754 my $schema_name = $schema->name('Foo Database');
759 $self->{'name'} = shift if @_;
760 return $self->{'name'} || '';
769 Get the SQL::Translator instance that instantiated the parser.
774 $self->{'translator'} = shift if @_;
775 return $self->{'translator'};
780 undef $_ for values %{ $self->{'tables'} };
781 undef $_ for values %{ $self->{'views'} };
790 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.