X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FTranslator%2FSchema%2FTable.pm;h=feb0014e403d5e53ad68c93c7be6da982e8ffcbf;hb=5342f5c1a47ee390b226bf3d9b57cdfb8c803b63;hp=9e4e4e216426673d0896e1487827ad86d60edaf2;hpb=65dd38c0dfaf8674117cdd07a943287d736a02af;p=dbsrgits%2FSQL-Translator.git diff --git a/lib/SQL/Translator/Schema/Table.pm b/lib/SQL/Translator/Schema/Table.pm index 9e4e4e2..feb0014 100644 --- a/lib/SQL/Translator/Schema/Table.pm +++ b/lib/SQL/Translator/Schema/Table.pm @@ -1,7 +1,7 @@ package SQL::Translator::Schema::Table; # ---------------------------------------------------------------------- -# $Id: Table.pm,v 1.25 2004-03-23 21:05:20 grommit Exp $ +# $Id: Table.pm,v 1.37 2007-10-24 10:55:44 schiffbruechige Exp $ # ---------------------------------------------------------------------- # Copyright (C) 2002-4 SQLFairy Authors # @@ -40,7 +40,6 @@ C is the table object. =cut use strict; -use Class::Base; use SQL::Translator::Utils 'parse_list_arg'; use SQL::Translator::Schema::Constants; use SQL::Translator::Schema::Constraint; @@ -48,11 +47,11 @@ use SQL::Translator::Schema::Field; use SQL::Translator::Schema::Index; use Data::Dumper; -use base 'Class::Base'; -use vars qw( $VERSION $FIELD_ORDER ); +use base 'SQL::Translator::Schema::Object'; -$VERSION = sprintf "%d.%02d", q$Revision: 1.25 $ =~ /(\d+)\.(\d+)/; +use vars qw( $VERSION $FIELD_ORDER ); +$VERSION = sprintf "%d.%02d", q$Revision: 1.27 $ =~ /(\d+)\.(\d+)/; # Stringify to our name, being careful not to pass any args through so we don't # accidentally set it to undef. We also have to tweak bool so the object is @@ -64,7 +63,8 @@ use overload ; # ---------------------------------------------------------------------- -sub init { + +__PACKAGE__->_attributes( qw/schema name comments options order/ ); =pod @@ -79,16 +79,6 @@ Object constructor. =cut - my ( $self, $config ) = @_; - - for my $arg ( qw[ schema name comments ] ) { - next unless defined $config->{ $arg }; - defined $self->$arg( $config->{ $arg } ) or return; - } - - return $self; -} - # ---------------------------------------------------------------------- sub add_constraint { @@ -122,7 +112,7 @@ C object. my %args = @_; $args{'table'} = $self; $constraint = $constraint_class->new( \%args ) or - return $self->error( $constraint_class->error ); + return $self->error( $constraint_class->error ); } # @@ -133,6 +123,9 @@ C object. my $pk = $self->primary_key; if ( $pk && $constraint->type eq PRIMARY_KEY ) { $self->primary_key( $constraint->fields ); + $pk->name($constraint->name) if $constraint->name; + my %extra = $constraint->extra; + $pk->extra(%extra) if keys %extra; $constraint = $pk; $ok = 0; } @@ -173,6 +166,43 @@ C object. } # ---------------------------------------------------------------------- +sub drop_constraint { + +=pod + +=head2 drop_constraint + +Remove a constraint from the table. Returns the constraint object if the index +was found and removed, an error otherwise. The single parameter can be either +an index name or an C object. + + $table->drop_constraint('myconstraint'); + +=cut + + my $self = shift; + my $constraint_class = 'SQL::Translator::Schema::Constraint'; + my $constraint_name; + + if ( UNIVERSAL::isa( $_[0], $constraint_class ) ) { + $constraint_name = shift->name; + } + else { + $constraint_name = shift; + } + + if ( ! grep { $_->name eq $constraint_name } @ { $self->{'constraints'} } ) { + return $self->error(qq[Can't drop constraint: "$constraint_name" doesn't exist]); + } + + my @cs = @{ $self->{'constraints'} }; + my ($constraint_id) = grep { $cs[$_]->name eq $constraint_name } (0..$#cs); + my $constraint = splice(@{$self->{'constraints'}}, $constraint_id, 1); + + return $constraint; +} + +# ---------------------------------------------------------------------- sub add_index { =pod @@ -207,12 +237,51 @@ C object. $index = $index_class->new( \%args ) or return $self->error( $index_class->error ); } - + foreach my $ex_index ($self->get_indices) { + return if ($ex_index->equals($index)); + } push @{ $self->{'indices'} }, $index; return $index; } # ---------------------------------------------------------------------- +sub drop_index { + +=pod + +=head2 drop_index + +Remove an index from the table. Returns the index object if the index was +found and removed, an error otherwise. The single parameter can be either +an index name of an C object. + + $table->drop_index('myindex'); + +=cut + + my $self = shift; + my $index_class = 'SQL::Translator::Schema::Index'; + my $index_name; + + if ( UNIVERSAL::isa( $_[0], $index_class ) ) { + $index_name = shift->name; + } + else { + $index_name = shift; + } + + if ( ! grep { $_->name eq $index_name } @{ $self->{'indices'} }) { + return $self->error(qq[Can't drop index: "$index_name" doesn't exist]); + } + + my @is = @{ $self->{'indices'} }; + my ($index_id) = grep { $is[$_]->name eq $index_name } (0..$#is); + my $index = splice(@{$self->{'indices'}}, $index_id, 1); + + return $index; +} + +# ---------------------------------------------------------------------- sub add_field { =pod @@ -266,6 +335,58 @@ existing field, you will get an error and the field will not be created. return $field; } +# ---------------------------------------------------------------------- +sub drop_field { + +=pod + +=head2 drop_field + +Remove a field from the table. Returns the field object if the field was +found and removed, an error otherwise. The single parameter can be either +a field name or an C object. + + $table->drop_field('myfield'); + +=cut + + my $self = shift; + my $field_class = 'SQL::Translator::Schema::Field'; + my $field_name; + + if ( UNIVERSAL::isa( $_[0], $field_class ) ) { + $field_name = shift->name; + } + else { + $field_name = shift; + } + my %args = @_; + my $cascade = $args{'cascade'}; + + if ( ! exists $self->{'fields'}{ $field_name } ) { + return $self->error(qq[Can't drop field: "$field_name" doesn't exists]); + } + + my $field = delete $self->{'fields'}{ $field_name }; + + if ( $cascade ) { + # Remove this field from all indices using it + foreach my $i ($self->get_indices()) { + my @fs = $i->fields(); + @fs = grep { $_ ne $field->name } @fs; + $i->fields(@fs); + } + + # Remove this field from all constraints using it + foreach my $c ($self->get_constraints()) { + my @cs = $c->fields(); + @cs = grep { $_ ne $field->name } @cs; + $c->fields(@cs); + } + } + + return $field; +} # ---------------------------------------------------------------------- sub comments { @@ -370,6 +491,14 @@ Returns a field by the name provided. my $self = shift; my $field_name = shift or return $self->error('No field name'); + my $case_insensitive = shift; + if ( $case_insensitive ) { + $field_name = uc($field_name); + foreach my $field ( keys %{$self->{fields}} ) { + return $self->{fields}{$field} if $field_name eq uc($field); + } + return $self->error(qq[Field "$field_name" does not exist]); + } return $self->error( qq[Field "$field_name" does not exist] ) unless exists $self->{'fields'}{ $field_name }; return $self->{'fields'}{ $field_name }; @@ -435,7 +564,9 @@ sub is_trivial_link { =pod -=head2 is_data +=head2 is_trivial_link + +True if table has no data (non-key) fields and only uses single key joins. =cut @@ -469,6 +600,8 @@ sub is_data { =head2 is_data +Returns true if the table has some non-key fields. + =cut my $self = shift; @@ -720,7 +853,7 @@ an array or array reference. push @{ $self->{'options'} }, @$options; if ( ref $self->{'options'} ) { - return wantarray ? @{ $self->{'options'} || [] } : $self->{'options'}; + return wantarray ? @{ $self->{'options'} || [] } : ($self->{'options'} || ''); } else { return wantarray ? () : []; @@ -750,6 +883,217 @@ Get or set the table's order. } # ---------------------------------------------------------------------- +sub field_names { + +=head2 field_names + +Read-only method to return a list or array ref of the field names. Returns undef +or an empty list if the table has no fields set. Usefull if you want to +avoid the overload magic of the Field objects returned by the get_fields method. + + my @names = $constraint->field_names; + +=cut + + my $self = shift; + my @fields = + map { $_->name } + sort { $a->order <=> $b->order } + values %{ $self->{'fields'} || {} }; + + if ( @fields ) { + return wantarray ? @fields : \@fields; + } + else { + $self->error('No fields'); + return wantarray ? () : undef; + } +} + +# ---------------------------------------------------------------------- +sub equals { + +=pod + +=head2 equals + +Determines if this table is the same as another + + my $isIdentical = $table1->equals( $table2 ); + +=cut + + my $self = shift; + my $other = shift; + my $case_insensitive = shift; + + return 0 unless $self->SUPER::equals($other); + return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name; + return 0 unless $self->_compare_objects(scalar $self->options, scalar $other->options); + return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra); + + # Fields + # Go through our fields + my %checkedFields; + foreach my $field ( $self->get_fields ) { + my $otherField = $other->get_field($field->name, $case_insensitive); + return 0 unless $field->equals($otherField, $case_insensitive); + $checkedFields{$field->name} = 1; + } + # Go through the other table's fields + foreach my $otherField ( $other->get_fields ) { + next if $checkedFields{$otherField->name}; + return 0; + } + + # Constraints + # Go through our constraints + my %checkedConstraints; +CONSTRAINT: + foreach my $constraint ( $self->get_constraints ) { + foreach my $otherConstraint ( $other->get_constraints ) { + if ( $constraint->equals($otherConstraint, $case_insensitive) ) { + $checkedConstraints{$otherConstraint} = 1; + next CONSTRAINT; + } + } + return 0; + } + # Go through the other table's constraints +CONSTRAINT2: + foreach my $otherConstraint ( $other->get_constraints ) { + next if $checkedFields{$otherConstraint}; + foreach my $constraint ( $self->get_constraints ) { + if ( $otherConstraint->equals($constraint, $case_insensitive) ) { + next CONSTRAINT2; + } + } + return 0; + } + + # Indices + # Go through our indices + my %checkedIndices; +INDEX: + foreach my $index ( $self->get_indices ) { + foreach my $otherIndex ( $other->get_indices ) { + if ( $index->equals($otherIndex, $case_insensitive) ) { + $checkedIndices{$otherIndex} = 1; + next INDEX; + } + } + return 0; + } + # Go through the other table's indices +INDEX2: + foreach my $otherIndex ( $other->get_indices ) { + next if $checkedIndices{$otherIndex}; + foreach my $index ( $self->get_indices ) { + if ( $otherIndex->equals($index, $case_insensitive) ) { + next INDEX2; + } + } + return 0; + } + + return 1; +} + +# ---------------------------------------------------------------------- + +=head1 LOOKUP METHODS + +The following are a set of shortcut methods for getting commonly used lists of +fields and constraints. They all return lists or array refs of Field or +Constraint objects. + +=over 4 + +=item pkey_fields + +The primary key fields. + +=item fkey_fields + +All foreign key fields. + +=item nonpkey_fields + +All the fields except the primary key. + +=item data_fields + +All non key fields. + +=item unique_fields + +All fields with unique constraints. + +=item unique_constraints + +All this tables unique constraints. + +=item fkey_constraints + +All this tables foreign key constraints. (See primary_key method to get the +primary key constraint) + +=back + +=cut + +sub pkey_fields { + my $me = shift; + my @fields = grep { $_->is_primary_key } $me->get_fields; + return wantarray ? @fields : \@fields; +} + +# ---------------------------------------------------------------------- +sub fkey_fields { + my $me = shift; + my @fields; + push @fields, $_->fields foreach $me->fkey_constraints; + return wantarray ? @fields : \@fields; +} + +# ---------------------------------------------------------------------- +sub nonpkey_fields { + my $me = shift; + my @fields = grep { !$_->is_primary_key } $me->get_fields; + return wantarray ? @fields : \@fields; +} + +# ---------------------------------------------------------------------- +sub data_fields { + my $me = shift; + my @fields = + grep { !$_->is_foreign_key and !$_->is_primary_key } $me->get_fields; + return wantarray ? @fields : \@fields; +} + +# ---------------------------------------------------------------------- +sub unique_fields { + my $me = shift; + my @fields; + push @fields, $_->fields foreach $me->unique_constraints; + return wantarray ? @fields : \@fields; +} + +# ---------------------------------------------------------------------- +sub unique_constraints { + my $me = shift; + my @cons = grep { $_->type eq UNIQUE } $me->get_constraints; + return wantarray ? @cons : \@cons; +} + +# ---------------------------------------------------------------------- +sub fkey_constraints { + my $me = shift; + my @cons = grep { $_->type eq FOREIGN_KEY } $me->get_constraints; + return wantarray ? @cons : \@cons; +} + +# ---------------------------------------------------------------------- sub DESTROY { my $self = shift; undef $self->{'schema'}; # destroy cyclical reference