line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package # hide from the pauses |
2
|
|
|
|
|
|
|
DBIx::Class::ResultSource::RowParser; |
3
|
|
|
|
|
|
|
|
4
|
312
|
|
|
312
|
|
2319
|
use strict; |
|
312
|
|
|
|
|
761
|
|
|
312
|
|
|
|
|
8561
|
|
5
|
312
|
|
|
312
|
|
1641
|
use warnings; |
|
312
|
|
|
|
|
655
|
|
|
312
|
|
|
|
|
8018
|
|
6
|
|
|
|
|
|
|
|
7
|
312
|
|
|
312
|
|
1650
|
use base 'DBIx::Class'; |
|
312
|
|
|
|
|
728
|
|
|
312
|
|
|
|
|
35513
|
|
8
|
|
|
|
|
|
|
|
9
|
312
|
|
|
|
|
23466
|
use DBIx::Class::ResultSource::RowParser::Util qw( |
10
|
|
|
|
|
|
|
assemble_simple_parser |
11
|
|
|
|
|
|
|
assemble_collapsing_parser |
12
|
312
|
|
|
312
|
|
105397
|
); |
|
312
|
|
|
|
|
1023
|
|
13
|
312
|
|
|
312
|
|
2576
|
use DBIx::Class::_Util qw( DUMMY_ALIASPAIR dbic_internal_try dbic_internal_catch ); |
|
312
|
|
|
|
|
833
|
|
|
312
|
|
|
|
|
15136
|
|
14
|
|
|
|
|
|
|
|
15
|
312
|
|
|
312
|
|
2012
|
use DBIx::Class::Carp; |
|
312
|
|
|
|
|
783
|
|
|
312
|
|
|
|
|
2436
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# FIXME - this should go away |
18
|
|
|
|
|
|
|
# instead Carp::Skip should export usable keywords or something like that |
19
|
|
|
|
|
|
|
my $unique_carper; |
20
|
312
|
|
|
312
|
|
6575
|
BEGIN { $unique_carper = \&carp_unique } |
21
|
|
|
|
|
|
|
|
22
|
312
|
|
|
312
|
|
1973
|
use namespace::clean; |
|
312
|
|
|
|
|
858
|
|
|
312
|
|
|
|
|
2392
|
|
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# Accepts a prefetch map (one or more relationships for the current source), |
25
|
|
|
|
|
|
|
# returns a set of select/as pairs for each of those relationships. Columns |
26
|
|
|
|
|
|
|
# are fully qualified inflation_slot names |
27
|
|
|
|
|
|
|
sub _resolve_selection_from_prefetch { |
28
|
964
|
|
|
964
|
|
2905
|
my ($self, $pre, $alias_map, $pref_path) = @_; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# internal recursion marker |
31
|
964
|
|
100
|
|
|
3490
|
$pref_path ||= []; |
32
|
|
|
|
|
|
|
|
33
|
964
|
100
|
100
|
|
|
8196
|
if (not defined $pre or not length $pre) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
34
|
97
|
|
|
|
|
590
|
return (); |
35
|
|
|
|
|
|
|
} |
36
|
|
|
|
|
|
|
elsif( ref $pre eq 'ARRAY' ) { |
37
|
360
|
|
|
|
|
1095
|
map { $self->_resolve_selection_from_prefetch( $_, $alias_map, [ @$pref_path ] ) } |
|
420
|
|
|
|
|
1883
|
|
38
|
|
|
|
|
|
|
@$pre; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
elsif( ref $pre eq 'HASH' ) { |
41
|
|
|
|
|
|
|
map { |
42
|
97
|
|
|
|
|
421
|
$self->_resolve_selection_from_prefetch($_, $alias_map, [ @$pref_path ] ), |
43
|
|
|
|
|
|
|
$self->related_source($_)->_resolve_selection_from_prefetch( |
44
|
96
|
|
|
|
|
1643
|
$pre->{$_}, $alias_map, [ @$pref_path, $_] ) |
45
|
|
|
|
|
|
|
} keys %$pre; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
elsif( ref $pre ) { |
48
|
0
|
|
|
|
|
0
|
$self->throw_exception( |
49
|
|
|
|
|
|
|
"don't know how to resolve prefetch reftype ".ref($pre)); |
50
|
|
|
|
|
|
|
} |
51
|
|
|
|
|
|
|
else { |
52
|
410
|
|
|
|
|
882
|
my $p = $alias_map; |
53
|
410
|
|
|
|
|
1506
|
$p = $p->{$_} for @$pref_path, $pre; |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$self->throw_exception ( |
56
|
|
|
|
|
|
|
"Unable to resolve prefetch '$pre' - join alias map does not contain an entry for path: " |
57
|
|
|
|
|
|
|
. join (' -> ', @$pref_path, $pre) |
58
|
410
|
50
|
33
|
|
|
1836
|
) if (ref $p->{-join_aliases} ne 'ARRAY' or not @{$p->{-join_aliases}} ); |
|
410
|
|
|
|
|
1550
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# this shift() is critical - it is what allows prefetch => [ (foo) x 2 ] to work |
61
|
410
|
|
|
|
|
821
|
my $src_alias = shift @{$p->{-join_aliases}}; |
|
410
|
|
|
|
|
1088
|
|
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# ordered [select => as] pairs |
64
|
410
|
|
|
|
|
1723
|
map { [ |
65
|
1848
|
|
|
|
|
10305
|
"${src_alias}.$_" => join ( '.', |
66
|
|
|
|
|
|
|
@$pref_path, |
67
|
|
|
|
|
|
|
$pre, |
68
|
|
|
|
|
|
|
$_, |
69
|
|
|
|
|
|
|
) |
70
|
|
|
|
|
|
|
] } $self->related_source($pre)->columns; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
} |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _resolve_prefetch { |
75
|
0
|
|
|
0
|
|
0
|
carp_unique( |
76
|
|
|
|
|
|
|
'There is no good reason to call this internal deprecated method - ' |
77
|
|
|
|
|
|
|
. 'please open a ticket detailing your usage, so that a better plan can ' |
78
|
|
|
|
|
|
|
. 'be devised for your case. In either case _resolve_prefetch() is ' |
79
|
|
|
|
|
|
|
. 'deprecated in favor of _resolve_selection_from_prefetch(), which has ' |
80
|
|
|
|
|
|
|
. 'a greatly simplified arglist.' |
81
|
|
|
|
|
|
|
); |
82
|
|
|
|
|
|
|
|
83
|
0
|
|
|
|
|
0
|
$_[0]->_resolve_selection_from_prefetch( $_[1], $_[3] ); |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# Takes an arrayref of {as} dbic column aliases and the collapse and select |
88
|
|
|
|
|
|
|
# attributes from the same $rs (the selector requirement is a temporary |
89
|
|
|
|
|
|
|
# workaround... I hope), and returns a coderef capable of: |
90
|
|
|
|
|
|
|
# my $me_pref_clps = $coderef->([$rs->cursor->next/all]) |
91
|
|
|
|
|
|
|
# Where the $me_pref_clps arrayref is the future argument to inflate_result() |
92
|
|
|
|
|
|
|
# |
93
|
|
|
|
|
|
|
# For an example of this coderef in action (and to see its guts) look at |
94
|
|
|
|
|
|
|
# t/resultset/rowparser_internals.t |
95
|
|
|
|
|
|
|
# |
96
|
|
|
|
|
|
|
# This is a huge performance win, as we call the same code for every row |
97
|
|
|
|
|
|
|
# returned from the db, thus avoiding repeated method lookups when traversing |
98
|
|
|
|
|
|
|
# relationships |
99
|
|
|
|
|
|
|
# |
100
|
|
|
|
|
|
|
# Also since the coderef is completely stateless (the returned structure is |
101
|
|
|
|
|
|
|
# always fresh on every new invocation) this is a very good opportunity for |
102
|
|
|
|
|
|
|
# memoization if further speed improvements are needed |
103
|
|
|
|
|
|
|
# |
104
|
|
|
|
|
|
|
# The way we construct this coderef is somewhat fugly, although the result is |
105
|
|
|
|
|
|
|
# really worth it. The final coderef does not perform any kind of recursion - |
106
|
|
|
|
|
|
|
# the entire nested structure constructor is rolled out into a single scope. |
107
|
|
|
|
|
|
|
# |
108
|
|
|
|
|
|
|
# In any case - the output of this thing is meticulously micro-tested, so |
109
|
|
|
|
|
|
|
# any sort of adjustment/rewrite should be relatively easy (fsvo relatively) |
110
|
|
|
|
|
|
|
# |
111
|
|
|
|
|
|
|
sub _mk_row_parser { |
112
|
|
|
|
|
|
|
# $args and $attrs are separated to delineate what is core collapser stuff and |
113
|
|
|
|
|
|
|
# what is dbic $rs specific |
114
|
242
|
|
|
242
|
|
979
|
my ($self, $args, $attrs) = @_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
die "HRI without pruning makes zero sense" |
117
|
242
|
50
|
66
|
|
|
1345
|
if ( $args->{hri_style} && ! $args->{prune_null_branches} ); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
my %common = ( |
120
|
|
|
|
|
|
|
hri_style => $args->{hri_style}, |
121
|
|
|
|
|
|
|
prune_null_branches => $args->{prune_null_branches}, |
122
|
|
|
|
|
|
|
val_index => { map |
123
|
2249
|
|
|
|
|
6177
|
{ $args->{inflate_map}[$_] => $_ } |
124
|
242
|
|
|
|
|
1012
|
( 0 .. $#{$args->{inflate_map}} ) |
|
242
|
|
|
|
|
1015
|
|
125
|
|
|
|
|
|
|
}, |
126
|
|
|
|
|
|
|
); |
127
|
|
|
|
|
|
|
|
128
|
242
|
100
|
|
|
|
1458
|
my $src = (! $args->{collapse} ) ? assemble_simple_parser(\%common) : do { |
129
|
|
|
|
|
|
|
my $collapse_map = $self->_resolve_collapse ({ |
130
|
|
|
|
|
|
|
# FIXME |
131
|
|
|
|
|
|
|
# only consider real columns (not functions) during collapse resolution |
132
|
|
|
|
|
|
|
# this check shouldn't really be here, as fucktards are not supposed to |
133
|
|
|
|
|
|
|
# alias random crap to existing column names anyway, but still - just in |
134
|
|
|
|
|
|
|
# case |
135
|
|
|
|
|
|
|
# FIXME !!!! - this does not yet deal with unbalanced selectors correctly |
136
|
|
|
|
|
|
|
# (it is now trivial as the attrs specify where things go out of sync |
137
|
|
|
|
|
|
|
# needs MOAR tests) |
138
|
|
|
|
|
|
|
as => { map |
139
|
1687
|
100
|
|
|
|
5402
|
{ ref $attrs->{select}[$common{val_index}{$_}] ? () : ( $_ => $common{val_index}{$_} ) } |
140
|
174
|
|
|
|
|
854
|
keys %{$common{val_index}} |
141
|
|
|
|
|
|
|
}, |
142
|
|
|
|
|
|
|
premultiplied => $args->{premultiplied}, |
143
|
174
|
|
|
|
|
512
|
}); |
144
|
|
|
|
|
|
|
|
145
|
174
|
|
|
|
|
2802
|
assemble_collapsing_parser({ |
146
|
|
|
|
|
|
|
%common, |
147
|
|
|
|
|
|
|
collapse_map => $collapse_map, |
148
|
|
|
|
|
|
|
}); |
149
|
|
|
|
|
|
|
}; |
150
|
|
|
|
|
|
|
|
151
|
242
|
|
|
|
|
1006
|
utf8::upgrade($src) |
152
|
|
|
|
|
|
|
if DBIx::Class::_ENV_::STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE; |
153
|
|
|
|
|
|
|
|
154
|
242
|
|
|
|
|
2214
|
$src; |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# Takes an arrayref selection list and generates a collapse-map representing |
159
|
|
|
|
|
|
|
# row-object fold-points. Every relationship is assigned a set of unique, |
160
|
|
|
|
|
|
|
# non-nullable columns (which may *not even be* from the same resultset) |
161
|
|
|
|
|
|
|
# and the collapser will use this information to correctly distinguish |
162
|
|
|
|
|
|
|
# data of individual to-be-row-objects. See t/resultset/rowparser_internals.t |
163
|
|
|
|
|
|
|
# for extensive RV examples |
164
|
|
|
|
|
|
|
sub _resolve_collapse { |
165
|
639
|
|
|
639
|
|
2627
|
my ($self, $args, $common_args) = @_; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# for comprehensible error messages put ourselves at the head of the relationship chain |
168
|
639
|
|
100
|
|
|
7917
|
$args->{_rel_chain} ||= [ $self->source_name ]; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# record top-level fully-qualified column index, signify toplevelness |
171
|
639
|
100
|
|
|
|
2902
|
unless ($common_args->{_as_fq_idx}) { |
172
|
178
|
|
|
|
|
427
|
$common_args->{_as_fq_idx} = { %{$args->{as}} }; |
|
178
|
|
|
|
|
1355
|
|
173
|
178
|
|
|
|
|
683
|
$args->{_is_top_level} = 1; |
174
|
|
|
|
|
|
|
}; |
175
|
|
|
|
|
|
|
|
176
|
639
|
|
|
|
|
1875
|
my ($my_cols, $rel_cols, $native_cols); |
177
|
639
|
|
|
|
|
1425
|
for (keys %{$args->{as}}) { |
|
639
|
|
|
|
|
3359
|
|
178
|
3878
|
100
|
|
|
|
11931
|
if ($_ =~ /^ ([^\.]+) \. (.+) /x) { |
179
|
2101
|
|
|
|
|
7120
|
$rel_cols->{$1}{$2} = 1; |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
else { |
182
|
1777
|
|
|
|
|
4711
|
$native_cols->{$_} = $my_cols->{$_} = {}; # important for ||='s below |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
639
|
|
|
|
|
1671
|
my $relinfo; |
187
|
|
|
|
|
|
|
# run through relationships, collect metadata |
188
|
639
|
|
|
|
|
2334
|
for my $rel (keys %$rel_cols) { |
189
|
446
|
|
|
|
|
10420
|
my $inf = $self->relationship_info ($rel); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
$relinfo->{$rel} = { |
192
|
|
|
|
|
|
|
is_single => ( $inf->{attrs}{accessor} && $inf->{attrs}{accessor} ne 'multi' ), |
193
|
|
|
|
|
|
|
is_inner => ( ( $inf->{attrs}{join_type} || '' ) !~ /^left/i), |
194
|
|
|
|
|
|
|
rsrc => $self->related_source($rel), |
195
|
|
|
|
|
|
|
fk_map => ( |
196
|
|
|
|
|
|
|
dbic_internal_try { |
197
|
|
|
|
|
|
|
$self->resolve_relationship_condition( |
198
|
|
|
|
|
|
|
rel_name => $rel, |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# an API where these are optional would be too cumbersome, |
201
|
|
|
|
|
|
|
# instead always pass in some dummy values |
202
|
|
|
|
|
|
|
DUMMY_ALIASPAIR, |
203
|
|
|
|
|
|
|
)->{identity_map}, |
204
|
446
|
|
|
446
|
|
2677
|
} |
205
|
|
|
|
|
|
|
dbic_internal_catch { |
206
|
|
|
|
|
|
|
|
207
|
0
|
|
|
0
|
|
0
|
$unique_carper->( |
208
|
|
|
|
|
|
|
"Resolution of relationship '$rel' failed unexpectedly, " |
209
|
|
|
|
|
|
|
. 'please relay the following error and seek assistance via ' |
210
|
|
|
|
|
|
|
. DBIx::Class::_ENV_::HELP_URL . ". Encountered error: $_" |
211
|
|
|
|
|
|
|
); |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# RV |
214
|
|
|
|
|
|
|
+{} |
215
|
0
|
|
|
|
|
0
|
} |
216
|
446
|
|
66
|
|
|
5729
|
), |
|
|
|
100
|
|
|
|
|
217
|
|
|
|
|
|
|
}; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# inject non-left fk-bridges from *INNER-JOINED* children (if any) |
221
|
639
|
|
|
|
|
2635
|
for my $rel (grep { $relinfo->{$_}{is_inner} } keys %$relinfo) { |
|
446
|
|
|
|
|
1794
|
|
222
|
158
|
|
|
|
|
479
|
my $ri = $relinfo->{$rel}; |
223
|
158
|
|
|
|
|
390
|
for (keys %{$ri->{fk_map}} ) { |
|
158
|
|
|
|
|
667
|
|
224
|
|
|
|
|
|
|
# need to know source from *our* pov, hence $rel.col |
225
|
|
|
|
|
|
|
$my_cols->{$_} ||= { via_fk => "$rel.$ri->{fk_map}{$_}" } |
226
|
158
|
100
|
100
|
|
|
1412
|
if defined $rel_cols->{$rel}{$ri->{fk_map}{$_}} # in fact selected |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# if the parent is already defined *AND* we have an inner reverse relationship |
231
|
|
|
|
|
|
|
# (i.e. do not exist without it) , assume all of its related FKs are selected |
232
|
|
|
|
|
|
|
# (even if they in fact are NOT in the select list). Keep a record of what we |
233
|
|
|
|
|
|
|
# assumed, and if any such phantom-column becomes part of our own collapser, |
234
|
|
|
|
|
|
|
# throw everything assumed-from-parent away and replace with the collapser of |
235
|
|
|
|
|
|
|
# the parent (whatever it may be) |
236
|
639
|
|
|
|
|
1606
|
my $assumed_from_parent; |
237
|
639
|
100
|
100
|
|
|
4140
|
if ( ! $args->{_parent_info}{underdefined} and ! $args->{_parent_info}{rev_rel_is_optional} ) { |
238
|
434
|
100
|
|
|
|
938
|
for my $col ( values %{$args->{_parent_info}{rel_condition} || {}} ) { |
|
434
|
|
|
|
|
2466
|
|
239
|
256
|
100
|
|
|
|
1103
|
next if exists $my_cols->{$col}; |
240
|
66
|
|
|
|
|
210
|
$my_cols->{$col} = {}; |
241
|
66
|
|
|
|
|
283
|
$assumed_from_parent->{columns}{$col}++; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# get colinfo for everything |
246
|
639
|
100
|
|
|
|
2196
|
if ($my_cols) { |
247
|
604
|
|
|
|
|
16360
|
my $ci = $self->columns_info; |
248
|
604
|
|
|
|
|
4934
|
$my_cols->{$_}{colinfo} = $ci->{$_} for keys %$my_cols; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
639
|
|
|
|
|
1633
|
my $collapse_map; |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
# first try to reuse the parent's collapser (i.e. reuse collapser over 1:1) |
254
|
|
|
|
|
|
|
# (makes for a leaner coderef later) |
255
|
639
|
100
|
66
|
|
|
4057
|
if( |
256
|
|
|
|
|
|
|
! $collapse_map->{-identifying_columns} |
257
|
|
|
|
|
|
|
and |
258
|
|
|
|
|
|
|
$args->{_parent_info}{collapser_reusable} |
259
|
|
|
|
|
|
|
) { |
260
|
|
|
|
|
|
|
$collapse_map->{-identifying_columns} = $args->{_parent_info}{collapse_on_idcols} |
261
|
121
|
|
|
|
|
420
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# Still don't know how to collapse - in case we are a *single* relationship |
264
|
|
|
|
|
|
|
# AND our parent is defined AND we have any *native* non-nullable pieces: then |
265
|
|
|
|
|
|
|
# we are still good to go |
266
|
|
|
|
|
|
|
# NOTE: it doesn't matter if the nonnullable set is unique or not - it will be |
267
|
|
|
|
|
|
|
# made unique by the parents identifying cols |
268
|
639
|
100
|
100
|
|
|
3818
|
if( |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
269
|
|
|
|
|
|
|
! $collapse_map->{-identifying_columns} |
270
|
|
|
|
|
|
|
and |
271
|
|
|
|
|
|
|
$args->{_parent_info}{is_single} |
272
|
|
|
|
|
|
|
and |
273
|
44
|
|
|
|
|
335
|
@{ $args->{_parent_info}{collapse_on_idcols} } |
274
|
|
|
|
|
|
|
and |
275
|
|
|
|
|
|
|
( my @native_nonnull_cols = grep { |
276
|
|
|
|
|
|
|
$native_cols->{$_}{colinfo} |
277
|
|
|
|
|
|
|
and |
278
|
|
|
|
|
|
|
! $native_cols->{$_}{colinfo}{is_nullable} |
279
|
72
|
50
|
|
|
|
335
|
} keys %$native_cols ) |
280
|
|
|
|
|
|
|
) { |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
$collapse_map->{-identifying_columns} = [ __unique_numlist( |
283
|
17
|
50
|
|
|
|
75
|
@{ $args->{_parent_info}{collapse_on_idcols}||[] }, |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# FIXME - we don't really need *all* of the columns, $our_nonnull_cols[0] |
286
|
|
|
|
|
|
|
# is sufficient. However map the entire thing to engage the extra nonnull |
287
|
|
|
|
|
|
|
# explicit checks, just to be on the safe side |
288
|
|
|
|
|
|
|
# Remove some day in the future |
289
|
|
|
|
|
|
|
(map |
290
|
|
|
|
|
|
|
{ |
291
|
17
|
|
|
|
|
42
|
$common_args->{_as_fq_idx}{join ('.', |
292
|
52
|
|
|
|
|
96
|
@{$args->{_rel_chain}}[1 .. $#{$args->{_rel_chain}}], |
|
52
|
|
|
|
|
203
|
|
|
52
|
|
|
|
|
98
|
|
293
|
|
|
|
|
|
|
$_, |
294
|
|
|
|
|
|
|
)} |
295
|
|
|
|
|
|
|
} |
296
|
|
|
|
|
|
|
@native_nonnull_cols |
297
|
|
|
|
|
|
|
), |
298
|
|
|
|
|
|
|
)]; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Still don't know how to collapse - try to resolve based on our columns (plus already inserted FK bridges) |
302
|
639
|
100
|
100
|
|
|
4312
|
if ( |
|
|
|
100
|
|
|
|
|
303
|
|
|
|
|
|
|
! $collapse_map->{-identifying_columns} |
304
|
|
|
|
|
|
|
and |
305
|
|
|
|
|
|
|
$my_cols |
306
|
|
|
|
|
|
|
and |
307
|
1538
|
|
|
|
|
5175
|
my $idset = $self->_identifying_column_set ({map { $_ => $my_cols->{$_}{colinfo} } keys %$my_cols}) |
308
|
|
|
|
|
|
|
) { |
309
|
|
|
|
|
|
|
# see if the resulting collapser relies on any implied columns, |
310
|
|
|
|
|
|
|
# and fix stuff up if this is the case |
311
|
444
|
|
|
|
|
1363
|
my @reduced_set = grep { ! $assumed_from_parent->{columns}{$_} } @$idset; |
|
559
|
|
|
|
|
2792
|
|
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
$collapse_map->{-identifying_columns} = [ __unique_numlist( |
314
|
444
|
100
|
|
|
|
2418
|
@{ $args->{_parent_info}{collapse_on_idcols}||[] }, |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
(map |
317
|
|
|
|
|
|
|
{ |
318
|
444
|
|
|
|
|
1112
|
my $fqc = join ('.', |
319
|
523
|
|
|
|
|
3482
|
@{$args->{_rel_chain}}[1 .. $#{$args->{_rel_chain}}], |
|
523
|
|
|
|
|
1545
|
|
320
|
523
|
|
66
|
|
|
1371
|
( $my_cols->{$_}{via_fk} || $_ ), |
321
|
|
|
|
|
|
|
); |
322
|
|
|
|
|
|
|
|
323
|
523
|
|
|
|
|
2697
|
$common_args->{_as_fq_idx}->{$fqc}; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
@reduced_set |
326
|
|
|
|
|
|
|
), |
327
|
|
|
|
|
|
|
)]; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# Stil don't know how to collapse - keep descending down 1:1 chains - if |
331
|
|
|
|
|
|
|
# a related non-LEFT 1:1 is resolvable - its condition will collapse us |
332
|
|
|
|
|
|
|
# too |
333
|
639
|
100
|
|
|
|
3132
|
unless ($collapse_map->{-identifying_columns}) { |
334
|
57
|
|
|
|
|
140
|
my @candidates; |
335
|
|
|
|
|
|
|
|
336
|
57
|
|
|
|
|
199
|
for my $rel (keys %$relinfo) { |
337
|
74
|
100
|
100
|
|
|
403
|
next unless ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner}); |
338
|
|
|
|
|
|
|
|
339
|
40
|
50
|
|
|
|
118
|
if ( my $rel_collapse = $relinfo->{$rel}{rsrc}->_resolve_collapse ({ |
340
|
|
|
|
|
|
|
as => $rel_cols->{$rel}, |
341
|
40
|
|
|
|
|
368
|
_rel_chain => [ @{$args->{_rel_chain}}, $rel ], |
342
|
|
|
|
|
|
|
_parent_info => { underdefined => 1 }, |
343
|
|
|
|
|
|
|
}, $common_args)) { |
344
|
40
|
|
|
|
|
171
|
push @candidates, $rel_collapse->{-identifying_columns}; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# get the set with least amount of columns |
349
|
|
|
|
|
|
|
# FIXME - maybe need to implement a data type order as well (i.e. prefer several ints |
350
|
|
|
|
|
|
|
# to a single varchar) |
351
|
57
|
100
|
|
|
|
218
|
if (@candidates) { |
352
|
40
|
|
|
|
|
117
|
($collapse_map->{-identifying_columns}) = sort { scalar @$a <=> scalar @$b } (@candidates); |
|
0
|
|
|
|
|
0
|
|
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# Stil don't know how to collapse, and we are the root node. Last ditch |
357
|
|
|
|
|
|
|
# effort in case we are *NOT* premultiplied. |
358
|
|
|
|
|
|
|
# Run through *each multi* all the way down, left or not, and all |
359
|
|
|
|
|
|
|
# *left* singles (a single may become a multi underneath) . When everything |
360
|
|
|
|
|
|
|
# gets back see if all the rels link to us definitively. If this is the |
361
|
|
|
|
|
|
|
# case we are good - either one of them will define us, or if all are NULLs |
362
|
|
|
|
|
|
|
# we know we are "unique" due to the "non-premultiplied" check |
363
|
639
|
50
|
66
|
|
|
2952
|
if ( |
|
|
|
66
|
|
|
|
|
364
|
|
|
|
|
|
|
! $collapse_map->{-identifying_columns} |
365
|
|
|
|
|
|
|
and |
366
|
|
|
|
|
|
|
! $args->{premultiplied} |
367
|
|
|
|
|
|
|
and |
368
|
|
|
|
|
|
|
$args->{_is_top_level} |
369
|
|
|
|
|
|
|
) { |
370
|
17
|
|
|
|
|
58
|
my (@collapse_sets, $uncollapsible_chain); |
371
|
|
|
|
|
|
|
|
372
|
17
|
|
|
|
|
65
|
for my $rel (keys %$relinfo) { |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# we already looked at these higher up |
375
|
27
|
50
|
66
|
|
|
144
|
next if ($relinfo->{$rel}{is_single} && $relinfo->{$rel}{is_inner}); |
376
|
|
|
|
|
|
|
|
377
|
27
|
50
|
|
|
|
94
|
if (my $clps = $relinfo->{$rel}{rsrc}->_resolve_collapse ({ |
378
|
|
|
|
|
|
|
as => $rel_cols->{$rel}, |
379
|
27
|
|
|
|
|
237
|
_rel_chain => [ @{$args->{_rel_chain}}, $rel ], |
380
|
|
|
|
|
|
|
_parent_info => { underdefined => 1 }, |
381
|
|
|
|
|
|
|
}, $common_args) ) { |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# for singles use the idcols wholesale (either there or not) |
384
|
27
|
100
|
|
|
|
138
|
if ($relinfo->{$rel}{is_single}) { |
|
|
50
|
|
|
|
|
|
385
|
10
|
|
|
|
|
63
|
push @collapse_sets, $clps->{-identifying_columns}; |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
elsif (! $relinfo->{$rel}{fk_map}) { |
388
|
0
|
|
|
|
|
0
|
$uncollapsible_chain = 1; |
389
|
0
|
|
|
|
|
0
|
last; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
else { |
392
|
17
|
|
|
|
|
51
|
my $defined_cols_parent_side; |
393
|
|
|
|
|
|
|
|
394
|
17
|
|
|
|
|
44
|
for my $fq_col ( grep { /^$rel\.[^\.]+$/ } keys %{$args->{as}} ) { |
|
117
|
|
|
|
|
689
|
|
|
17
|
|
|
|
|
92
|
|
395
|
44
|
|
|
|
|
261
|
my ($col) = $fq_col =~ /([^\.]+)$/; |
396
|
|
|
|
|
|
|
|
397
|
44
|
|
|
|
|
105
|
$defined_cols_parent_side->{$_} = $args->{as}{$fq_col} for grep |
398
|
44
|
|
|
|
|
224
|
{ $relinfo->{$rel}{fk_map}{$_} eq $col } |
399
|
44
|
|
|
|
|
153
|
keys %{$relinfo->{$rel}{fk_map}} |
400
|
|
|
|
|
|
|
; |
401
|
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
17
|
50
|
|
|
|
122
|
if (my $set = $self->_identifying_column_set([ keys %$defined_cols_parent_side ]) ) { |
404
|
17
|
|
|
|
|
69
|
push @collapse_sets, [ sort map { $defined_cols_parent_side->{$_} } @$set ]; |
|
17
|
|
|
|
|
168
|
|
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
else { |
407
|
0
|
|
|
|
|
0
|
$uncollapsible_chain = 1; |
408
|
0
|
|
|
|
|
0
|
last; |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
else { |
413
|
0
|
|
|
|
|
0
|
$uncollapsible_chain = 1; |
414
|
0
|
|
|
|
|
0
|
last; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
17
|
50
|
|
|
|
74
|
unless ($uncollapsible_chain) { |
419
|
|
|
|
|
|
|
# if we got here - we are good to go, but the construction is tricky |
420
|
|
|
|
|
|
|
# since our children will want to include our collapse criteria - we |
421
|
|
|
|
|
|
|
# don't give them anything (safe, since they are all collapsible on their own) |
422
|
|
|
|
|
|
|
# in addition we record the individual collapse possibilities |
423
|
|
|
|
|
|
|
# of all left children node collapsers, and merge them in the rowparser |
424
|
|
|
|
|
|
|
# coderef later |
425
|
17
|
|
|
|
|
52
|
$collapse_map->{-identifying_columns} = []; |
426
|
|
|
|
|
|
|
$collapse_map->{-identifying_columns_variants} = [ sort { |
427
|
17
|
|
|
|
|
80
|
(scalar @$a) <=> (scalar @$b) |
428
|
|
|
|
|
|
|
or |
429
|
|
|
|
|
|
|
( |
430
|
|
|
|
|
|
|
# Poor man's max() |
431
|
0
|
|
|
|
|
0
|
( sort { $b <=> $a } @$a )[0] |
432
|
|
|
|
|
|
|
<=> |
433
|
10
|
0
|
|
|
|
106
|
( sort { $b <=> $a } @$b )[0] |
|
0
|
|
|
|
|
0
|
|
434
|
|
|
|
|
|
|
) |
435
|
|
|
|
|
|
|
} @collapse_sets ]; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
# stop descending into children if we were called by a parent for first-pass |
440
|
|
|
|
|
|
|
# and don't despair if nothing was found (there may be other parallel branches |
441
|
|
|
|
|
|
|
# to dive into) |
442
|
639
|
100
|
|
|
|
3016
|
if ($args->{_parent_info}{underdefined}) { |
|
|
50
|
|
|
|
|
|
443
|
67
|
50
|
|
|
|
584
|
return $collapse_map->{-identifying_columns} ? $collapse_map : undef |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
# nothing down the chain resolved - can't calculate a collapse-map |
446
|
|
|
|
|
|
|
elsif (! $collapse_map->{-identifying_columns}) { |
447
|
|
|
|
|
|
|
$self->throw_exception ( sprintf |
448
|
|
|
|
|
|
|
"Unable to calculate a definitive collapse column set for %s%s: fetch more unique non-nullable columns", |
449
|
|
|
|
|
|
|
$self->source_name, |
450
|
0
|
|
|
|
|
0
|
@{$args->{_rel_chain}} > 1 |
451
|
0
|
0
|
|
|
|
0
|
? sprintf (' (last member of the %s chain)', join ' -> ', @{$args->{_rel_chain}} ) |
|
0
|
|
|
|
|
0
|
|
452
|
|
|
|
|
|
|
: '' |
453
|
|
|
|
|
|
|
, |
454
|
|
|
|
|
|
|
); |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# If we got that far - we are collapsable - GREAT! Now go down all children |
458
|
|
|
|
|
|
|
# a second time, and fill in the rest |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
$collapse_map->{-identifying_columns} = [ __unique_numlist( |
461
|
572
|
100
|
|
|
|
2548
|
@{ $args->{_parent_info}{collapse_on_idcols}||[] }, |
462
|
572
|
|
|
|
|
1239
|
@{ $collapse_map->{-identifying_columns} }, |
|
572
|
|
|
|
|
2075
|
|
463
|
|
|
|
|
|
|
)]; |
464
|
|
|
|
|
|
|
|
465
|
572
|
|
|
|
|
2671
|
for my $rel (sort keys %$relinfo) { |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
$collapse_map->{$rel} = $relinfo->{$rel}{rsrc}->_resolve_collapse ({ |
468
|
1913
|
|
|
|
|
4206
|
as => { map { $_ => 1 } ( keys %{$rel_cols->{$rel}} ) }, |
|
394
|
|
|
|
|
1631
|
|
469
|
394
|
|
|
|
|
1595
|
_rel_chain => [ @{$args->{_rel_chain}}, $rel], |
470
|
|
|
|
|
|
|
_parent_info => { |
471
|
|
|
|
|
|
|
# shallow copy |
472
|
394
|
|
|
|
|
1857
|
collapse_on_idcols => [ @{$collapse_map->{-identifying_columns}} ], |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
rel_condition => $relinfo->{$rel}{fk_map}, |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
is_optional => ! $relinfo->{$rel}{is_inner}, |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
is_single => $relinfo->{$rel}{is_single}, |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# if there is at least one *inner* reverse relationship ( meaning identity-only ) |
481
|
|
|
|
|
|
|
# we can safely assume that the child can not exist without us |
482
|
|
|
|
|
|
|
rev_rel_is_optional => ( |
483
|
|
|
|
|
|
|
( grep { |
484
|
804
|
|
100
|
|
|
10998
|
($_->{attrs}{join_type}||'') !~ /^left/i |
485
|
394
|
|
|
|
|
2051
|
} values %{ $self->reverse_relationship_info($rel) } ) |
486
|
|
|
|
|
|
|
? 0 |
487
|
|
|
|
|
|
|
: 1 |
488
|
|
|
|
|
|
|
), |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
# if this is a 1:1 our own collapser can be used as a collapse-map |
491
|
|
|
|
|
|
|
# (regardless of left or not) |
492
|
|
|
|
|
|
|
collapser_reusable => ( |
493
|
|
|
|
|
|
|
$relinfo->{$rel}{is_single} |
494
|
|
|
|
|
|
|
&& |
495
|
|
|
|
|
|
|
$relinfo->{$rel}{is_inner} |
496
|
|
|
|
|
|
|
&& |
497
|
394
|
100
|
66
|
|
|
1289
|
@{$collapse_map->{-identifying_columns}} |
|
|
100
|
|
|
|
|
|
498
|
|
|
|
|
|
|
) ? 1 : 0, |
499
|
|
|
|
|
|
|
}, |
500
|
|
|
|
|
|
|
}, $common_args ); |
501
|
|
|
|
|
|
|
|
502
|
394
|
100
|
|
|
|
3865
|
$collapse_map->{$rel}{-is_single} = 1 if $relinfo->{$rel}{is_single}; |
503
|
394
|
100
|
50
|
|
|
2193
|
$collapse_map->{$rel}{-is_optional} ||= 1 unless $relinfo->{$rel}{is_inner}; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
572
|
|
|
|
|
5114
|
return $collapse_map; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
# adding a dep on MoreUtils *just* for this is retarded |
510
|
|
|
|
|
|
|
sub __unique_numlist { |
511
|
1033
|
|
|
1033
|
|
1905
|
sort { $a <=> $b } keys %{ {map { $_ => 1 } @_ }} |
|
1522
|
|
|
|
|
5413
|
|
|
1033
|
|
|
|
|
2343
|
|
|
2680
|
|
|
|
|
10498
|
|
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
1; |