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';
40 SQL::Translator::Schema::Role::Extra
41 SQL::Translator::Schema::Role::Error
42 SQL::Translator::Schema::Role::Compare
45 our $VERSION = '1.59';
48 has _order => (is => 'ro', default => sub { +{ map { $_ => 0 } qw/
56 # FIXME - to be removed, together with the SQL::Translator::Schema::Graph* stuff
57 # looks like a remnant of the Turnkey project integration back in 2003-4
58 # Appears to be quite dead
61 eval { require Class::MakeMethods }
62 or croak 'You need to install the CPAN dependency Class::MakeMethods to use as_graph()';
64 require SQL::Translator::Schema::Graph;
68 return SQL::Translator::Schema::Graph->new(
69 translator => $self->translator );
78 Returns a Graph::Directed object with the table names for nodes.
82 require Graph::Directed;
85 my $g = Graph::Directed->new;
87 for my $table ( $self->get_tables ) {
88 my $tname = $table->name;
89 $g->add_vertex( $tname );
91 for my $field ( $table->get_fields ) {
92 if ( $field->is_foreign_key ) {
93 my $fktable = $field->foreign_key_reference->reference_table;
95 $g->add_edge( $fktable, $tname );
103 has _tables => ( is => 'ro', init_arg => undef, default => sub { +{} } );
111 Add a table object. Returns the new SQL::Translator::Schema::Table object.
112 The "name" parameter is required. If you try to create a table with the
113 same name as an existing table, you will get an error and the table will
116 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
117 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
118 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
123 my $table_class = 'SQL::Translator::Schema::Table';
126 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
128 $table->schema($self);
131 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
132 $args{'schema'} = $self;
133 $table = $table_class->new( \%args )
134 or return $self->error( $table_class->error );
137 $table->order( ++$self->_order->{table} );
139 # We know we have a name as the Table->new above errors if none given.
140 my $table_name = $table->name;
142 if ( defined $self->_tables->{$table_name} ) {
143 return $self->error(qq[Can't use table name "$table_name": table exists]);
146 $self->_tables->{$table_name} = $table;
158 Remove a table from the schema. Returns the table object if the table was found
159 and removed, an error otherwise. The single parameter can be either a table
160 name or an C<SQL::Translator::Schema::Table> object. The "cascade" parameter
161 can be set to 1 to also drop all triggers on the table, default is 0.
163 $schema->drop_table('mytable');
164 $schema->drop_table('mytable', cascade => 1);
169 my $table_class = 'SQL::Translator::Schema::Table';
172 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
173 $table_name = shift->name;
179 my $cascade = $args{'cascade'};
181 if ( !exists $self->_tables->{$table_name} ) {
182 return $self->error(qq[Can't drop table: $table_name" doesn't exist]);
185 my $table = delete $self->_tables->{$table_name};
189 # Drop all triggers on this table
190 $self->drop_trigger()
191 for ( grep { $_->on_table eq $table_name } values %{ $self->_triggers } );
196 has _procedures => ( is => 'ro', init_arg => undef, default => sub { +{} } );
204 Add a procedure object. Returns the new SQL::Translator::Schema::Procedure
205 object. The "name" parameter is required. If you try to create a procedure
206 with the same name as an existing procedure, you will get an error and the
207 procedure will not be created.
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;
216 my $procedure_class = 'SQL::Translator::Schema::Procedure';
219 if ( UNIVERSAL::isa( $_[0], $procedure_class ) ) {
221 $procedure->schema($self);
224 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
225 $args{'schema'} = $self;
226 return $self->error('No procedure name') unless $args{'name'};
227 $procedure = $procedure_class->new( \%args )
228 or return $self->error( $procedure_class->error );
231 $procedure->order( ++$self->_order->{proc} );
232 my $procedure_name = $procedure->name
233 or return $self->error('No procedure name');
235 if ( defined $self->_procedures->{$procedure_name} ) {
237 qq[Can't create procedure: "$procedure_name" exists] );
240 $self->_procedures->{$procedure_name} = $procedure;
250 =head2 drop_procedure
252 Remove a procedure from the schema. Returns the procedure object if the
253 procedure was found and removed, an error otherwise. The single parameter
254 can be either a procedure name or an C<SQL::Translator::Schema::Procedure>
257 $schema->drop_procedure('myprocedure');
262 my $proc_class = 'SQL::Translator::Schema::Procedure';
265 if ( UNIVERSAL::isa( $_[0], $proc_class ) ) {
266 $proc_name = shift->name;
272 if ( !exists $self->_procedures->{$proc_name} ) {
274 qq[Can't drop procedure: $proc_name" doesn't exist]);
277 my $proc = delete $self->_procedures->{$proc_name};
282 has _triggers => ( is => 'ro', init_arg => undef, default => sub { +{} } );
290 Add a trigger object. Returns the new SQL::Translator::Schema::Trigger object.
291 The "name" parameter is required. If you try to create a trigger with the
292 same name as an existing trigger, you will get an error and the trigger will
295 my $t1 = $schema->add_trigger( name => 'foo' );
296 my $t2 = SQL::Translator::Schema::Trigger->new( name => 'bar' );
297 $t2 = $schema->add_trigger( $trigger_bar ) or die $schema->error;
302 my $trigger_class = 'SQL::Translator::Schema::Trigger';
305 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
307 $trigger->schema($self);
310 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
311 $args{'schema'} = $self;
312 return $self->error('No trigger name') unless $args{'name'};
313 $trigger = $trigger_class->new( \%args )
314 or return $self->error( $trigger_class->error );
317 $trigger->order( ++$self->_order->{trigger} );
319 my $trigger_name = $trigger->name or return $self->error('No trigger name');
320 if ( defined $self->_triggers->{$trigger_name} ) {
321 return $self->error(qq[Can't create trigger: "$trigger_name" exists]);
324 $self->_triggers->{$trigger_name} = $trigger;
336 Remove a trigger from the schema. Returns the trigger object if the trigger was
337 found and removed, an error otherwise. The single parameter can be either a
338 trigger name or an C<SQL::Translator::Schema::Trigger> object.
340 $schema->drop_trigger('mytrigger');
345 my $trigger_class = 'SQL::Translator::Schema::Trigger';
348 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
349 $trigger_name = shift->name;
352 $trigger_name = shift;
355 if ( !exists $self->_triggers->{$trigger_name} ) {
357 qq[Can't drop trigger: $trigger_name" doesn't exist]);
360 my $trigger = delete $self->_triggers->{$trigger_name};
365 has _views => ( is => 'ro', init_arg => undef, default => sub { +{} } );
373 Add a view object. Returns the new SQL::Translator::Schema::View object.
374 The "name" parameter is required. If you try to create a view with the
375 same name as an existing view, you will get an error and the view will
378 my $v1 = $schema->add_view( name => 'foo' );
379 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
380 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
385 my $view_class = 'SQL::Translator::Schema::View';
388 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
390 $view->schema($self);
393 my %args = ref $_[0] eq 'HASH' ? %{ $_[0] } : @_;
394 $args{'schema'} = $self;
395 return $self->error('No view name') unless $args{'name'};
396 $view = $view_class->new( \%args ) or return $view_class->error;
399 $view->order( ++$self->_order->{view} );
400 my $view_name = $view->name or return $self->error('No view name');
402 if ( defined $self->_views->{$view_name} ) {
403 return $self->error(qq[Can't create view: "$view_name" exists]);
406 $self->_views->{$view_name} = $view;
418 Remove a view from the schema. Returns the view object if the view was found
419 and removed, an error otherwise. The single parameter can be either a view
420 name or an C<SQL::Translator::Schema::View> object.
422 $schema->drop_view('myview');
427 my $view_class = 'SQL::Translator::Schema::View';
430 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
431 $view_name = shift->name;
437 if ( !exists $self->_views->{$view_name} ) {
438 return $self->error(qq[Can't drop view: $view_name" doesn't exist]);
441 my $view = delete $self->_views->{$view_name};
448 Get or set the schema's database. (optional)
450 my $database = $schema->database('PostgreSQL');
454 has database => ( is => 'rw', default => sub { '' } );
462 Returns true if all the tables and views are valid.
464 my $ok = $schema->is_valid or die $schema->error;
470 return $self->error('No tables') unless $self->get_tables;
472 for my $object ( $self->get_tables, $self->get_views ) {
473 return $object->error unless $object->is_valid;
485 Returns a procedure by the name provided.
487 my $procedure = $schema->get_procedure('foo');
492 my $procedure_name = shift or return $self->error('No procedure name');
493 return $self->error(qq[Table "$procedure_name" does not exist])
494 unless exists $self->_procedures->{$procedure_name};
495 return $self->_procedures->{$procedure_name};
502 =head2 get_procedures
504 Returns all the procedures as an array or array reference.
506 my @procedures = $schema->get_procedures;
513 sort { $a->[0] <=> $b->[0] }
514 map { [ $_->order, $_ ] } values %{ $self->_procedures };
517 return wantarray ? @procedures : \@procedures;
520 $self->error('No procedures');
521 return wantarray ? () : undef;
531 Returns a table by the name provided.
533 my $table = $schema->get_table('foo');
538 my $table_name = shift or return $self->error('No table name');
539 my $case_insensitive = shift;
540 if ( $case_insensitive ) {
541 $table_name = uc($table_name);
542 foreach my $table ( keys %{$self->_tables} ) {
543 return $self->_tables->{$table} if $table_name eq uc($table);
545 return $self->error(qq[Table "$table_name" does not exist]);
547 return $self->error(qq[Table "$table_name" does not exist])
548 unless exists $self->_tables->{$table_name};
549 return $self->_tables->{$table_name};
558 Returns all the tables as an array or array reference.
560 my @tables = $schema->get_tables;
567 sort { $a->[0] <=> $b->[0] }
568 map { [ $_->order, $_ ] } values %{ $self->_tables };
571 return wantarray ? @tables : \@tables;
574 $self->error('No tables');
575 return wantarray ? () : undef;
585 Returns a trigger by the name provided.
587 my $trigger = $schema->get_trigger('foo');
592 my $trigger_name = shift or return $self->error('No trigger name');
593 return $self->error(qq[Table "$trigger_name" does not exist])
594 unless exists $self->_triggers->{$trigger_name};
595 return $self->_triggers->{$trigger_name};
604 Returns all the triggers as an array or array reference.
606 my @triggers = $schema->get_triggers;
613 sort { $a->[0] <=> $b->[0] }
614 map { [ $_->order, $_ ] } values %{ $self->_triggers };
617 return wantarray ? @triggers : \@triggers;
620 $self->error('No triggers');
621 return wantarray ? () : undef;
631 Returns a view by the name provided.
633 my $view = $schema->get_view('foo');
638 my $view_name = shift or return $self->error('No view name');
639 return $self->error('View "$view_name" does not exist')
640 unless exists $self->_views->{$view_name};
641 return $self->_views->{$view_name};
650 Returns all the views as an array or array reference.
652 my @views = $schema->get_views;
659 sort { $a->[0] <=> $b->[0] }
660 map { [ $_->order, $_ ] } values %{ $self->_views };
663 return wantarray ? @views : \@views;
666 $self->error('No views');
667 return wantarray ? () : undef;
671 sub make_natural_joins {
675 =head2 make_natural_joins
677 Creates foriegn key relationships among like-named fields in different
678 tables. Accepts the following arguments:
684 A True or False argument which determins whether or not to perform
685 the joins from primary keys to fields of the same name in other tables
689 A list of fields to skip in the joins
693 $schema->make_natural_joins(
695 skip_fields => 'name,department_id',
702 my $join_pk_only = $args{'join_pk_only'} || 0;
704 map { s/^\s+|\s+$//g; $_, 1 } @{ parse_list_arg( $args{'skip_fields'} ) };
706 my ( %common_keys, %pk );
707 for my $table ( $self->get_tables ) {
708 for my $field ( $table->get_fields ) {
709 my $field_name = $field->name or next;
710 next if $skip_fields{$field_name};
711 $pk{$field_name} = 1 if $field->is_primary_key;
712 push @{ $common_keys{$field_name} }, $table->name;
716 for my $field ( keys %common_keys ) {
717 next if $join_pk_only and !defined $pk{$field};
719 my @table_names = @{ $common_keys{$field} };
720 next unless scalar @table_names > 1;
722 for my $i ( 0 .. $#table_names ) {
723 my $table1 = $self->get_table( $table_names[$i] ) or next;
725 for my $j ( 1 .. $#table_names ) {
726 my $table2 = $self->get_table( $table_names[$j] ) or next;
727 next if $table1->name eq $table2->name;
729 $table1->add_constraint(
732 reference_table => $table2->name,
733 reference_fields => $field,
744 Get or set the schema's name. (optional)
746 my $schema_name = $schema->name('Foo Database');
750 has name => ( is => 'rw', default => sub { '' } );
756 Get the SQL::Translator instance that instantiated the parser.
760 has translator => ( is => 'rw', weak_ref => 1 );
768 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.