view Lib/IMPL/SQL/Schema/Traits/Diff.pm @ 167:1f7a6d762394

SQL schema in progress
author sourcer
date Thu, 12 May 2011 08:57:19 +0400
parents 76515373dac0
children 6148f89bb7bf
line wrap: on
line source

package IMPL::SQL::Schema::Traits::Diff;
use strict;
use warnings;
use IMPL::lang;

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()
		} 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->constraints],
					$tbl->{tag}
				)
			)
		}
	
	}  
}

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,
					IMPL::SQL::Schema::Traits::AlterTableDropConstraint->new( $src->name, $cnSrcName );
				push @createConstraints,
					IMPL::SQL::Schema::Traits::AlterTableAddConstraint->new( $dst->name, _Constraint2Traits($cnDst) );
			}
		} else {
			push @dropConstraints, IMPL::SQL::Schema::Traits::AlterTableDropConstrait->new( $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;
	
	# get changed and
	
	my @columns;
	
	for( my $i=0; $i < $src->ColumnsCount; $i++) {
		my $colSrc = $src->GetColumnAt($i);
		
		if ( my $infoDst = delete $dstColumnIndexes{$colSrc->name} ) {
			$infoDst->{update} = 1 unless $infoDst->{column}->SameValue($colSrc);
			push @columns,$infoDst;
		} else {
			push @deleteColumns, IMPL::SQL::Schema::Traits::AlterTableDropColumn($src->name,$colSrc->name);
		}
	}
	
	splice(@columns,$_->{index},0,$_) foreach ( sort { $a->{index} <=> $b->{index} } values %dstColumnIndexes );
	
	for(my $i =0; $i< @columns; $i ++) {
		
	}
	
	# determine constraints to be dropped,
	# drop columns
	# create columns
	# update/reorder columns
	# create constraints
}

sub _Column2Traits {
	my ($column) = @_;
	
	return new IMPL::SQL::Schema::Traits::Columns(
		$column->name,
		$column->type,
		$column->isNullable,
		$column->defaultValue,
		$column->tag
	);
}

sub _Constraint2Traits {
	my ($constraint) = @_;
	
	return new IMPL::SQL::Schema::Traits::Constraint(
		$constraint->name,
		[ map $_->name, $_->columns ]
	)
}

1;