From: skaufman Date: Wed, 27 May 2015 14:47:19 +0000 (+0000) Subject: Merge branch 'people/TBSliver/source_order_fix' X-Git-Tag: v1.001_031~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=9a6169dede3a03778458e7d0bc2c7fb0ad5cc8f0;hp=46f9b0e519aa9bc57d4e9c13a8effeb419f746fd;p=dbsrgits%2FDBIx-Class-Fixtures.git Merge branch 'people/TBSliver/source_order_fix' --- diff --git a/lib/DBIx/Class/Fixtures.pm b/lib/DBIx/Class/Fixtures.pm index 870890b..0cf6786 100644 --- a/lib/DBIx/Class/Fixtures.pm +++ b/lib/DBIx/Class/Fixtures.pm @@ -1353,9 +1353,10 @@ sub populate { $fixup_visitor = new Data::Visitor::Callback(%callbacks); } + my @sorted_source_names = $self->_get_sorted_sources( $schema ); $schema->storage->txn_do(sub { $schema->storage->with_deferred_fk_checks(sub { - foreach my $source (sort $schema->sources) { + foreach my $source (@sorted_source_names) { $self->msg("- adding " . $source); my $rs = $schema->resultset($source); my $source_dir = io->catdir($tmp_fixture_dir, $self->_name_for_source($rs->result_source)); @@ -1423,6 +1424,92 @@ sub populate { return 1; } +# the overall logic is modified from SQL::Translator::Parser::DBIx::Class->parse +sub _get_sorted_sources { + my ( $self, $dbicschema ) = @_; + + + my %table_monikers = map { $_ => 1 } $dbicschema->sources; + + my %tables; + foreach my $moniker (sort keys %table_monikers) { + my $source = $dbicschema->source($moniker); + + my $table_name = $source->name; + my @primary = $source->primary_columns; + my @rels = $source->relationships(); + + my %created_FK_rels; + foreach my $rel (sort @rels) { + my $rel_info = $source->relationship_info($rel); + + # Ignore any rel cond that isn't a straight hash + next unless ref $rel_info->{cond} eq 'HASH'; + + my @keys = map {$rel_info->{cond}->{$_} =~ /^\w+\.(\w+)$/} keys(%{$rel_info->{cond}}); + + # determine if this relationship is a self.fk => foreign.pk (i.e. belongs_to) + my $fk_constraint; + if ( exists $rel_info->{attrs}{is_foreign_key_constraint} ) { + $fk_constraint = $rel_info->{attrs}{is_foreign_key_constraint}; + } elsif ( $rel_info->{attrs}{accessor} + && $rel_info->{attrs}{accessor} eq 'multi' ) { + $fk_constraint = 0; + } else { + $fk_constraint = not $source->_compare_relationship_keys(\@keys, \@primary); + } + + # Dont add a relation if its not constraining + next unless $fk_constraint; + + my $rel_table = $source->related_source($rel)->source_name; + # Make sure we don't create the same relation twice + my $key_test = join("\x00", sort @keys); + next if $created_FK_rels{$rel_table}->{$key_test}; + + if (scalar(@keys)) { + $created_FK_rels{$rel_table}->{$key_test} = 1; + + # calculate dependencies: do not consider deferrable constraints and + # self-references for dependency calculations + if (! $rel_info->{attrs}{is_deferrable} and $rel_table ne $table_name) { + $tables{$moniker}{$rel_table}++; + } + } + } + $tables{$moniker} = {} unless exists $tables{$moniker}; + } + + # resolve entire dep tree + my $dependencies = { + map { $_ => _resolve_deps ($_, \%tables) } (keys %tables) + }; + + # return the sorted result + return sort { + keys %{$dependencies->{$a} || {} } <=> keys %{ $dependencies->{$b} || {} } + || + $a cmp $b + } (keys %tables); +} + +sub _resolve_deps { + my ( $question, $answers, $seen ) = @_; + my $ret = {}; + $seen ||= {}; + + my %seen = map { $_ => $seen->{$_} + 1 } ( keys %$seen ); + $seen{$question} = 1; + + for my $dep (keys %{ $answers->{$question} }) { + return {} if $seen->{$dep}; + my $subdeps = _resolve_deps( $dep, $answers, \%seen ); + $ret->{$_} += $subdeps->{$_} for ( keys %$subdeps ); + ++$ret->{$dep}; + } + return $ret; +} + sub do_post_ddl { my ($self, $params) = @_;