Mercurial > pub > Impl
view Lib/IMPL/SQL/Schema/Traits/Diff.pm @ 393:69a1f1508696
minor security refactoring
author | cin |
---|---|
date | Fri, 14 Feb 2014 16:41:12 +0400 |
parents | 77df11605d3a |
children |
line wrap: on
line source
package IMPL::SQL::Schema::Traits::Diff; use strict; use warnings; use IMPL::lang qw(:compare :hash is typeof); use IMPL::SQL::Schema(); use IMPL::SQL::Schema::Traits(); # defining a constant is a good style to enable compile checks use constant { schema_t => 'IMPL::SQL::Schema', ConstraintForeignKey => 'IMPL::SQL::Schema::Constraint::ForeignKey', TraitsForeignKey => 'IMPL::SQL::Schema::Traits::ForeignKey', ConstraintPrimaryKey => 'IMPL::SQL::Schema::Constraint::PrimaryKey', TraitsPrimaryKey => 'IMPL::SQL::Schema::Traits::PrimaryKey', ConstraintUnique => 'IMPL::SQL::Schema::Constraint::Unique', TraitsUnique => 'IMPL::SQL::Schema::Traits::Unique', ConstraintIndex => 'IMPL::SQL::Schema::Constraint::Index', TraitsIndex => 'IMPL::SQL::Schema::Traits::Index' }; sub Diff { my ($self,$src,$dst) = @_; die new IMPL::InvalidArgumentException( src => "A valid source schema is required") unless is($src,schema_t); die new IMPL::InvalidArgumentException( dst => "A valid desctination schema is requried" ) unless is($src,schema_t); my %dstTables = map { $_->name, $_ } $dst->GetTables; my @operations; foreach my $srcTable ( $src->GetTables) { my $dstTable = delete $dstTables{$srcTable->name}; if (not $dstTable) { # if a source table doesn't have a corresponding destination table, it should be deleted push @operations, new IMPL::SQL::Schema::Traits::DropTable($srcTable->name); } else { # a source table needs to be updated push @operations, $self->_DiffTables($srcTable,$dstTable); } } foreach my $tbl ( values %dstTables ) { push @operations, new IMPL::SQL::Schema::Traits::CreateTable( new IMPL::SQL::Schema::Traits::Table( $tbl->name, [ map _Column2Traits($_), @{$tbl->columns} ], [ map _Constraint2Traits($_), $tbl->GetConstraints()], $tbl->{tag} ) ) } return \@operations; } sub _DiffTables { my ($self,$src,$dst) = @_; my @dropConstraints; my @createConstraints; my %srcConstraints = map { $_->name, $_ } $src->GetConstraints(); my %dstConstraints = map { $_->name, $_ } $dst->GetConstraints(); foreach my $cnSrcName (keys %srcConstraints) { if ( my $cnDst = delete $dstConstraints{$cnSrcName} ) { unless ( $srcConstraints{$cnSrcName}->SameValue($cnDst) ) { push @dropConstraints, new IMPL::SQL::Schema::Traits::AlterTableDropConstraint( $src->name, $cnSrcName ); push @createConstraints, new IMPL::SQL::Schema::Traits::AlterTableAddConstraint( $dst->name, _Constraint2Traits($cnDst) ); } } else { push @dropConstraints,new IMPL::SQL::Schema::Traits::AlterTableDropConstraint( $src->name, $cnSrcName ); } } foreach my $cnDst (values %dstConstraints) { push @createConstraints, IMPL::SQL::Schema::Traits::AlterTableAddConstraint->new( $dst->name, _Constraint2Traits($cnDst) ); } my @deleteColumns; my @addColumns; my @updateColumns; my %dstColumnIndexes = map { my $col = $dst->GetColumnAt($_); ($col->name, { column => $col, index => $_ }) } 0 .. $dst->ColumnsCount-1; my @columns; # remove old columns, mark for update changed columns for( my $i=0; $i < $src->ColumnsCount; $i++) { my $colSrc = $src->GetColumnAt($i); if ( my $infoDst = delete $dstColumnIndexes{$colSrc->name} ) { $infoDst->{prevColumn} = $colSrc; push @columns,$infoDst; } else { push @deleteColumns,new IMPL::SQL::Schema::Traits::AlterTableDropColumn($src->name,$colSrc->name); } } #insert new columns at specified positions foreach ( sort { $a->{index} <=> $b->{index} } values %dstColumnIndexes ) { splice(@columns,$_->{index},0,$_); push @addColumns, new IMPL::SQL::Schema::Traits::AlterTableAddColumn($src->name, _Column2Traits( $_->{column}, position => $_->{index} )); } # remember old indexes for(my $i =0; $i< @columns; $i ++) { $columns[$i]->{prevIndex} = $i; } # reorder columns @columns = sort { $a->{index} <=> $b->{index} } @columns; foreach my $info (@columns) { if ($info->{prevColumn} && ( !$info->{column}->SameValue($info->{prevColumn}) or $info->{index}!= $info->{prevIndex} ) ) { my $op = new IMPL::SQL::Schema::Traits::AlterTableChangeColumn($src->name,$info->{column}->name); $op->position( $info->{index} ) unless $info->{prevIndex} == $info->{index}; $op->isNullable( $info->{column}->isNullable ) unless equals($info->{column}->isNullable,$info->{prevColumn}->isNullable); $op->defaultValue( $info->{column}->defaultValue ) unless equals($info->{column}->defaultValue, $info->{prevColumn}->defaultValue); my $diff = hashDiff($info->{prevColumn}->tag,$info->{column}->tag); $op->options($diff) if %$diff; push @updateColumns, $op; } } my @result = (@dropConstraints, @deleteColumns, @addColumns, @updateColumns, @createConstraints); return @result; } sub _Column2Traits { my ($column,%options) = @_; return new IMPL::SQL::Schema::Traits::Column( $column->name, $column->type, isNullable => $column->isNullable, defaultValue => $column->defaultValue, tag => $column->tag, %options ); } sub _Constraint2Traits { my ($constraint) = @_; my $map = { ConstraintForeignKey , TraitsForeignKey, ConstraintPrimaryKey , TraitsPrimaryKey, ConstraintUnique , TraitsUnique, ConstraintIndex , TraitsIndex }; my $class = $map->{typeof($constraint)} or die new IMPL::Exception("Can't map the constraint",typeof($constraint)); return $class->new( $constraint->name, [ map $_->name, $constraint->columns ] ) } 1;