Mercurial > pub > Impl
view Lib/IMPL/SQL/Schema/Traits/Diff.pm @ 250:129e48bb5afb
DOM refactoring
ObjectToDOM methods are virtual
QueryToDOM uses inflators
Fixed transform for the complex values in the ObjectToDOM
QueryToDOM doesn't allow to use complex values (HASHes) as values for nodes (overpost problem)
author | sergey |
---|---|
date | Wed, 07 Nov 2012 04:17:53 +0400 |
parents | 4d0e1962161c |
children | 77df11605d3a |
line wrap: on
line source
package IMPL::SQL::Schema::Traits::Diff; use strict; use warnings; use IMPL::lang qw(:compare :hash is); use IMPL::SQL::Schema(); use IMPL::SQL::Schema::Traits(); use constant { schema_t => typeof IMPL::SQL::Schema # defining a constant is a good style to enable compile checks }; 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 = { typeof IMPL::SQL::Schema::Constraint::ForeignKey , typeof IMPL::SQL::Schema::Traits::ForeignKey, typeof IMPL::SQL::Schema::Constraint::PrimaryKey , typeof IMPL::SQL::Schema::Traits::PrimaryKey, typeof IMPL::SQL::Schema::Constraint::Unique , typeof IMPL::SQL::Schema::Traits::Unique, typeof IMPL::SQL::Schema::Constraint::Index , typeof IMPL::SQL::Schema::Traits::Index }; my $class = $map->{$constraint->typeof} or die new IMPL::Exception("Can't map the constraint",$constraint->typeof); return $class->new( $constraint->name, [ map $_->name, $constraint->columns ] ) } 1;