diff Lib/IMPL/SQL/Schema/Traits/Diff.pm @ 168:6148f89bb7bf

IMPL::SQL::Schema::Traits::Diff alfa version IMPL::lang added hash traits
author sourcer
date Mon, 16 May 2011 04:30:38 +0400
parents 1f7a6d762394
children fd92830036c3
line wrap: on
line diff
--- a/Lib/IMPL/SQL/Schema/Traits/Diff.pm	Thu May 12 08:57:19 2011 +0400
+++ b/Lib/IMPL/SQL/Schema/Traits/Diff.pm	Mon May 16 04:30:38 2011 +0400
@@ -1,7 +1,7 @@
 package IMPL::SQL::Schema::Traits::Diff;
 use strict;
 use warnings;
-use IMPL::lang;
+use IMPL::lang qw(:compare :hash is);
 
 use IMPL::SQL::Schema();
 use IMPL::SQL::Schema::Traits();
@@ -25,24 +25,26 @@
 		
 		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()
+			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->constraints],
-					$tbl->{tag}
-				)
+	}
+	
+	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 {
@@ -58,12 +60,12 @@
 		if ( my $cnDst = delete $dstConstraints{$cnSrcName} ) {
 			unless ( $srcConstraints{$cnSrcName}->SameValue($cnDst) ) {
 				push @dropConstraints,
-					IMPL::SQL::Schema::Traits::AlterTableDropConstraint->new( $src->name, $cnSrcName );
+					new IMPL::SQL::Schema::Traits::AlterTableDropConstraint( $src->name, $cnSrcName );
 				push @createConstraints,
-					IMPL::SQL::Schema::Traits::AlterTableAddConstraint->new( $dst->name, _Constraint2Traits($cnDst) );
+					new IMPL::SQL::Schema::Traits::AlterTableAddConstraint( $dst->name, _Constraint2Traits($cnDst) );
 			}
 		} else {
-			push @dropConstraints, IMPL::SQL::Schema::Traits::AlterTableDropConstrait->new( $src->name, $cnSrcName );
+			push @dropConstraints,new IMPL::SQL::Schema::Traits::AlterTableDropConstrait( $src->name, $cnSrcName );
 		}
 	}
 	
@@ -81,52 +83,82 @@
 		($col->name, { column => $col, index => $_ })
 	} 0 .. $dst->ColumnsCount-1;
 	
-	# get changed and
-	
 	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->{update} = 1 unless $infoDst->{column}->SameValue($colSrc);
+			$infoDst->{prevColumn} = $colSrc;
 			push @columns,$infoDst;
 		} else {
-			push @deleteColumns, IMPL::SQL::Schema::Traits::AlterTableDropColumn($src->name,$colSrc->name);
+			push @deleteColumns,new IMPL::SQL::Schema::Traits::AlterTableDropColumn($src->name,$colSrc->name);
 		}
 	}
 	
-	splice(@columns,$_->{index},0,$_) foreach ( sort { $a->{index} <=> $b->{index} } values %dstColumnIndexes );
+	#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;
 	}
 	
-	# determine constraints to be dropped,
-	# drop columns
-	# create columns
-	# update/reorder columns
-	# create constraints
+	# 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},$info->{column});
+			$op->options(\%diff) if %diff;
+			
+			push @updateColumns, $op;
+		}
+	}
+	
+	my @result = (@dropConstraints, @deleteColumns, @addColumns, @updateColumns, @createConstraints); 
+	
+	return @result;
 }
 
 sub _Column2Traits {
-	my ($column) = @_;
+	my ($column,%options) = @_;
 	
-	return new IMPL::SQL::Schema::Traits::Columns(
+	return new IMPL::SQL::Schema::Traits::Column(
 		$column->name,
 		$column->type,
-		$column->isNullable,
-		$column->defaultValue,
-		$column->tag
+		isNullable => $column->isNullable,
+		defaultValue => $column->defaultValue,
+		tag => $column->tag,
+		%options
 	);
 }
 
 sub _Constraint2Traits {
 	my ($constraint) = @_;
 	
-	return new IMPL::SQL::Schema::Traits::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, $_->columns ]
+		[ map $_->name, $constraint->columns ]
 	)
 }