line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package DBIx::Class::TopoSort; |
2
|
|
|
|
|
|
|
|
3
|
7
|
|
|
7
|
|
1844094
|
use 5.008_004; |
|
7
|
|
|
|
|
36
|
|
4
|
|
|
|
|
|
|
|
5
|
7
|
|
|
7
|
|
50
|
use strict; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
308
|
|
6
|
7
|
|
|
7
|
|
51
|
use warnings FATAL => 'all'; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
453
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.050010'; |
9
|
|
|
|
|
|
|
|
10
|
7
|
|
|
7
|
|
5400
|
use Graph; |
|
7
|
|
|
|
|
776159
|
|
|
7
|
|
|
|
|
2617
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub toposort_graph { |
13
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
14
|
0
|
|
|
|
|
|
my ($schema, %opts) = @_; |
15
|
|
|
|
|
|
|
|
16
|
0
|
|
|
|
|
|
my $g = Graph->new; |
17
|
|
|
|
|
|
|
|
18
|
0
|
|
|
|
|
|
my @source_names = $schema->sources; |
19
|
|
|
|
|
|
|
|
20
|
0
|
|
|
|
|
|
my %table_sources; |
21
|
0
|
|
|
|
|
|
foreach my $name ( @source_names ) { |
22
|
0
|
|
|
|
|
|
my $table_name = $schema->source($name)->name; |
23
|
0
|
|
0
|
|
|
|
$table_sources{$table_name} //= []; |
24
|
0
|
|
|
|
|
|
push @{ $table_sources{$table_name} }, $name; |
|
0
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
0
|
|
|
|
|
|
foreach my $name ( @source_names ) { |
28
|
0
|
|
|
|
|
|
my $source = $schema->source($name); |
29
|
0
|
|
|
|
|
|
$g->add_vertex($name); |
30
|
|
|
|
|
|
|
|
31
|
0
|
|
|
|
|
|
foreach my $rel_name ( $source->relationships ) { |
32
|
0
|
0
|
|
|
|
|
next if grep { $_ eq $rel_name } @{$opts{skip}{$name}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
33
|
0
|
|
|
|
|
|
my $rel_info = $source->relationship_info($rel_name); |
34
|
|
|
|
|
|
|
|
35
|
0
|
0
|
|
|
|
|
if ( $rel_info->{attrs}{is_foreign_key_constraint} ) { |
36
|
0
|
|
|
|
|
|
my $sources = $table_sources{$schema->source($rel_info->{source})->name}; |
37
|
0
|
|
|
|
|
|
foreach my $source ( @$sources ) { |
38
|
0
|
|
|
|
|
|
$g->add_edge($source, $name); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
} |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
0
|
|
|
|
|
|
return $g; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub toposort { |
48
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
49
|
0
|
|
|
|
|
|
my $schema; |
50
|
0
|
0
|
0
|
|
|
|
if (ref($self) && $self->isa('DBIx::Class::Schema')) { |
51
|
0
|
|
|
|
|
|
$schema = $self; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
else { |
54
|
0
|
|
|
|
|
|
$schema = shift(@_); |
55
|
|
|
|
|
|
|
} |
56
|
0
|
|
|
|
|
|
return $self->toposort_graph($schema, @_)->toposort(); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
1; |
60
|
|
|
|
|
|
|
__END__ |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=head1 NAME |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
DBIx::Class::TopoSort - The addition of topological sorting to DBIx::Class |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 SYNOPSIS |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
Within your schema class: |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
__PACKAGE__->load_components('TopoSort'); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
Later: |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
my $schema = Your::App::Schema->connect(...); |
75
|
|
|
|
|
|
|
my @toposorted_sourcenames = $schema->toposort(); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
If you have a cycle in your relationships |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
my @toposorted_sourcenames = $schema->toposort( |
80
|
|
|
|
|
|
|
skip => { |
81
|
|
|
|
|
|
|
Artist => [qw/ first_album /], |
82
|
|
|
|
|
|
|
}, |
83
|
|
|
|
|
|
|
); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
Alternately: |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
my @toposorted_sourcenames = DBIx::Class::TopoSort->toposort($schema); |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head1 DESCRIPTION |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
This adds a method to L<DBIx::Class::Schema> which returns the full list of |
92
|
|
|
|
|
|
|
sources (similar to L<DBIx::Class::Schema/sources>) in topological-sorted order. |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head2 TOPOLOGICAL SORT |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
A topological sort of the tables returns the list of tables such that any table |
97
|
|
|
|
|
|
|
with a foreign key relationship appears after any table it has a foreign key |
98
|
|
|
|
|
|
|
relationship to. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head1 METHODS |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
This class is not instantiable nor does it provide any methods of its own. All |
103
|
|
|
|
|
|
|
methods are added to the L<DBIx::Class::Schema> class and are callable on |
104
|
|
|
|
|
|
|
objects instantiated of that class. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 toposort |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
This is sugar for: |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
$self->toposort_graph(@_)->toposort(); |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Calling this method multiple times may return the list of source names in |
113
|
|
|
|
|
|
|
different order. Each order will conform to the gurantee described in the |
114
|
|
|
|
|
|
|
section on TOPOLOGICAL SORT. |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
This method will throw an error if there are any cycles in your tables. You will |
117
|
|
|
|
|
|
|
need to specify the skip parameter (described below) to break those cycles. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head2 toposort (Class method) |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
Alternately, if you do not wish to use TopoSort as a component, you can call it |
122
|
|
|
|
|
|
|
as a class method on this class. The toposort() method is smart enough to |
123
|
|
|
|
|
|
|
distinguish. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
Note: toposort_graph() does B<not> distinguish - it assumes it will be called |
126
|
|
|
|
|
|
|
with the C<$schema> object passed in. |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head2 toposort_graph |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
This returns a L<Graph> object with a vertex for every source and an edge for |
131
|
|
|
|
|
|
|
every foreign key relationship. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
It takes the following parameters. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=over 4 |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=item skip |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
This describes the list of relationships that should be ignored by the toposort |
140
|
|
|
|
|
|
|
algorithm. This is generally used if you have cycles in your schema (though it |
141
|
|
|
|
|
|
|
could possibly be useful in other ways, I guess). The value is a hashref. The |
142
|
|
|
|
|
|
|
keys of this hashref are source names and the values are arrays of relationship |
143
|
|
|
|
|
|
|
names. |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
skip => { |
146
|
|
|
|
|
|
|
Artist => [ qw/ first_album / ], |
147
|
|
|
|
|
|
|
}, |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=back |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head1 SEE ALSO |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
L<Graph/toposort> |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head1 AUTHOR |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=over 4 |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item * Rob Kinyon <rob.kinyon@gmail.com> |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=back |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head1 LICENSE |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Copyright (c) 2013 Rob Kinyon. All Rights Reserved. |
166
|
|
|
|
|
|
|
This is free software, you may use it and distribute it under the same terms |
167
|
|
|
|
|
|
|
as Perl itself. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=cut |